Make the process class instantiable so that it can better manage its list of children processes. This helps ensure that individual tests don't collide with each other.
Signed-off-by: Tom Hromatka <tom.hroma...@oracle.com> --- ftests/002-cgdelete-recursive_delete.py | 4 +- ftests/config.py | 10 ++-- ftests/container.py | 6 +-- ftests/ftests.py | 3 +- ftests/process.py | 66 ++++++++++++++++++------- 5 files changed, 62 insertions(+), 27 deletions(-) diff --git a/ftests/002-cgdelete-recursive_delete.py b/ftests/002-cgdelete-recursive_delete.py index 220a763a87a0..34dd28ff9692 100755 --- a/ftests/002-cgdelete-recursive_delete.py +++ b/ftests/002-cgdelete-recursive_delete.py @@ -2,7 +2,7 @@ # # Cgroup recursive cgdelete functionality test # -# Copyright (c) 2020 Oracle and/or its affiliates. +# Copyright (c) 2020-2021 Oracle and/or its affiliates. # Author: Tom Hromatka <tom.hroma...@oracle.com> # @@ -49,7 +49,7 @@ def setup(config): # Moving a process from a child cgroup to its parent isn't (easily) # supported in cgroup v2 because of cgroup v2's restriction that # processes only be located in leaf cgroups - Process.create_process_in_cgroup(config, CONTROLLER, + config.process.create_process_in_cgroup(config, CONTROLLER, os.path.join(PARENT, CHILD, GRANDCHILD)) def test(config): diff --git a/ftests/config.py b/ftests/config.py index 10664609113f..55b3da04b853 100644 --- a/ftests/config.py +++ b/ftests/config.py @@ -22,6 +22,8 @@ import consts from container import Container import os +from process import Process +import utils class Config(object): def __init__(self, args, container=None): @@ -36,6 +38,8 @@ class Config(object): stop_timeout=args.timeout, arch=None, distro=args.distro, release=args.release) + self.process = Process() + self.ftest_dir = os.path.dirname(os.path.abspath(__file__)) self.libcg_dir = os.path.dirname(self.ftest_dir) @@ -44,13 +48,13 @@ class Config(object): self.verbose = False def __str__(self): - out_str = "Configuration" + out_str = "Configuration\n" if self.args.container: - out_str += "\n\tcontainer = {}".format(self.container) + out_str += utils.indent(str(self.container), 4) + out_str += utils.indent(str(self.process), 4) return out_str - class ConfigError(Exception): def __init__(self, message): super(ConfigError, self).__init__(message) diff --git a/ftests/container.py b/ftests/container.py index 8fc468d05a50..a944a5dc5843 100644 --- a/ftests/container.py +++ b/ftests/container.py @@ -1,7 +1,7 @@ # # Container class for the libcgroup functional tests # -# Copyright (c) 2019 Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2019-2021 Oracle and/or its affiliates. # Author: Tom Hromatka <tom.hroma...@oracle.com> # @@ -58,11 +58,11 @@ class Container(object): def __str__(self): - out_str = "{}".format(self.name) + out_str = "Container {}".format(self.name) out_str += "\n\tdistro = {}".format(self.distro) out_str += "\n\trelease = {}".format(self.release) out_str += "\n\tarch = {}".format(self.arch) - out_str += "\n\tstop_timeout = {}".format(self.stop_timeout) + out_str += "\n\tstop_timeout = {}\n".format(self.stop_timeout) return out_str diff --git a/ftests/ftests.py b/ftests/ftests.py index 6b3c96f897fb..e2efb7b52b3a 100755 --- a/ftests/ftests.py +++ b/ftests/ftests.py @@ -141,6 +141,7 @@ def update_host_subgid(): def setup(config, do_teardown=True, record_time=False): global setup_time + start_time = time.time() if do_teardown: # belt and suspenders here. In case a previous run wasn't properly @@ -289,7 +290,7 @@ def teardown(config, record_time=False): global teardown_time start_time = time.time() - Process.join_children() + config.process.join_children(config) if config.args.container: try: diff --git a/ftests/process.py b/ftests/process.py index 8996ce32382c..6b5d9c9ff2e9 100644 --- a/ftests/process.py +++ b/ftests/process.py @@ -22,26 +22,40 @@ from cgroup import Cgroup import multiprocessing as mp from run import Run +from run import RunError import time -children = list() - class Process(object): + def __init__(self): + self.children = list() + self.children_pids = list() + + def __str__(self): + out_str = "Process Class\n" + out_str += "\tchildren = {}\n".format(self.children) + out_str += "\tchildren_pids = {}\n".format(self.children_pids) + + return out_str + @staticmethod def __infinite_loop(config, sleep_time=1): - cmd = ['nohup', 'perl', '-e', '\'while(1){{sleep({})}};\''.format(sleep_time), '&'] + cmd = ['/usr/bin/perl', '-e', '\'while(1){{sleep({})}};\''.format(sleep_time)] - if config.args.container: - config.container.run(cmd, shell_bool=True) - else: - Run.run(cmd, shell_bool=True) + try: + if config.args.container: + config.container.run(cmd, shell_bool=True) + else: + Run.run(cmd, shell_bool=True) + except RunError as re: + # when the process is killed, a RunError will be thrown. let's + # catch and suppress it + pass - @staticmethod - def create_process(config): + def create_process(self, config): # To allow for multiple processes to be created, each new process # sleeps for a different amount of time. This lets us uniquely find # each process later in this function - sleep_time = len(children) + 1 + sleep_time = len(self.children) + 1 p = mp.Process(target=Process.__infinite_loop, args=(config, sleep_time, )) @@ -56,22 +70,38 @@ class Process(object): if config.args.container: pid = config.container.run(cmd, shell_bool=True) else: - pid = Run.run(cmd, shell_bool=True) + pid = Run.run(cmd, shell_bool=True).decode('ascii') + + for _pid in pid.splitlines(): + self.children_pids.append(_pid) + + if pid.find('\n') >= 0: + # The second pid in the list contains the actual perl process + pid = pid.splitlines()[1] if pid == "" or int(pid) <= 0: raise ValueException('Failed to get the pid of the child process: {}'.format(pid)) - children.append(p) + self.children.append(p) return pid # Create a simple process in the requested cgroup - @staticmethod - def create_process_in_cgroup(config, controller, cgname): - child_pid = Process.create_process(config) + def create_process_in_cgroup(self, config, controller, cgname): + child_pid = self.create_process(config) Cgroup.classify(config, controller, cgname, child_pid) # The caller will block until all children are stopped. - @staticmethod - def join_children(): - for child in children: + def join_children(self, config): + for child in self.children: child.join(1) + + for child in self.children_pids: + try: + if config.args.container: + config.container.run(['kill', child]) + else: + Run.run(['kill', child]) + except: + # ignore any errors during the kill command. this is belt + # and suspenders code + pass -- 2.26.2 _______________________________________________ Libcg-devel mailing list Libcg-devel@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/libcg-devel