Merge "Implement "find_child_pids" natively"

This commit is contained in:
Zuul 2020-11-20 02:27:57 +00:00 committed by Gerrit Code Review
commit 2fba36a88e
3 changed files with 37 additions and 43 deletions

View File

@ -164,20 +164,14 @@ def find_child_pids(pid, recursive=False):
It can also find all children through the hierarchy if recursive=True
"""
try:
raw_pids = execute(['ps', '--ppid', pid, '-o', 'pid='],
log_fail_as_error=False)
except exceptions.ProcessExecutionError as e:
# Unexpected errors are the responsibility of the caller
with excutils.save_and_reraise_exception() as ctxt:
# Exception has already been logged by execute
no_children_found = e.returncode == 1
if no_children_found:
ctxt.reraise = False
return []
child_pids = [x.strip() for x in raw_pids.split('\n') if x.strip()]
process = psutil.Process(pid=int(pid))
except psutil.NoSuchProcess:
return []
child_pids = [str(p.pid) for p in process.children()]
if recursive:
for child in child_pids:
child_pids = child_pids + find_child_pids(child, True)
child_pids += find_child_pids(child, recursive=True)
return child_pids

View File

@ -141,3 +141,34 @@ class TestGetProcessCountByName(functional_base.BaseSudoTestCase):
# NOTE(ralonsoh): other tests can spawn sleep processes too, but at
# this point we know there are, at least, 20 "sleep" processes running.
self.assertLessEqual(20, number_of_sleep)
class TestFindChildPids(functional_base.BaseSudoTestCase):
def _stop_process(self, process):
process.stop(kill_signal=signal.SIGKILL)
def test_find_child_pids(self):
pid = os.getppid()
child_pids = utils.find_child_pids(pid)
child_pids_recursive = utils.find_child_pids(pid, recursive=True)
for _pid in child_pids:
self.assertIn(_pid, child_pids_recursive)
cmd = ['sleep', '100']
process = async_process.AsyncProcess(cmd)
process.start()
common_utils.wait_until_true(lambda: process._process.pid,
sleep=0.5, timeout=10)
self.addCleanup(self._stop_process, process)
child_pids_after = utils.find_child_pids(pid)
child_pids_recursive_after = utils.find_child_pids(pid, recursive=True)
self.assertEqual(child_pids, child_pids_after)
for _pid in child_pids + [process.pid]:
self.assertIn(_pid, child_pids_recursive_after)
def test_find_non_existing_process(self):
with open('/proc/sys/kernel/pid_max', 'r') as fd:
pid_max = int(fd.readline().strip())
self.assertEqual([], utils.find_child_pids(pid_max))

View File

@ -313,37 +313,6 @@ class TestGetCmdlineFromPid(base.BaseTestCase):
self.assertEqual([], cmdline)
class TestFindChildPids(base.BaseTestCase):
def test_returns_empty_list_for_exit_code_1(self):
with mock.patch.object(utils, 'execute',
side_effect=exceptions.ProcessExecutionError(
'', returncode=1)):
self.assertEqual([], utils.find_child_pids(-1))
def test_returns_empty_list_for_no_output(self):
with mock.patch.object(utils, 'execute', return_value=''):
self.assertEqual([], utils.find_child_pids(-1))
def test_returns_list_of_child_process_ids_for_good_ouput(self):
with mock.patch.object(utils, 'execute', return_value=' 123 \n 185\n'):
self.assertEqual(utils.find_child_pids(-1), ['123', '185'])
def test_returns_list_of_child_process_ids_recursively(self):
with mock.patch.object(utils, 'execute',
side_effect=[' 123 \n 185\n',
' 40 \n', '\n',
'41\n', '\n']):
actual = utils.find_child_pids(-1, True)
self.assertEqual(actual, ['123', '185', '40', '41'])
def test_raises_unknown_exception(self):
with testtools.ExpectedException(RuntimeError):
with mock.patch.object(utils, 'execute',
side_effect=RuntimeError()):
utils.find_child_pids(-1)
class TestGetRoothelperChildPid(base.BaseTestCase):
def _test_get_root_helper_child_pid(self, expected=_marker,
run_as_root=False, pids=None,