]> review.fuel-infra Code Review - openstack-build/neutron-build.git/commitdiff
Add full-stack test
authorJohn Schwarz <jschwarz@redhat.com>
Tue, 14 Oct 2014 11:12:35 +0000 (14:12 +0300)
committerJohn Schwarz <jschwarz@redhat.com>
Tue, 14 Apr 2015 12:42:54 +0000 (15:42 +0300)
Currently, the full-stack framework has only one test which only uses
the neutron-server. This patch adds an actual test which makes sure that
once a router is created, an actual namespace is create for it. Since
this test requires 3 processes (neutron-server, l3-agent, ovs-agent),
existing full-stack code is modified to add more streamlined support for
such code.

Partially-Implements: blueprint integration-tests
Change-Id: Id5a8852d38543590b90e4bbed261a7a458071a9a

neutron/tests/common/net_helpers.py
neutron/tests/fullstack/base.py
neutron/tests/fullstack/config_fixtures.py
neutron/tests/fullstack/fullstack_fixtures.py
neutron/tests/fullstack/test_l3_agent.py [new file with mode: 0644]
neutron/tests/fullstack/test_sanity.py [deleted file]

index 4ccf06d2007ffddc54ebb45c31080a5fa763ae77..3a1fdc513401e09f3749d2190d0ea042c1b8eb75 100644 (file)
@@ -144,14 +144,19 @@ class PortFixture(fixtures.Fixture):
 class OVSBridgeFixture(fixtures.Fixture):
     """Create an OVS bridge.
 
+    :ivar prefix: bridge name prefix
+    :type prefix: str
     :ivar bridge: created bridge
     :type bridge: OVSBridge
     """
 
+    def __init__(self, prefix=BR_PREFIX):
+        self.prefix = prefix
+
     def setUp(self):
         super(OVSBridgeFixture, self).setUp()
         ovs = ovs_lib.BaseOVS()
-        self.bridge = common_base.create_resource(BR_PREFIX, ovs.add_bridge)
+        self.bridge = common_base.create_resource(self.prefix, ovs.add_bridge)
         self.addCleanup(self.bridge.destroy)
 
 
index a69cc989bfcbc443120b0651c8c1d62a0eb7dfe0..1a50acb74508427df5715a99cc8858ad40f9ca57 100644 (file)
@@ -21,23 +21,21 @@ from neutron.tests.fullstack import fullstack_fixtures as f_fixtures
 
 
 class BaseFullStackTestCase(test_base.MySQLOpportunisticTestCase):
-    """Base test class for full-stack tests.
+    """Base test class for full-stack tests."""
 
-    :param process_fixtures: a list of fixture classes (not instances).
-    """
+    def __init__(self, environment=None, *args, **kwargs):
+        super(BaseFullStackTestCase, self).__init__(*args, **kwargs)
+        self.environment = (environment if environment
+                            else f_fixtures.EnvironmentFixture())
 
     def setUp(self):
         super(BaseFullStackTestCase, self).setUp()
         self.create_db_tables()
 
-        self.neutron_server = self.useFixture(
-            f_fixtures.NeutronServerFixture())
-        self.client = self.neutron_server.client
+        if self.environment:
+            self.useFixture(self.environment)
 
