diff mbox

[1/4,NEW] cgroup test * general smoke_test + module dependend subtests (memory test included) * library for future use in other tests (kvm)

Message ID 1313411017-2873-2-git-send-email-ldoktor@redhat.com (mailing list archive)
State New, archived
Headers show

Commit Message

Lukáš Doktor Aug. 15, 2011, 12:23 p.m. UTC
From: root <root@dhcp-26-193.brq.redhat.com>

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 <ldoktor@redhat.com>
---
 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

Comments

Jiri Zupka Aug. 18, 2011, 11:33 a.m. UTC | #1
Hi,
  some minor problem in some timeout. 
Commented down in code. 
Otherwise good work.

----- Original Message -----
> From: root <root@dhcp-26-193.brq.redhat.com>
> 
> 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 <ldoktor@redhat.com>
> ---
> 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 <ldoktor@redhat.com>
> +"""
> +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 <ldoktor@redhat.com>
> +"""
> +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 <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
--
To unsubscribe from this list: send the line "unsubscribe kvm" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Lukáš Doktor Aug. 21, 2011, 9:15 a.m. UTC | #2
#SNIP

>> + 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.
>

OKi, anyway Ypu is trying to get get_mem_usage() function into utils. 
I'll use it then.
>> + mem = f.readline()
>> + while not mem.startswith("MemFree"):
>> + mem = f.readline()

#SNIP

>> + 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.
>

OK, Lucas, can you please change it in patchset (if you intend to accept 
it?). 10 seconds seems to be more safe DL, thanks.

>> + if (ps.poll() == None):
>> + logging.error("cg.smoke_test[%d]: Process is not finished", part)
>> + return -1
>> +
>> + return 0
>> +
>> +

#SNIP

Thank you, Ji?í.

kind regards,
Lukáš
--
To unsubscribe from this list: send the line "unsubscribe kvm" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
diff mbox

Patch

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')
+        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 <ldoktor@redhat.com>
+"""
+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 <ldoktor@redhat.com>
+"""
+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)
+        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 <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')