From patchwork Tue Aug 30 23:32:38 2011 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Lucas Meneghel Rodrigues X-Patchwork-Id: 1114612 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by demeter1.kernel.org (8.14.4/8.14.4) with ESMTP id p7UNZHef014220 for ; Tue, 30 Aug 2011 23:35:18 GMT Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1753561Ab1H3Xcx (ORCPT ); Tue, 30 Aug 2011 19:32:53 -0400 Received: from mx1.redhat.com ([209.132.183.28]:33387 "EHLO mx1.redhat.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1753192Ab1H3Xcw (ORCPT ); Tue, 30 Aug 2011 19:32:52 -0400 Received: from int-mx10.intmail.prod.int.phx2.redhat.com (int-mx10.intmail.prod.int.phx2.redhat.com [10.5.11.23]) by mx1.redhat.com (8.14.4/8.14.4) with ESMTP id p7UNWpeR018747 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=OK); Tue, 30 Aug 2011 19:32:51 -0400 Received: from freedom.local.com (vpn-8-93.rdu.redhat.com [10.11.8.93]) by int-mx10.intmail.prod.int.phx2.redhat.com (8.14.4/8.14.4) with ESMTP id p7UNWmTE008542; Tue, 30 Aug 2011 19:32:49 -0400 From: Lucas Meneghel Rodrigues To: autotest@test.kernel.org Cc: kvm@vger.kernel.org, root , Lukas Doktor Subject: [PATCH] Client: Add cgroup testing v2 Date: Tue, 30 Aug 2011 20:32:38 -0300 Message-Id: <1314747158-14101-1-git-send-email-lmr@redhat.com> X-Scanned-By: MIMEDefang 2.68 on 10.5.11.23 Sender: kvm-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: kvm@vger.kernel.org X-Greylist: IP, sender and recipient auto-whitelisted, not delayed by milter-greylist-4.2.6 (demeter1.kernel.org [140.211.167.41]); Tue, 30 Aug 2011 23:35:18 +0000 (UTC) From: root 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 --- 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 +""" +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 +""" +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 " +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')