diff --git a/neutron/agent/linux/utils.py b/neutron/agent/linux/utils.py index 8c8387447b7..4e7bee42e76 100644 --- a/neutron/agent/linux/utils.py +++ b/neutron/agent/linux/utils.py @@ -314,7 +314,20 @@ def get_cmdline_from_pid(pid): if not process_is_running(pid): return [] with open('/proc/%s/cmdline' % pid, 'r') as f: - return f.readline().split('\0')[:-1] + cmdline = f.readline() + cmdline_args = cmdline.split('\0')[:-1] + + # NOTE(slaweq): sometimes it may happen that values in + # /proc/{pid}/cmdline are separated by space instead of NUL char, + # in such case we would have everything in one element of cmdline_args + # list and it would not match to expected cmd so we need to try to + # split it by spaces + if len(cmdline_args) == 1: + cmdline_args = cmdline_args[0].split(' ') + + LOG.debug("Found cmdline %s for rocess with PID %s.", + cmdline_args, pid) + return cmdline_args def cmd_matches_expected(cmd, expected_cmd): diff --git a/neutron/tests/unit/agent/linux/test_utils.py b/neutron/tests/unit/agent/linux/test_utils.py index d94cf0c4a31..145071d628f 100644 --- a/neutron/tests/unit/agent/linux/test_utils.py +++ b/neutron/tests/unit/agent/linux/test_utils.py @@ -26,6 +26,7 @@ from neutron.agent.linux import utils from neutron.common import exceptions as n_exc from neutron.tests import base from neutron.tests.common import helpers +from neutron.tests import tools _marker = object() @@ -293,6 +294,43 @@ class TestKillProcess(base.BaseTestCase): self._test_kill_process('1', kill_signal=signal.SIGTERM) +class TestGetCmdlineFromPid(base.BaseTestCase): + + def setUp(self): + super(TestGetCmdlineFromPid, self).setUp() + self.pid = 34 + self.process_is_running_mock = mock.patch.object( + utils, "process_is_running").start() + + def _test_cmdline(self, process, expected_cmd): + self.process_is_running_mock.return_value = True + mock_open = self.useFixture( + tools.OpenFixture('/proc/%s/cmdline' % self.pid, process) + ).mock_open + cmdline = utils.get_cmdline_from_pid(self.pid) + mock_open.assert_called_once_with('/proc/%s/cmdline' % self.pid, 'r') + self.assertEqual(expected_cmd, cmdline) + + def test_cmdline_separated_with_null_char(self): + process_cmd = "python3\0test-binary\0test option\0" + expected_cmdline = ["python3", "test-binary", "test option"] + self._test_cmdline(process_cmd, expected_cmdline) + + def test_cmdline_separated_with_space_char(self): + process_cmd = "python3 test-binary test option\0" + expected_cmdline = ["python3", "test-binary", "test", "option"] + self._test_cmdline(process_cmd, expected_cmdline) + + def test_no_process_running(self): + self.process_is_running_mock.return_value = False + mock_open = self.useFixture( + tools.OpenFixture('/proc/%s/cmdline' % self.pid) + ).mock_open + cmdline = utils.get_cmdline_from_pid(self.pid) + mock_open.assert_not_called() + self.assertEqual([], cmdline) + + class TestFindChildPids(base.BaseTestCase): def test_returns_empty_list_for_exit_code_1(self):