Home | History | Annotate | Download | only in bin
      1 """
      2 DO NOT import this file directly - import client/bin/utils.py,
      3 which will mix this in
      4 
      5 Convenience functions for use by tests or whomever.
      6 
      7 Note that this file is mixed in by utils.py - note very carefully the
      8 precedence order defined there
      9 """
     10 import os, shutil, commands, pickle, glob
     11 import math, re, fnmatch, logging, multiprocessing
     12 from autotest_lib.client.common_lib import error, utils, magic
     13 
     14 
     15 def grep(pattern, file):
     16     """
     17     This is mainly to fix the return code inversion from grep
     18     Also handles compressed files.
     19 
     20     returns 1 if the pattern is present in the file, 0 if not.
     21     """
     22     command = 'grep "%s" > /dev/null' % pattern
     23     ret = cat_file_to_cmd(file, command, ignore_status=True)
     24     return not ret
     25 
     26 
     27 def difflist(list1, list2):
     28     """returns items in list2 that are not in list1"""
     29     diff = [];
     30     for x in list2:
     31         if x not in list1:
     32             diff.append(x)
     33     return diff
     34 
     35 
     36 def cat_file_to_cmd(file, command, ignore_status=0, return_output=False):
     37     """
     38     equivalent to 'cat file | command' but knows to use
     39     zcat or bzcat if appropriate
     40     """
     41     if not os.path.isfile(file):
     42         raise NameError('invalid file %s to cat to command %s'
     43                 % (file, command))
     44 
     45     if return_output:
     46         run_cmd = utils.system_output
     47     else:
     48         run_cmd = utils.system
     49 
     50     if magic.guess_type(file) == 'application/x-bzip2':
     51         cat = 'bzcat'
     52     elif magic.guess_type(file) == 'application/x-gzip':
     53         cat = 'zcat'
     54     else:
     55         cat = 'cat'
     56     return run_cmd('%s %s | %s' % (cat, file, command),
     57                                                     ignore_status=ignore_status)
     58 
     59 
     60 def extract_tarball_to_dir(tarball, dir):
     61     """
     62     Extract a tarball to a specified directory name instead of whatever
     63     the top level of a tarball is - useful for versioned directory names, etc
     64     """
     65     if os.path.exists(dir):
     66         if os.path.isdir(dir):
     67             shutil.rmtree(dir)
     68         else:
     69             os.remove(dir)
     70     pwd = os.getcwd()
     71     os.chdir(os.path.dirname(os.path.abspath(dir)))
     72     newdir = extract_tarball(tarball)
     73     os.rename(newdir, dir)
     74     os.chdir(pwd)
     75 
     76 
     77 def extract_tarball(tarball):
     78     """Returns the directory extracted by the tarball."""
     79     extracted = cat_file_to_cmd(tarball, 'tar xvf - 2>/dev/null',
     80                                     return_output=True).splitlines()
     81 
     82     dir = None
     83 
     84     for line in extracted:
     85         if line.startswith('./'):
     86             line = line[2:]
     87         if not line or line == '.':
     88             continue
     89         topdir = line.split('/')[0]
     90         if os.path.isdir(topdir):
     91             if dir:
     92                 assert(dir == topdir)
     93             else:
     94                 dir = topdir
     95     if dir:
     96         return dir
     97     else:
     98         raise NameError('extracting tarball produced no dir')
     99 
    100 
    101 def hash_file(filename, size=None, method="md5"):
    102     """
    103     Calculate the hash of filename.
    104     If size is not None, limit to first size bytes.
    105     Throw exception if something is wrong with filename.
    106     Can be also implemented with bash one-liner (assuming size%1024==0):
    107     dd if=filename bs=1024 count=size/1024 | sha1sum -
    108 
    109     @param filename: Path of the file that will have its hash calculated.
    110     @param method: Method used to calculate the hash. Supported methods:
    111             * md5
    112             * sha1
    113     @returns: Hash of the file, if something goes wrong, return None.
    114     """
    115     chunksize = 4096
    116     fsize = os.path.getsize(filename)
    117 
    118     if not size or size > fsize:
    119         size = fsize
    120     f = open(filename, 'rb')
    121 
    122     try:
    123         hash = utils.hash(method)
    124     except ValueError:
    125         logging.error("Unknown hash type %s, returning None", method)
    126 
    127     while size > 0:
    128         if chunksize > size:
    129             chunksize = size
    130         data = f.read(chunksize)
    131         if len(data) == 0:
    132             logging.debug("Nothing left to read but size=%d", size)
    133             break
    134         hash.update(data)
    135         size -= len(data)
    136     f.close()
    137     return hash.hexdigest()
    138 
    139 
    140 def unmap_url_cache(cachedir, url, expected_hash, method="md5"):
    141     """
    142     Downloads a file from a URL to a cache directory. If the file is already
    143     at the expected position and has the expected hash, let's not download it
    144     again.
    145 
    146     @param cachedir: Directory that might hold a copy of the file we want to
    147             download.
    148     @param url: URL for the file we want to download.
    149     @param expected_hash: Hash string that we expect the file downloaded to
    150             have.
    151     @param method: Method used to calculate the hash string (md5, sha1).
    152     """
    153     # Let's convert cachedir to a canonical path, if it's not already
    154     cachedir = os.path.realpath(cachedir)
    155     if not os.path.isdir(cachedir):
    156         try:
    157             os.makedirs(cachedir)
    158         except:
    159             raise ValueError('Could not create cache directory %s' % cachedir)
    160     file_from_url = os.path.basename(url)
    161     file_local_path = os.path.join(cachedir, file_from_url)
    162 
    163     file_hash = None
    164     failure_counter = 0
    165     while not file_hash == expected_hash:
    166         if os.path.isfile(file_local_path):
    167             file_hash = hash_file(file_local_path, method)
    168             if file_hash == expected_hash:
    169                 # File is already at the expected position and ready to go
    170                 src = file_from_url
    171             else:
    172                 # Let's download the package again, it's corrupted...
    173                 logging.error("Seems that file %s is corrupted, trying to "
    174                               "download it again", file_from_url)
    175                 src = url
    176                 failure_counter += 1
    177         else:
    178             # File is not there, let's download it
    179             src = url
    180         if failure_counter > 1:
    181             raise EnvironmentError("Consistently failed to download the "
    182                                    "package %s. Aborting further download "
    183                                    "attempts. This might mean either the "
    184                                    "network connection has problems or the "
    185                                    "expected hash string that was determined "
    186                                    "for this file is wrong", file_from_url)
    187         file_path = utils.unmap_url(cachedir, src, cachedir)
    188 
    189     return file_path
    190 
    191 
    192 def force_copy(src, dest):
    193     """Replace dest with a new copy of src, even if it exists"""
    194     if os.path.isfile(dest):
    195         os.remove(dest)
    196     if os.path.isdir(dest):
    197         dest = os.path.join(dest, os.path.basename(src))
    198     shutil.copyfile(src, dest)
    199     return dest
    200 
    201 
    202 def force_link(src, dest):
    203     """Link src to dest, overwriting it if it exists"""
    204     return utils.system("ln -sf %s %s" % (src, dest))
    205 
    206 
    207 def file_contains_pattern(file, pattern):
    208     """Return true if file contains the specified egrep pattern"""
    209     if not os.path.isfile(file):
    210         raise NameError('file %s does not exist' % file)
    211     return not utils.system('egrep -q "' + pattern + '" ' + file, ignore_status=True)
    212 
    213 
    214 def list_grep(list, pattern):
    215     """True if any item in list matches the specified pattern."""
    216     compiled = re.compile(pattern)
    217     for line in list:
    218         match = compiled.search(line)
    219         if (match):
    220             return 1
    221     return 0
    222 
    223 
    224 def get_os_vendor():
    225     """Try to guess what's the os vendor
    226     """
    227     if os.path.isfile('/etc/SuSE-release'):
    228         return 'SUSE'
    229 
    230     issue = '/etc/issue'
    231 
    232     if not os.path.isfile(issue):
    233         return 'Unknown'
    234 
    235     if file_contains_pattern(issue, 'Red Hat'):
    236         return 'Red Hat'
    237     elif file_contains_pattern(issue, 'Fedora'):
    238         return 'Fedora Core'
    239     elif file_contains_pattern(issue, 'SUSE'):
    240         return 'SUSE'
    241     elif file_contains_pattern(issue, 'Ubuntu'):
    242         return 'Ubuntu'
    243     elif file_contains_pattern(issue, 'Debian'):
    244         return 'Debian'
    245     else:
    246         return 'Unknown'
    247 
    248 
    249 def get_cc():
    250     try:
    251         return os.environ['CC']
    252     except KeyError:
    253         return 'gcc'
    254 
    255 
    256 def get_vmlinux():
    257     """Return the full path to vmlinux
    258 
    259     Ahem. This is crap. Pray harder. Bad Martin.
    260     """
    261     vmlinux = '/boot/vmlinux-%s' % utils.system_output('uname -r')
    262     if os.path.isfile(vmlinux):
    263         return vmlinux
    264     vmlinux = '/lib/modules/%s/build/vmlinux' % utils.system_output('uname -r')
    265     if os.path.isfile(vmlinux):
    266         return vmlinux
    267     return None
    268 
    269 
    270 def get_systemmap():
    271     """Return the full path to System.map
    272 
    273     Ahem. This is crap. Pray harder. Bad Martin.
    274     """
    275     map = '/boot/System.map-%s' % utils.system_output('uname -r')
    276     if os.path.isfile(map):
    277         return map
    278     map = '/lib/modules/%s/build/System.map' % utils.system_output('uname -r')
    279     if os.path.isfile(map):
    280         return map
    281     return None
    282 
    283 
    284 def get_modules_dir():
    285     """Return the modules dir for the running kernel version"""
    286     kernel_version = utils.system_output('uname -r')
    287     return '/lib/modules/%s/kernel' % kernel_version
    288 
    289 
    290 _CPUINFO_RE = re.compile(r'^(?P<key>[^\t]*)\t*: ?(?P<value>.*)$')
    291 
    292 
    293 def get_cpuinfo():
    294     """Read /proc/cpuinfo and convert to a list of dicts."""
    295     cpuinfo = []
    296     with open('/proc/cpuinfo', 'r') as f:
    297         cpu = {}
    298         for line in f:
    299             line = line.strip()
    300             if not line:
    301                 cpuinfo.append(cpu)
    302                 cpu = {}
    303                 continue
    304             match = _CPUINFO_RE.match(line)
    305             cpu[match.group('key')] = match.group('value')
    306         if cpu:
    307             # cpuinfo usually ends in a blank line, so this shouldn't happen.
    308             cpuinfo.append(cpu)
    309     return cpuinfo
    310 
    311 
    312 def get_cpu_arch():
    313     """Work out which CPU architecture we're running on"""
    314     f = open('/proc/cpuinfo', 'r')
    315     cpuinfo = f.readlines()
    316     f.close()
    317     if list_grep(cpuinfo, '^cpu.*(RS64|POWER3|Broadband Engine)'):
    318         return 'power'
    319     elif list_grep(cpuinfo, '^cpu.*POWER4'):
    320         return 'power4'
    321     elif list_grep(cpuinfo, '^cpu.*POWER5'):
    322         return 'power5'
    323     elif list_grep(cpuinfo, '^cpu.*POWER6'):
    324         return 'power6'
    325     elif list_grep(cpuinfo, '^cpu.*POWER7'):
    326         return 'power7'
    327     elif list_grep(cpuinfo, '^cpu.*PPC970'):
    328         return 'power970'
    329     elif list_grep(cpuinfo, 'ARM'):
    330         return 'arm'
    331     elif list_grep(cpuinfo, '^flags.*:.* lm .*'):
    332         return 'x86_64'
    333     elif list_grep(cpuinfo, 'CPU.*implementer.*0x41'):
    334         return 'arm'
    335     else:
    336         return 'i386'
    337 
    338 
    339 def get_arm_soc_family():
    340     """Work out which ARM SoC we're running on"""
    341     f = open('/proc/cpuinfo', 'r')
    342     cpuinfo = f.readlines()
    343     f.close()
    344     if list_grep(cpuinfo, 'EXYNOS5'):
    345         return 'exynos5'
    346     elif list_grep(cpuinfo, 'Tegra'):
    347         return 'tegra'
    348     elif list_grep(cpuinfo, 'Rockchip'):
    349         return 'rockchip'
    350     return 'arm'
    351 
    352 
    353 def get_cpu_soc_family():
    354     """Like get_cpu_arch, but for ARM, returns the SoC family name"""
    355     family = get_cpu_arch()
    356     if family == 'arm':
    357         family = get_arm_soc_family()
    358     return family
    359 
    360 
    361 INTEL_UARCH_TABLE = {
    362     '06_1C': 'Atom',
    363     '06_26': 'Atom',
    364     '06_36': 'Atom',
    365     '06_4C': 'Braswell',
    366     '06_3D': 'Broadwell',
    367     '06_0D': 'Dothan',
    368     '06_3A': 'IvyBridge',
    369     '06_3E': 'IvyBridge',
    370     '06_3C': 'Haswell',
    371     '06_3F': 'Haswell',
    372     '06_45': 'Haswell',
    373     '06_46': 'Haswell',
    374     '06_0F': 'Merom',
    375     '06_16': 'Merom',
    376     '06_17': 'Nehalem',
    377     '06_1A': 'Nehalem',
    378     '06_1D': 'Nehalem',
    379     '06_1E': 'Nehalem',
    380     '06_1F': 'Nehalem',
    381     '06_2E': 'Nehalem',
    382     '06_2A': 'SandyBridge',
    383     '06_2D': 'SandyBridge',
    384     '06_4E': 'Skylake',
    385     '0F_03': 'Prescott',
    386     '0F_04': 'Prescott',
    387     '0F_06': 'Presler',
    388     '06_25': 'Westmere',
    389     '06_2C': 'Westmere',
    390     '06_2F': 'Westmere',
    391 }
    392 
    393 
    394 def get_intel_cpu_uarch(numeric=False):
    395     """Return the Intel microarchitecture we're running on, or None.
    396 
    397     Returns None if this is not an Intel CPU. Returns the family and model as
    398     underscore-separated hex (per Intel manual convention) if the uarch is not
    399     known, or if numeric is True.
    400     """
    401     if not get_current_kernel_arch().startswith('x86'):
    402         return None
    403     cpuinfo = get_cpuinfo()[0]
    404     if cpuinfo['vendor_id'] != 'GenuineIntel':
    405         return None
    406     family_model = '%02X_%02X' % (int(cpuinfo['cpu family']),
    407                                   int(cpuinfo['model']))
    408     if numeric:
    409         return family_model
    410     return INTEL_UARCH_TABLE.get(family_model, family_model)
    411 
    412 
    413 def get_current_kernel_arch():
    414     """Get the machine architecture, now just a wrap of 'uname -m'."""
    415     return os.popen('uname -m').read().rstrip()
    416 
    417 
    418 def get_file_arch(filename):
    419     # -L means follow symlinks
    420     file_data = utils.system_output('file -L ' + filename)
    421     if file_data.count('80386'):
    422         return 'i386'
    423     return None
    424 
    425 
    426 def count_cpus():
    427     """number of CPUs in the local machine according to /proc/cpuinfo"""
    428     try:
    429        return multiprocessing.cpu_count()
    430     except Exception as e:
    431        logging.exception('can not get cpu count from'
    432                         ' multiprocessing.cpu_count()')
    433     cpuinfo = get_cpuinfo()
    434     # Returns at least one cpu. Check comment #1 in crosbug.com/p/9582.
    435     return len(cpuinfo) or 1
    436 
    437 
    438 def cpu_online_map():
    439     """
    440     Check out the available cpu online map
    441     """
    442     cpuinfo = get_cpuinfo()
    443     cpus = []
    444     for cpu in cpuinfo:
    445         cpus.append(cpu['processor'])  # grab cpu number
    446     return cpus
    447 
    448 
    449 def get_cpu_family():
    450     cpuinfo = get_cpuinfo()[0]
    451     return int(cpuinfo['cpu_family'])
    452 
    453 
    454 def get_cpu_vendor():
    455     cpuinfo = get_cpuinfo()
    456     vendors = [cpu['vendor_id'] for cpu in cpuinfo]
    457     for v in vendors[1:]:
    458         if v != vendors[0]:
    459             raise error.TestError('multiple cpu vendors found: ' + str(vendors))
    460     return vendors[0]
    461 
    462 
    463 def probe_cpus():
    464     """
    465     This routine returns a list of cpu devices found under
    466     /sys/devices/system/cpu.
    467     """
    468     cmd = 'find /sys/devices/system/cpu/ -maxdepth 1 -type d -name cpu*'
    469     return utils.system_output(cmd).splitlines()
    470 
    471 
    472 # Returns total memory in kb
    473 def read_from_meminfo(key):
    474     meminfo = utils.system_output('grep %s /proc/meminfo' % key)
    475     return int(re.search(r'\d+', meminfo).group(0))
    476 
    477 
    478 def memtotal():
    479     return read_from_meminfo('MemTotal')
    480 
    481 
    482 def freememtotal():
    483     return read_from_meminfo('MemFree')
    484 
    485 def usable_memtotal():
    486     # Reserved 5% for OS use
    487     return int(read_from_meminfo('MemFree') * 0.95)
    488 
    489 
    490 def rounded_memtotal():
    491     # Get total of all physical mem, in kbytes
    492     usable_kbytes = memtotal()
    493     # usable_kbytes is system's usable DRAM in kbytes,
    494     #   as reported by memtotal() from device /proc/meminfo memtotal
    495     #   after Linux deducts 1.5% to 5.1% for system table overhead
    496     # Undo the unknown actual deduction by rounding up
    497     #   to next small multiple of a big power-of-two
    498     #   eg  12GB - 5.1% gets rounded back up to 12GB
    499     mindeduct = 0.015  # 1.5 percent
    500     maxdeduct = 0.055  # 5.5 percent
    501     # deduction range 1.5% .. 5.5% supports physical mem sizes
    502     #    6GB .. 12GB in steps of .5GB
    503     #   12GB .. 24GB in steps of 1 GB
    504     #   24GB .. 48GB in steps of 2 GB ...
    505     # Finer granularity in physical mem sizes would require
    506     #   tighter spread between min and max possible deductions
    507 
    508     # increase mem size by at least min deduction, without rounding
    509     min_kbytes = int(usable_kbytes / (1.0 - mindeduct))
    510     # increase mem size further by 2**n rounding, by 0..roundKb or more
    511     round_kbytes = int(usable_kbytes / (1.0 - maxdeduct)) - min_kbytes
    512     # find least binary roundup 2**n that covers worst-cast roundKb
    513     mod2n = 1 << int(math.ceil(math.log(round_kbytes, 2)))
    514     # have round_kbytes <= mod2n < round_kbytes*2
    515     # round min_kbytes up to next multiple of mod2n
    516     phys_kbytes = min_kbytes + mod2n - 1
    517     phys_kbytes = phys_kbytes - (phys_kbytes % mod2n)  # clear low bits
    518     return phys_kbytes
    519 
    520 
    521 def sysctl(key, value=None):
    522     """Generic implementation of sysctl, to read and write.
    523 
    524     @param key: A location under /proc/sys
    525     @param value: If not None, a value to write into the sysctl.
    526 
    527     @return The single-line sysctl value as a string.
    528     """
    529     path = '/proc/sys/%s' % key
    530     if value is not None:
    531         utils.write_one_line(path, str(value))
    532     return utils.read_one_line(path)
    533 
    534 
    535 def sysctl_kernel(key, value=None):
    536     """(Very) partial implementation of sysctl, for kernel params"""
    537     if value is not None:
    538         # write
    539         utils.write_one_line('/proc/sys/kernel/%s' % key, str(value))
    540     else:
    541         # read
    542         out = utils.read_one_line('/proc/sys/kernel/%s' % key)
    543         return int(re.search(r'\d+', out).group(0))
    544 
    545 
    546 def _convert_exit_status(sts):
    547     if os.WIFSIGNALED(sts):
    548         return -os.WTERMSIG(sts)
    549     elif os.WIFEXITED(sts):
    550         return os.WEXITSTATUS(sts)
    551     else:
    552         # impossible?
    553         raise RuntimeError("Unknown exit status %d!" % sts)
    554 
    555 
    556 def where_art_thy_filehandles():
    557     """Dump the current list of filehandles"""
    558     os.system("ls -l /proc/%d/fd >> /dev/tty" % os.getpid())
    559 
    560 
    561 def print_to_tty(string):
    562     """Output string straight to the tty"""
    563     open('/dev/tty', 'w').write(string + '\n')
    564 
    565 
    566 def dump_object(object):
    567     """Dump an object's attributes and methods
    568 
    569     kind of like dir()
    570     """
    571     for item in object.__dict__.iteritems():
    572         print item
    573         try:
    574             (key, value) = item
    575             dump_object(value)
    576         except:
    577             continue
    578 
    579 
    580 def environ(env_key):
    581     """return the requested environment variable, or '' if unset"""
    582     if (os.environ.has_key(env_key)):
    583         return os.environ[env_key]
    584     else:
    585         return ''
    586 
    587 
    588 def prepend_path(newpath, oldpath):
    589     """prepend newpath to oldpath"""
    590     if (oldpath):
    591         return newpath + ':' + oldpath
    592     else:
    593         return newpath
    594 
    595 
    596 def append_path(oldpath, newpath):
    597     """append newpath to oldpath"""
    598     if (oldpath):
    599         return oldpath + ':' + newpath
    600     else:
    601         return newpath
    602 
    603 
    604 _TIME_OUTPUT_RE = re.compile(
    605         r'([\d\.]*)user ([\d\.]*)system '
    606         r'(\d*):([\d\.]*)elapsed (\d*)%CPU')
    607 
    608 
    609 def avgtime_print(dir):
    610     """ Calculate some benchmarking statistics.
    611         Input is a directory containing a file called 'time'.
    612         File contains one-per-line results of /usr/bin/time.
    613         Output is average Elapsed, User, and System time in seconds,
    614           and average CPU percentage.
    615     """
    616     user = system = elapsed = cpu = count = 0
    617     with open(dir + "/time") as f:
    618         for line in f:
    619             try:
    620                 m = _TIME_OUTPUT_RE.match(line);
    621                 user += float(m.group(1))
    622                 system += float(m.group(2))
    623                 elapsed += (float(m.group(3)) * 60) + float(m.group(4))
    624                 cpu += float(m.group(5))
    625                 count += 1
    626             except:
    627                 raise ValueError("badly formatted times")
    628 
    629     return "Elapsed: %0.2fs User: %0.2fs System: %0.2fs CPU: %0.0f%%" % \
    630           (elapsed / count, user / count, system / count, cpu / count)
    631 
    632 
    633 def to_seconds(time_string):
    634     """Converts a string in M+:SS.SS format to S+.SS"""
    635     elts = time_string.split(':')
    636     if len(elts) == 1:
    637         return time_string
    638     return str(int(elts[0]) * 60 + float(elts[1]))
    639 
    640 
    641 _TIME_OUTPUT_RE_2 = re.compile(r'(.*?)user (.*?)system (.*?)elapsed')
    642 
    643 
    644 def extract_all_time_results(results_string):
    645     """Extract user, system, and elapsed times into a list of tuples"""
    646     results = []
    647     for result in _TIME_OUTPUT_RE_2.findall(results_string):
    648         results.append(tuple([to_seconds(elt) for elt in result]))
    649     return results
    650 
    651 
    652 def running_config():
    653     """
    654     Return path of config file of the currently running kernel
    655     """
    656     version = utils.system_output('uname -r')
    657     for config in ('/proc/config.gz', \
    658                    '/boot/config-%s' % version,
    659                    '/lib/modules/%s/build/.config' % version):
    660         if os.path.isfile(config):
    661             return config
    662     return None
    663 
    664 
    665 def check_for_kernel_feature(feature):
    666     config = running_config()
    667 
    668     if not config:
    669         raise TypeError("Can't find kernel config file")
    670 
    671     if magic.guess_type(config) == 'application/x-gzip':
    672         grep = 'zgrep'
    673     else:
    674         grep = 'grep'
    675     grep += ' ^CONFIG_%s= %s' % (feature, config)
    676 
    677     if not utils.system_output(grep, ignore_status=True):
    678         raise ValueError("Kernel doesn't have a %s feature" % (feature))
    679 
    680 
    681 def check_glibc_ver(ver):
    682     glibc_ver = commands.getoutput('ldd --version').splitlines()[0]
    683     glibc_ver = re.search(r'(\d+\.\d+(\.\d+)?)', glibc_ver).group()
    684     if utils.compare_versions(glibc_ver, ver) == -1:
    685         raise error.TestError("Glibc too old (%s). Glibc >= %s is needed." %
    686                               (glibc_ver, ver))
    687 
    688 def check_kernel_ver(ver):
    689     kernel_ver = utils.system_output('uname -r')
    690     kv_tmp = re.split(r'[-]', kernel_ver)[0:3]
    691     # In compare_versions, if v1 < v2, return value == -1
    692     if utils.compare_versions(kv_tmp[0], ver) == -1:
    693         raise error.TestError("Kernel too old (%s). Kernel > %s is needed." %
    694                               (kernel_ver, ver))
    695 
    696 
    697 def human_format(number):
    698     # Convert number to kilo / mega / giga format.
    699     if number < 1024:
    700         return "%d" % number
    701     kilo = float(number) / 1024.0
    702     if kilo < 1024:
    703         return "%.2fk" % kilo
    704     meg = kilo / 1024.0
    705     if meg < 1024:
    706         return "%.2fM" % meg
    707     gig = meg / 1024.0
    708     return "%.2fG" % gig
    709 
    710 
    711 def numa_nodes():
    712     node_paths = glob.glob('/sys/devices/system/node/node*')
    713     nodes = [int(re.sub(r'.*node(\d+)', r'\1', x)) for x in node_paths]
    714     return (sorted(nodes))
    715 
    716 
    717 def node_size():
    718     nodes = max(len(numa_nodes()), 1)
    719     return ((memtotal() * 1024) / nodes)
    720 
    721 
    722 def pickle_load(filename):
    723     return pickle.load(open(filename, 'r'))
    724 
    725 
    726 # Return the kernel version and build timestamp.
    727 def running_os_release():
    728     return os.uname()[2:4]
    729 
    730 
    731 def running_os_ident():
    732     (version, timestamp) = running_os_release()
    733     return version + '::' + timestamp
    734 
    735 
    736 def running_os_full_version():
    737     (version, timestamp) = running_os_release()
    738     return version
    739 
    740 
    741 # much like find . -name 'pattern'
    742 def locate(pattern, root=os.getcwd()):
    743     for path, dirs, files in os.walk(root):
    744         for f in files:
    745             if fnmatch.fnmatch(f, pattern):
    746                 yield os.path.abspath(os.path.join(path, f))
    747 
    748 
    749 def freespace(path):
    750     """Return the disk free space, in bytes"""
    751     s = os.statvfs(path)
    752     return s.f_bavail * s.f_bsize
    753 
    754 
    755 def disk_block_size(path):
    756     """Return the disk block size, in bytes"""
    757     return os.statvfs(path).f_bsize
    758 
    759 
    760 _DISK_PARTITION_3_RE = re.compile(r'^(/dev/hd[a-z]+)3', re.M)
    761 
    762 def get_disks():
    763     df_output = utils.system_output('df')
    764     return _DISK_PARTITION_3_RE.findall(df_output)
    765 
    766 
    767 def get_disk_size(disk_name):
    768     """
    769     Return size of disk in byte. Return 0 in Error Case
    770 
    771     @param disk_name: disk name to find size
    772     """
    773     device = os.path.basename(disk_name)
    774     for line in file('/proc/partitions'):
    775         try:
    776             _, _, blocks, name = re.split(r' +', line.strip())
    777         except ValueError:
    778             continue
    779         if name == device:
    780             return 1024 * int(blocks)
    781     return 0
    782 
    783 
    784 def get_disk_size_gb(disk_name):
    785     """
    786     Return size of disk in GB (10^9). Return 0 in Error Case
    787 
    788     @param disk_name: disk name to find size
    789     """
    790     return int(get_disk_size(disk_name) / (10.0 ** 9) + 0.5)
    791 
    792 
    793 def get_disk_model(disk_name):
    794     """
    795     Return model name for internal storage device
    796 
    797     @param disk_name: disk name to find model
    798     """
    799     cmd1 = 'udevadm info --query=property --name=%s' % disk_name
    800     cmd2 = 'grep -E "ID_(NAME|MODEL)="'
    801     cmd3 = 'cut -f 2 -d"="'
    802     cmd = ' | '.join([cmd1, cmd2, cmd3])
    803     return utils.system_output(cmd)
    804 
    805 
    806 _DISK_DEV_RE = re.compile(r'/dev/sd[a-z]|/dev/mmcblk[0-9]*')
    807 
    808 
    809 def get_disk_from_filename(filename):
    810     """
    811     Return the disk device the filename is on.
    812     If the file is on tmpfs or other special file systems,
    813     return None.
    814 
    815     @param filename: name of file, full path.
    816     """
    817 
    818     if not os.path.exists(filename):
    819         raise error.TestError('file %s missing' % filename)
    820 
    821     if filename[0] != '/':
    822         raise error.TestError('This code works only with full path')
    823 
    824     m = _DISK_DEV_RE.match(filename)
    825     while not m:
    826         if filename[0] != '/':
    827             return None
    828         if filename == '/dev/root':
    829             cmd = 'rootdev -d -s'
    830         elif filename.startswith('/dev/mapper'):
    831             cmd = 'dmsetup table "%s"' % os.path.basename(filename)
    832             dmsetup_output = utils.system_output(cmd).split(' ')
    833             if dmsetup_output[2] == 'verity':
    834                 maj_min = dmsetup_output[4]
    835             elif dmsetup_output[2] == 'crypt':
    836                 maj_min = dmsetup_output[6]
    837             cmd = 'realpath "/dev/block/%s"' % maj_min
    838         elif filename.startswith('/dev/loop'):
    839             cmd = 'losetup -O BACK-FILE "%s" | tail -1' % filename
    840         else:
    841             cmd = 'df "%s" | tail -1 | cut -f 1 -d" "' % filename
    842         filename = utils.system_output(cmd)
    843         m = _DISK_DEV_RE.match(filename)
    844     return m.group(0)
    845 
    846 
    847 def get_disk_firmware_version(disk_name):
    848     """
    849     Return firmware version for internal storage device. (empty string for eMMC)
    850 
    851     @param disk_name: disk name to find model
    852     """
    853     cmd1 = 'udevadm info --query=property --name=%s' % disk_name
    854     cmd2 = 'grep -E "ID_REVISION="'
    855     cmd3 = 'cut -f 2 -d"="'
    856     cmd = ' | '.join([cmd1, cmd2, cmd3])
    857     return utils.system_output(cmd)
    858 
    859 
    860 def is_disk_scsi(disk_name):
    861     """
    862     Return true if disk is a scsi device, return false otherwise
    863 
    864     @param disk_name: disk name check
    865     """
    866     return re.match('/dev/sd[a-z]+', disk_name)
    867 
    868 
    869 def is_disk_harddisk(disk_name):
    870     """
    871     Return true if disk is a harddisk, return false otherwise
    872 
    873     @param disk_name: disk name check
    874     """
    875     cmd1 = 'udevadm info --query=property --name=%s' % disk_name
    876     cmd2 = 'grep -E "ID_ATA_ROTATION_RATE_RPM="'
    877     cmd3 = 'cut -f 2 -d"="'
    878     cmd = ' | '.join([cmd1, cmd2, cmd3])
    879 
    880     rtt = utils.system_output(cmd)
    881 
    882     # eMMC will not have this field; rtt == ''
    883     # SSD will have zero rotation rate; rtt == '0'
    884     # For harddisk rtt > 0
    885     return rtt and int(rtt) > 0
    886 
    887 
    888 def verify_hdparm_feature(disk_name, feature):
    889     """
    890     Check for feature support for SCSI disk using hdparm
    891 
    892     @param disk_name: target disk
    893     @param feature: hdparm output string of the feature
    894     """
    895     cmd = 'hdparm -I %s | grep -q "%s"' % (disk_name, feature)
    896     ret = utils.system(cmd, ignore_status=True)
    897     if ret == 0:
    898         return True
    899     elif ret == 1:
    900         return False
    901     else:
    902         raise error.TestFail('Error running command %s' % cmd)
    903 
    904 
    905 def get_storage_error_msg(disk_name, reason):
    906     """
    907     Get Error message for storage test which include disk model.
    908     and also include the firmware version for the SCSI disk
    909 
    910     @param disk_name: target disk
    911     @param reason: Reason of the error.
    912     """
    913 
    914     msg = reason
    915 
    916     model = get_disk_model(disk_name)
    917     msg += ' Disk model: %s' % model
    918 
    919     if is_disk_scsi(disk_name):
    920         fw = get_disk_firmware_version(disk_name)
    921         msg += ' firmware: %s' % fw
    922 
    923     return msg
    924 
    925 
    926 def load_module(module_name, params=None):
    927     # Checks if a module has already been loaded
    928     if module_is_loaded(module_name):
    929         return False
    930 
    931     cmd = '/sbin/modprobe ' + module_name
    932     if params:
    933         cmd += ' ' + params
    934     utils.system(cmd)
    935     return True
    936 
    937 
    938 def unload_module(module_name):
    939     """
    940     Removes a module. Handles dependencies. If even then it's not possible
    941     to remove one of the modules, it will trhow an error.CmdError exception.
    942 
    943     @param module_name: Name of the module we want to remove.
    944     """
    945     l_raw = utils.system_output("/bin/lsmod").splitlines()
    946     lsmod = [x for x in l_raw if x.split()[0] == module_name]
    947     if len(lsmod) > 0:
    948         line_parts = lsmod[0].split()
    949         if len(line_parts) == 4:
    950             submodules = line_parts[3].split(",")
    951             for submodule in submodules:
    952                 unload_module(submodule)
    953         utils.system("/sbin/modprobe -r %s" % module_name)
    954         logging.info("Module %s unloaded", module_name)
    955     else:
    956         logging.info("Module %s is already unloaded", module_name)
    957 
    958 
    959 def module_is_loaded(module_name):
    960     module_name = module_name.replace('-', '_')
    961     modules = utils.system_output('/bin/lsmod').splitlines()
    962     for module in modules:
    963         if module.startswith(module_name) and module[len(module_name)] == ' ':
    964             return True
    965     return False
    966 
    967 
    968 def get_loaded_modules():
    969     lsmod_output = utils.system_output('/bin/lsmod').splitlines()[1:]
    970     return [line.split(None, 1)[0] for line in lsmod_output]
    971 
    972 
    973 def get_huge_page_size():
    974     output = utils.system_output('grep Hugepagesize /proc/meminfo')
    975     return int(output.split()[1]) # Assumes units always in kB. :(
    976 
    977 
    978 def get_num_huge_pages():
    979     raw_hugepages = utils.system_output('/sbin/sysctl vm.nr_hugepages')
    980     return int(raw_hugepages.split()[2])
    981 
    982 
    983 def set_num_huge_pages(num):
    984     utils.system('/sbin/sysctl vm.nr_hugepages=%d' % num)
    985 
    986 
    987 def ping_default_gateway():
    988     """Ping the default gateway."""
    989 
    990     network = open('/etc/sysconfig/network')
    991     m = re.search('GATEWAY=(\S+)', network.read())
    992 
    993     if m:
    994         gw = m.group(1)
    995         cmd = 'ping %s -c 5 > /dev/null' % gw
    996         return utils.system(cmd, ignore_status=True)
    997 
    998     raise error.TestError('Unable to find default gateway')
    999 
   1000 
   1001 def drop_caches():
   1002     """Writes back all dirty pages to disk and clears all the caches."""
   1003     utils.system("sync")
   1004     # We ignore failures here as this will fail on 2.6.11 kernels.
   1005     utils.system("echo 3 > /proc/sys/vm/drop_caches", ignore_status=True)
   1006 
   1007 
   1008 def process_is_alive(name_pattern):
   1009     """
   1010     'pgrep name' misses all python processes and also long process names.
   1011     'pgrep -f name' gets all shell commands with name in args.
   1012     So look only for command whose initial pathname ends with name.
   1013     Name itself is an egrep pattern, so it can use | etc for variations.
   1014     """
   1015     return utils.system("pgrep -f '^([^ /]*/)*(%s)([ ]|$)'" % name_pattern,
   1016                         ignore_status=True) == 0
   1017 
   1018 
   1019 def get_hwclock_seconds(utc=True):
   1020     """
   1021     Return the hardware clock in seconds as a floating point value.
   1022     Use Coordinated Universal Time if utc is True, local time otherwise.
   1023     Raise a ValueError if unable to read the hardware clock.
   1024     """
   1025     cmd = '/sbin/hwclock --debug'
   1026     if utc:
   1027         cmd += ' --utc'
   1028     hwclock_output = utils.system_output(cmd, ignore_status=True)
   1029     match = re.search(r'= ([0-9]+) seconds since .+ (-?[0-9.]+) seconds$',
   1030                       hwclock_output, re.DOTALL)
   1031     if match:
   1032         seconds = int(match.group(1)) + float(match.group(2))
   1033         logging.debug('hwclock seconds = %f', seconds)
   1034         return seconds
   1035 
   1036     raise ValueError('Unable to read the hardware clock -- ' +
   1037                      hwclock_output)
   1038 
   1039 
   1040 def set_wake_alarm(alarm_time):
   1041     """
   1042     Set the hardware RTC-based wake alarm to 'alarm_time'.
   1043     """
   1044     utils.write_one_line('/sys/class/rtc/rtc0/wakealarm', str(alarm_time))
   1045 
   1046 
   1047 def set_power_state(state):
   1048     """
   1049     Set the system power state to 'state'.
   1050     """
   1051     utils.write_one_line('/sys/power/state', state)
   1052 
   1053 
   1054 def standby():
   1055     """
   1056     Power-on suspend (S1)
   1057     """
   1058     set_power_state('standby')
   1059 
   1060 
   1061 def suspend_to_ram():
   1062     """
   1063     Suspend the system to RAM (S3)
   1064     """
   1065     set_power_state('mem')
   1066 
   1067 
   1068 def suspend_to_disk():
   1069     """
   1070     Suspend the system to disk (S4)
   1071     """
   1072     set_power_state('disk')
   1073