From: Kamalesh Babulal Date: Fri, 10 Feb 2023 21:54:22 +0000 (-0700) Subject: ftests: Add a test to validate cgx{get,set} systemd options (cgroup v2) X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=32d97e4433bbaa1d6e691cb782ff12a9718dd2b7;p=thirdparty%2Flibcgroup.git ftests: Add a test to validate cgx{get,set} systemd options (cgroup v2) Add a test case to validate cgx{get, set} tool's systemd options on cgroup unified setup mode. ----------------------------------------------------------------- Test Results: Run Date: Feb 05 08:15:40 Passed: 1 test(s) Skipped: 0 test(s) Failed: 0 test(s) ----------------------------------------------------------------- Timing Results: Test Time (sec) --------------------------------------------------------- setup 0.00 070-sudo-systemd_cgxget-cpu-settings-v2.py 3.00 teardown 0.00 --------------------------------------------------------- Total Run Time Signed-off-by: Kamalesh Babulal Signed-off-by: Tom Hromatka (cherry picked from commit 418842dcae4285741f1a8ed22a41bfe03d58bdb7) --- diff --git a/tests/ftests/070-sudo-systemd_cgxget-cpu-settings-v2.py b/tests/ftests/070-sudo-systemd_cgxget-cpu-settings-v2.py new file mode 100755 index 00000000..81ebba6f --- /dev/null +++ b/tests/ftests/070-sudo-systemd_cgxget-cpu-settings-v2.py @@ -0,0 +1,181 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: LGPL-2.1-only +# +# Advanced cgxget/cgxset functionality test - '-b' '-g' (cgroup v2) +# +# Copyright (c) 2023 Oracle and/or its affiliates. +# Author: Kamalesh Babulal +# + +from cgroup import Cgroup, CgroupVersion +from systemd import Systemd +from run import RunError +import consts +import ftests +import sys +import os + + +CONTROLLER = 'cpu' +SYSTEMD_CGNAME = '070_cg_in_scope' +OTHER_CGNAME = '070_cg_not_in_scope' + +SLICE = 'libcgtests.slice' +SCOPE = 'test070.scope' + +CONFIG_FILE_NAME = os.path.join(os.getcwd(), '070cgconfig.conf') + +CGRP_VER_V1 = CgroupVersion.CGROUP_V1 +CGRP_VER_V2 = CgroupVersion.CGROUP_V2 + +TABLE = [ + # writesetting, writeval, writever, readsetting, readval, readver + ['cpu.shares', '512', CGRP_VER_V1, 'cpu.shares', '512', CGRP_VER_V1], + ['cpu.shares', '512', CGRP_VER_V1, 'cpu.weight', '50', CGRP_VER_V2], + + ['cpu.weight', '200', CGRP_VER_V2, 'cpu.shares', '2048', CGRP_VER_V1], + ['cpu.weight', '200', CGRP_VER_V2, 'cpu.weight', '200', CGRP_VER_V2], + + ['cpu.cfs_quota_us', '10000', CGRP_VER_V1, + 'cpu.cfs_quota_us', '10000', CGRP_VER_V1], + ['cpu.cfs_period_us', '100000', CGRP_VER_V1, + 'cpu.cfs_period_us', '100000', CGRP_VER_V1], + ['cpu.cfs_period_us', '50000', CGRP_VER_V1, + 'cpu.max', '10000 50000', CGRP_VER_V2], + + ['cpu.cfs_quota_us', '-1', CGRP_VER_V1, + 'cpu.cfs_quota_us', '-1', CGRP_VER_V1], + ['cpu.cfs_period_us', '100000', CGRP_VER_V1, + 'cpu.max', 'max 100000', CGRP_VER_V2], + + ['cpu.max', '5000 25000', CGRP_VER_V2, + 'cpu.max', '5000 25000', CGRP_VER_V2], + ['cpu.max', '6000 26000', CGRP_VER_V2, + 'cpu.cfs_quota_us', '6000', CGRP_VER_V1], + ['cpu.max', '7000 27000', CGRP_VER_V2, + 'cpu.cfs_period_us', '27000', CGRP_VER_V1], + + ['cpu.max', 'max 40000', CGRP_VER_V2, + 'cpu.max', 'max 40000', CGRP_VER_V2], + ['cpu.max', 'max 41000', CGRP_VER_V2, + 'cpu.cfs_quota_us', '-1', CGRP_VER_V1], +] + + +def prereqs(config): + result = consts.TEST_PASSED + cause = None + + if CgroupVersion.get_version('cpu') != CgroupVersion.CGROUP_V2: + result = consts.TEST_SKIPPED + cause = 'This test requires the cgroup v2 cpu controller' + return result, cause + + if config.args.container: + result = consts.TEST_SKIPPED + cause = 'This test cannot be run within a container' + + return result, cause + + +def setup(config): + result = consts.TEST_PASSED + cause = None + + pid = Systemd.write_config_with_pid(config, CONFIG_FILE_NAME, SLICE, SCOPE) + + Cgroup.configparser(config, load_file=CONFIG_FILE_NAME) + + # create and check if the cgroup was created under the systemd default path + if not Cgroup.create_and_validate(config, None, SYSTEMD_CGNAME): + result = consts.TEST_FAILED + cause = ( + 'Failed to create systemd delegated cgroup {} under ' + '/sys/fs/cgroup/{}/{}/'.format(SYSTEMD_CGNAME, SLICE, SCOPE) + ) + return result, cause + + # With cgroup v2, we can't enable controller for the child cgroup, while + # a task is attached to test070.scope. Attach the task from test070.scope + # to child cgroup SYSTEMD_CGNAME and then enable cpu controller in the parent + # and then in the SYSTEMD_CGNAME cgroup, so that the cgroup.get() works + Cgroup.set(config, cgname=SYSTEMD_CGNAME, setting='cgroup.procs', value=pid) + + Cgroup.set( + config, cgname=(os.path.join(SLICE, SCOPE)), setting='cgroup.subtree_control', + value='+cpu', ignore_systemd=True + ) + Cgroup.set(config, cgname=SYSTEMD_CGNAME, setting='cgroup.subtree_control', value='+cpu') + + # create and check if the cgroup was created under the controller root + if not Cgroup.create_and_validate(config, CONTROLLER, OTHER_CGNAME, ignore_systemd=True): + result = consts.TEST_FAILED + cause = ( + 'Failed to create cgroup {} under ' + '/sys/fs/cgroup/{}/'.format(OTHER_CGNAME, CONTROLLER) + ) + + return result, cause + + +def test(config): + result = consts.TEST_PASSED + cause = None + + cgrps = {SYSTEMD_CGNAME: False, OTHER_CGNAME: True} + for i in cgrps: + for entry in TABLE: + Cgroup.xset(config, cgname=i, setting=entry[0], value=entry[1], + version=entry[2], ignore_systemd=cgrps[i]) + + out = Cgroup.xget(config, cgname=i, setting=entry[3], + version=entry[5], values_only=True, + print_headers=False, ignore_systemd=cgrps[i]) + if out != entry[4]: + result = consts.TEST_FAILED + tmp_cause = ( + 'After setting {}={}, expected {}={}, but received ' + '{}={}'.format(entry[0], entry[1], entry[3], entry[4], + entry[3], out) + ) + cause = '\n'.join(filter(None, [cause, tmp_cause])) + + return result, cause + + +def teardown(config): + Systemd.remove_scope_slice_conf(config, SLICE, SCOPE, CONTROLLER, CONFIG_FILE_NAME) + + # Incase the error occurs before the creation of OTHER_CGNAME, + # let's ignore the exception + try: + Cgroup.delete(config, CONTROLLER, OTHER_CGNAME, ignore_systemd=True) + except RunError as re: + if 'No such file or directory' not in re.stderr: + raise re + + +def main(config): + [result, cause] = prereqs(config) + if result != consts.TEST_PASSED: + return [result, cause] + + [result, cause] = setup(config) + if result != consts.TEST_PASSED: + return [result, cause] + + try: + [result, cause] = test(config) + finally: + teardown(config) + + return [result, cause] + + +if __name__ == '__main__': + config = ftests.parse_args() + # this test was invoked directly. run only it + config.args.num = int(os.path.basename(__file__).split('-')[0]) + sys.exit(ftests.main(config)) + +# vim: set et ts=4 sw=4: diff --git a/tests/ftests/Makefile.am b/tests/ftests/Makefile.am index c853db69..d10c7c99 100644 --- a/tests/ftests/Makefile.am +++ b/tests/ftests/Makefile.am @@ -90,6 +90,7 @@ EXTRA_DIST_PYTHON_TESTS = \ 067-sudo-systemd_cgexec-v1.py \ 068-sudo-systemd_cgexec-v2.py \ 069-sudo-systemd_cgxget-cpu-settings-v1.py \ + 070-sudo-systemd_cgxget-cpu-settings-v2.py \ 098-cgdelete-non-existing-shared-mnt-cgroup-v1.py # Intentionally omit the stress test from the extra dist # 999-stress-cgroup_init.py