Hi Kakuma-San,

On 2016年10月24日 11:12, fumihiko kakuma wrote:
> This adds some scenario tests using a scenario test tool.
>
> Signed-off-by: Fumihiko Kakuma <[email protected]>
> ---
>  ryu/tests/integrated/bgp/__init__.py   |   0
>  ryu/tests/integrated/bgp/base.py       |  81 +++++++++++
>  ryu/tests/integrated/bgp/ryubgp_app.py | 238 
> +++++++++++++++++++++++++++++++++

Is this file is just a copy of ryu/services/protocols/bgp/application.py?
Can we use the original one?

BTW, this file has some problems(e.g. options for this app are not passed to 
'ryu-manager'),
I am re-implementing it as following.
(Currently, this is working in progress, though...)
   
https://github.com/osrg/ryu/compare/master...iwaseyusuke:BGPSpeaker-Sort_out_application_for_BGPSpeaker


>  ryu/tests/integrated/bgp/test_basic.py |  47 +++++++
>  ryu/tests/integrated/run_test.py       |  50 +++++++
>  5 files changed, 416 insertions(+)
>  create mode 100644 ryu/tests/integrated/bgp/__init__.py
>  create mode 100644 ryu/tests/integrated/bgp/base.py
>  create mode 100644 ryu/tests/integrated/bgp/ryubgp_app.py
>  create mode 100644 ryu/tests/integrated/bgp/test_basic.py
>  create mode 100644 ryu/tests/integrated/run_test.py
>
> diff --git a/ryu/tests/integrated/bgp/__init__.py 
> b/ryu/tests/integrated/bgp/__init__.py
> new file mode 100644
> index 0000000..e69de29
> diff --git a/ryu/tests/integrated/bgp/base.py 
> b/ryu/tests/integrated/bgp/base.py
> new file mode 100644
> index 0000000..d1e4331
> --- /dev/null
> +++ b/ryu/tests/integrated/bgp/base.py
> @@ -0,0 +1,81 @@
> +# Copyright (C) 2016 Nippon Telegraph and Telephone Corporation.
> +# Copyright (C) 2016 Fumihiko Kakuma <kakuma at valinux co jp>
> +#
> +# Licensed under the Apache License, Version 2.0 (the "License");
> +# you may not use this file except in compliance with the License.
> +# You may obtain a copy of the License at
> +#
> +#    http://www.apache.org/licenses/LICENSE-2.0
> +#
> +# Unless required by applicable law or agreed to in writing, software
> +# distributed under the License is distributed on an "AS IS" BASIS,
> +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
> +# implied.
> +# See the License for the specific language governing permissions and
> +# limitations under the License.
> +
> +from __future__ import absolute_import
> +
> +import unittest
> +
> +from ryu.tests.integrated.common import docker_base as ctn_base
> +from ryu.tests.integrated.common import ryubgp
> +from ryu.tests.integrated.common import quagga
> +
> +
> +class BgpSpeakerTestBase(unittest.TestCase):
> +
> +    checktime = 120
> +
> +    @classmethod
> +    def setUpClass(cls):
> +        cls.images = []
> +        cls.containers = []
> +        cls.bridges = []
> +
> +        cls.brdc1 = ctn_base.Bridge(name='brdc1',
> +                                    subnet='192.168.10.0/24')
> +        cls.bridges.append(cls.brdc1)
> +
> +        cls.dockerimg = ctn_base.DockerImage()
> +        cls.r_img = cls.dockerimg.create_ryu()
> +        cls.images.append(cls.r_img)
> +        cls.q_img = cls.dockerimg.create_quagga()
> +        cls.images.append(cls.q_img)
> +
> +        cls.r1 = ryubgp.RyuBGPContainer(name='r1', asn=64512,
> +                                        router_id='192.168.0.1',
> +                                        ctn_image_name=cls.r_img)
> +        cls.containers.append(cls.r1)
> +        cls.r1.add_route('10.10.0.0/28')
> +        cls.r1.run(wait=True)
> +        cls.r1_ip_cidr = cls.brdc1.addif(cls.r1)
> +        cls.r1_ip = cls.r1_ip_cidr.split('/')[0]
> +
> +        cls.q1 = quagga.QuaggaBGPContainer(name='q1', asn=64522,
> +                                           router_id='192.168.0.2',
> +                                           ctn_image_name=cls.q_img)
> +        cls.containers.append(cls.q1)
> +        cls.q1.add_route('192.168.160.0/24')
> +        cls.q1.run(wait=True)
> +        cls.q1_ip_cidr = cls.brdc1.addif(cls.q1)
> +        cls.q1_ip = cls.q1_ip_cidr.split('/')[0]
> +
> +        cls.r1.add_peer(cls.q1, bridge=cls.brdc1.name)
> +        cls.q1.add_peer(cls.r1, bridge=cls.brdc1.name)
> +
> +        super(BgpSpeakerTestBase, cls).setUpClass()
> +
> +    @classmethod
> +    def tearDownClass(cls):
> +        for ctn in cls.containers:
> +            try:
> +                ctn.stop()
> +            except ctn_base.CommandError as e:
> +                pass
> +            ctn.remove()
> +        for br in cls.bridges:
> +            br.delete()
> +        for img in cls.images:
> +            cls.dockerimg.remove(img)
> +        super(BgpSpeakerTestBase, cls).tearDownClass()
> diff --git a/ryu/tests/integrated/bgp/ryubgp_app.py 
> b/ryu/tests/integrated/bgp/ryubgp_app.py
> new file mode 100644
> index 0000000..7e364b1
> --- /dev/null
> +++ b/ryu/tests/integrated/bgp/ryubgp_app.py
> @@ -0,0 +1,238 @@
> +# Copyright (C) 2014 Nippon Telegraph and Telephone Corporation.
> +#
> +# Licensed under the Apache License, Version 2.0 (the "License");
> +# you may not use this file except in compliance with the License.
> +# You may obtain a copy of the License at
> +#
> +#    http://www.apache.org/licenses/LICENSE-2.0
> +#
> +# Unless required by applicable law or agreed to in writing, software
> +# distributed under the License is distributed on an "AS IS" BASIS,
> +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
> +# implied.
> +# See the License for the specific language governing permissions and
> +# limitations under the License.
> +"""
> +  Defines bases classes to create a BGP application.
> +"""
> +from __future__ import absolute_import
> +
> +import imp
> +import logging
> +import traceback
> +from ryu import cfg
> +
> +from ryu.lib import hub
> +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.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_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
> +
> +try:
> +    from logging.config import dictConfig
> +except Exception:
> +    from ryu.services.protocols.bgp.utils.dictconfig import dictConfig
> +
> +
> +LOG = logging.getLogger(__name__)
> +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')
> +])
> +
> +
> +@add_bgp_error_metadata(code=BIN_ERROR,
> +                        sub_code=1,
> +                        def_desc='Unknown bootstrap exception.')
> +class ApplicationException(BGPSException):
> +    """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 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')
> +
> +        super(RyuBGPSpeaker, self).start()
> +
> +        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
> +
> +    @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)
> +
> +        return port
> +
> +    def load_config(self, config_file):
> +        """Validates give file as settings file for BGPSpeaker.
> +
> +        Load the configuration from file as settings module.
> +        """
> +        if not config_file or not isinstance(config_file, str):
> +            raise ApplicationException('Invalid configuration file.')
> +
> +        # Check if file can be read
> +        try:
> +            return imp.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.
> +        """
> +        # Get common settings
> +        routing_settings = settings.BGP.get('routing')
> +        common_settings = {}
> +
> +        # Get required common settings.
> +        try:
> +            common_settings[LOCAL_AS] = routing_settings.pop(LOCAL_AS)
> +            common_settings[ROUTER_ID] = routing_settings.pop(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.
> +        """
> +        bgp_neighbors = routing_settings.setdefault('bgp_neighbors', {})
> +        for ip, bgp_neighbor in bgp_neighbors.items():
> +            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
> +
> +    def _add_vrfs(self, routing_settings):
> +        """Add VRFs from given settings to BGPS runtime.
> +
> +        If any of the VRFs are miss-configured errors are logged.
> +        All valid VRFs are loaded.
> +        """
> +        vpns_conf = routing_settings.setdefault('vpns', {})
> +        for vrfname, vrf in vpns_conf.items():
> +            try:
> +                vrf[vrfs.VRF_NAME] = vrfname
> +                call('vrf.create', **vrf)
> +                LOG.debug('Added vrf  %s', vrf)
> +            except RuntimeConfigError as e:
> +                LOG.error(e)
> +                continue
> +
> +    def _add_networks(self, routing_settings):
> +        """Add networks from given settings to BGPS runtime.
> +
> +        If any of the networks are miss-configured errors are logged.
> +        All valid networks are loaded.
> +        """
> +        networks = routing_settings.setdefault('networks', [])
> +        for prefix in networks:
> +            try:
> +                call('network.add', prefix=prefix)
> +                LOG.debug('Added network %s', prefix)
> +            except RuntimeConfigError as e:
> +                LOG.error(e)
> +                continue
> diff --git a/ryu/tests/integrated/bgp/test_basic.py 
> b/ryu/tests/integrated/bgp/test_basic.py
> new file mode 100644
> index 0000000..6321191
> --- /dev/null
> +++ b/ryu/tests/integrated/bgp/test_basic.py
> @@ -0,0 +1,47 @@
> +# Copyright (C) 2016 Nippon Telegraph and Telephone Corporation.
> +# Copyright (C) 2016 Fumihiko Kakuma <kakuma at valinux co jp>
> +#
> +# Licensed under the Apache License, Version 2.0 (the "License");
> +# you may not use this file except in compliance with the License.
> +# You may obtain a copy of the License at
> +#
> +#    http://www.apache.org/licenses/LICENSE-2.0
> +#
> +# Unless required by applicable law or agreed to in writing, software
> +# distributed under the License is distributed on an "AS IS" BASIS,
> +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
> +# implied.
> +# See the License for the specific language governing permissions and
> +# limitations under the License.
> +
> +from __future__ import absolute_import
> +
> +import time
> +
> +from . import base
> +from ryu.tests.integrated.common import docker_base as ctn_base
> +
> +
> +class BgpSpeakerBasicTest(base.BgpSpeakerTestBase):
> +    def setUp(self):
> +        super(BgpSpeakerBasicTest, self).setUp()
> +        self.r1.stop_ryubgp(retry=True)
> +        self.r1.start_ryubgp(retry=True)
> +
> +    def test_check_neighbor_established(self):
> +        for i in range(0, self.checktime):
> +            neighbor_state = self.q1.get_neighbor_state(self.r1)
> +            if neighbor_state == ctn_base.BGP_FSM_ESTABLISHED:
> +                break
> +            time.sleep(1)
> +        self.assertEqual(neighbor_state, ctn_base.BGP_FSM_ESTABLISHED)
> +
> +    def test_check_rib_nexthop(self):
> +        for i in range(0, self.checktime):
> +            neighbor_state = self.q1.get_neighbor_state(self.r1)
> +            if neighbor_state == ctn_base.BGP_FSM_ESTABLISHED:
> +                break
> +            time.sleep(1)
> +        self.assertEqual(neighbor_state, ctn_base.BGP_FSM_ESTABLISHED)
> +        rib = self.q1.get_global_rib(prefix='10.10.0.0/28')
> +        self.assertEqual(self.r1_ip, rib[0]['nexthop'])
> diff --git a/ryu/tests/integrated/run_test.py 
> b/ryu/tests/integrated/run_test.py
> new file mode 100644
> index 0000000..c7001d0
> --- /dev/null
> +++ b/ryu/tests/integrated/run_test.py
> @@ -0,0 +1,50 @@
> +# Copyright (C) 2016 Nippon Telegraph and Telephone Corporation.
> +# Copyright (C) 2016 Fumihiko Kakuma <kakuma at valinux co jp>
> +#
> +# Licensed under the Apache License, Version 2.0 (the "License");
> +# you may not use this file except in compliance with the License.
> +# You may obtain a copy of the License at
> +#
> +#    http://www.apache.org/licenses/LICENSE-2.0
> +#
> +# Unless required by applicable law or agreed to in writing, software
> +# distributed under the License is distributed on an "AS IS" BASIS,
> +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
> +# implied.
> +# See the License for the specific language governing permissions and
> +# limitations under the License.
> +
> +from __future__ import absolute_import
> +
> +import logging
> +import os
> +import sys
> +import unittest
> +
> +from ryu import log
> +
> +
> +def load_tests(loader, tests, pattern):
> +    dirname = os.path.dirname(os.path.abspath(__file__))
> +    base_path = os.path.abspath(dirname + '/../../..')
> +    suite = unittest.TestSuite()
> +    for test_dir in ['ryu/tests/integrated/bgp']:
> +        if not pattern:
> +            suite.addTests(loader.discover(test_dir,
> +                                           top_level_dir=base_path))
> +        else:
> +            suite.addTests(loader.discover(test_dir, pattern=pattern,
> +                                           top_level_dir=base_path))
> +    return suite
> +
> +
> +if __name__ == '__main__':
> +    log.early_init_log(logging.DEBUG)
> +    log.init_log()
> +    LOG = logging.getLogger(__name__)
> +    pattern = None
> +    if len(sys.argv) == 2:
> +        pattern = sys.argv[1]
> +    loader = unittest.defaultTestLoader
> +    suite = load_tests(loader, None, pattern)
> +    unittest.TextTestRunner(verbosity=2).run(suite)
>

------------------------------------------------------------------------------
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