All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH] Client: Add cgroup testing v2
@ 2011-08-30 23:32 Lucas Meneghel Rodrigues
  0 siblings, 0 replies; only message in thread
From: Lucas Meneghel Rodrigues @ 2011-08-30 23:32 UTC (permalink / raw)
  To: autotest; +Cc: root, kvm

From: root <root@dhcp-26-193.brq.redhat.com>

This patchset adds a cgroup client test module,
plus support libraries for doing cgroup testing:

cgroup.py:
* Structure for different cgroup subtests
* Contains basic "cgroup-memory" test

cgroup_common.py:
* Library for cgroup handling (intended to be
  used from kvm test in the future)
* Universal smoke_test for every module

cgroup_client.py:
* Application which is executed and controled
  using cgroups
* Contains smoke, memory, cpu and devices tests
  which were manually tested to break cgroup
  rules and will be used in the cgroup.py
  subtests

Changes from v1:
 * Coding style fixes
 * Use of new style classes
 * Drop the use of eval(), replacing with getattr()
 * Replace os.system with utils.system
 * More detailed logging of cgroup module init

Signed-off-by: Lukas Doktor <ldoktor@redhat.com>
---
 client/tests/cgroup/cgroup.py        |  421 ++++++++++++++++++++++++++++++++++
 client/tests/cgroup/cgroup_client.py |  132 +++++++++++
 client/tests/cgroup/cgroup_common.py |  379 ++++++++++++++++++++++++++++++
 client/tests/cgroup/control          |   12 +
 4 files changed, 944 insertions(+), 0 deletions(-)
 create mode 100755 client/tests/cgroup/cgroup.py
 create mode 100755 client/tests/cgroup/cgroup_client.py
 create mode 100755 client/tests/cgroup/cgroup_common.py
 create mode 100644 client/tests/cgroup/control

