Currently, options for bgp/application.py is not passed to 'ryu-manager',
bgp/application.py does only start RPC server and can not start other
threads including BGP core and SSH server using bgp_sample_conf.py.

This patch enables bgp/application.py to start BGP threads using
the specified configuration file and reconstructs configuration file
format.

With this patch, BGPSpaker application can be started like:
$ ryu-manager --bgp-app-config-file 
ryu/services/protocols/bgp/bgp_sample_conf.py
              ryu/services/protocols/bgp/application.py

Signed-off-by: IWASE Yusuke <[email protected]>
---
 ryu/services/protocols/bgp/application.py     | 304 +++++++++++++-------------
 ryu/services/protocols/bgp/bgp_sample_conf.py | 120 +++++++---
 2 files changed, 232 insertions(+), 192 deletions(-)

diff --git a/ryu/services/protocols/bgp/application.py 
b/ryu/services/protocols/bgp/application.py
index a73e83e..c8d0f6a 100644
--- a/ryu/services/protocols/bgp/application.py
+++ b/ryu/services/protocols/bgp/application.py
@@ -12,221 +12,213 @@
 # implied.
 # See the License for the specific language governing permissions and
 # limitations under the License.
+
 """
   Defines bases classes to create a BGP application.
 """
-import logging
-from logging.config import dictConfig
-import traceback
 
-from oslo_config import cfg
+import logging
+import os
 
+from ryu import cfg
 from ryu.lib import hub
 from ryu.utils import load_source
 from ryu.base.app_manager import RyuApp
-from ryu.services.protocols.bgp.api.base import call
 from ryu.services.protocols.bgp.base import add_bgp_error_metadata
 from ryu.services.protocols.bgp.base import BGPSException
 from ryu.services.protocols.bgp.base import BIN_ERROR
-from ryu.services.protocols.bgp.core_manager import CORE_MANAGER
-from ryu.services.protocols.bgp import net_ctrl
+from ryu.services.protocols.bgp.bgpspeaker import BGPSpeaker
+from ryu.services.protocols.bgp.net_ctrl import NET_CONTROLLER
+from ryu.services.protocols.bgp.net_ctrl import NC_RPC_BIND_IP
+from ryu.services.protocols.bgp.net_ctrl import NC_RPC_BIND_PORT
+from ryu.services.protocols.bgp.operator.ssh import SSH_CLI_CONTROLLER
 from ryu.services.protocols.bgp.rtconf.base import RuntimeConfigError
 from ryu.services.protocols.bgp.rtconf.common import BGP_SERVER_PORT
 from ryu.services.protocols.bgp.rtconf.common import DEFAULT_BGP_SERVER_PORT
-from ryu.services.protocols.bgp.rtconf.common import \
-    DEFAULT_REFRESH_MAX_EOR_TIME
-from ryu.services.protocols.bgp.rtconf.common import \
-    DEFAULT_REFRESH_STALEPATH_TIME
+from ryu.services.protocols.bgp.rtconf.common import (
+    DEFAULT_REFRESH_MAX_EOR_TIME, DEFAULT_REFRESH_STALEPATH_TIME)
 from ryu.services.protocols.bgp.rtconf.common import DEFAULT_LABEL_RANGE
 from ryu.services.protocols.bgp.rtconf.common import LABEL_RANGE
 from ryu.services.protocols.bgp.rtconf.common import LOCAL_AS
 from ryu.services.protocols.bgp.rtconf.common import REFRESH_MAX_EOR_TIME
 from ryu.services.protocols.bgp.rtconf.common import REFRESH_STALEPATH_TIME
 from ryu.services.protocols.bgp.rtconf.common import ROUTER_ID
-from ryu.services.protocols.bgp.rtconf import neighbors
-from ryu.services.protocols.bgp.rtconf import vrfs
 from ryu.services.protocols.bgp.utils.validation import is_valid_ipv4
-from ryu.services.protocols.bgp.operator import ssh
+from ryu.services.protocols.bgp.utils.validation import is_valid_ipv6
 
 
 LOG = logging.getLogger('bgpspeaker.application')
-CONF = cfg.CONF
 
