From mboxrd@z Thu Jan 1 00:00:00 1970 From: Jiri Zupka Subject: Re: [PATCH 1/4] [NEW] cgroup test * general smoke_test + module dependend subtests (memory test included) * library for future use in other tests (kvm) Date: Thu, 18 Aug 2011 07:33:22 -0400 (EDT) Message-ID: <919030077.251272.1313667202539.JavaMail.root@zmail05.collab.prod.int.phx2.redhat.com> References: <1313411017-2873-2-git-send-email-ldoktor@redhat.com> Mime-Version: 1.0 Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit Cc: kvm@vger.kernel.org, root , autotest@test.kernel.org, kvm-autotest@redhat.com To: Lukas Doktor Return-path: In-Reply-To: <1313411017-2873-2-git-send-email-ldoktor@redhat.com> List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: autotest-bounces@test.kernel.org Errors-To: autotest-bounces@test.kernel.org List-Id: kvm.vger.kernel.org Hi, some minor problem in some timeout. Commented down in code. Otherwise good work. ----- Original Message ----- > From: root > > 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 > > Signed-off-by: Lukas Doktor > --- > client/tests/cgroup/cgroup.py | 239 +++++++++++++++++++++++++ > client/tests/cgroup/cgroup_client.py | 116 ++++++++++++ > client/tests/cgroup/cgroup_common.py | 327 > ++++++++++++++++++++++++++++++++++ > client/tests/cgroup/control | 12 ++ > 4 files changed, 694 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..112f012 > --- /dev/null > +++ b/client/tests/cgroup/cgroup.py > @@ -0,0 +1,239 @@ > +from autotest_lib.client.bin import test > +from autotest_lib.client.common_lib import error > +import os, logging > +import time > +from cgroup_common import Cgroup as CG > +from cgroup_common import CgroupModules > + > +class cgroup(test.test): > + """ > + Tests the cgroup functionalities > + """ > + version = 1 > + _client = "" > + modules = CgroupModules() > + > + > + def run_once(self): > + """ > + Try to access different resources which are restricted by cgroup. > + """ > + logging.info('Start') > + > + err = "" > + # Run available tests > + for i in ['memory']: > + try: > + if self.modules.get_pwd(i): > + if (eval ("self.test_%s()" % i)): > + err += "%s, " % i > + else: > + logging.error("CGROUP: Skipping test_%s, module not " > + "available/mounted", i) > + err += "%s, " % i > + except Exception, inst: > + logging.error("CGROUP: test_%s fatal failure: %s", i, inst) > + err += "%s, " % i > + > + if err: > + raise error.TestFail('CGROUP: Some subtests failed (%s)' % err[:-2]) > + > + > + def setup(self): > + """ > + Setup > + """ > + logging.info('Setup') > + > + 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 > + """ > + # Preparation > + logging.info("Entering 'test_memory'") > + item = CG('memory', self._client) > + if item.initialize(self.modules): > + logging.error("test_memory: cgroup init failed") > + return -1 > + > + if item.smoke_test(): > + logging.error("test_memory: smoke_test failed") > + return -1 > + > + pwd = item.mk_cgroup() > + if pwd == None: > + logging.error("test_memory: Can't create cgroup") > + return -1 > + > + logging.debug("test_memory: Memory filling test") > + > + f = open('/proc/meminfo','r') Not clean way how to do this.. It is better to use regular expression. But this is absolutely no important. > + 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 > + if (item.get_property("memory.memsw.limit_in_bytes", supress=True) > + != None): > + memsw = True > + # Clear swap > + os.system("swapoff -a") > + os.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("'memsw' not supported") > + os.system("swapoff -a") > + memsw = False > + 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" % mem) > + ps.stdin.write('\n') > + i = 0 > + while ps.poll() == None: > + if i > 60: > + break > + i += 1 > + time.sleep(1) > + if i > 60: > + logging.error("test_memory: Memory filling failed (WO cgroup)") > + ps.terminate() > + return -1 > + if not ps.stdout.readlines()[-1].startswith("PASS"): > + logging.error("test_memory: Unsuccessful memory filling " > + "(WO cgroup)") > + return -1 > + 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" % mem) > + if item.set_cgroup(ps.pid, pwd): > + logging.error("test_memory: Could not set cgroup") > + return -1 > + if item.set_property("memory.limit_in_bytes", (1024*1024*mem/2), > pwd): > + logging.error("test_memory: Could not set mem limit (mem)") > + return -1 > + ps.stdin.write('\n') > + i = 0 > + while ps.poll() == None: > + if i > 60: > + break > + i += 1 > + time.sleep(1) > + if i > 60: > + logging.error("test_memory: Memory filling failed (mem)") > + ps.terminate() > + return -1 > + out = ps.stdout.readlines() > + if len(out) < 2: > + logging.error("test_memory: Process failed; output:\n%s", out) > + return -1 > + if memsw: > + if not out[-1].startswith("PASS"): > + logging.error("test_memory: Unsuccessful memory filling (mem)") > + logging.error("test_memory: cgroup_client.py returned %d; " > + "output:\n%s", ps.poll(), out) > + return -1 > + else: > + if out[-1].startswith("PASS"): > + logging.error("test_memory: Unexpected memory filling (mem)") > + return -1 > + 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 > + ################################################ > + logging.debug("test_memory: Memfill mem + swap limit") > + if memsw: > + ps = item.test("memfill %d" % mem) > + if item.set_cgroup(ps.pid, pwd): > + logging.error("test_memory: Could not set cgroup (memsw)") > + return -1 > + if item.set_property("memory.memsw.limit_in_bytes", > + (1024*1024*mem/2), pwd): > + logging.error("test_memory: Could not set mem limit (memsw)") > + return -1 > + ps.stdin.write('\n') > + i = 0 > + while ps.poll() == None: > + if i > 60: > + break > + i += 1 > + time.sleep(1) > + if i > 60: > + logging.error("test_memory: Memory filling failed (mem)") > + ps.terminate() > + return -1 > + out = ps.stdout.readlines() > + if len(out) < 2: > + logging.error("test_memory: Process failed; output:\n%s", out) > + return -1 > + if out[-1].startswith("PASS"): > + logging.error("test_memory: Unexpected memory filling (memsw)", > + mem) > + return -1 > + 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 > + logging.debug("test_memory: Cleanup") > + if item.rm_cgroup(pwd): > + logging.error("test_memory: Can't remove cgroup directory") > + return -1 > + os.system("swapon -a") > + > + logging.info("Leaving 'test_memory': PASSED") > + return 0 > diff --git a/client/tests/cgroup/cgroup_client.py > b/client/tests/cgroup/cgroup_client.py > new file mode 100755 > index 0000000..ff098ef > --- /dev/null > +++ b/client/tests/cgroup/cgroup_client.py > @@ -0,0 +1,116 @@ > +#!/usr/bin/python > +# -*- coding: utf-8 -*- > +""" > +Interactive python script for testing cgroups > + > +@copyright: 2011 Red Hat Inc. > +@author: Lukas Doktor > +""" > +import array, sys, time, math > + > +def test_smoke(): > + """ > + SIGSTOP the process and after SIGCONT exits. > + """ > + print "TEST: smoke" > + print "TEST: wait for input" > + raw_input() > + print "PASS: smoke" > + > + > +def test_memfill(size=1024): > + """ > + SIGSTOP and after SIGCONT fills the memory up to size size. > + """ > + print "TEST: memfill (%dM)" % size > + print "TEST: wait for input" > + raw_input() > + mem = array.array('B') > + buf = "" > + for i in range(1024*1024): > + buf += '\x00' > + for i in range(size): > + mem.fromstring(buf) > + #for j in range(1024*1024): > + # mem.append(0) > + print "TEST: %dM" % i > + print "PASS: memfill (%dM)" % size > + > + > +def test_cpu(): > + """ > + 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_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" > + return -1 > + if sys.argv[1] == "smoke": > + test_smoke() > + elif sys.argv[1] == "memfill": > + if len(sys.argv) > 2: > + test_memfill(int(sys.argv[2])) > + else: > + test_memfill() > + elif sys.argv[1] == "cpu": > + test_cpu() > + elif sys.argv[1] == "devices": > + if len(sys.argv) > 2: > + if (sys.argv[2] == "write"): > + test_devices_write() > + else: > + test_devices_read() > + else: > + test_devices_read() > + else: > + print "FAIL: No test specified" > + > +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..3fd1cf7 > --- /dev/null > +++ b/client/tests/cgroup/cgroup_common.py > @@ -0,0 +1,327 @@ > +#!/usr/bin/python > +# -*- coding: utf-8 -*- > +""" > +Helpers for cgroup testing > + > +@copyright: 2011 Red Hat Inc. > +@author: Lukas Doktor > +""" > +import os, logging > +import subprocess > +from tempfile import mkdtemp > +import time > + > +class Cgroup: > + """ > + 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): > + """ > + Inicializes 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) > + process = subprocess.Popen((self._client + ' ' + 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_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: String value 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_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 direcotry", > + part) > + return -1 > + > + # Finish the process > + part += 1 > + ps.stdin.write('\n') > + time.sleep(2) There should be bigger timeout. This is sometime make problem. Process ends correct way but not in timeout. > + if (ps.poll() == None): > + logging.error("cg.smoke_test[%d]: Process is not finished", part) > + return -1 > + > + return 0 > + > + > +class CgroupModules: > + """ > + 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 necessarily mounts them into tmp > + mountdir. > + @param _modules: desired modules > + @return: Number of initialized 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) > + logging.info('mount -t cgroup -o %s %s %s', module, module, > + self.mountdir+module) > + if (os.system('mount -t cgroup -o %s %s %s' > + % (module, module, self.mountdir+module)) == 0): > + self.modules[0].append(module) > + self.modules[1].append(self.mountdir+module) > + self.modules[2].append(True) > + else: > + logging.error("Module '%s' is not available, mount failed", > + module) > + 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]: > + os.system('umount %s -l' % self.modules[1][i]) > + os.system('rm -rf %s' % 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') > -- > 1.7.6