diff --git a/client/tests/cgroup/cgroup.py b/client/tests/cgroup/cgroup.py
new file mode 100755
index 0000000..2fd0b23
--- /dev/null
+++ b/client/tests/cgroup/cgroup.py
@@ -0,0 +1,421 @@
+import os, logging
+import time
+from tempfile import NamedTemporaryFile
+
+from autotest_lib.client.bin import test, utils
+from autotest_lib.client.common_lib import error
+from cgroup_common import Cgroup as CG
+from cgroup_common import CgroupModules
+
+class cgroup(test.test):
+    """
+    Tests the cgroup functionalities. It works by creating a process (which is
+    also a python application) that will try to use CPU and memory. We will
+    then verify whether the cgroups rules are obeyed.
+    """
+    version = 1
+    _client = ""
+    modules = CgroupModules()
+
+    def run_once(self):
+        """
+	    Try to access different resources which are restricted by cgroup.
+        """
+        logging.info('Starting cgroup testing')
+
+        err = ""
+        # Run available tests
+        for i in ['memory', 'cpuset']:
+            logging.info("---< 'test_%s' START >---", i)
+            try:
+                if not self.modules.get_pwd(i):
+                    raise error.TestFail("module not available/mounted")
+                t_function = getattr(self, "test_%s" % i)
+                t_function()
+                logging.info("---< 'test_%s' PASSED >---", i)
+            except AttributeError:
+                err += "%s, " % i
+                logging.error("test_%s: Test doesn't exist", i)
+                logging.info("---< 'test_%s' FAILED >---", i)
+            except Exception, inst:
+                err += "%s, " % i
+                logging.error("test_%s: %s", i, inst)
+                logging.info("---< 'test_%s' FAILED >---", i)
+
+        if err:
+            logging.error('Some subtests failed (%s)' % err[:-2])
+            raise error.TestFail('Some subtests failed (%s)' % err[:-2])
+
+
+    def setup(self):
+        """
+        Setup
+        """
+        logging.debug('Setting up cgroups modules')
+
+        self._client = os.path.join(self.bindir, "cgroup_client.py")
+
+        _modules = ['cpuset', 'ns', 'cpu', 'cpuacct', 'memory', 'devices',
+                    'freezer', 'net_cls', 'blkio']
+        if (self.modules.init(_modules) <= 0):
+            raise error.TestFail('Can\'t mount any cgroup modules')
+
+
+    def cleanup(self):
+        """
+        Unmount all cgroups and remove directories
+        """
+        logging.info('Cleanup')
+        self.modules.cleanup()
+
+
+    #############################
+    # TESTS
+    #############################
+    def test_memory(self):
+        """
+        Memory test
+        """
+        def cleanup(supress=False):
+            # cleanup
+            logging.debug("test_memory: Cleanup")
+            err = ""
+            if item.rm_cgroup(pwd):
+                err += "\nCan't remove cgroup directory"
+
+            utils.system("swapon -a")
+
+            if err:
+                if supress:
+                    logging.warn("Some parts of cleanup failed%s" % err)
+                else:
+                    raise error.TestFail("Some parts of cleanup failed%s" % err)
+
+        # Preparation
+        item = CG('memory', self._client)
+        if item.initialize(self.modules):
+            raise error.TestFail("cgroup init failed")
+
+        if item.smoke_test():
+            raise error.TestFail("smoke_test failed")
+
+        pwd = item.mk_cgroup()
+        if pwd == None:
+            raise error.TestFail("Can't create cgroup")
+
+        logging.debug("test_memory: Memory filling test")
+
+        f = open('/proc/meminfo','r')
+        mem = f.readline()
+        while not mem.startswith("MemFree"):
+            mem = f.readline()
+        # Use only 1G or max of the free memory
+        mem = min(int(mem.split()[1])/1024, 1024)
+        mem = max(mem, 100) # at least 100M
+        memsw_limit_bytes = item.get_property("memory.memsw.limit_in_bytes",
+                                              supress=True)
+        if memsw_limit_bytes is not None:
+            memsw = True
+            # Clear swap
+            utils.system("swapoff -a")
+            utils.system("swapon -a")
+            f.seek(0)
+            swap = f.readline()
+            while not swap.startswith("SwapTotal"):
+                swap = f.readline()
+            swap = int(swap.split()[1])/1024
+            if swap < mem / 2:
+                logging.error("Not enough swap memory to test 'memsw'")
+                memsw = False
+        else:
+            # Doesn't support swap + memory limitation, disable swap
+            logging.info("System does not support 'memsw'")
+            utils.system("swapoff -a")
+            memsw = False
+        outf = NamedTemporaryFile('w+', prefix="cgroup_client-",
+                                  dir="/tmp")
+        logging.debug("test_memory: Initializition passed")
+
+        ################################################
+        # Fill the memory without cgroup limitation
+        # Should pass
+        ################################################
+        logging.debug("test_memory: Memfill WO cgroup")
+        ps = item.test("memfill %d %s" % (mem, outf.name))
+        ps.stdin.write('\n')
+        i = 0
+        while ps.poll() == None:
+            if i > 60:
+                break
+            i += 1
+            time.sleep(1)
+        if i > 60:
+            ps.terminate()
+            raise error.TestFail("Memory filling failed (WO cgroup)")
+        outf.seek(0)
+        outf.flush()
+        out = outf.readlines()
+        if (len(out) < 2) or (ps.poll() != 0):
+            raise error.TestFail("Process failed (WO cgroup); output:\n%s"
+                                 "\nReturn: %d" % (out, ps.poll()))
+        if not out[-1].startswith("PASS"):
+            raise error.TestFail("Unsuccessful memory filling "
+                                 "(WO cgroup)")
+        logging.debug("test_memory: Memfill WO cgroup passed")
+
+        ################################################
+        # Fill the memory with 1/2 memory limit
+        # memsw: should swap out part of the process and pass
+        # WO memsw: should fail (SIGKILL)
+        ################################################
+        logging.debug("test_memory: Memfill mem only limit")
+        ps = item.test("memfill %d %s" % (mem, outf.name))
+        if item.set_cgroup(ps.pid, pwd):
+            raise error.TestFail("Could not set cgroup")
+        if item.set_prop("memory.limit_in_bytes", ("%dM" % (mem/2)), pwd):
+            raise error.TestFail("Could not set mem limit (mem)")
+        ps.stdin.write('\n')
+        i = 0
+        while ps.poll() == None:
+            if i > 120:
+                break
+            i += 1
+            time.sleep(1)
+        if i > 120:
+            ps.terminate()
+            raise error.TestFail("Memory filling failed (mem)")
+        outf.seek(0)
+        outf.flush()
+        out = outf.readlines()
+        if (len(out) < 2):
+            raise error.TestFail("Process failed (mem); output:\n%s"
+                          "\nReturn: %d" % (out, ps.poll()))
+        if memsw:
+            if not out[-1].startswith("PASS"):
+                logging.error("test_memory: cgroup_client.py returned %d; "
+                              "output:\n%s", ps.poll(), out)
+                raise error.TestFail("Unsuccessful memory filling (mem)")
+        else:
+            if out[-1].startswith("PASS"):
+                raise error.TestFail("Unexpected memory filling (mem)")
+            else:
+                filled = int(out[-2].split()[1][:-1])
+                if mem/2 > 1.5 * filled:
+                    logging.error("test_memory: Limit = %dM, Filled = %dM (+ "
+                                  "python overhead upto 1/3 (mem))", mem/2,
+                                  filled)
+                else:
+                    logging.debug("test_memory: Limit = %dM, Filled = %dM (+ "
+                                  "python overhead upto 1/3 (mem))", mem/2,
+                                  filled)
+        logging.debug("test_memory: Memfill mem only cgroup passed")
+
+        ################################################
+        # Fill the memory with 1/2 memory+swap limit
+        # Should fail
+        # (memory.limit_in_bytes have to be set prior to this test)
+        ################################################
+        if memsw:
+            logging.debug("test_memory: Memfill mem + swap limit")
+            ps = item.test("memfill %d %s" % (mem, outf.name))
+            if item.set_cgroup(ps.pid, pwd):
+                raise error.TestFail("Could not set cgroup (memsw)")
+            if item.set_prop("memory.memsw.limit_in_bytes", "%dM"%(mem/2), pwd):
+                raise error.TestFail("Could not set mem limit (memsw)")
+            ps.stdin.write('\n')
+            i = 0
+            while ps.poll() == None:
+                if i > 120:
+                    break
+                i += 1
+                time.sleep(1)
+            if i > 120:
+                ps.terminate()
+                raise error.TestFail("Memory filling failed (mem)")
+            outf.seek(0)
+            outf.flush()
+            out = outf.readlines()
+            if (len(out) < 2):
+                raise error.TestFail("Process failed (memsw); output:\n%s"
+                                     "\nReturn: %d" % (out, ps.poll()))
+            if out[-1].startswith("PASS"):
+                raise error.TestFail("Unexpected memory filling (memsw)",
+                              mem)
+            else:
+                filled = int(out[-2].split()[1][:-1])
+                if mem / 2 > 1.5 * filled:
+                    logging.error("test_memory: Limit = %dM, Filled = %dM (+ "
+                                  "python overhead upto 1/3 (memsw))", mem/2,
+                                  filled)
+                else:
+                    logging.debug("test_memory: Limit = %dM, Filled = %dM (+ "
+                                  "python overhead upto 1/3 (memsw))", mem/2,
+                                  filled)
+            logging.debug("test_memory: Memfill mem + swap cgroup passed")
+
+        ################################################
+        # CLEANUP
+        ################################################
+        cleanup()
+
+
+
+    def test_cpuset(self):
+        """
+        Cpuset test
+        1) Initiate CPU load on CPU0, than spread into CPU* - CPU0
+        """
+        class per_cpu_load:
+            """
+            Handles the per_cpu_load stats
+            self.values [cpus, cpu0, cpu1, ...]
+            """
+            def __init__(self):
+                """
+                Init
+                """
+                self.values = []
+                self.f = open('/proc/stat', 'r')
+                line = self.f.readline()
+                while line:
+                    if line.startswith('cpu'):
+                        self.values.append(int(line.split()[1]))
+                    else:
+                        break
+                    line = self.f.readline()
+
+            def reload(self):
+                """
+                Reload current values
+                """
+                self.values = self.get()
+
+            def get(self):
+                """
+                Get the current values
+                @return vals: array of current values [cpus, cpu0, cpu1..]
+                """
+                self.f.seek(0)
+                self.f.flush()
+                vals = []
+                for i in range(len(self.values)):
+                    vals.append(int(self.f.readline().split()[1]))
+                return vals
+
+            def tick(self):
+                """
+                Reload values and returns the load between the last tick/reload
+                @return vals: array of load between ticks/reloads
+                              values [cpus, cpu0, cpu1..]
+                """
+                vals = self.get()
+                ret = []
+                for i in range(len(self.values)):
+                    ret.append(vals[i] - self.values[i])
+                self.values = vals
+                return ret
+
+        def cleanup(supress=False):
+            # cleanup
+            logging.debug("test_cpuset: Cleanup")
+            err = ""
+            try:
+                for task in tasks:
+                    for i in range(10):
+                        task.terminate()
+                        if task.poll() != None:
+                            break
+                        time.sleep(1)
+                    if i >= 9:
+                        logging.error("test_cpuset: Subprocess didn't finish")
+            except Exception, inst:
+                err += "\nCan't terminate tasks: %s" % inst
+            if item.rm_cgroup(pwd):
+                err += "\nCan't remove cgroup direcotry"
+            if err:
+                if supress:
+                    logging.warn("Some parts of cleanup failed%s" % err)
+                else:
+                    raise error.TestFail("Some parts of cleanup failed%s" % err)
+
+        # Preparation
+        item = CG('cpuset', self._client)
+        if item.initialize(self.modules):
+            raise error.TestFail("cgroup init failed")
+
+        # FIXME: new cpuset cgroup doesn't have any mems and cpus assigned
+        # thus smoke_test won't work
+        #if item.smoke_test():
+        #    raise error.TestFail("smoke_test failed")
+
+        try:
+            # Available cpus: cpuset.cpus = "0-$CPUS\n"
+            no_cpus = int(item.get_prop("cpuset.cpus").split('-')[1]) + 1
+        except:
+            raise error.TestFail("Failed to get no_cpus or no_cpus = 1")
+
+        pwd = item.mk_cgroup()
+        if pwd == None:
+            raise error.TestFail("Can't create cgroup")
+        # FIXME: new cpuset cgroup doesn't have any mems and cpus assigned
+        try:
+            tmp = item.get_prop("cpuset.cpus")
+            item.set_property("cpuset.cpus", tmp, pwd)
+            tmp = item.get_prop("cpuset.mems")
+            item.set_property("cpuset.mems", tmp, pwd)
+        except:
+            cleanup(True)
+            raise error.TestFail("Failed to set cpus and mems of"
+                                 "a new cgroup")
+
+        ################################################
+        # Cpu allocation test
+        # Use cpu0 and verify, than all cpu* - cpu0 and verify
+        ################################################
+        logging.debug("test_cpuset: Cpu allocation test")
+
+        tasks = []
+        # Run no_cpus + 1 jobs
+        for i in range(no_cpus + 1):
+            tasks.append(item.test("cpu"))
+            if item.set_cgroup(tasks[i].pid, pwd):
+                cleanup(True)
+                raise error.TestFail("Failed to set cgroup")
+            tasks[i].stdin.write('\n')
+        stats = per_cpu_load()
+        # Use only the first CPU
+        item.set_property("cpuset.cpus", 0, pwd)
+        stats.reload()
+        time.sleep(10)
+        # [0] = all cpus
+        s1 = stats.tick()[1:]
+        s2 = s1[1:]
+        s1 = s1[0]
+        for _s in s2:
+            if s1 < _s:
+                cleanup(True)
+                raise error.TestFail("Unused processor had higher utilization\n"
+                                     "used cpu: %s, remaining cpus: %s"
+                                     % (s1, s2))
+
+        if no_cpus == 2:
+            item.set_property("cpuset.cpus", "1", pwd)
+        else:
+            item.set_property("cpuset.cpus", "1-%d"%(no_cpus-1), pwd)
+        stats.reload()
+        time.sleep(10)
+        s1 = stats.tick()[1:]
+        s2 = s1[0]
+        s1 = s1[1:]
+        for _s in s1:
+            if s2 > _s:
+                cleanup(True)
+                raise error.TestFail("Unused processor had higher utilization\n"
+                                     "used cpus: %s, remaining cpu: %s"
+                                     % (s1, s2))
+        logging.debug("test_cpuset: Cpu allocation test passed")
+
+        ################################################
+        # CLEANUP
+        ################################################
+        cleanup()
diff --git a/client/tests/cgroup/cgroup_client.py b/client/tests/cgroup/cgroup_client.py
new file mode 100755
index 0000000..63127f4
--- /dev/null
+++ b/client/tests/cgroup/cgroup_client.py
@@ -0,0 +1,132 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+"""
+Interactive python script for testing cgroups. It will try to use system
+resources such as cpu, memory and device IO. The other cgroups test
+instrumentation will inspect whether the linux box behaved as it should.
+
+@copyright: 2011 Red Hat Inc.
+@author: Lukas Doktor <ldoktor@redhat.com>
+"""
+import array, sys, time, math, os
+from tempfile import mktemp
+
+def test_smoke(args):
+    """
+    SIGSTOP the process and after SIGCONT exits.
+    """
+    print "TEST: smoke"
+    print "TEST: wait for input"
+    raw_input()
+    print "PASS: smoke"
+
+
+def test_memfill(args):
+    """
+    SIGSTOP and after SIGCONT fills the memory up to size size.
+    """
+    size = 1024
+    f = sys.stdout
+    if args:
+        size = int(args[0])
+        if len(args) > 1:
+            f = open(args[1], 'w', 0)
+    print "TEST: memfill (%dM)" % size
+    print "Redirecting to: %s" % f.name
+    f.write("TEST: memfill (%dM)\n" % size)
+    f.write("TEST: wait for input\n")
+    raw_input()
+    mem = array.array('B')
+    buf = ""
+    for i in range(1024 * 1024):
+        buf += '\x00'
+    for i in range(size):
+        mem.fromstring(buf)
+        f.write("TEST: %dM\n" % i)
+        try:
+            f.flush()
+            os.fsync(f)
+        except:
+            pass
+    f.write("PASS: memfill (%dM)\n" % size)
+
+
+def test_cpu(args):
+    """
+    Stress the CPU.
+    """
+    print "TEST: cpu"
+    print "TEST: wait for input"
+    raw_input()
+    while True:
+        for i in range (1000, 10000):
+            math.factorial(i)
+
+
+def test_devices(args):
+    if args:
+        if args[0] == "write":
+            test_devices_write()
+        else:
+            test_devices_read()
+    else:
+        test_devices_read()
+
+
+def test_devices_read():
+    """
+    Inf read from /dev/zero
+    """
+    print "TEST: devices read"
+    print "TEST: wait for input"
+    raw_input()
+
+    dev = open("/dev/zero", 'r')
+    while True:
+        print "TEST: tick"
+        dev.flush()
+        dev.read(1024*1024)
+        time.sleep(1)
+
+
+def test_devices_write():
+    """
+    Inf write into /dev/null device
+    """
+    print "TEST: devices write"
+    print "TEST: wait for input"
+    raw_input()
+
+    dev = open("/dev/null", 'w')
+    buf = ""
+    for _ in range(1024*1024):
+        buf += '\x00'
+    while True:
+        print "TEST: tick"
+        dev.write(buf)
+        dev.flush()
+        time.sleep(1)
+
+
+def main():
+    """
+    Main (infinite) loop.
+    """
+    if len(sys.argv) < 2:
+        print "FAIL: Incorrect usage (%s)" % sys.argv
+        return -1
+    args = sys.argv[2:]
+    if sys.argv[1] == "smoke":
+        test_smoke(args)
+    elif sys.argv[1] == "memfill":
+        test_memfill(args)
+    elif sys.argv[1] == "cpu":
+        test_cpu(args)
+    elif sys.argv[1] == "devices":
+        test_devices(args)
+    else:
+        print "FAIL: No test specified (%s)" % sys.argv
+
+if __name__ == "__main__":
+    main()
+
diff --git a/client/tests/cgroup/cgroup_common.py b/client/tests/cgroup/cgroup_common.py
new file mode 100755
index 0000000..51e93f7
--- /dev/null
+++ b/client/tests/cgroup/cgroup_common.py
@@ -0,0 +1,379 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+"""
+Helpers for cgroup testing.
+
+@copyright: 2011 Red Hat Inc.
+@author: Lukas Doktor <ldoktor@redhat.com>
+"""
+import os, logging, subprocess, time, shutil
+from tempfile import mkdtemp
+from autotest_lib.client.bin import utils
+from autotest_lib.client.common_lib import error
+
+
+class Cgroup(object):
+    """
+    Cgroup handling class.
+    """
+    def __init__(self, module, _client):
+        """
+        Constructor
+        @param module: Name of the cgroup module
+        @param _client: Test script pwd + name
+        """
+        self.module = module
+        self._client = _client
+        self.root = None
+
+
+    def initialize(self, modules):
+        """
+        Initializes object for use.
+
+        @param modules: Array of all available cgroup modules.
+        @return: 0 when PASSED.
+        """
+        self.root = modules.get_pwd(self.module)
+        if self.root:
+            return 0
+        else:
+            logging.error("cg.initialize(): Module %s not found", self.module)
+            return -1
+        return 0
+
+
+    def mk_cgroup(self, root=None):
+        """
+        Creates new temporary cgroup
+        @param root: where to create this cgroup (default: self.root)
+        @return: 0 when PASSED
+        """
+        try:
+            if root:
+                pwd = mkdtemp(prefix='cgroup-', dir=root) + '/'
+            else:
+                pwd = mkdtemp(prefix='cgroup-', dir=self.root) + '/'
+        except Exception, inst:
+            logging.error("cg.mk_cgroup(): %s" , inst)
+            return None
+        return pwd
+
+
+    def rm_cgroup(self, pwd, supress=False):
+        """
+        Removes cgroup.
+
+        @param pwd: cgroup directory.
+        @param supress: supress output.
+        @return: 0 when PASSED
+        """
+        try:
+            os.rmdir(pwd)
+        except Exception, inst:
+            if not supress:
+                logging.error("cg.rm_cgroup(): %s" , inst)
+            return -1
+        return 0
+
+
+    def test(self, cmd):
+        """
+        Executes cgroup_client.py with cmd parameter.
+
+        @param cmd: command to be executed
+        @return: subprocess.Popen() process
+        """
+        logging.debug("cg.test(): executing paralel process '%s'", cmd)
+        cmd = self._client + ' ' + cmd
+        process = subprocess.Popen(cmd, shell=True, stdin=subprocess.PIPE,
+                                   stdout=subprocess.PIPE,
+                                   stderr=subprocess.PIPE, close_fds=True)
+        return process
+
+
+    def is_cgroup(self, pid, pwd):
+        """
+        Checks if the 'pid' process is in 'pwd' cgroup
+        @param pid: pid of the process
+        @param pwd: cgroup directory
+        @return: 0 when is 'pwd' member
+        """
+        if open(pwd + '/tasks').readlines().count("%d\n" % pid) > 0:
+            return 0
+        else:
+            return -1
+
+
+    def is_root_cgroup(self, pid):
+        """
+        Checks if the 'pid' process is in root cgroup (WO cgroup)
+        @param pid: pid of the process
+        @return: 0 when is 'root' member
+        """
+        return self.is_cgroup(pid, self.root)
+
+
+    def set_cgroup(self, pid, pwd):
+        """
+        Sets cgroup membership
+        @param pid: pid of the process
+        @param pwd: cgroup directory
+        @return: 0 when PASSED
+        """
+        try:
+            open(pwd+'/tasks', 'w').write(str(pid))
+        except Exception, inst:
+            logging.error("cg.set_cgroup(): %s" , inst)
+            return -1
+        if self.is_cgroup(pid, pwd):
+            logging.error("cg.set_cgroup(): Setting %d pid into %s cgroup "
+                          "failed", pid, pwd)
+            return -1
+        else:
+            return 0
+
+    def set_root_cgroup(self, pid):
+        """
+        Resets the cgroup membership (sets to root)
+        @param pid: pid of the process
+        @return: 0 when PASSED
+        """
+        return self.set_cgroup(pid, self.root)
+
+
+    def get_prop(self, prop, pwd=None, supress=False):
+        """
+        Gets one line of the property value
+        @param prop: property name (file)
+        @param pwd: cgroup directory
+        @param supress: supress the output
+        @return: String value or None when FAILED
+        """
+        tmp = self.get_property(prop, pwd, supress)
+        if tmp:
+            if tmp[0][-1] == '\n':
+                tmp[0] = tmp[0][:-1]
+            return tmp[0]
+        else:
+            return None
+
+
+    def get_property(self, prop, pwd=None, supress=False):
+        """
+        Gets the property value
+        @param prop: property name (file)
+        @param pwd: cgroup directory
+        @param supress: supress the output
+        @return: [] values or None when FAILED
+        """
+        if pwd == None:
+            pwd = self.root
+        try:
+            ret = open(pwd+prop, 'r').readlines()
+        except Exception, inst:
+            ret = None
+            if not supress:
+                logging.error("cg.get_property(): %s" , inst)
+        return ret
+
+
+    def set_prop(self, prop, value, pwd=None, check=True):
+        """
+        Sets the one-line property value concerning the K,M,G postfix
+        @param prop: property name (file)
+        @param value: desired value
+        @param pwd: cgroup directory
+        @param check: check the value after setup
+        @return: 0 when PASSED
+        """
+        _value = value
+        try:
+            value = str(value)
+            if value[-1] == '\n':
+                value = value[:-1]
+            if value[-1] == 'K':
+                value = int(value[:-1]) * 1024
+            elif value[-1] == 'M':
+                value = int(value[:-1]) * 1048576
+            elif value[-1] == 'G':
+                value = int(value[:-1]) * 1073741824
+        except:
+            logging.error("cg.set_prop() fallback into cg.set_property.")
+            value = _value
+        return self.set_property(prop, value, pwd, check)
+
+
+    def set_property(self, prop, value, pwd=None, check=True):
+        """
+        Sets the property value
+        @param prop: property name (file)
+        @param value: desired value
+        @param pwd: cgroup directory
+        @param check: check the value after setup
+        @return: 0 when PASSED
+        """
+        value = str(value)
+        if pwd == None:
+            pwd = self.root
+        try:
+            open(pwd+prop, 'w').write(value)
+        except Exception, inst:
+            logging.error("cg.set_property(): %s" , inst)
+            return -1
+        if check:
+            # Get the first line - '\n'
+            _value = self.get_property(prop, pwd)[0][:-1]
+            if value != _value:
+                logging.error("cg.set_property(): Setting failed: desired = %s,"
+                              " real value = %s", value, _value)
+                return -1
+        return 0
+
+
+    def smoke_test(self):
+        """
+        Smoke test
+        Module independent basic tests
+        """
+        part = 0
+        pwd = self.mk_cgroup()
+        if pwd == None:
+            logging.error("cg.smoke_test[%d]: Can't create cgroup", part)
+            return -1
+
+        part += 1
+        ps = self.test("smoke")
+        if ps == None:
+            logging.error("cg.smoke_test[%d]: Couldn't create process", part)
+            return -1
+
+        part += 1
+        if (ps.poll() != None):
+            logging.error("cg.smoke_test[%d]: Process died unexpectidly", part)
+            return -1
+
+        # New process should be a root member
+        part += 1
+        if self.is_root_cgroup(ps.pid):
+            logging.error("cg.smoke_test[%d]: Process is not a root member",
+                          part)
+            return -1
+
+        # Change the cgroup
+        part += 1
+        if self.set_cgroup(ps.pid, pwd):
+            logging.error("cg.smoke_test[%d]: Could not set cgroup", part)
+            return -1
+
+        # Try to remove used cgroup
+        part += 1
+        if self.rm_cgroup(pwd, supress=True) == 0:
+            logging.error("cg.smoke_test[%d]: Unexpected successful deletion of"
+                          " the used cgroup", part)
+            return -1
+
+        # Return the process into the root cgroup
+        part += 1
+        if self.set_root_cgroup(ps.pid):
+            logging.error("cg.smoke_test[%d]: Could not return the root cgroup "
+                          "membership", part)
+            return -1
+
+        # It should be safe to remove the cgroup now
+        part += 1
+        if self.rm_cgroup(pwd):
+            logging.error("cg.smoke_test[%d]: Can't remove cgroup directory",
+                          part)
+            return -1
+
+        # Finish the process
+        part += 1
+        ps.stdin.write('\n')
+        time.sleep(2)
+        if (ps.poll() == None):
+            logging.error("cg.smoke_test[%d]: Process is not finished", part)
+            return -1
+
+        return 0
+
+
+class CgroupModules(object):
+    """
+    Handles the list of different cgroup filesystems.
+    """
+    def __init__(self):
+        self.modules = []
+        self.modules.append([])
+        self.modules.append([])
+        self.modules.append([])
+        self.mountdir = mkdtemp(prefix='cgroup-') + '/'
+
+
+    def init(self, _modules):
+        """
+        Checks the mounted modules and if necessary mounts them into tmp
+            mountdir.
+        @param _modules: Desired modules.
+        @return: Number of initialized modules.
+        """
+        logging.debug("Desired cgroup modules: %s", _modules)
+        mounts = []
+        fp = open('/proc/mounts', 'r')
+        line = fp.readline().split()
+        while line:
+            if line[2] == 'cgroup':
+                mounts.append(line)
+            line = fp.readline().split()
+        fp.close()
+
+        for module in _modules:
+            # Is it already mounted?
+            i = False
+            for mount in mounts:
+                if mount[3].find(module) != -1:
+                    self.modules[0].append(module)
+                    self.modules[1].append(mount[1] + '/')
+                    self.modules[2].append(False)
+                    i = True
+                    break
+            if not i:
+                # Not yet mounted
+                os.mkdir(self.mountdir + module)
+                cmd = ('mount -t cgroup -o %s %s %s' %
+                       (module, module, self.mountdir + module))
+                try:
+                    utils.run(cmd)
+                    self.modules[0].append(module)
+                    self.modules[1].append(self.mountdir + module)
+                    self.modules[2].append(True)
+                except error.CmdError:
+                    logging.info("Cgroup module '%s' not available", module)
+
+        logging.debug("Initialized cgroup modules: %s", self.modules[0])
+        return len(self.modules[0])
+
+
+    def cleanup(self):
+        """
+        Unmount all cgroups and remove the mountdir.
+        """
+        for i in range(len(self.modules[0])):
+            if self.modules[2][i]:
+                utils.system('umount %s -l' % self.modules[1][i],
+                             ignore_status=True)
+        shutil.rmtree(self.mountdir)
+
+
+    def get_pwd(self, module):
+        """
+        Returns the mount directory of 'module'
+        @param module: desired module (memory, ...)
+        @return: mount directory of 'module' or None
+        """
+        try:
+            i = self.modules[0].index(module)
+        except Exception, inst:
+            logging.error("module %s not found: %s", module, inst)
+            return None
+        return self.modules[1][i]
diff --git a/client/tests/cgroup/control b/client/tests/cgroup/control
new file mode 100644
index 0000000..86aec06
--- /dev/null
+++ b/client/tests/cgroup/control
@@ -0,0 +1,12 @@
+AUTHOR = "Lukas Doktor <ldoktor@redhat.com>"
+NAME = "Cgroup"
+TIME = "SHORT"
+TEST_CATEGORY = "Functional"
+TEST_CLASS = "General"
+TEST_TYPE = "client"
+
+DOC = """
+This test checks basic functionality of cgroups
+"""
+
+job.run_test('cgroup')
-- 
1.7.6

^ permalink raw reply related	[flat|nested] only message in thread

only message in thread, other threads:[~2011-08-30 23:32 UTC | newest]

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2011-08-30 23:32 [PATCH] Client: Add cgroup testing v2 Lucas Meneghel Rodrigues

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.