Hi Iwase-san,

Thank you for review.

On Mon, 24 Oct 2016 11:40:40 +0900
Iwase Yusuke <[email protected]> wrote:

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

Yes, it is the same except 'from ryu import cfg' line.
I thought that this app may need peculiar customization to a test.
So I copied it.
I know it has some problems and it is troblesome to maintain two files.
I will use 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)
> >

-- 
fumihiko kakuma <[email protected]>



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