-    @property
-    def test_name(self):
-        """Return the name of the test currently running."""
-        return self.id().split(".")[-1]
+        self.client = self.environment.neutron_server.client
 
     def create_db_tables(self):
         """Populate the new database.
index 8767867c4e9a491d43431aa304cfcc475898fe44..6c25c9655073baf19b1e3aca3fc2ca79497e80b3 100644 (file)
@@ -105,12 +105,15 @@ class NeutronConfigFixture(ConfigFixture):
             'DEFAULT': {
                 'host': self._generate_host(),
                 'state_path': self._generate_state_path(temp_dir),
+                'lock_path': '$state_path/lock',
                 'bind_port': self._generate_port(),
                 'api_paste_config': self._generate_api_paste(),
                 'policy_file': self._generate_policy_json(),
                 'core_plugin': 'neutron.plugins.ml2.plugin.Ml2Plugin',
+                'service_plugins': ('neutron.services.l3_router.'
+                                    'l3_router_plugin.L3RouterPlugin'),
                 'rabbit_userid': 'stackrabbit',
-                'rabbit_password': 'secretrabbit',
+                'rabbit_password': '127.0.0.1',
                 'rabbit_hosts': '127.0.0.1',
                 'auth_strategy': 'noauth',
                 'verbose': 'True',
@@ -169,6 +172,10 @@ class ML2ConfigFixture(ConfigFixture):
                 'local_ip': '127.0.0.1',
                 'bridge_mappings': self._generate_bridge_mappings(),
                 'integration_bridge': self._generate_integration_bridge(),
+            },
+            'securitygroup': {
+                'firewall_driver': ('neutron.agent.linux.iptables_firewall.'
+                                    'OVSHybridIptablesFirewallDriver'),
             }
         })
 
@@ -181,3 +188,28 @@ class ML2ConfigFixture(ConfigFixture):
     def _generate_integration_bridge(self):
         return base.get_rand_name(prefix='br-int',
                                   max_length=constants.DEVICE_NAME_MAX_LEN)
+
+
+class L3ConfigFixture(ConfigFixture):
+
+    def __init__(self, temp_dir, integration_bridge):
+        super(L3ConfigFixture, self).__init__(
+            temp_dir, base_filename='l3_agent.ini')
+
+        self.config.update({
+            'DEFAULT': {
+                'l3_agent_manager': ('neutron.agent.l3_agent.'
+                                     'L3NATAgentWithStateReport'),
+                'interface_driver': ('neutron.agent.linux.interface.'
+                                     'OVSInterfaceDriver'),
+                'ovs_integration_bridge': integration_bridge,
+                'external_network_bridge': self._generate_external_bridge(),
+                'router_delete_namespaces': 'True',
+                'debug': 'True',
+                'verbose': 'True',
+            }
+        })
+
+    def _generate_external_bridge(self):
+        return base.get_rand_name(prefix='br-ex',
+                                  max_length=constants.DEVICE_NAME_MAX_LEN)
index 9feed50d692f00b5989879c2e4f2539da38da9e2..fc2ab0bc885536da6b1a8ec9376bbbd7361faedd 100644 (file)
@@ -13,6 +13,7 @@
 #    under the License.
 
 from distutils import spawn
+import functools
 
 import fixtures
 from neutronclient.common import exceptions as nc_exc
@@ -23,6 +24,7 @@ from oslo_utils import timeutils
 
 from neutron.agent.linux import async_process
 from neutron.agent.linux import utils
+from neutron.tests.common import net_helpers
 from neutron.tests.fullstack import config_fixtures
 
 LOG = logging.getLogger(__name__)
@@ -33,7 +35,6 @@ DEFAULT_LOG_DIR = '/opt/stack/logs'
 
 class ProcessFixture(fixtures.Fixture):
     def __init__(self, name, exec_name, config_filenames):
-        super(ProcessFixture, self).__init__()
         self.name = name
         self.exec_name = exec_name
         self.config_filenames = config_filenames
@@ -61,11 +62,39 @@ class ProcessFixture(fixtures.Fixture):
         super(ProcessFixture, self, *args, **kwargs)
 
 
+class EnvironmentFixture(fixtures.Fixture):
+
+    def setUp(self):
+        super(EnvironmentFixture, self).setUp()
+
+        self.temp_dir = self.useFixture(fixtures.TempDir()).path
+
+        self.neutron_server = self.useFixture(
+            NeutronServerFixture(self.temp_dir))
+
+    def wait_until_env_is_up(self, agents_count=0):
+        utils.wait_until_true(
+            functools.partial(self._processes_are_ready, agents_count))
+
+    def _processes_are_ready(self, agents_count):
+        try:
+            running_agents = self.neutron_server.client.list_agents()['agents']
+            LOG.warn("There are %d agents running!", len(running_agents))
+            return len(running_agents) == agents_count
+        except nc_exc.NeutronClientException:
+            LOG.warn("neutron-server isn't up yet (cannot contact REST API).")
+            return False
+
+
 class NeutronServerFixture(fixtures.Fixture):
 
+    NEUTRON_SERVER = "neutron-server"
+
+    def __init__(self, temp_dir):
+        self.temp_dir = temp_dir
+
     def setUp(self):
         super(NeutronServerFixture, self).setUp()
-        self.temp_dir = self.useFixture(fixtures.TempDir()).path
 
         self.neutron_cfg_fixture = config_fixtures.NeutronConfigFixture(
             self.temp_dir, cfg.CONF.database.connection)
@@ -76,29 +105,91 @@ class NeutronServerFixture(fixtures.Fixture):
         self.useFixture(self.plugin_cfg_fixture)
 
         self.neutron_config = self.neutron_cfg_fixture.config
+        self.plugin_config = self.plugin_cfg_fixture.config
 
         config_filenames = [self.neutron_cfg_fixture.filename,
                             self.plugin_cfg_fixture.filename]
 
         self.process_fixture = self.useFixture(ProcessFixture(
-            name='neutron_server',
-            exec_name='neutron-server',
-            config_filenames=config_filenames,
-        ))
+            name=self.NEUTRON_SERVER,
+            exec_name=self.NEUTRON_SERVER,
+            config_filenames=config_filenames))
 
-        utils.wait_until_true(self.processes_are_ready)
+        utils.wait_until_true(self.server_is_live)
+
+    def server_is_live(self):
+        try:
+            self.client.list_networks()
+            return True
+        except nc_exc.NeutronClientException:
+            LOG.warn("neutron-server isn't up yet (cannot contact REST API).")
+            return False
 
     @property
     def client(self):
         url = "http://127.0.0.1:%s" % self.neutron_config.DEFAULT.bind_port
         return client.Client(auth_strategy="noauth", endpoint_url=url)
 
-    def processes_are_ready(self):
-        # ProcessFixture will ensure that the server has started, but
-        # that doesn't mean that the server will be serving commands yet, nor
-        # that all processes are up.
-        try:
-            return len(self.client.list_agents()['agents']) == 0
-        except nc_exc.NeutronClientException:
-            LOG.debug("Processes aren't up yet.")
-            return False
+
+class OVSAgentFixture(fixtures.Fixture):
+
+    NEUTRON_OVS_AGENT = "neutron-openvswitch-agent"
+
+    def __init__(self, neutron_cfg_fixture, ml2_cfg_fixture):
+        self.neutron_cfg_fixture = neutron_cfg_fixture
+        self.plugin_cfg_fixture = ml2_cfg_fixture
+
+        self.neutron_config = self.neutron_cfg_fixture.config
+        self.plugin_config = self.plugin_cfg_fixture.config
+
+    def setUp(self):
+        super(OVSAgentFixture, self).setUp()
+
+        self.useFixture(net_helpers.OVSBridgeFixture(self._get_br_int_name()))
+        self.useFixture(net_helpers.OVSBridgeFixture(self._get_br_phys_name()))
+
+        config_filenames = [self.neutron_cfg_fixture.filename,
+                            self.plugin_cfg_fixture.filename]
+
+        self.process_fixture = self.useFixture(ProcessFixture(
+            name=self.NEUTRON_OVS_AGENT,
+            exec_name=self.NEUTRON_OVS_AGENT,
+            config_filenames=config_filenames))
+
+    def _get_br_int_name(self):
+        return self.plugin_config.ovs.integration_bridge
+
+    def _get_br_phys_name(self):
+        return self.plugin_config.ovs.bridge_mappings.split(':')[1]
+
+
+class L3AgentFixture(fixtures.Fixture):
+
+    NEUTRON_L3_AGENT = "neutron-l3-agent"
+
+    def __init__(self, temp_dir, neutron_cfg_fixture, integration_bridge_name):
+        self.temp_dir = temp_dir
+        self.neutron_cfg_fixture = neutron_cfg_fixture
+        self.neutron_config = self.neutron_cfg_fixture.config
+        self.integration_bridge_name = integration_bridge_name
+
+    def setUp(self):
+        super(L3AgentFixture, self).setUp()
+
+        self.plugin_cfg_fixture = config_fixtures.L3ConfigFixture(
+            self.temp_dir, self.integration_bridge_name)
+        self.useFixture(self.plugin_cfg_fixture)
+        self.plugin_config = self.plugin_cfg_fixture.config
+
+        self.useFixture(net_helpers.OVSBridgeFixture(self._get_br_ex_name()))
+
+        config_filenames = [self.neutron_cfg_fixture.filename,
+                            self.plugin_cfg_fixture.filename]
+
+        self.process_fixture = self.useFixture(ProcessFixture(
+            name=self.NEUTRON_L3_AGENT,
+            exec_name=self.NEUTRON_L3_AGENT,
+            config_filenames=config_filenames))
+
+    def _get_br_ex_name(self):
+        return self.plugin_config.DEFAULT.external_network_bridge
diff --git a/neutron/tests/fullstack/test_l3_agent.py b/neutron/tests/fullstack/test_l3_agent.py
new file mode 100644 (file)
index 0000000..b527d06
--- /dev/null
@@ -0,0 +1,88 @@
+# Copyright 2015 Red Hat, Inc.
+#
+#    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 neutron.agent.l3 import agent as l3_agent
+from neutron.agent.linux import ip_lib
+from neutron.agent.linux import utils
+from neutron.openstack.common import uuidutils
+from neutron.tests.fullstack import base
+from neutron.tests.fullstack import fullstack_fixtures as f_fixtures
+
+
+class SingleNodeEnvironment(f_fixtures.EnvironmentFixture):
+    def setUp(self):
+        super(SingleNodeEnvironment, self).setUp()
+
+        neutron_config = self.neutron_server.neutron_cfg_fixture
+        ml2_config = self.neutron_server.plugin_cfg_fixture
+
+        self.ovs_agent = self.useFixture(
+            f_fixtures.OVSAgentFixture(
+                neutron_config, ml2_config))
+
+        self.l3_agent = self.useFixture(
+            f_fixtures.L3AgentFixture(
+                self.temp_dir,
+                neutron_config,
+                self.ovs_agent._get_br_int_name()))
+
+        self.wait_until_env_is_up(agents_count=2)
+
+
+class TestLegacyL3Agent(base.BaseFullStackTestCase):
+    def __init__(self, *args, **kwargs):
+        super(TestLegacyL3Agent, self).__init__(
+            SingleNodeEnvironment(), *args, **kwargs)
+
+    def _get_namespace(self, router_id):
+        return "%s%s" % (l3_agent.NS_PREFIX, router_id)
+
+    def _assert_namespace_exists(self, ns_name):
+        ip = ip_lib.IPWrapper(ns_name)
+        utils.wait_until_true(lambda: ip.netns.exists(ns_name))
+
+    def test_namespace_exists(self):
+        uuid = uuidutils.generate_uuid()
+
+        router = self.client.create_router(
+            body={'router': {'name': 'router-test',
+                             'tenant_id': uuid}})
+
+        network = self.client.create_network(
+            body={'network': {'name': 'network-test',
+                              'tenant_id': uuid}})
+
+        subnet = self.client.create_subnet(
+            body={'subnet': {'name': 'subnet-test',
+                             'tenant_id': uuid,
+                             'network_id': network['network']['id'],
+                             'cidr': '20.0.0.0/24',
+                             'gateway_ip': '20.0.0.1',
+                             'ip_version': 4,
+                             'enable_dhcp': True}})
+
+        self.client.add_interface_router(
+            router=router['router']['id'],
+            body={'subnet_id': subnet['subnet']['id']})
+
+        router_id = router['router']['id']
+        self._assert_namespace_exists(self._get_namespace(router_id))
+
+        self.client.remove_interface_router(
+            router=router['router']['id'],
+            body={'subnet_id': subnet['subnet']['id']})
+
+        self.client.delete_subnet(subnet['subnet']['id'])
+        self.client.delete_network(network['network']['id'])
+        self.client.delete_router(router['router']['id'])
diff --git a/neutron/tests/fullstack/test_sanity.py b/neutron/tests/fullstack/test_sanity.py
deleted file mode 100644 (file)
index 6fc9129..0000000
+++ /dev/null
@@ -1,25 +0,0 @@
-# Copyright 2015 Red Hat, Inc.
-#
-#    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.
-
-#TODO(jschwarz): This is an example test file which demonstrates the
-# general usage of fullstack. Once we add more FullStack tests, this should
-# be deleted.
-
-from neutron.tests.fullstack import base
-
-
-class TestSanity(base.BaseFullStackTestCase):
-
-    def test_sanity(self):
-        self.assertEqual(self.client.list_networks(), {'networks': []})