-CONF.register_opts([
-    cfg.IntOpt('bind-port', default=50002, help='rpc-port'),
-    cfg.StrOpt('bind-ip', default='0.0.0.0', help='rpc-bind-ip'),
-    cfg.StrOpt('bgp-config-file', default=None,
-               help='bgp-config-file')
-])
+CONF = cfg.CONF['bgp-app']
 
 
 @add_bgp_error_metadata(code=BIN_ERROR,
                         sub_code=1,
                         def_desc='Unknown bootstrap exception.')
 class ApplicationException(BGPSException):
-    """Specific Base exception related to `BSPSpeaker`."""
+    """
+    Specific Base exception related to `BSPSpeaker`.
+    """
     pass
 
 
-class RyuBGPSpeaker(RyuApp):
-    def __init__(self, *args, **kwargs):
-        self.bind_ip = RyuBGPSpeaker.validate_rpc_ip(CONF.bind_ip)
-        self.bind_port = RyuBGPSpeaker.validate_rpc_port(CONF.bind_port)
-        self.config_file = CONF.bgp_config_file
-        super(RyuBGPSpeaker, self).__init__(*args, **kwargs)
+def validate_rpc_host(ip):
+    """
+    Validates the given ip for use as RPC server address.
+    """
+    if not is_valid_ipv4(ip) and not is_valid_ipv6(ip):
+        raise ApplicationException(
+            desc='Invalid RPC ip address: %s' % ip)
+    return ip
 
-    def start(self):
-        # Only two main green threads are required for APGW bgp-agent.
-        # One for NetworkController, another for BGPS core.
 
-        # If configuration file was provided and loaded successfully. We start
-        # BGPS core using these settings. If no configuration file is provided
-        # or if configuration file is missing minimum required settings BGPS
-        # core is not started.
-        if self.config_file:
-            LOG.debug('Loading config. from settings file.')
-            settings = self.load_config(self.config_file)
-            # Configure log settings, if available.
-            if getattr(settings, 'LOGGING', None):
-                dictConfig(settings.LOGGING)
-
-            if getattr(settings, 'BGP', None):
-                self._start_core(settings)
-
-            if getattr(settings, 'SSH', None) is not None:
-                hub.spawn(ssh.SSH_CLI_CONTROLLER.start, None, **settings.SSH)
-        # Start Network Controller to server RPC peers.
-        t = hub.spawn(net_ctrl.NET_CONTROLLER.start, *[],
-                      **{net_ctrl.NC_RPC_BIND_IP: self.bind_ip,
-                      net_ctrl.NC_RPC_BIND_PORT: self.bind_port})
-        LOG.debug('Started Network Controller')
+def load_config(config_file):
+    """
+    Validates the given file for use as the settings file for BGPSpeaker
+    and loads the configuration from the given file as a module instance.
+    """
+    if not config_file or not os.path.isfile(config_file):
+        raise ApplicationException(
+            desc='Invalid configuration file: %s' % config_file)
 
-        super(RyuBGPSpeaker, self).start()
+    # Loads the configuration from the given file, if available.
+    try:
+        return load_source('bgpspeaker.application.settings', config_file)
+    except Exception as e:
+        raise ApplicationException(desc=str(e))
 
-        return t
 
-    @classmethod
-    def validate_rpc_ip(cls, ip):
-        """Validates given ip for use as rpc host bind address.
-        """
-        if not is_valid_ipv4(ip):
-            raise ApplicationException(desc='Invalid rpc ip address.')
-        return ip
+class RyuBGPSpeaker(RyuApp):
 
-    @classmethod
-    def validate_rpc_port(cls, port):
-        """Validates give port for use as rpc server port.
-        """
-        if not port:
-            raise ApplicationException(desc='Invalid rpc port number.')
-        if isinstance(port, str):
-            port = int(port)
+    def __init__(self, *args, **kwargs):
+        super(RyuBGPSpeaker, self).__init__(*args, **kwargs)
+        self.config_file = CONF.config_file
 
-        return port
+        # BGPSpeaker instance (not instantiated yet)
+        self.speaker = None
 
