diff --git a/etc/neutron/rootwrap.d/debug.filters b/etc/neutron/rootwrap.d/debug.filters index 8d72ce2b1e3..8d7a2dc69e4 100644 --- a/etc/neutron/rootwrap.d/debug.filters +++ b/etc/neutron/rootwrap.d/debug.filters @@ -15,4 +15,8 @@ ping: RegExpFilter, ping, root, ping, -w, \d+, -c, \d+, [0-9\.]+ ping_alt: RegExpFilter, ping, root, ping, -c, \d+, -w, \d+, [0-9\.]+ ping6: RegExpFilter, ping6, root, ping6, -w, \d+, -c, \d+, [0-9A-Fa-f:]+ -ping6_alt: RegExpFilter, ping6, root, ping6, -c, \d+, -w, \d+, [0-9A-Fa-f:]+ \ No newline at end of file +ping6_alt: RegExpFilter, ping6, root, ping6, -c, \d+, -w, \d+, [0-9A-Fa-f:]+ + +# "sleep" command, only for testing +sleep: RegExpFilter, sleep, root, sleep, \d+ +kill_sleep: KillFilter, root, sleep, -9 diff --git a/neutron/agent/linux/ip_lib.py b/neutron/agent/linux/ip_lib.py index 7e008b67e11..81d0acca866 100644 --- a/neutron/agent/linux/ip_lib.py +++ b/neutron/agent/linux/ip_lib.py @@ -932,6 +932,15 @@ def network_namespace_exists(namespace, try_is_ready=False, **kwargs): return False +def list_namespace_pids(namespace): + """List namespace process PIDs + + :param namespace: (string) the name of the namespace + :return: (tuple) + """ + return privileged.list_ns_pids(namespace) + + def ensure_device_is_ready(device_name, namespace=None): dev = IPDevice(device_name, namespace=namespace) try: diff --git a/neutron/privileged/__init__.py b/neutron/privileged/__init__.py index 43bb3c2228e..296dba5c777 100644 --- a/neutron/privileged/__init__.py +++ b/neutron/privileged/__init__.py @@ -25,5 +25,6 @@ default = priv_context.PrivContext( capabilities=[caps.CAP_SYS_ADMIN, caps.CAP_NET_ADMIN, caps.CAP_DAC_OVERRIDE, - caps.CAP_DAC_READ_SEARCH], + caps.CAP_DAC_READ_SEARCH, + caps.CAP_SYS_PTRACE], ) diff --git a/neutron/privileged/agent/linux/ip_lib.py b/neutron/privileged/agent/linux/ip_lib.py index 65122b32ea3..e4c58bf8929 100644 --- a/neutron/privileged/agent/linux/ip_lib.py +++ b/neutron/privileged/agent/linux/ip_lib.py @@ -187,6 +187,12 @@ def open_namespace(namespace): pass +@privileged.default.entrypoint +def list_ns_pids(namespace): + """List namespace process PIDs""" + return netns.ns_pids().get(namespace, []) + + def _translate_ip_device_exception(e, device=None, namespace=None): if e.code == errno.ENODEV: raise NetworkInterfaceNotFound(device=device, namespace=namespace) diff --git a/neutron/tests/common/net_helpers.py b/neutron/tests/common/net_helpers.py index 5a4c9110bb5..4f274935448 100644 --- a/neutron/tests/common/net_helpers.py +++ b/neutron/tests/common/net_helpers.py @@ -585,6 +585,8 @@ class NamespaceFixture(fixtures.Fixture): def destroy(self): if self.ip_wrapper.netns.exists(self.name): + for pid in ip_lib.list_namespace_pids(self.name): + utils.kill_process(pid, signal.SIGKILL, run_as_root=True) self.ip_wrapper.netns.delete(self.name) diff --git a/neutron/tests/functional/privileged/agent/linux/test_ip_lib.py b/neutron/tests/functional/privileged/agent/linux/test_ip_lib.py index 6ec96a96928..32fa9a9abff 100644 --- a/neutron/tests/functional/privileged/agent/linux/test_ip_lib.py +++ b/neutron/tests/functional/privileged/agent/linux/test_ip_lib.py @@ -12,6 +12,9 @@ # License for the specific language governing permissions and limitations # under the License. +import functools + +import eventlet import netaddr from neutron_lib import constants as n_cons from oslo_utils import uuidutils @@ -618,3 +621,35 @@ class GetLinkAttributesTestCase(functional_base.BaseSudoTestCase): 'alias', 'allmulticast', 'link_kind'] attr = self.device.link.attributes self.assertSetEqual(set(expected_attr), set(attr.keys())) + + +class ListNamespacePids(functional_base.BaseSudoTestCase): + + def setUp(self): + super(ListNamespacePids, self).setUp() + self.namespace = self.useFixture(net_helpers.NamespaceFixture()).name + + @staticmethod + def _run_sleep(namespace): + ip_wrapper = ip_lib.IPWrapper(namespace=namespace) + ip_wrapper.netns.execute(['sleep', '100'], check_exit_code=False) + + def _check_pids(self, num_pids, namespace=None): + namespace = self.namespace if not namespace else namespace + self.pids = priv_ip_lib.list_ns_pids(namespace) + return len(self.pids) == num_pids + + def test_list_namespace_pids(self): + eventlet.spawn_n(self._run_sleep, self.namespace) + + try: + check_pids = functools.partial(self._check_pids, 1) + common_utils.wait_until_true(check_pids, timeout=5) + except common_utils.WaitTimeout: + self.fail('Process no found in namespace %s' % self.namespace) + + def test_list_namespace_pids_nothing_running_inside(self): + self.assertTrue(self._check_pids(0)) + + def test_list_namespace_not_created(self): + self.assertTrue(self._check_pids(0, namespace='othernamespace'))