From: Tom Hromatka Date: Wed, 3 Feb 2021 17:32:14 +0000 (-0700) Subject: ftests: Make the Process() class instantiable X-Git-Tag: v2.0.3~11^2^2~26^2~2 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=1046263e8db539702ceb1be6693281e92fd52565;p=thirdparty%2Flibcgroup.git ftests: Make the Process() class instantiable 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 --- diff --git a/ftests/002-cgdelete-recursive_delete.py b/ftests/002-cgdelete-recursive_delete.py index 220a763a..34dd28ff 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 # @@ -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 10664609..55b3da04 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 8fc468d0..a944a5dc 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 # @@ -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 6b3c96f8..e2efb7b5 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 8996ce32..6b5d9c9f 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