-    def load_config(self, config_file):
-        """Validates give file as settings file for BGPSpeaker.
+    def start(self):
+        super(RyuBGPSpeaker, self).start()
 
-        Load the configuration from file as settings module.
+        # If configuration file was provided and loaded successfully, we start
+        # BGPSpeaker using the given settings.
+        # If no configuration file is provided or if any minimum required
+        # setting is missing, BGPSpeaker will not be started.
+        if self.config_file:
+            LOG.debug('Loading config file %s...', self.config_file)
+            settings = load_config(self.config_file)
+
+            # Configure logging settings, if available.
+            if hasattr(settings, 'LOGGING'):
+                # Not implemented yet.
+                LOG.debug('Loading LOGGING settings... (NOT implemented yet)')
+                # from logging.config import dictConfig
+                # logging_settings = dictConfig(settings.LOGGING)
+
+            # Configure BGP settings, if available.
+            if hasattr(settings, 'BGP'):
+                LOG.debug('Loading BGP settings...')
+                self._start_speaker(settings.BGP)
+
+            # Configure SSH settings, if available.
+            if hasattr(settings, 'SSH'):
+                LOG.debug('Loading SSH settings...')
+                hub.spawn(SSH_CLI_CONTROLLER.start, **settings.SSH)
+
+        # Start RPC server with the given RPC settings.
+        rpc_settings = {
+            NC_RPC_BIND_PORT: CONF.rpc_port,
+            NC_RPC_BIND_IP: validate_rpc_host(CONF.rpc_host),
+        }
+        return hub.spawn(NET_CONTROLLER.start, **rpc_settings)
+
+    def _start_speaker(self, settings):
         """
-        if not config_file or not isinstance(config_file, str):
-            raise ApplicationException('Invalid configuration file.')
-
-        # Check if file can be read
-        try:
-            return load_source('settings', config_file)
-        except Exception as e:
-            raise ApplicationException(desc=str(e))
-
-    def _start_core(self, settings):
-        """Starts BGPS core using setting and given pool.
+        Starts BGPSpeaker using the given settings.
         """
-        # Get common settings
-        routing_settings = settings.BGP.get('routing')
-        common_settings = {}
+        # Settings for starting BGPSpeaker
+        bgp_settings = {}
 
-        # Get required common settings.
+        # Get required settings.
         try:
-            common_settings[LOCAL_AS] = routing_settings.pop(LOCAL_AS)
-            common_settings[ROUTER_ID] = routing_settings.pop(ROUTER_ID)
+            bgp_settings['as_number'] = settings.get(LOCAL_AS)
+            bgp_settings['router_id'] = settings.get(ROUTER_ID)
         except KeyError as e:
             raise ApplicationException(
-                desc='Required minimum configuration missing %s' %
-                     e)
-
-        # Get optional common settings
-        common_settings[BGP_SERVER_PORT] = \
-            routing_settings.get(BGP_SERVER_PORT, DEFAULT_BGP_SERVER_PORT)
-        common_settings[REFRESH_STALEPATH_TIME] = \
-            routing_settings.get(REFRESH_STALEPATH_TIME,
-                                 DEFAULT_REFRESH_STALEPATH_TIME)
-        common_settings[REFRESH_MAX_EOR_TIME] = \
-            routing_settings.get(REFRESH_MAX_EOR_TIME,
-                                 DEFAULT_REFRESH_MAX_EOR_TIME)
-        common_settings[LABEL_RANGE] = \
-            routing_settings.get(LABEL_RANGE, DEFAULT_LABEL_RANGE)
-
-        # Start BGPS core service
-        waiter = hub.Event()
-        call('core.start', waiter=waiter, **common_settings)
-        waiter.wait()
-
-        LOG.debug('Core started %s', CORE_MANAGER.started)
-        # Core manager started add configured neighbor and vrfs
-        if CORE_MANAGER.started:
-            # Add neighbors.
-            self._add_neighbors(routing_settings)
-
-            # Add Vrfs.
-            self._add_vrfs(routing_settings)
-
-            # Add Networks
-            self._add_networks(routing_settings)
-
-    def _add_neighbors(self, routing_settings):
-        """Add bgp peers/neighbors from given settings to BGPS runtime.
-
-        All valid neighbors are loaded. Miss-configured neighbors are ignored
-        and error is logged.
+                desc='Required BGP configuration missing: %s' % e)
+
+        # Get optional settings.
+        bgp_settings[BGP_SERVER_PORT] = settings.get(
+            BGP_SERVER_PORT, DEFAULT_BGP_SERVER_PORT)
+        bgp_settings[REFRESH_STALEPATH_TIME] = settings.get(
+            REFRESH_STALEPATH_TIME, DEFAULT_REFRESH_STALEPATH_TIME)
+        bgp_settings[REFRESH_MAX_EOR_TIME] = settings.get(
+            REFRESH_MAX_EOR_TIME, DEFAULT_REFRESH_MAX_EOR_TIME)
+        bgp_settings[LABEL_RANGE] = settings.get(
+            LABEL_RANGE, DEFAULT_LABEL_RANGE)
+
+        # Create BGPSpeaker instance.
+        LOG.debug('Starting BGPSpeaker...')
+        self.speaker = BGPSpeaker(**bgp_settings)
+
+        # Add neighbors.
+        LOG.debug('Adding neighbors...')
+        self._add_neighbors(settings.get('neighbors', []))
+
+        # Add VRFs.
+        LOG.debug('Adding VRFs...')
+        self._add_vrfs(settings.get('vrfs', []))
+
+        # Add Networks
+        LOG.debug('Adding routes...')
+        self._add_routes(settings.get('routes', []))
+
+    def _add_neighbors(self, settings):
+        """
+        Add BGP neighbors from the given settings.
+
+        All valid neighbors are loaded.
+        Miss-configured neighbors are ignored and errors are logged.
         """
-        bgp_neighbors = routing_settings.setdefault('bgp_neighbors', {})
-        for ip, bgp_neighbor in bgp_neighbors.items():
+        for neighbor_settings in settings:
+            LOG.debug('Adding neighbor settings: %s', neighbor_settings)
             try:
-                bgp_neighbor[neighbors.IP_ADDRESS] = ip
-                call('neighbor.create', **bgp_neighbor)
-                LOG.debug('Added neighbor %s', ip)
-            except RuntimeConfigError as re:
-                LOG.error(re)
-                LOG.error(traceback.format_exc())
-                continue
+                self.speaker.neighbor_add(**neighbor_settings)
+            except RuntimeConfigError as e:
+                LOG.exception(e)
 
-    def _add_vrfs(self, routing_settings):
-        """Add VRFs from given settings to BGPS runtime.
+    def _add_vrfs(self, settings):
+        """
+        Add BGP VRFs from the given settings.
 
-        If any of the VRFs are miss-configured errors are logged.
         All valid VRFs are loaded.
+        Miss-configured VRFs are ignored and errors are logged.
         """
