Move utils to base_utils
Signed-off-by: Martin Bligh <[email protected]>
git-svn-id: http://test.kernel.org/svn/autotest/trunk@2666 592f7852-d20e-0410-864c-8624ca9c26a4
diff --git a/client/bin/base_utils.py b/client/bin/base_utils.py
new file mode 100755
index 0000000..9fb7a68
--- /dev/null
+++ b/client/bin/base_utils.py
@@ -0,0 +1,641 @@
+"""
+DO NOT import this file directly - import client/bin/utils.py,
+which will mix this in
+
+Convenience functions for use by tests or whomever.
+
+Note that this file is mixed in by utils.py - note very carefully the
+precedence order defined there
+"""
+import os, shutil, sys, signal, commands, pickle, glob, statvfs
+import math, re, string, fnmatch
+from autotest_lib.client.common_lib import error, utils
+
+
+def grep(pattern, file):
+ """
+ This is mainly to fix the return code inversion from grep
+ Also handles compressed files.
+
+ returns 1 if the pattern is present in the file, 0 if not.
+ """
+ command = 'grep "%s" > /dev/null' % pattern
+ ret = cat_file_to_cmd(file, command, ignore_status=True)
+ return not ret
+
+
+def difflist(list1, list2):
+ """returns items in list2 that are not in list1"""
+ diff = [];
+ for x in list2:
+ if x not in list1:
+ diff.append(x)
+ return diff
+
+
+def cat_file_to_cmd(file, command, ignore_status=0, return_output=False):
+ """
+ equivalent to 'cat file | command' but knows to use
+ zcat or bzcat if appropriate
+ """
+ if not os.path.isfile(file):
+ raise NameError('invalid file %s to cat to command %s'
+ % (file, command))
+
+ if return_output:
+ run_cmd = utils.system_output
+ else:
+ run_cmd = utils.system
+
+ if file.endswith('.bz2'):
+ cat = 'bzcat'
+ elif (file.endswith('.gz') or file.endswith('.tgz')):
+ cat = 'zcat'
+ else:
+ cat = 'cat'
+ return run_cmd('%s %s | %s' % (cat, file, command),
+ ignore_status=ignore_status)
+
+
+def extract_tarball_to_dir(tarball, dir):
+ """
+ Extract a tarball to a specified directory name instead of whatever
+ the top level of a tarball is - useful for versioned directory names, etc
+ """
+ if os.path.exists(dir):
+ raise NameError, 'target %s already exists' % dir
+ pwd = os.getcwd()
+ os.chdir(os.path.dirname(os.path.abspath(dir)))
+ newdir = extract_tarball(tarball)
+ os.rename(newdir, dir)
+ os.chdir(pwd)
+
+
+def extract_tarball(tarball):
+ """Returns the directory extracted by the tarball."""
+ extracted = cat_file_to_cmd(tarball, 'tar xvf - 2>/dev/null',
+ return_output=True).splitlines()
+
+ dir = None
+
+ for line in extracted:
+ line = re.sub(r'^./', '', line)
+ if not line or line == '.':
+ continue
+ topdir = line.split('/')[0]
+ if os.path.isdir(topdir):
+ if dir:
+ assert(dir == topdir)
+ else:
+ dir = topdir
+ if dir:
+ return dir
+ else:
+ raise NameError('extracting tarball produced no dir')
+
+
+def get_md5sum(file_path):
+ """Gets the md5sum of a file. You must provide a valid path to the file"""
+ if not os.path.isfile(file_path):
+ raise ValueError, 'invalid file %s to verify' % file_path
+ md5sum = utils.system_output("md5sum " + file_path)
+ return md5sum.split()[0]
+
+
+def unmap_url_cache(cachedir, url, expected_md5):
+ """
+ Downloads a file from a URL to a cache directory. If the file is already
+ at the expected position and has the expected md5 number, let's not
+ download it again.
+ """
+ # Let's convert cachedir to a canonical path, if it's not already
+ cachedir = os.path.realpath(cachedir)
+ if not os.path.isdir(cachedir):
+ try:
+ utils.system('mkdir -p ' + cachedir)
+ except:
+ raise ValueError('Could not create cache directory %s' % cachedir)
+ file_from_url = os.path.basename(url)
+ file_local_path = os.path.join(cachedir, file_from_url)
+ if os.path.isfile(file_local_path):
+ file_md5 = get_md5sum(file_local_path)
+ if file_md5 == expected_md5:
+ # File is already at the expected position and ready to go
+ src = file_from_url
+ else:
+ # Let's download the package again, it's corrupted...
+ src = url
+ else:
+ # File is not there, let's download it
+ src = url
+ return utils.unmap_url(cachedir, src, cachedir)
+
+
+def force_copy(src, dest):
+ """Replace dest with a new copy of src, even if it exists"""
+ if os.path.isfile(dest):
+ os.remove(dest)
+ if os.path.isdir(dest):
+ dest = os.path.join(dest, os.path.basename(src))
+ shutil.copyfile(src, dest)
+ return dest
+
+
+def force_link(src, dest):
+ """Link src to dest, overwriting it if it exists"""
+ return utils.system("ln -sf %s %s" % (src, dest))
+
+
+def file_contains_pattern(file, pattern):
+ """Return true if file contains the specified egrep pattern"""
+ if not os.path.isfile(file):
+ raise NameError('file %s does not exist' % file)
+ return not utils.system('egrep -q "' + pattern + '" ' + file, ignore_status=True)
+
+
+def list_grep(list, pattern):
+ """True if any item in list matches the specified pattern."""
+ compiled = re.compile(pattern)
+ for line in list:
+ match = compiled.search(line)
+ if (match):
+ return 1
+ return 0
+
+
+def get_os_vendor():
+ """Try to guess what's the os vendor
+ """
+ issue = '/etc/issue'
+
+ if not os.path.isfile(issue):
+ return 'Unknown'
+
+ if file_contains_pattern(issue, 'Red Hat'):
+ return 'Red Hat'
+ elif file_contains_pattern(issue, 'Fedora'):
+ return 'Fedora Core'
+ elif file_contains_pattern(issue, 'SUSE'):
+ return 'SUSE'
+ elif file_contains_pattern(issue, 'Ubuntu'):
+ return 'Ubuntu'
+ elif file_contains_pattern(issue, 'Debian'):
+ return 'Debian'
+ else:
+ return 'Unknown'
+
+
+def get_vmlinux():
+ """Return the full path to vmlinux
+
+ Ahem. This is crap. Pray harder. Bad Martin.
+ """
+ vmlinux = '/boot/vmlinux-%s' % utils.system_output('uname -r')
+ if os.path.isfile(vmlinux):
+ return vmlinux
+ vmlinux = '/lib/modules/%s/build/vmlinux' % utils.system_output('uname -r')
+ if os.path.isfile(vmlinux):
+ return vmlinux
+ return None
+
+
+def get_systemmap():
+ """Return the full path to System.map
+
+ Ahem. This is crap. Pray harder. Bad Martin.
+ """
+ map = '/boot/System.map-%s' % utils.system_output('uname -r')
+ if os.path.isfile(map):
+ return map
+ map = '/lib/modules/%s/build/System.map' % utils.system_output('uname -r')
+ if os.path.isfile(map):
+ return map
+ return None
+
+
+def get_modules_dir():
+ """Return the modules dir for the running kernel version"""
+ kernel_version = utils.system_output('uname -r')
+ return '/lib/modules/%s/kernel' % kernel_version
+
+
+def get_cpu_arch():
+ """Work out which CPU architecture we're running on"""
+ f = open('/proc/cpuinfo', 'r')
+ cpuinfo = f.readlines()
+ f.close()
+ if list_grep(cpuinfo, '^cpu.*(RS64|POWER3|Broadband Engine)'):
+ return 'power'
+ elif list_grep(cpuinfo, '^cpu.*POWER4'):
+ return 'power4'
+ elif list_grep(cpuinfo, '^cpu.*POWER5'):
+ return 'power5'
+ elif list_grep(cpuinfo, '^cpu.*POWER6'):
+ return 'power6'
+ elif list_grep(cpuinfo, '^cpu.*PPC970'):
+ return 'power970'
+ elif list_grep(cpuinfo, '^flags.*:.* lm .*'):
+ return 'x86_64'
+ else:
+ return 'i386'
+
+
+def get_current_kernel_arch():
+ """Get the machine architecture, now just a wrap of 'uname -m'."""
+ return os.popen('uname -m').read().rstrip()
+
+
+def get_file_arch(filename):
+ # -L means follow symlinks
+ file_data = utils.system_output('file -L ' + filename)
+ if file_data.count('80386'):
+ return 'i386'
+ return None
+
+
+def count_cpus():
+ """number of CPUs in the local machine according to /proc/cpuinfo"""
+ f = file('/proc/cpuinfo', 'r')
+ cpus = 0
+ for line in f.readlines():
+ if line.startswith('processor'):
+ cpus += 1
+ return cpus
+
+
+# Returns total memory in kb
+def read_from_meminfo(key):
+ meminfo = utils.system_output('grep %s /proc/meminfo' % key)
+ return int(re.search(r'\d+', meminfo).group(0))
+
+
+def memtotal():
+ return read_from_meminfo('MemTotal')
+
+
+def freememtotal():
+ return read_from_meminfo('MemFree')
+
+
+def rounded_memtotal():
+ # Get total of all physical mem, in kbytes
+ usable_kbytes = memtotal()
+ # usable_kbytes is system's usable DRAM in kbytes,
+ # as reported by memtotal() from device /proc/meminfo memtotal
+ # after Linux deducts 1.5% to 5.1% for system table overhead
+ # Undo the unknown actual deduction by rounding up
+ # to next small multiple of a big power-of-two
+ # eg 12GB - 5.1% gets rounded back up to 12GB
+ mindeduct = 0.015 # 1.5 percent
+ maxdeduct = 0.055 # 5.5 percent
+ # deduction range 1.5% .. 5.5% supports physical mem sizes
+ # 6GB .. 12GB in steps of .5GB
+ # 12GB .. 24GB in steps of 1 GB
+ # 24GB .. 48GB in steps of 2 GB ...
+ # Finer granularity in physical mem sizes would require
+ # tighter spread between min and max possible deductions
+
+ # increase mem size by at least min deduction, without rounding
+ min_kbytes = int(usable_kbytes / (1.0 - mindeduct))
+ # increase mem size further by 2**n rounding, by 0..roundKb or more
+ round_kbytes = int(usable_kbytes / (1.0 - maxdeduct)) - min_kbytes
+ # find least binary roundup 2**n that covers worst-cast roundKb
+ mod2n = 1 << int(math.ceil(math.log(round_kbytes, 2)))
+ # have round_kbytes <= mod2n < round_kbytes*2
+ # round min_kbytes up to next multiple of mod2n
+ phys_kbytes = min_kbytes + mod2n - 1
+ phys_kbytes = phys_kbytes - (phys_kbytes % mod2n) # clear low bits
+ return phys_kbytes
+
+
+def sysctl_kernel(key, value=None):
+ """(Very) partial implementation of sysctl, for kernel params"""
+ if value:
+ # write
+ utils.write_one_line('/proc/sys/kernel/%s' % key, str(value))
+ else:
+ # read
+ out = utils.read_one_line('/proc/sys/kernel/%s' % key)
+ return int(re.search(r'\d+', out).group(0))
+
+
+def _convert_exit_status(sts):
+ if os.WIFSIGNALED(sts):
+ return -os.WTERMSIG(sts)
+ elif os.WIFEXITED(sts):
+ return os.WEXITSTATUS(sts)
+ else:
+ # impossible?
+ raise RuntimeError("Unknown exit status %d!" % sts)
+
+
+def where_art_thy_filehandles():
+ """Dump the current list of filehandles"""
+ os.system("ls -l /proc/%d/fd >> /dev/tty" % os.getpid())
+
+
+def print_to_tty(string):
+ """Output string straight to the tty"""
+ open('/dev/tty', 'w').write(string + '\n')
+
+
+def dump_object(object):
+ """Dump an object's attributes and methods
+
+ kind of like dir()
+ """
+ for item in object.__dict__.iteritems():
+ print item
+ try:
+ (key,value) = item
+ dump_object(value)
+ except:
+ continue
+
+
+def environ(env_key):
+ """return the requested environment variable, or '' if unset"""
+ if (os.environ.has_key(env_key)):
+ return os.environ[env_key]
+ else:
+ return ''
+
+
+def prepend_path(newpath, oldpath):
+ """prepend newpath to oldpath"""
+ if (oldpath):
+ return newpath + ':' + oldpath
+ else:
+ return newpath
+
+
+def append_path(oldpath, newpath):
+ """append newpath to oldpath"""
+ if (oldpath):
+ return oldpath + ':' + newpath
+ else:
+ return newpath
+
+
+def avgtime_print(dir):
+ """ Calculate some benchmarking statistics.
+ Input is a directory containing a file called 'time'.
+ File contains one-per-line results of /usr/bin/time.
+ Output is average Elapsed, User, and System time in seconds,
+ and average CPU percentage.
+ """
+ f = open(dir + "/time")
+ user = system = elapsed = cpu = count = 0
+ r = re.compile('([\d\.]*)user ([\d\.]*)system (\d*):([\d\.]*)elapsed (\d*)%CPU')
+ for line in f.readlines():
+ try:
+ s = r.match(line);
+ user += float(s.group(1))
+ system += float(s.group(2))
+ elapsed += (float(s.group(3)) * 60) + float(s.group(4))
+ cpu += float(s.group(5))
+ count += 1
+ except:
+ raise ValueError("badly formatted times")
+
+ f.close()
+ return "Elapsed: %0.2fs User: %0.2fs System: %0.2fs CPU: %0.0f%%" % \
+ (elapsed/count, user/count, system/count, cpu/count)
+
+
+def running_config():
+ """
+ Return path of config file of the currently running kernel
+ """
+ version = utils.system_output('uname -r')
+ for config in ('/proc/config.gz', \
+ '/boot/config-%s' % version,
+ '/lib/modules/%s/build/.config' % version):
+ if os.path.isfile(config):
+ return config
+ return None
+
+
+def check_for_kernel_feature(feature):
+ config = running_config()
+
+ if not config:
+ raise TypeError("Can't find kernel config file")
+
+ if config.endswith('.gz'):
+ grep = 'zgrep'
+ else:
+ grep = 'grep'
+ grep += ' ^CONFIG_%s= %s' % (feature, config)
+
+ if not utils.system_output(grep, ignore_status=True):
+ raise ValueError("Kernel doesn't have a %s feature" % (feature))
+
+
+def cpu_online_map():
+ """
+ Check out the available cpu online map
+ """
+ cpus = []
+ for line in open('/proc/cpuinfo', 'r').readlines():
+ if line.startswith('processor'):
+ cpus.append(line.split()[2]) # grab cpu number
+ return cpus
+
+
+def check_glibc_ver(ver):
+ glibc_ver = commands.getoutput('ldd --version').splitlines()[0]
+ glibc_ver = re.search(r'(\d+\.\d+(\.\d+)?)', glibc_ver).group()
+ if glibc_ver.split('.') < ver.split('.'):
+ raise error.TestError("Glibc too old (%s). Glibc >= %s is needed." % \
+ (glibc_ver, ver))
+
+def check_kernel_ver(ver):
+ kernel_ver = utils.system_output('uname -r')
+ kv_tmp = re.split(r'[-]', kernel_ver)[0:3]
+ if kv_tmp[0].split('.') < ver.split('.'):
+ raise error.TestError("Kernel too old (%s). Kernel > %s is needed." % \
+ (kernel_ver, ver))
+
+
+def human_format(number):
+ # Convert number to kilo / mega / giga format.
+ if number < 1024:
+ return "%d" % number
+ kilo = float(number) / 1024.0
+ if kilo < 1024:
+ return "%.2fk" % kilo
+ meg = kilo / 1024.0
+ if meg < 1024:
+ return "%.2fM" % meg
+ gig = meg / 1024.0
+ return "%.2fG" % gig
+
+
+def numa_nodes():
+ node_paths = glob.glob('/sys/devices/system/node/node*')
+ nodes = [int(re.sub(r'.*node(\d+)', r'\1', x)) for x in node_paths]
+ return (sorted(nodes))
+
+
+def node_size():
+ nodes = max(len(numa_nodes()), 1)
+ return ((memtotal() * 1024) / nodes)
+
+
+def to_seconds(time_string):
+ """Converts a string in M+:SS.SS format to S+.SS"""
+ elts = time_string.split(':')
+ if len(elts) == 1:
+ return time_string
+ return str(int(elts[0]) * 60 + float(elts[1]))
+
+
+def extract_all_time_results(results_string):
+ """Extract user, system, and elapsed times into a list of tuples"""
+ pattern = re.compile(r"(.*?)user (.*?)system (.*?)elapsed")
+ results = []
+ for result in pattern.findall(results_string):
+ results.append(tuple([to_seconds(elt) for elt in result]))
+ return results
+
+
+def pickle_load(filename):
+ return pickle.load(open(filename, 'r'))
+
+
+# Return the kernel version and build timestamp.
+def running_os_release():
+ return os.uname()[2:4]
+
+
+def running_os_ident():
+ (version, timestamp) = running_os_release()
+ return version + '::' + timestamp
+
+
+def running_os_full_version():
+ (version, timestamp) = running_os_release()
+ return version
+
+
+# much like find . -name 'pattern'
+def locate(pattern, root=os.getcwd()):
+ for path, dirs, files in os.walk(root):
+ for f in files:
+ if fnmatch.fnmatch(f, pattern):
+ yield os.path.abspath(os.path.join(path, f))
+
+
+def freespace(path):
+ """Return the disk free space, in bytes"""
+ s = os.statvfs(path)
+ return s.f_bavail * s.f_bsize
+
+
+def disk_block_size(path):
+ """Return the disk block size, in bytes"""
+ return os.statvfs(path).f_bsize
+
+
+def get_cpu_family():
+ procinfo = utils.system_output('cat /proc/cpuinfo')
+ CPU_FAMILY_RE = re.compile(r'^cpu family\s+:\s+(\S+)', re.M)
+ matches = CPU_FAMILY_RE.findall(procinfo)
+ if matches:
+ return int(matches[0])
+ else:
+ raise error.TestError('Could not get valid cpu family data')
+
+
+def get_disks():
+ df_output = utils.system_output('df')
+ disk_re = re.compile(r'^(/dev/hd[a-z]+)3', re.M)
+ return disk_re.findall(df_output)
+
+
+def load_module(module_name):
+ # Checks if a module has already been loaded
+ if module_is_loaded(module_name):
+ return False
+
+ utils.system('/sbin/modprobe ' + module_name)
+ return True
+
+
+def unload_module(module_name):
+ utils.system('/sbin/rmmod ' + module_name)
+
+
+def module_is_loaded(module_name):
+ module_name = module_name.replace('-', '_')
+ modules = utils.system_output('/sbin/lsmod').splitlines()
+ for module in modules:
+ if module.startswith(module_name) and module[len(module_name)] == ' ':
+ return True
+ return False
+
+
+def get_loaded_modules():
+ lsmod_output = utils.system_output('/sbin/lsmod').splitlines()[1:]
+ return [line.split(None, 1)[0] for line in lsmod_output]
+
+
+def get_huge_page_size():
+ output = utils.system_output('grep Hugepagesize /proc/meminfo')
+ return int(output.split()[1]) # Assumes units always in kB. :(
+
+
+def get_num_huge_pages():
+ raw_hugepages = utils.system_output('/sbin/sysctl vm.nr_hugepages')
+ return int(raw_hugepages.split()[2])
+
+
+def set_num_huge_pages(num):
+ utils.system('/sbin/sysctl vm.nr_hugepages=%d' % num)
+
+
+def get_system_nodes():
+ nodes = os.listdir('/sys/devices/system/node')
+ nodes.sort()
+ return nodes
+
+
+def get_cpu_vendor():
+ cpuinfo = open('/proc/cpuinfo').read()
+ vendors = re.findall(r'(?m)^vendor_id\s*:\s*(\S+)\s*$', cpuinfo)
+ for i in xrange(1, len(vendors)):
+ if vendors[i] != vendors[0]:
+ raise error.TestError('multiple cpu vendors found: ' + str(vendors))
+ return vendors[0]
+
+
+def probe_cpus():
+ """
+ This routine returns a list of cpu devices found under
+ /sys/devices/system/cpu.
+ """
+ cmd = 'find /sys/devices/system/cpu/ -maxdepth 1 -type d -name cpu*'
+ return utils.system_output(cmd).splitlines()
+
+
+def ping_default_gateway():
+ """Ping the default gateway."""
+
+ network = open('/etc/sysconfig/network')
+ m = re.search('GATEWAY=(\S+)', network.read())
+
+ if m:
+ gw = m.group(1)
+ cmd = 'ping %s -c 5 > /dev/null' % gw
+ return utils.system(cmd, ignore_status=True)
+
+ raise error.TestError('Unable to find default gateway')
+
+
+def drop_caches():
+ """Writes back all dirty pages to disk and clears all the caches."""
+ utils.system("sync")
+ utils.system("sync")
+ # We ignore failures here as this will fail on 2.6.11 kernels.
+ utils.system("echo 3 > /proc/sys/vm/drop_caches", ignore_status=True)