From: Tom Hromatka Date: Tue, 4 Apr 2023 22:31:51 +0000 (+0000) Subject: ftests: Add cgcreate systemd scope support X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=40a0f9576ad5d6c3a73de586645ced0d1ab8a5be;p=thirdparty%2Flibcgroup.git ftests: Add cgcreate systemd scope support Add support for creating systemd scopes via the cgcreate command line tool. Python's subprocess.Popen.communicate() will not return until the spawned process and all of its children finish running. This will cause a cgcreate of a scope to hang because libcgroup creates an idle process in the newly-created scope. Add a timeout to the run() method so that it can complete even when a child process remains. Signed-off-by: Tom Hromatka Reviewed-by: Kamalesh Babulal (cherry picked from commit 5fc8e77fc70667a02af20ad62876f715d96c6241) --- diff --git a/tests/ftests/cgroup.py b/tests/ftests/cgroup.py index 3be9ba04..2d3d3167 100644 --- a/tests/ftests/cgroup.py +++ b/tests/ftests/cgroup.py @@ -127,7 +127,7 @@ class Cgroup(object): def create(config, controller_list, cgname, user_name=None, group_name=None, dperm=None, fperm=None, tperm=None, tasks_user_name=None, tasks_group_name=None, cghelp=False, - ignore_systemd=False): + ignore_systemd=False, create_scope=False, set_default_scope=False): if isinstance(controller_list, str): controller_list = [controller_list] @@ -160,9 +160,20 @@ class Cgroup(object): if cghelp: cmd.append('-h') + # -b and -c are mutually exclusive but the safety check is done in libcgroup + # itself and thus doesn't need to be done here + if ignore_systemd: cmd.append('-b') + if create_scope: + cmd.append('-c') + + # -S requires -c to be provided, but this is checked in libcgroup itself + + if set_default_scope: + cmd.append('-S') + if controller_list: controllers_and_path = '{}:{}'.format( ','.join(controller_list), cgname) @@ -175,14 +186,23 @@ class Cgroup(object): if config.args.container: config.container.run(cmd) else: - Run.run(cmd) + if create_scope: + # creating a scope causes libcgroup to create a child process. + # subprocess.Popen.communicate() will not return until the created + # process and all of its children complete. Get around this by + # timing out the operation + Run.run(cmd, timeout=5) + else: + Run.run(cmd) # This is a simple wrapper, that calls Cgroup.create() to create the # cgroup and cgroup_exists(), which builds the cgroup path and returns # True if the cgroup exists, False otherwise @staticmethod - def create_and_validate(config, ctrl_name, cgroup_name, ignore_systemd=False): - Cgroup.create(config, ctrl_name, cgroup_name, ignore_systemd=ignore_systemd) + def create_and_validate(config, ctrl_name, cgroup_name, ignore_systemd=False, + create_scope=False, set_default_scope=False): + Cgroup.create(config, ctrl_name, cgroup_name, ignore_systemd=ignore_systemd, + create_scope=create_scope, set_default_scope=set_default_scope) return Cgroup.exists(config, ctrl_name, cgroup_name, ignore_systemd=ignore_systemd) @staticmethod diff --git a/tests/ftests/run.py b/tests/ftests/run.py index d7e84431..b6bd3824 100644 --- a/tests/ftests/run.py +++ b/tests/ftests/run.py @@ -6,13 +6,14 @@ # Author: Tom Hromatka # +from subprocess import TimeoutExpired from log import Log import subprocess class Run(object): @staticmethod - def run(command, shell_bool=False, ignore_profiling_errors=True): + def run(command, shell_bool=False, ignore_profiling_errors=True, timeout=None): if shell_bool: if isinstance(command, str): # nothing to do. command is already formatted as a string @@ -25,11 +26,34 @@ class Run(object): subproc = subprocess.Popen(command, shell=shell_bool, stdout=subprocess.PIPE, stderr=subprocess.PIPE) - out, err = subproc.communicate() - ret = subproc.returncode - out = out.strip().decode('UTF-8') - err = err.strip().decode('UTF-8') + if timeout: + try: + out, err = subproc.communicate(timeout=timeout) + ret = subproc.returncode + + out = out.strip().decode('UTF-8') + err = err.strip().decode('UTF-8') + except TimeoutExpired as te: + if te.stdout: + out = te.stdout.strip().decode('UTF-8') + else: + out = '' + if te.stderr: + err = te.stderr.strip().decode('UTF-8') + else: + err = '' + + if len(err): + ret = -1 + else: + ret = 0 + else: + out, err = subproc.communicate() + ret = subproc.returncode + + out = out.strip().decode('UTF-8') + err = err.strip().decode('UTF-8') if shell_bool: Log.log_debug(