-        vpns_conf = routing_settings.setdefault('vpns', {})
-        for vrfname, vrf in vpns_conf.items():
+        for vrf_settings in settings:
+            LOG.debug('Adding VRF settings: %s', vrf_settings)
             try:
-                vrf[vrfs.VRF_NAME] = vrfname
-                call('vrf.create', **vrf)
-                LOG.debug('Added vrf  %s', vrf)
+                self.speaker.vrf_add(**vrf_settings)
             except RuntimeConfigError as e:
-                LOG.error(e)
-                continue
+                LOG.exception(e)
 
-    def _add_networks(self, routing_settings):
-        """Add networks from given settings to BGPS runtime.
+    def _add_routes(self, settings):
+        """
+        Add BGP routes from given settings.
 
-        If any of the networks are miss-configured errors are logged.
-        All valid networks are loaded.
+        All valid routes are loaded.
+        Miss-configured routes are ignored and errors are logged.
         """
-        networks = routing_settings.setdefault('networks', [])
-        for prefix in networks:
+        for route_settings in settings:
+            if 'prefix' in route_settings:
+                prefix_add = self.speaker.prefix_add
+            elif 'route_type' in route_settings:
+                prefix_add = self.speaker.evpn_prefix_add
+            else:
+                LOG.debug('Skip invalid route settings: %s', route_settings)
+                continue
+
+            LOG.debug('Adding route settings: %s', route_settings)
             try:
-                call('network.add', prefix=prefix)
-                LOG.debug('Added network %s', prefix)
+                prefix_add(**route_settings)
             except RuntimeConfigError as e:
-                LOG.error(e)
-                continue
+                LOG.exception(e)
diff --git a/ryu/services/protocols/bgp/bgp_sample_conf.py 
b/ryu/services/protocols/bgp/bgp_sample_conf.py
index b380156..4c40fef 100644
--- a/ryu/services/protocols/bgp/bgp_sample_conf.py
+++ b/ryu/services/protocols/bgp/bgp_sample_conf.py
@@ -1,49 +1,98 @@
 import os
 
+from ryu.services.protocols.bgp.bgpspeaker import RF_VPN_V4
+from ryu.services.protocols.bgp.bgpspeaker import RF_L2_EVPN
+from ryu.services.protocols.bgp.bgpspeaker import EVPN_MAC_IP_ADV_ROUTE
+from ryu.services.protocols.bgp.bgpspeaker import TUNNEL_TYPE_VXLAN
+
+
 # =============================================================================
 # BGP configuration.
 # =============================================================================
 BGP = {
 
-    # General BGP configuration.
-    'routing': {
-        # ASN for this BGP instance.
-        'local_as': 64512,
-
-        # BGP Router ID.
-        'router_id': '10.10.0.1',
-
-        # We list all BGP neighbors below. We establish EBGP sessions with peer
-        # with different AS number then configured above. We will
-        # establish IBGP session if AS number is same.
-        'bgp_neighbors': {
-            '10.0.0.1': {
-                'remote_as': 64513,
-                'multi_exit_disc': 100
-            },
-            '10.10.0.2': {
-                'remote_as': 64514,
-            },
-        },
-
-        'networks': [
-            '10.20.0.0/24',
-            '10.30.0.0/24',
-            '10.40.0.0/16',
-            '10.50.0.0/16',
-        ],
-    },
+    # AS number for this BGP instance.
+    'local_as': 65001,
 
+    # BGP Router ID.
+    'router_id': '172.17.0.1',
+
+    # List of BGP neighbors.
+    # The parameters for each neighbor are the same as the arguments of
+    # BGPSpeaker.neighbor_add() method.
+    'neighbors': [
+        {
+            'address': '172.17.0.2',
+            'remote_as': 65002,
+            'enable_ipv4': True,
+            'enable_vpnv4': True,
+        },
+        {
+            'address': '172.17.0.3',
+            'remote_as': 65000,
+            'enable_evpn': True,
+        },
+    ],
+
+    # List of BGP VRF tables.
+    # The parameters for each VRF table are the same as the arguments of
+    # BGPSpeaker.vrf_add() method.
+    'vrfs': [
+        {
+            'route_dist': '65001:100',
+            'import_rts': ['65001:100'],
+            'export_rts': ['65001:100'],
+            'route_family': RF_VPN_V4,
+        },
+        {
+            'route_dist': '65000:200',
+            'import_rts': ['65000:200'],
+            'export_rts': ['65000:200'],
+            'route_family': RF_L2_EVPN,
+        },
+    ],
+
+    # List of BGP routes.
+    # The parameters for each route are the same as the arguments of
+    # BGPSpeaker.prefix_add() or BGPSpeaker.evpn_prefix_add() method.
+    'routes': [
+        # Example of IPv4 prefix
+        {
+            'prefix': '10.10.1.0/24',
+        },
+        # Example of VPNv4 prefix
+        {
+            'prefix': '10.20.1.0/24',
+            'next_hop': '172.17.0.1',
+            'route_dist': '65001:100',
+        },
+        # Example of EVPN prefix
+        {
+            'route_type': EVPN_MAC_IP_ADV_ROUTE,
+            'route_dist': '65000:200',
+            'esi': 0,
+            'ethernet_tag_id': 0,
+            'tunnel_type': TUNNEL_TYPE_VXLAN,
+            'vni': 200,
+            'mac_addr': 'aa:bb:cc:dd:ee:ff',
+            'ip_addr': '10.30.1.1',
+            'next_hop': '172.17.0.1',
+        },
+    ],
 }
 
 
-# SSH = {
-#     'ssh_port': 4990,
-#     'ssh_host': 'localhost',
-#     'ssh_hostkey': '/etc/ssh_host_rsa_key',
-#     'ssh_username': 'ryu',
-#     'ssh_password': 'ryu'
-# }
+# =============================================================================
+# SSH server configuration.
+# =============================================================================
+SSH = {
+    'ssh_port': 4990,
+    'ssh_host': 'localhost',
+    # 'ssh_host_key': '/etc/ssh_host_rsa_key',
+    # 'ssh_username': 'ryu',
+    # 'ssh_password': 'ryu',
+}
+
 
 # =============================================================================
 # Logging configuration.
@@ -101,7 +150,6 @@ LOGGING = {
     'loggers': {
         'bgpspeaker': {
             'handlers': ['console', 'log_file'],
-            'handlers': ['console'],
             'level': 'DEBUG',
             'propagate': False,
         },
-- 
2.7.4


------------------------------------------------------------------------------
Check out the vibrant tech community on one of the world's most 
engaging tech sites, SlashDot.org! http://sdm.link/slashdot
_______________________________________________
Ryu-devel mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/ryu-devel

Reply via email to