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
This commit is contained in:
@@ -142,6 +142,19 @@ def destroy_namespace(conf, namespace, force=False):
|
|||||||
LOG.exception(_LE('Error unable to destroy namespace: %s'), namespace)
|
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():
|
def main():
|
||||||
"""Main method for cleaning up network namespaces.
|
"""Main method for cleaning up network namespaces.
|
||||||
|
|
||||||
@@ -162,14 +175,4 @@ def main():
|
|||||||
conf = setup_conf()
|
conf = setup_conf()
|
||||||
conf()
|
conf()
|
||||||
config.setup_logging()
|
config.setup_logging()
|
||||||
|
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)
|
|
||||||
|
@@ -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
|
MARKED_BLOCK_RULE = '-m mark --mark %s -j DROP' % MARK_VALUE
|
||||||
ICMP_BLOCK_RULE = '-p icmp -j DROP'
|
ICMP_BLOCK_RULE = '-p icmp -j DROP'
|
||||||
VETH_PREFIX = 'tst-vth'
|
VETH_PREFIX = 'tst-vth'
|
||||||
|
NS_PREFIX = 'func-'
|
||||||
|
|
||||||
|
|
||||||
#TODO(jschwarz): Move these two functions to neutron/tests/common/
|
#TODO(jschwarz): Move these two functions to neutron/tests/common/
|
||||||
@@ -62,11 +63,16 @@ class BaseLinuxTestCase(functional_base.BaseSudoTestCase):
|
|||||||
self.skipTest(skip_msg)
|
self.skipTest(skip_msg)
|
||||||
raise
|
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()
|
ip_cmd = ip_lib.IPWrapper()
|
||||||
name = "func-%s" % uuidutils.generate_uuid()
|
name = prefix + uuidutils.generate_uuid()
|
||||||
namespace = ip_cmd.ensure_namespace(name)
|
namespace = ip_cmd.ensure_namespace(name)
|
||||||
self.addCleanup(namespace.netns.delete, namespace.namespace)
|
self.addCleanup(BaseLinuxTestCase._cleanup_namespace, namespace)
|
||||||
|
|
||||||
return namespace
|
return namespace
|
||||||
|
|
||||||
@@ -165,14 +171,15 @@ class BaseIPVethTestCase(BaseLinuxTestCase):
|
|||||||
device.addr.add(cidr)
|
device.addr.add(cidr)
|
||||||
device.link.set_up()
|
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
|
src_addr = self.SRC_ADDRESS
|
||||||
dst_addr = self.DST_ADDRESS
|
dst_addr = self.DST_ADDRESS
|
||||||
src_veth = get_rand_veth_name()
|
src_veth = get_rand_veth_name()
|
||||||
dst_veth = get_rand_veth_name()
|
dst_veth = get_rand_veth_name()
|
||||||
src_ns = self._create_namespace()
|
src_ns = self._create_namespace(src_ns_prefix)
|
||||||
dst_ns = self._create_namespace()
|
dst_ns = self._create_namespace(dst_ns_prefix)
|
||||||
|
|
||||||
src_veth, dst_veth = src_ns.add_veth(src_veth,
|
src_veth, dst_veth = src_ns.add_veth(src_veth,
|
||||||
dst_veth,
|
dst_veth,
|
||||||
|
0
neutron/tests/functional/cmd/__init__.py
Normal file
0
neutron/tests/functional/cmd/__init__.py
Normal file
59
neutron/tests/functional/cmd/test_netns_cleanup.py
Executable file
59
neutron/tests/functional/cmd/test_netns_cleanup.py
Executable file
@@ -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)
|
Reference in New Issue
Block a user