#!/usr/bin/env python3 # SPDX-License-Identifier: LGPL-2.1-only # # Test to create a systemd scope with pid using cgcreate # # Copyright (c) 2023 Oracle and/or its affiliates. # Author: Kamalesh Babulal # from cgroup import Cgroup from process import Process from libcgroup import Mode from run import RunError import consts import ftests import sys import os CONTROLLERS = ['cpu', 'pids'] SLICE = 'libcgroup.slice' CGNAME1 = os.path.join(SLICE, '084cgcreate1.scope') CGNAME2 = os.path.join(SLICE, '084cgcreate2.scope') def prereqs(config): result = consts.TEST_PASSED cause = None if config.args.container: result = consts.TEST_SKIPPED cause = 'This test cannot be run within a container' return result, cause if Cgroup.get_cgroup_mode(config) != Mode.CGROUP_MODE_UNIFIED: result = consts.TEST_SKIPPED cause = 'This test requires the unified cgroup hierarchy' return result, cause def setup(config): pass def test(config): result = consts.TEST_PASSED cause = None # # Test 1: Pass invalid task pid as scope_pid and systemd scope creation # should fail # try: Cgroup.create_and_validate(config, CONTROLLERS, CGNAME1, create_scope=True, scope_pid=1000000) except RunError as re: if 'Process with ID 1000000 does not exist' not in str(re): raise re # # Test 2: Pass a valid task pid as scope_pid and systemd scope creation # will succeed. Read the scope cgroup.procs to find if the task # passed by us was used as scope task. # scope_pid = config.process.create_process(config) Cgroup.create_and_validate(config, CONTROLLERS, CGNAME1, create_scope=True, scope_pid=scope_pid) try: pid = Cgroup.get_pids_in_cgroup(config, CGNAME1, CONTROLLERS[0])[0] if scope_pid != pid: result = consts.TEST_FAILED cause = 'scope created with other pid {}, expected pid {}'.format(pid, scope_pid) return result, cause except RunError: result = consts.TEST_FAILED cause = 'Failed to read pid in {}\'s cgroup.procs'.format(CGNAME1) return result, cause # # Test 3: Pass the already created task pid as scope_pid for a new systemd # scope. This would mean the CGNAME1 should be killed and the pid # should be the default scope task of CGNAME2 Cgroup.create_and_validate(config, CONTROLLERS, CGNAME2, create_scope=True, scope_pid=scope_pid) # CGNAME1 should be deleted by the systemd. try: pid = Cgroup.get_pids_in_cgroup(config, CGNAME1, CONTROLLERS[0])[0] except RunError as re: if 'No such file or directory' not in re.stderr: raise re else: result = consts.TEST_FAILED cause = 'Erroneously succeeded reading cgroup.procs in {}'.format(CGNAME1) return result, cause try: pid = Cgroup.get_pids_in_cgroup(config, CGNAME2, CONTROLLERS[0])[0] if scope_pid != pid: result = consts.TEST_FAILED cause = 'scope created with other pid {}, expected pid {}'.format(pid, scope_pid) return result, cause except RunError: result = consts.TEST_FAILED cause = 'Failed to read pid in {}\'s cgroup.procs'.format(CGNAME2) return result, cause def teardown(config): pid = Cgroup.get_pids_in_cgroup(config, CGNAME2, CONTROLLERS[0])[0] Process.kill(config, pid) # systemd will automatically remove the cgroup once there are no more pids in # the cgroup, so we don't need to delete CGNAME2. But let's try to remove the # slice try: Cgroup.delete(config, CONTROLLERS, SLICE) except RunError: pass def main(config): [result, cause] = prereqs(config) if result != consts.TEST_PASSED: return [result, cause] setup(config) [result, cause] = test(config) 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: