From 042aae6dd993f4a95ccac8874a45873b75cfe4fc Mon Sep 17 00:00:00 2001 From: Miguel Angel Ajo Date: Thu, 12 Mar 2015 14:58:39 +0000 Subject: [PATCH] Add a netns-cleanup functional test We have lots of regressions on netns, because unit test is not enough. This commit adds basic functional testing to the netns_cleanup. This work should be extended when we have functional testing for the dhcp agent, spawning dhcp services, and then making sure they're fully cleaned up. Change-Id: I0b5125dfa24a3dbcd44593ae2aee4dbbd47def67 --- neutron/cmd/netns_cleanup.py | 25 ++++---- neutron/tests/functional/agent/linux/base.py | 19 ++++-- neutron/tests/functional/cmd/__init__.py | 0 .../functional/cmd/test_netns_cleanup.py | 59 +++++++++++++++++++ 4 files changed, 86 insertions(+), 17 deletions(-) create mode 100644 neutron/tests/functional/cmd/__init__.py create mode 100755 neutron/tests/functional/cmd/test_netns_cleanup.py diff --git a/neutron/cmd/netns_cleanup.py b/neutron/cmd/netns_cleanup.py index 4d7e5f175..cde77b1ed 100644 --- a/neutron/cmd/netns_cleanup.py +++ b/neutron/cmd/netns_cleanup.py @@ -142,6 +142,19 @@ def destroy_namespace(conf, namespace, force=False): LOG.exception(_LE('Error unable to destroy namespace: %s'), namespace) +def cleanup_network_namespaces(conf): + # Identify namespaces that are candidates for deletion. + candidates = [ns for ns in + ip_lib.IPWrapper.get_namespaces() + if eligible_for_deletion(conf, ns, conf.force)] + + if candidates: + time.sleep(2) + + for namespace in candidates: + destroy_namespace(conf, namespace, conf.force) + + def main(): """Main method for cleaning up network namespaces. @@ -162,14 +175,4 @@ def main(): conf = setup_conf() conf() config.setup_logging() - - # Identify namespaces that are candidates for deletion. - candidates = [ns for ns in - ip_lib.IPWrapper.get_namespaces() - if eligible_for_deletion(conf, ns, conf.force)] - - if candidates: - time.sleep(2) - - for namespace in candidates: - destroy_namespace(conf, namespace, conf.force) + cleanup_network_namespaces(conf) diff --git a/neutron/tests/functional/agent/linux/base.py b/neutron/tests/functional/agent/linux/base.py index c626e0596..e1be0a8ec 100644 --- a/neutron/tests/functional/agent/linux/base.py +++ b/neutron/tests/functional/agent/linux/base.py @@ -33,6 +33,7 @@ ICMP_MARK_RULE = ('-j MARK --set-xmark %(value)s/%(mask)s' MARKED_BLOCK_RULE = '-m mark --mark %s -j DROP' % MARK_VALUE ICMP_BLOCK_RULE = '-p icmp -j DROP' VETH_PREFIX = 'tst-vth' +NS_PREFIX = 'func-' #TODO(jschwarz): Move these two functions to neutron/tests/common/ @@ -62,11 +63,16 @@ class BaseLinuxTestCase(functional_base.BaseSudoTestCase): self.skipTest(skip_msg) raise - def _create_namespace(self): + @staticmethod + def _cleanup_namespace(namespace): + if namespace.netns.exists(namespace.namespace): + namespace.netns.delete(namespace.namespace) + + def _create_namespace(self, prefix=NS_PREFIX): ip_cmd = ip_lib.IPWrapper() - name = "func-%s" % uuidutils.generate_uuid() + name = prefix + uuidutils.generate_uuid() namespace = ip_cmd.ensure_namespace(name) - self.addCleanup(namespace.netns.delete, namespace.namespace) + self.addCleanup(BaseLinuxTestCase._cleanup_namespace, namespace) return namespace @@ -165,14 +171,15 @@ class BaseIPVethTestCase(BaseLinuxTestCase): device.addr.add(cidr) device.link.set_up() - def prepare_veth_pairs(self): + def prepare_veth_pairs(self, src_ns_prefix=NS_PREFIX, + dst_ns_prefix=NS_PREFIX): src_addr = self.SRC_ADDRESS dst_addr = self.DST_ADDRESS src_veth = get_rand_veth_name() dst_veth = get_rand_veth_name() - src_ns = self._create_namespace() - dst_ns = self._create_namespace() + src_ns = self._create_namespace(src_ns_prefix) + dst_ns = self._create_namespace(dst_ns_prefix) src_veth, dst_veth = src_ns.add_veth(src_veth, dst_veth, diff --git a/neutron/tests/functional/cmd/__init__.py b/neutron/tests/functional/cmd/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/neutron/tests/functional/cmd/test_netns_cleanup.py b/neutron/tests/functional/cmd/test_netns_cleanup.py new file mode 100755 index 000000000..e6405c90a --- /dev/null +++ b/neutron/tests/functional/cmd/test_netns_cleanup.py @@ -0,0 +1,59 @@ +# Copyright (c) 2015 Red Hat, Inc. +# All Rights Reserved. +# +# 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. + +import mock + +from neutron.agent.l3 import agent as l3_agent +from neutron.agent.linux import dhcp +from neutron.agent.linux import ip_lib +from neutron.cmd import netns_cleanup +from neutron.tests.functional.agent.linux import base + +GET_NAMESPACES = 'neutron.agent.linux.ip_lib.IPWrapper.get_namespaces' +TEST_INTERFACE_DRIVER = 'neutron.agent.linux.interface.OVSInterfaceDriver' + + +class NetnsCleanupTest(base.BaseIPVethTestCase): + def setUp(self): + super(NetnsCleanupTest, self).setUp() + + self.get_namespaces_p = mock.patch(GET_NAMESPACES) + self.get_namespaces = self.get_namespaces_p.start() + + def setup_config(self, args=None): + if args is None: + args = [] + # force option enabled to make sure non-empty namespaces are + # cleaned up and deleted + args.append('--force') + + self.conf = netns_cleanup.setup_conf() + self.conf.set_override('interface_driver', TEST_INTERFACE_DRIVER) + self.config_parse(conf=self.conf, args=args) + + def test_cleanup_network_namespaces_cleans_dhcp_and_l3_namespaces(self): + l3_ns, dhcp_ns = self.prepare_veth_pairs(l3_agent.NS_PREFIX, + dhcp.NS_PREFIX) + # we scope the get_namespaces to our own ones not to affect other + # tests, as otherwise cleanup will kill them all + self.get_namespaces.return_value = [l3_ns.namespace, + dhcp_ns.namespace] + + netns_cleanup.cleanup_network_namespaces(self.conf) + + self.get_namespaces_p.stop() + namespaces_now = ip_lib.IPWrapper.get_namespaces() + self.assertNotIn(l3_ns.namespace, namespaces_now) + self.assertNotIn(dhcp_ns.namespace, namespaces_now) -- 2.45.2