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

Reply via email to