def _get_pid_to_kill(self): pid = self._process.pid # If root helper was used, two or more processes will be created: # # - a root helper process (e.g. sudo myscript) # - possibly a rootwrap script (e.g. neutron-rootwrap) # - a child process (e.g. myscript) # # Killing the root helper process will leave the child process # running, re-parented to init, so the only way to ensure that both # die is to target the child process directly. if self.root_helper: try: pid = utils.find_child_pids(pid)[0] except IndexError: # Process is already dead return None while True: try: # We shouldn't have more than one child per process # so keep getting the children of the first one pid = utils.find_child_pids(pid)[0] except IndexError: # Last process in the tree, return it break return pid
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 _kill_listen_processes(namespace, force=False): """Identify all listening processes within the given namespace. Then, for each one, find its top parent with same cmdline (in case this process forked) and issue a SIGTERM to all of them. If force is True, then a SIGKILL will be issued to all parents and all their children. Also, this function returns the number of listening processes. """ pids = find_listen_pids_namespace(namespace) pids_to_kill = {utils.find_fork_top_parent(pid) for pid in pids} kill_signal = signal.SIGTERM if force: kill_signal = signal.SIGKILL children = [utils.find_child_pids(pid, True) for pid in pids_to_kill] pids_to_kill.update(itertools.chain.from_iterable(children)) for pid in pids_to_kill: # Throw a warning since this particular cleanup may need a specific # implementation in the right module. Ideally, netns_cleanup wouldn't # kill any processes as the responsible module should've killed them # before cleaning up the namespace LOG.warning(_LW("Killing (%(signal)d) [%(pid)s] %(cmdline)s"), {'signal': kill_signal, 'pid': pid, 'cmdline': ' '.join(utils.get_cmdline_from_pid(pid))[:80] }) try: utils.kill_process(pid, kill_signal, run_as_root=True) except Exception as ex: LOG.error(_LE('An error occurred while killing ' '[%(pid)s]: %(msg)s'), {'pid': pid, 'msg': ex}) return len(pids)
def _kill_listen_processes(namespace, force=False): """Identify all listening processes within the given namespace. Then, for each one, find its top parent with same cmdline (in case this process forked) and issue a SIGTERM to all of them. If force is True, then a SIGKILL will be issued to all parents and all their children. Also, this function returns the number of listening processes. """ pids = find_listen_pids_namespace(namespace) pids_to_kill = {utils.find_fork_top_parent(pid) for pid in pids} kill_signal = signal.SIGTERM if force: kill_signal = signal.SIGKILL children = [utils.find_child_pids(pid, True) for pid in pids_to_kill] pids_to_kill.update(itertools.chain.from_iterable(children)) for pid in pids_to_kill: # Throw a warning since this particular cleanup may need a specific # implementation in the right module. Ideally, netns_cleanup wouldn't # kill any processes as the responsible module should've killed them # before cleaning up the namespace LOG.warning("Killing (%(signal)d) [%(pid)s] %(cmdline)s", {'signal': kill_signal, 'pid': pid, 'cmdline': ' '.join(utils.get_cmdline_from_pid(pid))[:80] }) try: utils.kill_process(pid, kill_signal, run_as_root=True) except Exception as ex: LOG.error('An error occurred while killing ' '[%(pid)s]: %(msg)s', {'pid': pid, 'msg': ex}) return len(pids)
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 _get_pid_to_kill(self): pid = self._process.pid # If root helper was used, two processes will be created: # # - a root helper process (e.g. sudo myscript) # - a child process (e.g. myscript) # # Killing the root helper process will leave the child process # as a zombie, so the only way to ensure that both die is to # target the child process directly. if self.root_helper: pids = utils.find_child_pids(pid) if pids: # The root helper will only ever launch a single child. pid = pids[0] else: # Process is already dead. pid = None return pid
def test_raises_unknown_exception(self): with testtools.ExpectedException(RuntimeError): with mock.patch.object(utils, 'execute', side_effect=RuntimeError()): 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_empty_list_for_no_output(self): with mock.patch.object(utils, 'execute', return_value=''): self.assertEqual([], utils.find_child_pids(-1))
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_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_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))
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_returns_empty_list_for_exit_code_1(self): with mock.patch.object(utils, 'execute', side_effect=RuntimeError('Exit code: 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_empty_list_for_no_output(self): with mock.patch.object(utils, "execute", return_value=""): self.assertEqual([], utils.find_child_pids(-1))