new file mode 100755
@@ -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()
new file mode 100755
@@ -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()
+
new file mode 100755
@@ -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]
new file mode 100644
@@ -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')