@ -1,12 +0,0 @@ | |||
# neutron-rootwrap command filters for nodes on which neutron is | |||
# expected to control network | |||
# | |||
# This file should be owned by (and only-writeable by) the root user | |||
# format seems to be | |||
# cmd-name: filter-name, raw-command, user, args | |||
[Filters] | |||
# netns-cleanup | |||
netstat: CommandFilter, netstat, root |
@ -0,0 +1,42 @@ | |||
# Copyright 2020 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. | |||
import re | |||
from oslo_concurrency import processutils | |||
from neutron import privileged | |||
NETSTAT_PIDS_REGEX = re.compile(r'.* (?P<pid>\d{2,6})/.*') | |||
@privileged.default.entrypoint | |||
def find_listen_pids_namespace(namespace): | |||
return _find_listen_pids_namespace(namespace) | |||
def _find_listen_pids_namespace(namespace): | |||
"""Retrieve a list of pids of listening processes within the given netns | |||
This method is implemented separately to allow unit testing. | |||
""" | |||
pids = set() | |||
cmd = ['ip', 'netns', 'exec', namespace, 'netstat', '-nlp'] | |||
output = processutils.execute(*cmd) | |||
for line in output[0].splitlines(): | |||
m = NETSTAT_PIDS_REGEX.match(line) | |||
if m: | |||
pids.add(m.group('pid')) | |||
return list(pids) |
@ -0,0 +1,39 @@ | |||
# Copyright 2020 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.linux import ip_lib | |||
from neutron.privileged.agent.linux import utils as priv_utils | |||
from neutron.tests.common import net_helpers | |||
from neutron.tests.functional import base as functional_base | |||
class FindListenPidsNamespaceTestCase(functional_base.BaseSudoTestCase): | |||
def test_find_listen_pids_namespace(self): | |||
ns = self.useFixture(net_helpers.NamespaceFixture()).name | |||
ip_wrapper = ip_lib.IPWrapper(namespace=ns) | |||
ip_wrapper.add_dummy('device') | |||
device = ip_lib.IPDevice('device', namespace=ns) | |||
device.addr.add('10.20.30.40/24') | |||
device.link.set_up() | |||
self.assertEqual(tuple(), priv_utils.find_listen_pids_namespace(ns)) | |||
netcat = net_helpers.NetcatTester(ns, ns, '10.20.30.40', 12345, 'udp') | |||
proc = netcat.server_process | |||
self.assertEqual((str(proc.child_pid), ), | |||
priv_utils.find_listen_pids_namespace(ns)) | |||
netcat.stop_processes() | |||
self.assertEqual(tuple(), priv_utils.find_listen_pids_namespace(ns)) |
@ -0,0 +1,76 @@ | |||
# Copyright 2020 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 unittest import mock | |||
from oslo_concurrency import processutils | |||
from neutron.privileged.agent.linux import utils as priv_utils | |||
from neutron.tests import base | |||
NETSTAT_NETNS_OUTPUT = (""" | |||
Active Internet connections (only servers) | |||
Proto Recv-Q Send-Q Local Address Foreign Address State\ | |||
PID/Program name | |||
tcp 0 0 0.0.0.0:9697 0.0.0.0:* LISTEN\ | |||
1347/python | |||
raw 0 0 0.0.0.0:112 0.0.0.0:* 7\ | |||
1279/keepalived | |||
raw 0 0 0.0.0.0:112 0.0.0.0:* 7\ | |||
1279/keepalived | |||
raw6 0 0 :::58 :::* 7\ | |||
1349/radvd | |||
Active UNIX domain sockets (only servers) | |||
Proto RefCnt Flags Type State I-Node PID/Program name\ | |||
Path | |||
unix 2 [ ACC ] STREAM LISTENING 82039530 1353/python\ | |||
/tmp/rootwrap-VKSm8a/rootwrap.sock | |||
""") | |||
NETSTAT_NO_NAMESPACE = (""" | |||
Cannot open network namespace "qrouter-e6f206b2-4e8d-4597-a7e1-c3a20337e9c6":\ | |||
No such file or directory | |||
""") | |||
NETSTAT_NO_LISTEN_PROCS = (""" | |||
Active Internet connections (only servers) | |||
Proto Recv-Q Send-Q Local Address Foreign Address State\ | |||
PID/Program name | |||
Active UNIX domain sockets (only servers) | |||
Proto RefCnt Flags Type State I-Node PID/Program name\ | |||
Path | |||
""") | |||
class FindListenPidsNamespaceTestCase(base.BaseTestCase): | |||
def _test_find_listen_pids_namespace_helper(self, expected, | |||
netstat_output=None): | |||
with mock.patch.object(processutils, 'execute') as mock_execute: | |||
mock_execute.return_value = (netstat_output, mock.ANY) | |||
observed = priv_utils._find_listen_pids_namespace(mock.ANY) | |||
self.assertEqual(sorted(expected), sorted(observed)) | |||
def test_find_listen_pids_namespace_correct_output(self): | |||
expected = ['1347', '1279', '1349', '1353'] | |||
self._test_find_listen_pids_namespace_helper(expected, | |||
NETSTAT_NETNS_OUTPUT) | |||
def test_find_listen_pids_namespace_no_procs(self): | |||
self._test_find_listen_pids_namespace_helper([], | |||
NETSTAT_NO_LISTEN_PROCS) | |||
def test_find_listen_pids_namespace_no_namespace(self): | |||
self._test_find_listen_pids_namespace_helper([], NETSTAT_NO_NAMESPACE) |