You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
380 lines
11 KiB
380 lines
11 KiB
#!/usr/bin/python2
|
|
# -*- coding: utf-8 -*-
|
|
"""
|
|
Helpers for cgroup testing.
|
|
|
|
@copyright: 2011 Red Hat Inc.
|
|
@author: Lukas Doktor <ldoktor@redhat.com>
|
|
"""
|
|
import os, logging, subprocess, time, shutil
|
|
from tempfile import mkdtemp
|
|
from autotest_lib.client.bin import utils
|
|
from autotest_lib.client.common_lib import error
|
|
|
|
|
|
class Cgroup(object):
|
|
"""
|
|
Cgroup handling class.
|
|
"""
|
|
def __init__(self, module, _client):
|
|
"""
|
|
Constructor
|
|
@param module: Name of the cgroup module
|
|
@param _client: Test script pwd + name
|
|
"""
|
|
self.module = module
|
|
self._client = _client
|
|
self.root = None
|
|
|
|
|
|
def initialize(self, modules):
|
|
"""
|
|
Initializes object for use.
|
|
|
|
@param modules: Array of all available cgroup modules.
|
|
@return: 0 when PASSED.
|
|
"""
|
|
self.root = modules.get_pwd(self.module)
|
|
if self.root:
|
|
return 0
|
|
else:
|
|
logging.error("cg.initialize(): Module %s not found", self.module)
|
|
return -1
|
|
return 0
|
|
|
|
|
|
def mk_cgroup(self, root=None):
|
|
"""
|
|
Creates new temporary cgroup
|
|
@param root: where to create this cgroup (default: self.root)
|
|
@return: 0 when PASSED
|
|
"""
|
|
try:
|
|
if root:
|
|
pwd = mkdtemp(prefix='cgroup-', dir=root) + '/'
|
|
else:
|
|
pwd = mkdtemp(prefix='cgroup-', dir=self.root) + '/'
|
|
except Exception, inst:
|
|
logging.error("cg.mk_cgroup(): %s" , inst)
|
|
return None
|
|
return pwd
|
|
|
|
|
|
def rm_cgroup(self, pwd, supress=False):
|
|
"""
|
|
Removes cgroup.
|
|
|
|
@param pwd: cgroup directory.
|
|
@param supress: supress output.
|
|
@return: 0 when PASSED
|
|
"""
|
|
try:
|
|
os.rmdir(pwd)
|
|
except Exception, inst:
|
|
if not supress:
|
|
logging.error("cg.rm_cgroup(): %s" , inst)
|
|
return -1
|
|
return 0
|
|
|
|
|
|
def test(self, cmd):
|
|
"""
|
|
Executes cgroup_client.py with cmd parameter.
|
|
|
|
@param cmd: command to be executed
|
|
@return: subprocess.Popen() process
|
|
"""
|
|
logging.debug("cg.test(): executing paralel process '%s'", cmd)
|
|
cmd = self._client + ' ' + cmd
|
|
process = subprocess.Popen(cmd, shell=True, stdin=subprocess.PIPE,
|
|
stdout=subprocess.PIPE,
|
|
stderr=subprocess.PIPE, close_fds=True)
|
|
return process
|
|
|
|
|
|
def is_cgroup(self, pid, pwd):
|
|
"""
|
|
Checks if the 'pid' process is in 'pwd' cgroup
|
|
@param pid: pid of the process
|
|
@param pwd: cgroup directory
|
|
@return: 0 when is 'pwd' member
|
|
"""
|
|
if open(pwd + '/tasks').readlines().count("%d\n" % pid) > 0:
|
|
return 0
|
|
else:
|
|
return -1
|
|
|
|
|
|
def is_root_cgroup(self, pid):
|
|
"""
|
|
Checks if the 'pid' process is in root cgroup (WO cgroup)
|
|
@param pid: pid of the process
|
|
@return: 0 when is 'root' member
|
|
"""
|
|
return self.is_cgroup(pid, self.root)
|
|
|
|
|
|
def set_cgroup(self, pid, pwd):
|
|
"""
|
|
Sets cgroup membership
|
|
@param pid: pid of the process
|
|
@param pwd: cgroup directory
|
|
@return: 0 when PASSED
|
|
"""
|
|
try:
|
|
open(pwd+'/tasks', 'w').write(str(pid))
|
|
except Exception, inst:
|
|
logging.error("cg.set_cgroup(): %s" , inst)
|
|
return -1
|
|
if self.is_cgroup(pid, pwd):
|
|
logging.error("cg.set_cgroup(): Setting %d pid into %s cgroup "
|
|
"failed", pid, pwd)
|
|
return -1
|
|
else:
|
|
return 0
|
|
|
|
def set_root_cgroup(self, pid):
|
|
"""
|
|
Resets the cgroup membership (sets to root)
|
|
@param pid: pid of the process
|
|
@return: 0 when PASSED
|
|
"""
|
|
return self.set_cgroup(pid, self.root)
|
|
|
|
|
|
def get_prop(self, prop, pwd=None, supress=False):
|
|
"""
|
|
Gets one line of the property value
|
|
@param prop: property name (file)
|
|
@param pwd: cgroup directory
|
|
@param supress: supress the output
|
|
@return: String value or None when FAILED
|
|
"""
|
|
tmp = self.get_property(prop, pwd, supress)
|
|
if tmp:
|
|
if tmp[0][-1] == '\n':
|
|
tmp[0] = tmp[0][:-1]
|
|
return tmp[0]
|
|
else:
|
|
return None
|
|
|
|
|
|
def get_property(self, prop, pwd=None, supress=False):
|
|
"""
|
|
Gets the property value
|
|
@param prop: property name (file)
|
|
@param pwd: cgroup directory
|
|
@param supress: supress the output
|
|
@return: [] values or None when FAILED
|
|
"""
|
|
if pwd == None:
|
|
pwd = self.root
|
|
try:
|
|
ret = open(pwd+prop, 'r').readlines()
|
|
except Exception, inst:
|
|
ret = None
|
|
if not supress:
|
|
logging.error("cg.get_property(): %s" , inst)
|
|
return ret
|
|
|
|
|
|
def set_prop(self, prop, value, pwd=None, check=True):
|
|
"""
|
|
Sets the one-line property value concerning the K,M,G postfix
|
|
@param prop: property name (file)
|
|
@param value: desired value
|
|
@param pwd: cgroup directory
|
|
@param check: check the value after setup
|
|
@return: 0 when PASSED
|
|
"""
|
|
_value = value
|
|
try:
|
|
value = str(value)
|
|
if value[-1] == '\n':
|
|
value = value[:-1]
|
|
if value[-1] == 'K':
|
|
value = int(value[:-1]) * 1024
|
|
elif value[-1] == 'M':
|
|
value = int(value[:-1]) * 1048576
|
|
elif value[-1] == 'G':
|
|
value = int(value[:-1]) * 1073741824
|
|
except:
|
|
logging.error("cg.set_prop() fallback into cg.set_property.")
|
|
value = _value
|
|
return self.set_property(prop, value, pwd, check)
|
|
|
|
|
|
def set_property(self, prop, value, pwd=None, check=True):
|
|
"""
|
|
Sets the property value
|
|
@param prop: property name (file)
|
|
@param value: desired value
|
|
@param pwd: cgroup directory
|
|
@param check: check the value after setup
|
|
@return: 0 when PASSED
|
|
"""
|
|
value = str(value)
|
|
if pwd == None:
|
|
pwd = self.root
|
|
try:
|
|
open(pwd+prop, 'w').write(value)
|
|
except Exception, inst:
|
|
logging.error("cg.set_property(): %s" , inst)
|
|
return -1
|
|
if check:
|
|
# Get the first line - '\n'
|
|
_value = self.get_property(prop, pwd)[0][:-1]
|
|
if value != _value:
|
|
logging.error("cg.set_property(): Setting failed: desired = %s,"
|
|
" real value = %s", value, _value)
|
|
return -1
|
|
return 0
|
|
|
|
|
|
def smoke_test(self):
|
|
"""
|
|
Smoke test
|
|
Module independent basic tests
|
|
"""
|
|
part = 0
|
|
pwd = self.mk_cgroup()
|
|
if pwd == None:
|
|
logging.error("cg.smoke_test[%d]: Can't create cgroup", part)
|
|
return -1
|
|
|
|
part += 1
|
|
ps = self.test("smoke")
|
|
if ps == None:
|
|
logging.error("cg.smoke_test[%d]: Couldn't create process", part)
|
|
return -1
|
|
|
|
part += 1
|
|
if (ps.poll() != None):
|
|
logging.error("cg.smoke_test[%d]: Process died unexpectidly", part)
|
|
return -1
|
|
|
|
# New process should be a root member
|
|
part += 1
|
|
if self.is_root_cgroup(ps.pid):
|
|
logging.error("cg.smoke_test[%d]: Process is not a root member",
|
|
part)
|
|
return -1
|
|
|
|
# Change the cgroup
|
|
part += 1
|
|
if self.set_cgroup(ps.pid, pwd):
|
|
logging.error("cg.smoke_test[%d]: Could not set cgroup", part)
|
|
return -1
|
|
|
|
# Try to remove used cgroup
|
|
part += 1
|
|
if self.rm_cgroup(pwd, supress=True) == 0:
|
|
logging.error("cg.smoke_test[%d]: Unexpected successful deletion of"
|
|
" the used cgroup", part)
|
|
return -1
|
|
|
|
# Return the process into the root cgroup
|
|
part += 1
|
|
if self.set_root_cgroup(ps.pid):
|
|
logging.error("cg.smoke_test[%d]: Could not return the root cgroup "
|
|
"membership", part)
|
|
return -1
|
|
|
|
# It should be safe to remove the cgroup now
|
|
part += 1
|
|
if self.rm_cgroup(pwd):
|
|
logging.error("cg.smoke_test[%d]: Can't remove cgroup directory",
|
|
part)
|
|
return -1
|
|
|
|
# Finish the process
|
|
part += 1
|
|
ps.stdin.write('\n')
|
|
time.sleep(2)
|
|
if (ps.poll() == None):
|
|
logging.error("cg.smoke_test[%d]: Process is not finished", part)
|
|
return -1
|
|
|
|
return 0
|
|
|
|
|
|
class CgroupModules(object):
|
|
"""
|
|
Handles the list of different cgroup filesystems.
|
|
"""
|
|
def __init__(self):
|
|
self.modules = []
|
|
self.modules.append([])
|
|
self.modules.append([])
|
|
self.modules.append([])
|
|
self.mountdir = mkdtemp(prefix='cgroup-') + '/'
|
|
|
|
|
|
def init(self, _modules):
|
|
"""
|
|
Checks the mounted modules and if necessary mounts them into tmp
|
|
mountdir.
|
|
@param _modules: Desired modules.
|
|
@return: Number of initialized modules.
|
|
"""
|
|
logging.debug("Desired cgroup modules: %s", _modules)
|
|
mounts = []
|
|
fp = open('/proc/mounts', 'r')
|
|
line = fp.readline().split()
|
|
while line:
|
|
if line[2] == 'cgroup':
|
|
mounts.append(line)
|
|
line = fp.readline().split()
|
|
fp.close()
|
|
|
|
for module in _modules:
|
|
# Is it already mounted?
|
|
i = False
|
|
for mount in mounts:
|
|
if mount[3].find(module) != -1:
|
|
self.modules[0].append(module)
|
|
self.modules[1].append(mount[1] + '/')
|
|
self.modules[2].append(False)
|
|
i = True
|
|
break
|
|
if not i:
|
|
# Not yet mounted
|
|
os.mkdir(self.mountdir + module)
|
|
cmd = ('mount -t cgroup -o %s %s %s' %
|
|
(module, module, self.mountdir + module))
|
|
try:
|
|
utils.run(cmd)
|
|
self.modules[0].append(module)
|
|
self.modules[1].append(self.mountdir + module)
|
|
self.modules[2].append(True)
|
|
except error.CmdError:
|
|
logging.info("Cgroup module '%s' not available", module)
|
|
|
|
logging.debug("Initialized cgroup modules: %s", self.modules[0])
|
|
return len(self.modules[0])
|
|
|
|
|
|
def cleanup(self):
|
|
"""
|
|
Unmount all cgroups and remove the mountdir.
|
|
"""
|
|
for i in range(len(self.modules[0])):
|
|
if self.modules[2][i]:
|
|
utils.system('umount %s -l' % self.modules[1][i],
|
|
ignore_status=True)
|
|
shutil.rmtree(self.mountdir)
|
|
|
|
|
|
def get_pwd(self, module):
|
|
"""
|
|
Returns the mount directory of 'module'
|
|
@param module: desired module (memory, ...)
|
|
@return: mount directory of 'module' or None
|
|
"""
|
|
try:
|
|
i = self.modules[0].index(module)
|
|
except Exception, inst:
|
|
logging.error("module %s not found: %s", module, inst)
|
|
return None
|
|
return self.modules[1][i]
|