• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Copyright 2017 The Chromium OS Authors. All rights reserved.
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
5"""
6Convenience functions for use by tests or whomever.
7"""
8
9# pylint: disable=missing-docstring
10
11import commands
12import fnmatch
13import glob
14import json
15import logging
16import math
17import multiprocessing
18import os
19import pickle
20import platform
21import re
22import shutil
23import signal
24import tempfile
25import time
26import uuid
27
28from autotest_lib.client.common_lib import error
29from autotest_lib.client.common_lib import magic
30from autotest_lib.client.common_lib import utils
31
32from autotest_lib.client.common_lib.utils import *
33
34
35def grep(pattern, file):
36    """
37    This is mainly to fix the return code inversion from grep
38    Also handles compressed files.
39
40    returns 1 if the pattern is present in the file, 0 if not.
41    """
42    command = 'grep "%s" > /dev/null' % pattern
43    ret = cat_file_to_cmd(file, command, ignore_status=True)
44    return not ret
45
46
47def difflist(list1, list2):
48    """returns items in list2 that are not in list1"""
49    diff = [];
50    for x in list2:
51        if x not in list1:
52            diff.append(x)
53    return diff
54
55
56def cat_file_to_cmd(file, command, ignore_status=0, return_output=False):
57    """
58    equivalent to 'cat file | command' but knows to use
59    zcat or bzcat if appropriate
60    """
61    if not os.path.isfile(file):
62        raise NameError('invalid file %s to cat to command %s'
63                % (file, command))
64
65    if return_output:
66        run_cmd = utils.system_output
67    else:
68        run_cmd = utils.system
69
70    if magic.guess_type(file) == 'application/x-bzip2':
71        cat = 'bzcat'
72    elif magic.guess_type(file) == 'application/x-gzip':
73        cat = 'zcat'
74    else:
75        cat = 'cat'
76    return run_cmd('%s %s | %s' % (cat, file, command),
77                   ignore_status=ignore_status)
78
79
80def extract_tarball_to_dir(tarball, dir):
81    """
82    Extract a tarball to a specified directory name instead of whatever
83    the top level of a tarball is - useful for versioned directory names, etc
84    """
85    if os.path.exists(dir):
86        if os.path.isdir(dir):
87            shutil.rmtree(dir)
88        else:
89            os.remove(dir)
90    pwd = os.getcwd()
91    os.chdir(os.path.dirname(os.path.abspath(dir)))
92    newdir = extract_tarball(tarball)
93    os.rename(newdir, dir)
94    os.chdir(pwd)
95
96
97def extract_tarball(tarball):
98    """Returns the directory extracted by the tarball."""
99    extracted = cat_file_to_cmd(tarball, 'tar xvf - 2>/dev/null',
100                                    return_output=True).splitlines()
101
102    dir = None
103
104    for line in extracted:
105        if line.startswith('./'):
106            line = line[2:]
107        if not line or line == '.':
108            continue
109        topdir = line.split('/')[0]
110        if os.path.isdir(topdir):
111            if dir:
112                assert(dir == topdir)
113            else:
114                dir = topdir
115    if dir:
116        return dir
117    else:
118        raise NameError('extracting tarball produced no dir')
119
120
121def unmap_url_cache(cachedir, url, expected_hash, method="md5"):
122    """
123    Downloads a file from a URL to a cache directory. If the file is already
124    at the expected position and has the expected hash, let's not download it
125    again.
126
127    @param cachedir: Directory that might hold a copy of the file we want to
128            download.
129    @param url: URL for the file we want to download.
130    @param expected_hash: Hash string that we expect the file downloaded to
131            have.
132    @param method: Method used to calculate the hash string (md5, sha1).
133    """
134    # Let's convert cachedir to a canonical path, if it's not already
135    cachedir = os.path.realpath(cachedir)
136    if not os.path.isdir(cachedir):
137        try:
138            os.makedirs(cachedir)
139        except:
140            raise ValueError('Could not create cache directory %s' % cachedir)
141    file_from_url = os.path.basename(url)
142    file_local_path = os.path.join(cachedir, file_from_url)
143
144    file_hash = None
145    failure_counter = 0
146    while not file_hash == expected_hash:
147        if os.path.isfile(file_local_path):
148            file_hash = hash_file(file_local_path, method)
149            if file_hash == expected_hash:
150                # File is already at the expected position and ready to go
151                src = file_from_url
152            else:
153                # Let's download the package again, it's corrupted...
154                logging.error("Seems that file %s is corrupted, trying to "
155                              "download it again", file_from_url)
156                src = url
157                failure_counter += 1
158        else:
159            # File is not there, let's download it
160            src = url
161        if failure_counter > 1:
162            raise EnvironmentError("Consistently failed to download the "
163                                   "package %s. Aborting further download "
164                                   "attempts. This might mean either the "
165                                   "network connection has problems or the "
166                                   "expected hash string that was determined "
167                                   "for this file is wrong", file_from_url)
168        file_path = utils.unmap_url(cachedir, src, cachedir)
169
170    return file_path
171
172
173def force_copy(src, dest):
174    """Replace dest with a new copy of src, even if it exists"""
175    if os.path.isfile(dest):
176        os.remove(dest)
177    if os.path.isdir(dest):
178        dest = os.path.join(dest, os.path.basename(src))
179    shutil.copyfile(src, dest)
180    return dest
181
182
183def force_link(src, dest):
184    """Link src to dest, overwriting it if it exists"""
185    return utils.system("ln -sf %s %s" % (src, dest))
186
187
188def file_contains_pattern(file, pattern):
189    """Return true if file contains the specified egrep pattern"""
190    if not os.path.isfile(file):
191        raise NameError('file %s does not exist' % file)
192    return not utils.system('egrep -q "' + pattern + '" ' + file,
193                            ignore_status=True)
194
195
196def list_grep(list, pattern):
197    """True if any item in list matches the specified pattern."""
198    compiled = re.compile(pattern)
199    for line in list:
200        match = compiled.search(line)
201        if (match):
202            return 1
203    return 0
204
205
206def get_os_vendor():
207    """Try to guess what's the os vendor
208    """
209    if os.path.isfile('/etc/SuSE-release'):
210        return 'SUSE'
211
212    issue = '/etc/issue'
213
214    if not os.path.isfile(issue):
215        return 'Unknown'
216
217    if file_contains_pattern(issue, 'Red Hat'):
218        return 'Red Hat'
219    elif file_contains_pattern(issue, 'Fedora'):
220        return 'Fedora Core'
221    elif file_contains_pattern(issue, 'SUSE'):
222        return 'SUSE'
223    elif file_contains_pattern(issue, 'Ubuntu'):
224        return 'Ubuntu'
225    elif file_contains_pattern(issue, 'Debian'):
226        return 'Debian'
227    else:
228        return 'Unknown'
229
230
231def get_cc():
232    try:
233        return os.environ['CC']
234    except KeyError:
235        return 'gcc'
236
237
238def get_vmlinux():
239    """Return the full path to vmlinux
240
241    Ahem. This is crap. Pray harder. Bad Martin.
242    """
243    vmlinux = '/boot/vmlinux-%s' % utils.system_output('uname -r')
244    if os.path.isfile(vmlinux):
245        return vmlinux
246    vmlinux = '/lib/modules/%s/build/vmlinux' % utils.system_output('uname -r')
247    if os.path.isfile(vmlinux):
248        return vmlinux
249    return None
250
251
252def get_systemmap():
253    """Return the full path to System.map
254
255    Ahem. This is crap. Pray harder. Bad Martin.
256    """
257    map = '/boot/System.map-%s' % utils.system_output('uname -r')
258    if os.path.isfile(map):
259        return map
260    map = '/lib/modules/%s/build/System.map' % utils.system_output('uname -r')
261    if os.path.isfile(map):
262        return map
263    return None
264
265
266def get_modules_dir():
267    """Return the modules dir for the running kernel version"""
268    kernel_version = utils.system_output('uname -r')
269    return '/lib/modules/%s/kernel' % kernel_version
270
271
272_CPUINFO_RE = re.compile(r'^(?P<key>[^\t]*)\t*: ?(?P<value>.*)$')
273
274
275def get_cpuinfo():
276    """Read /proc/cpuinfo and convert to a list of dicts."""
277    cpuinfo = []
278    with open('/proc/cpuinfo', 'r') as f:
279        cpu = {}
280        for line in f:
281            line = line.strip()
282            if not line:
283                cpuinfo.append(cpu)
284                cpu = {}
285                continue
286            match = _CPUINFO_RE.match(line)
287            cpu[match.group('key')] = match.group('value')
288        if cpu:
289            # cpuinfo usually ends in a blank line, so this shouldn't happen.
290            cpuinfo.append(cpu)
291    return cpuinfo
292
293
294def get_cpu_arch():
295    """Work out which CPU architecture we're running on"""
296    f = open('/proc/cpuinfo', 'r')
297    cpuinfo = f.readlines()
298    f.close()
299    if list_grep(cpuinfo, '^cpu.*(RS64|POWER3|Broadband Engine)'):
300        return 'power'
301    elif list_grep(cpuinfo, '^cpu.*POWER4'):
302        return 'power4'
303    elif list_grep(cpuinfo, '^cpu.*POWER5'):
304        return 'power5'
305    elif list_grep(cpuinfo, '^cpu.*POWER6'):
306        return 'power6'
307    elif list_grep(cpuinfo, '^cpu.*POWER7'):
308        return 'power7'
309    elif list_grep(cpuinfo, '^cpu.*PPC970'):
310        return 'power970'
311    elif list_grep(cpuinfo, 'ARM'):
312        return 'arm'
313    elif list_grep(cpuinfo, '^flags.*:.* lm .*'):
314        return 'x86_64'
315    elif list_grep(cpuinfo, 'CPU.*implementer.*0x41'):
316        return 'arm'
317    else:
318        return 'i386'
319
320
321def get_arm_soc_family_from_devicetree():
322    """
323    Work out which ARM SoC we're running on based on the 'compatible' property
324    of the base node of devicetree, if it exists.
325    """
326    devicetree_compatible = '/sys/firmware/devicetree/base/compatible'
327    if not os.path.isfile(devicetree_compatible):
328        return None
329    f = open(devicetree_compatible, 'r')
330    compatible = f.readlines()
331    f.close()
332    if list_grep(compatible, 'rk3399'):
333        return 'rockchip'
334    elif list_grep(compatible, 'mt8173'):
335        return 'mediatek'
336    return None
337
338
339def get_arm_soc_family():
340    """Work out which ARM SoC we're running on"""
341    family = get_arm_soc_family_from_devicetree()
342    if family is not None:
343        return family
344
345    f = open('/proc/cpuinfo', 'r')
346    cpuinfo = f.readlines()
347    f.close()
348    if list_grep(cpuinfo, 'EXYNOS5'):
349        return 'exynos5'
350    elif list_grep(cpuinfo, 'Tegra'):
351        return 'tegra'
352    elif list_grep(cpuinfo, 'Rockchip'):
353        return 'rockchip'
354    return 'arm'
355
356
357def get_cpu_soc_family():
358    """Like get_cpu_arch, but for ARM, returns the SoC family name"""
359    f = open('/proc/cpuinfo', 'r')
360    cpuinfo = f.readlines()
361    f.close()
362    family = get_cpu_arch()
363    if family == 'arm':
364        family = get_arm_soc_family()
365    if list_grep(cpuinfo, '^vendor_id.*:.*AMD'):
366        family = 'amd'
367    return family
368
369
370INTEL_UARCH_TABLE = {
371    '06_1C': 'Atom',
372    '06_26': 'Atom',
373    '06_36': 'Atom',
374    '06_4C': 'Braswell',
375    '06_3D': 'Broadwell',
376    '06_0D': 'Dothan',
377    '06_3A': 'IvyBridge',
378    '06_3E': 'IvyBridge',
379    '06_3C': 'Haswell',
380    '06_3F': 'Haswell',
381    '06_45': 'Haswell',
382    '06_46': 'Haswell',
383    '06_0F': 'Merom',
384    '06_16': 'Merom',
385    '06_17': 'Nehalem',
386    '06_1A': 'Nehalem',
387    '06_1D': 'Nehalem',
388    '06_1E': 'Nehalem',
389    '06_1F': 'Nehalem',
390    '06_2E': 'Nehalem',
391    '06_2A': 'SandyBridge',
392    '06_2D': 'SandyBridge',
393    '06_4E': 'Skylake',
394    '0F_03': 'Prescott',
395    '0F_04': 'Prescott',
396    '0F_06': 'Presler',
397    '06_25': 'Westmere',
398    '06_2C': 'Westmere',
399    '06_2F': 'Westmere',
400}
401
402
403def get_intel_cpu_uarch(numeric=False):
404    """Return the Intel microarchitecture we're running on, or None.
405
406    Returns None if this is not an Intel CPU. Returns the family and model as
407    underscore-separated hex (per Intel manual convention) if the uarch is not
408    known, or if numeric is True.
409    """
410    if not get_current_kernel_arch().startswith('x86'):
411        return None
412    cpuinfo = get_cpuinfo()[0]
413    if cpuinfo['vendor_id'] != 'GenuineIntel':
414        return None
415    family_model = '%02X_%02X' % (int(cpuinfo['cpu family']),
416                                  int(cpuinfo['model']))
417    if numeric:
418        return family_model
419    return INTEL_UARCH_TABLE.get(family_model, family_model)
420
421
422def get_current_kernel_arch():
423    """Get the machine architecture, now just a wrap of 'uname -m'."""
424    return os.popen('uname -m').read().rstrip()
425
426
427def get_file_arch(filename):
428    # -L means follow symlinks
429    file_data = utils.system_output('file -L ' + filename)
430    if file_data.count('80386'):
431        return 'i386'
432    return None
433
434
435def count_cpus():
436    """number of CPUs in the local machine according to /proc/cpuinfo"""
437    try:
438       return multiprocessing.cpu_count()
439    except Exception:
440       logging.exception('can not get cpu count from'
441                        ' multiprocessing.cpu_count()')
442    cpuinfo = get_cpuinfo()
443    # Returns at least one cpu. Check comment #1 in crosbug.com/p/9582.
444    return len(cpuinfo) or 1
445
446
447def cpu_online_map():
448    """
449    Check out the available cpu online map
450    """
451    cpuinfo = get_cpuinfo()
452    cpus = []
453    for cpu in cpuinfo:
454        cpus.append(cpu['processor'])  # grab cpu number
455    return cpus
456
457
458def get_cpu_family():
459    cpuinfo = get_cpuinfo()[0]
460    return int(cpuinfo['cpu_family'])
461
462
463def get_cpu_vendor():
464    cpuinfo = get_cpuinfo()
465    vendors = [cpu['vendor_id'] for cpu in cpuinfo]
466    for v in vendors[1:]:
467        if v != vendors[0]:
468            raise error.TestError('multiple cpu vendors found: ' + str(vendors))
469    return vendors[0]
470
471
472def probe_cpus():
473    """
474    This routine returns a list of cpu devices found under
475    /sys/devices/system/cpu.
476    """
477    cmd = 'find /sys/devices/system/cpu/ -maxdepth 1 -type d -name cpu*'
478    return utils.system_output(cmd).splitlines()
479
480
481# Returns total memory in kb
482def read_from_meminfo(key):
483    meminfo = utils.system_output('grep %s /proc/meminfo' % key)
484    return int(re.search(r'\d+', meminfo).group(0))
485
486
487def memtotal():
488    return read_from_meminfo('MemTotal')
489
490
491def freememtotal():
492    return read_from_meminfo('MemFree')
493
494def usable_memtotal():
495    # Reserved 5% for OS use
496    return int(read_from_meminfo('MemFree') * 0.95)
497
498
499def rounded_memtotal():
500    # Get total of all physical mem, in kbytes
501    usable_kbytes = memtotal()
502    # usable_kbytes is system's usable DRAM in kbytes,
503    #   as reported by memtotal() from device /proc/meminfo memtotal
504    #   after Linux deducts 1.5% to 5.1% for system table overhead
505    # Undo the unknown actual deduction by rounding up
506    #   to next small multiple of a big power-of-two
507    #   eg  12GB - 5.1% gets rounded back up to 12GB
508    mindeduct = 0.015  # 1.5 percent
509    maxdeduct = 0.055  # 5.5 percent
510    # deduction range 1.5% .. 5.5% supports physical mem sizes
511    #    6GB .. 12GB in steps of .5GB
512    #   12GB .. 24GB in steps of 1 GB
513    #   24GB .. 48GB in steps of 2 GB ...
514    # Finer granularity in physical mem sizes would require
515    #   tighter spread between min and max possible deductions
516
517    # increase mem size by at least min deduction, without rounding
518    min_kbytes = int(usable_kbytes / (1.0 - mindeduct))
519    # increase mem size further by 2**n rounding, by 0..roundKb or more
520    round_kbytes = int(usable_kbytes / (1.0 - maxdeduct)) - min_kbytes
521    # find least binary roundup 2**n that covers worst-cast roundKb
522    mod2n = 1 << int(math.ceil(math.log(round_kbytes, 2)))
523    # have round_kbytes <= mod2n < round_kbytes*2
524    # round min_kbytes up to next multiple of mod2n
525    phys_kbytes = min_kbytes + mod2n - 1
526    phys_kbytes = phys_kbytes - (phys_kbytes % mod2n)  # clear low bits
527    return phys_kbytes
528
529
530def sysctl(key, value=None):
531    """Generic implementation of sysctl, to read and write.
532
533    @param key: A location under /proc/sys
534    @param value: If not None, a value to write into the sysctl.
535
536    @return The single-line sysctl value as a string.
537    """
538    path = '/proc/sys/%s' % key
539    if value is not None:
540        utils.write_one_line(path, str(value))
541    return utils.read_one_line(path)
542
543
544def sysctl_kernel(key, value=None):
545    """(Very) partial implementation of sysctl, for kernel params"""
546    if value is not None:
547        # write
548        utils.write_one_line('/proc/sys/kernel/%s' % key, str(value))
549    else:
550        # read
551        out = utils.read_one_line('/proc/sys/kernel/%s' % key)
552        return int(re.search(r'\d+', out).group(0))
553
554
555def _convert_exit_status(sts):
556    if os.WIFSIGNALED(sts):
557        return -os.WTERMSIG(sts)
558    elif os.WIFEXITED(sts):
559        return os.WEXITSTATUS(sts)
560    else:
561        # impossible?
562        raise RuntimeError("Unknown exit status %d!" % sts)
563
564
565def where_art_thy_filehandles():
566    """Dump the current list of filehandles"""
567    os.system("ls -l /proc/%d/fd >> /dev/tty" % os.getpid())
568
569
570def print_to_tty(string):
571    """Output string straight to the tty"""
572    open('/dev/tty', 'w').write(string + '\n')
573
574
575def dump_object(object):
576    """Dump an object's attributes and methods
577
578    kind of like dir()
579    """
580    for item in object.__dict__.iteritems():
581        print item
582        try:
583            (key, value) = item
584            dump_object(value)
585        except:
586            continue
587
588
589def environ(env_key):
590    """return the requested environment variable, or '' if unset"""
591    if (os.environ.has_key(env_key)):
592        return os.environ[env_key]
593    else:
594        return ''
595
596
597def prepend_path(newpath, oldpath):
598    """prepend newpath to oldpath"""
599    if (oldpath):
600        return newpath + ':' + oldpath
601    else:
602        return newpath
603
604
605def append_path(oldpath, newpath):
606    """append newpath to oldpath"""
607    if (oldpath):
608        return oldpath + ':' + newpath
609    else:
610        return newpath
611
612
613_TIME_OUTPUT_RE = re.compile(
614        r'([\d\.]*)user ([\d\.]*)system '
615        r'(\d*):([\d\.]*)elapsed (\d*)%CPU')
616
617
618def avgtime_print(dir):
619    """ Calculate some benchmarking statistics.
620        Input is a directory containing a file called 'time'.
621        File contains one-per-line results of /usr/bin/time.
622        Output is average Elapsed, User, and System time in seconds,
623          and average CPU percentage.
624    """
625    user = system = elapsed = cpu = count = 0
626    with open(dir + "/time") as f:
627        for line in f:
628            try:
629                m = _TIME_OUTPUT_RE.match(line);
630                user += float(m.group(1))
631                system += float(m.group(2))
632                elapsed += (float(m.group(3)) * 60) + float(m.group(4))
633                cpu += float(m.group(5))
634                count += 1
635            except:
636                raise ValueError("badly formatted times")
637
638    return "Elapsed: %0.2fs User: %0.2fs System: %0.2fs CPU: %0.0f%%" % \
639          (elapsed / count, user / count, system / count, cpu / count)
640
641
642def to_seconds(time_string):
643    """Converts a string in M+:SS.SS format to S+.SS"""
644    elts = time_string.split(':')
645    if len(elts) == 1:
646        return time_string
647    return str(int(elts[0]) * 60 + float(elts[1]))
648
649
650_TIME_OUTPUT_RE_2 = re.compile(r'(.*?)user (.*?)system (.*?)elapsed')
651
652
653def extract_all_time_results(results_string):
654    """Extract user, system, and elapsed times into a list of tuples"""
655    results = []
656    for result in _TIME_OUTPUT_RE_2.findall(results_string):
657        results.append(tuple([to_seconds(elt) for elt in result]))
658    return results
659
660
661def running_config():
662    """
663    Return path of config file of the currently running kernel
664    """
665    version = utils.system_output('uname -r')
666    for config in ('/proc/config.gz', \
667                   '/boot/config-%s' % version,
668                   '/lib/modules/%s/build/.config' % version):
669        if os.path.isfile(config):
670            return config
671    return None
672
673
674def check_for_kernel_feature(feature):
675    config = running_config()
676
677    if not config:
678        raise TypeError("Can't find kernel config file")
679
680    if magic.guess_type(config) == 'application/x-gzip':
681        grep = 'zgrep'
682    else:
683        grep = 'grep'
684    grep += ' ^CONFIG_%s= %s' % (feature, config)
685
686    if not utils.system_output(grep, ignore_status=True):
687        raise ValueError("Kernel doesn't have a %s feature" % (feature))
688
689
690def check_glibc_ver(ver):
691    glibc_ver = commands.getoutput('ldd --version').splitlines()[0]
692    glibc_ver = re.search(r'(\d+\.\d+(\.\d+)?)', glibc_ver).group()
693    if utils.compare_versions(glibc_ver, ver) == -1:
694        raise error.TestError("Glibc too old (%s). Glibc >= %s is needed." %
695                              (glibc_ver, ver))
696
697def check_kernel_ver(ver):
698    kernel_ver = utils.system_output('uname -r')
699    kv_tmp = re.split(r'[-]', kernel_ver)[0:3]
700    # In compare_versions, if v1 < v2, return value == -1
701    if utils.compare_versions(kv_tmp[0], ver) == -1:
702        raise error.TestError("Kernel too old (%s). Kernel > %s is needed." %
703                              (kernel_ver, ver))
704
705
706def human_format(number):
707    # Convert number to kilo / mega / giga format.
708    if number < 1024:
709        return "%d" % number
710    kilo = float(number) / 1024.0
711    if kilo < 1024:
712        return "%.2fk" % kilo
713    meg = kilo / 1024.0
714    if meg < 1024:
715        return "%.2fM" % meg
716    gig = meg / 1024.0
717    return "%.2fG" % gig
718
719
720def numa_nodes():
721    node_paths = glob.glob('/sys/devices/system/node/node*')
722    nodes = [int(re.sub(r'.*node(\d+)', r'\1', x)) for x in node_paths]
723    return (sorted(nodes))
724
725
726def node_size():
727    nodes = max(len(numa_nodes()), 1)
728    return ((memtotal() * 1024) / nodes)
729
730
731def pickle_load(filename):
732    return pickle.load(open(filename, 'r'))
733
734
735# Return the kernel version and build timestamp.
736def running_os_release():
737    return os.uname()[2:4]
738
739
740def running_os_ident():
741    (version, timestamp) = running_os_release()
742    return version + '::' + timestamp
743
744
745def running_os_full_version():
746    (version, timestamp) = running_os_release()
747    return version
748
749
750# much like find . -name 'pattern'
751def locate(pattern, root=os.getcwd()):
752    for path, dirs, files in os.walk(root):
753        for f in files:
754            if fnmatch.fnmatch(f, pattern):
755                yield os.path.abspath(os.path.join(path, f))
756
757
758def freespace(path):
759    """Return the disk free space, in bytes"""
760    s = os.statvfs(path)
761    return s.f_bavail * s.f_bsize
762
763
764def disk_block_size(path):
765    """Return the disk block size, in bytes"""
766    return os.statvfs(path).f_bsize
767
768
769_DISK_PARTITION_3_RE = re.compile(r'^(/dev/hd[a-z]+)3', re.M)
770
771def get_disks():
772    df_output = utils.system_output('df')
773    return _DISK_PARTITION_3_RE.findall(df_output)
774
775
776def get_disk_size(disk_name):
777    """
778    Return size of disk in byte. Return 0 in Error Case
779
780    @param disk_name: disk name to find size
781    """
782    device = os.path.basename(disk_name)
783    for line in file('/proc/partitions'):
784        try:
785            _, _, blocks, name = re.split(r' +', line.strip())
786        except ValueError:
787            continue
788        if name == device:
789            return 1024 * int(blocks)
790    return 0
791
792
793def get_disk_size_gb(disk_name):
794    """
795    Return size of disk in GB (10^9). Return 0 in Error Case
796
797    @param disk_name: disk name to find size
798    """
799    return int(get_disk_size(disk_name) / (10.0 ** 9) + 0.5)
800
801
802def get_disk_model(disk_name):
803    """
804    Return model name for internal storage device
805
806    @param disk_name: disk name to find model
807    """
808    cmd1 = 'udevadm info --query=property --name=%s' % disk_name
809    cmd2 = 'grep -E "ID_(NAME|MODEL)="'
810    cmd3 = 'cut -f 2 -d"="'
811    cmd = ' | '.join([cmd1, cmd2, cmd3])
812    return utils.system_output(cmd)
813
814
815_DISK_DEV_RE = re.compile(r'/dev/sd[a-z]|'
816                          r'/dev/mmcblk[0-9]+|'
817                          r'/dev/nvme[0-9]+n[0-9]+')
818
819
820def get_disk_from_filename(filename):
821    """
822    Return the disk device the filename is on.
823    If the file is on tmpfs or other special file systems,
824    return None.
825
826    @param filename: name of file, full path.
827    """
828
829    if not os.path.exists(filename):
830        raise error.TestError('file %s missing' % filename)
831
832    if filename[0] != '/':
833        raise error.TestError('This code works only with full path')
834
835    m = _DISK_DEV_RE.match(filename)
836    while not m:
837        if filename[0] != '/':
838            return None
839        if filename == '/dev/root':
840            cmd = 'rootdev -d -s'
841        elif filename.startswith('/dev/mapper'):
842            cmd = 'dmsetup table "%s"' % os.path.basename(filename)
843            dmsetup_output = utils.system_output(cmd).split(' ')
844            if dmsetup_output[2] == 'verity':
845                maj_min = dmsetup_output[4]
846            elif dmsetup_output[2] == 'crypt':
847                maj_min = dmsetup_output[6]
848            cmd = 'realpath "/dev/block/%s"' % maj_min
849        elif filename.startswith('/dev/loop'):
850            cmd = 'losetup -O BACK-FILE "%s" | tail -1' % filename
851        else:
852            cmd = 'df "%s" | tail -1 | cut -f 1 -d" "' % filename
853        filename = utils.system_output(cmd)
854        m = _DISK_DEV_RE.match(filename)
855    return m.group(0)
856
857
858def get_disk_firmware_version(disk_name):
859    """
860    Return firmware version for internal storage device. (empty string for eMMC)
861
862    @param disk_name: disk name to find model
863    """
864    cmd1 = 'udevadm info --query=property --name=%s' % disk_name
865    cmd2 = 'grep -E "ID_REVISION="'
866    cmd3 = 'cut -f 2 -d"="'
867    cmd = ' | '.join([cmd1, cmd2, cmd3])
868    return utils.system_output(cmd)
869
870
871def is_disk_scsi(disk_name):
872    """
873    Return true if disk is a scsi device, return false otherwise
874
875    @param disk_name: disk name check
876    """
877    return re.match('/dev/sd[a-z]+', disk_name)
878
879
880def is_disk_harddisk(disk_name):
881    """
882    Return true if disk is a harddisk, return false otherwise
883
884    @param disk_name: disk name check
885    """
886    cmd1 = 'udevadm info --query=property --name=%s' % disk_name
887    cmd2 = 'grep -E "ID_ATA_ROTATION_RATE_RPM="'
888    cmd3 = 'cut -f 2 -d"="'
889    cmd = ' | '.join([cmd1, cmd2, cmd3])
890
891    rtt = utils.system_output(cmd)
892
893    # eMMC will not have this field; rtt == ''
894    # SSD will have zero rotation rate; rtt == '0'
895    # For harddisk rtt > 0
896    return rtt and int(rtt) > 0
897
898
899def verify_hdparm_feature(disk_name, feature):
900    """
901    Check for feature support for SCSI disk using hdparm
902
903    @param disk_name: target disk
904    @param feature: hdparm output string of the feature
905    """
906    cmd = 'hdparm -I %s | grep -q "%s"' % (disk_name, feature)
907    ret = utils.system(cmd, ignore_status=True)
908    if ret == 0:
909        return True
910    elif ret == 1:
911        return False
912    else:
913        raise error.TestFail('Error running command %s' % cmd)
914
915
916def get_storage_error_msg(disk_name, reason):
917    """
918    Get Error message for storage test which include disk model.
919    and also include the firmware version for the SCSI disk
920
921    @param disk_name: target disk
922    @param reason: Reason of the error.
923    """
924
925    msg = reason
926
927    model = get_disk_model(disk_name)
928    msg += ' Disk model: %s' % model
929
930    if is_disk_scsi(disk_name):
931        fw = get_disk_firmware_version(disk_name)
932        msg += ' firmware: %s' % fw
933
934    return msg
935
936
937def load_module(module_name, params=None):
938    # Checks if a module has already been loaded
939    if module_is_loaded(module_name):
940        return False
941
942    cmd = '/sbin/modprobe ' + module_name
943    if params:
944        cmd += ' ' + params
945    utils.system(cmd)
946    return True
947
948
949def unload_module(module_name):
950    """
951    Removes a module. Handles dependencies. If even then it's not possible
952    to remove one of the modules, it will trhow an error.CmdError exception.
953
954    @param module_name: Name of the module we want to remove.
955    """
956    l_raw = utils.system_output("/bin/lsmod").splitlines()
957    lsmod = [x for x in l_raw if x.split()[0] == module_name]
958    if len(lsmod) > 0:
959        line_parts = lsmod[0].split()
960        if len(line_parts) == 4:
961            submodules = line_parts[3].split(",")
962            for submodule in submodules:
963                unload_module(submodule)
964        utils.system("/sbin/modprobe -r %s" % module_name)
965        logging.info("Module %s unloaded", module_name)
966    else:
967        logging.info("Module %s is already unloaded", module_name)
968
969
970def module_is_loaded(module_name):
971    module_name = module_name.replace('-', '_')
972    modules = utils.system_output('/bin/lsmod').splitlines()
973    for module in modules:
974        if module.startswith(module_name) and module[len(module_name)] == ' ':
975            return True
976    return False
977
978
979def get_loaded_modules():
980    lsmod_output = utils.system_output('/bin/lsmod').splitlines()[1:]
981    return [line.split(None, 1)[0] for line in lsmod_output]
982
983
984def get_huge_page_size():
985    output = utils.system_output('grep Hugepagesize /proc/meminfo')
986    return int(output.split()[1]) # Assumes units always in kB. :(
987
988
989def get_num_huge_pages():
990    raw_hugepages = utils.system_output('/sbin/sysctl vm.nr_hugepages')
991    return int(raw_hugepages.split()[2])
992
993
994def set_num_huge_pages(num):
995    utils.system('/sbin/sysctl vm.nr_hugepages=%d' % num)
996
997
998def ping_default_gateway():
999    """Ping the default gateway."""
1000
1001    network = open('/etc/sysconfig/network')
1002    m = re.search('GATEWAY=(\S+)', network.read())
1003
1004    if m:
1005        gw = m.group(1)
1006        cmd = 'ping %s -c 5 > /dev/null' % gw
1007        return utils.system(cmd, ignore_status=True)
1008
1009    raise error.TestError('Unable to find default gateway')
1010
1011
1012def drop_caches():
1013    """Writes back all dirty pages to disk and clears all the caches."""
1014    utils.system("sync")
1015    # We ignore failures here as this will fail on 2.6.11 kernels.
1016    utils.system("echo 3 > /proc/sys/vm/drop_caches", ignore_status=True)
1017
1018
1019def process_is_alive(name_pattern):
1020    """
1021    'pgrep name' misses all python processes and also long process names.
1022    'pgrep -f name' gets all shell commands with name in args.
1023    So look only for command whose initial pathname ends with name.
1024    Name itself is an egrep pattern, so it can use | etc for variations.
1025    """
1026    return utils.system("pgrep -f '^([^ /]*/)*(%s)([ ]|$)'" % name_pattern,
1027                        ignore_status=True) == 0
1028
1029
1030def get_hwclock_seconds(utc=True):
1031    """
1032    Return the hardware clock in seconds as a floating point value.
1033    Use Coordinated Universal Time if utc is True, local time otherwise.
1034    Raise a ValueError if unable to read the hardware clock.
1035    """
1036    cmd = '/sbin/hwclock --debug'
1037    if utc:
1038        cmd += ' --utc'
1039    hwclock_output = utils.system_output(cmd, ignore_status=True)
1040    match = re.search(r'= ([0-9]+) seconds since .+ (-?[0-9.]+) seconds$',
1041                      hwclock_output, re.DOTALL)
1042    if match:
1043        seconds = int(match.group(1)) + float(match.group(2))
1044        logging.debug('hwclock seconds = %f', seconds)
1045        return seconds
1046
1047    raise ValueError('Unable to read the hardware clock -- ' +
1048                     hwclock_output)
1049
1050
1051def set_wake_alarm(alarm_time):
1052    """
1053    Set the hardware RTC-based wake alarm to 'alarm_time'.
1054    """
1055    utils.write_one_line('/sys/class/rtc/rtc0/wakealarm', str(alarm_time))
1056
1057
1058def set_power_state(state):
1059    """
1060    Set the system power state to 'state'.
1061    """
1062    utils.write_one_line('/sys/power/state', state)
1063
1064
1065def standby():
1066    """
1067    Power-on suspend (S1)
1068    """
1069    set_power_state('standby')
1070
1071
1072def suspend_to_ram():
1073    """
1074    Suspend the system to RAM (S3)
1075    """
1076    set_power_state('mem')
1077
1078
1079def suspend_to_disk():
1080    """
1081    Suspend the system to disk (S4)
1082    """
1083    set_power_state('disk')
1084
1085
1086_AMD_PCI_IDS_FILE_PATH = '/usr/local/autotest/bin/amd_pci_ids.json'
1087_INTEL_PCI_IDS_FILE_PATH = '/usr/local/autotest/bin/intel_pci_ids.json'
1088_UI_USE_FLAGS_FILE_PATH = '/etc/ui_use_flags.txt'
1089
1090# Command to check if a package is installed. If the package is not installed
1091# the command shall fail.
1092_CHECK_PACKAGE_INSTALLED_COMMAND =(
1093        "dpkg-query -W -f='${Status}\n' %s | head -n1 | awk '{print $3;}' | "
1094        "grep -q '^installed$'")
1095
1096pciid_to_amd_architecture = {}
1097pciid_to_intel_architecture = {}
1098
1099class Crossystem(object):
1100    """A wrapper for the crossystem utility."""
1101
1102    def __init__(self, client):
1103        self.cros_system_data = {}
1104        self._client = client
1105
1106    def init(self):
1107        self.cros_system_data = {}
1108        (_, fname) = tempfile.mkstemp()
1109        f = open(fname, 'w')
1110        self._client.run('crossystem', stdout_tee=f)
1111        f.close()
1112        text = utils.read_file(fname)
1113        for line in text.splitlines():
1114            assignment_string = line.split('#')[0]
1115            if not assignment_string.count('='):
1116                continue
1117            (name, value) = assignment_string.split('=', 1)
1118            self.cros_system_data[name.strip()] = value.strip()
1119        os.remove(fname)
1120
1121    def __getattr__(self, name):
1122        """
1123        Retrieve a crosssystem attribute.
1124
1125        The call crossystemobject.name() will return the crossystem reported
1126        string.
1127        """
1128        return lambda: self.cros_system_data[name]
1129
1130
1131def get_oldest_pid_by_name(name):
1132    """
1133    Return the oldest pid of a process whose name perfectly matches |name|.
1134
1135    name is an egrep expression, which will be matched against the entire name
1136    of processes on the system.  For example:
1137
1138      get_oldest_pid_by_name('chrome')
1139
1140    on a system running
1141      8600 ?        00:00:04 chrome
1142      8601 ?        00:00:00 chrome
1143      8602 ?        00:00:00 chrome-sandbox
1144
1145    would return 8600, as that's the oldest process that matches.
1146    chrome-sandbox would not be matched.
1147
1148    Arguments:
1149      name: egrep expression to match.  Will be anchored at the beginning and
1150            end of the match string.
1151
1152    Returns:
1153      pid as an integer, or None if one cannot be found.
1154
1155    Raises:
1156      ValueError if pgrep returns something odd.
1157    """
1158    str_pid = utils.system_output('pgrep -o ^%s$' % name,
1159                                  ignore_status=True).rstrip()
1160    if str_pid:
1161        return int(str_pid)
1162
1163
1164def get_oldest_by_name(name):
1165    """Return pid and command line of oldest process whose name matches |name|.
1166
1167    @param name: egrep expression to match desired process name.
1168    @return: A tuple of (pid, command_line) of the oldest process whose name
1169             matches |name|.
1170
1171    """
1172    pid = get_oldest_pid_by_name(name)
1173    if pid:
1174        command_line = utils.system_output('ps -p %i -o command=' % pid,
1175                                           ignore_status=True).rstrip()
1176        return (pid, command_line)
1177
1178
1179def get_chrome_remote_debugging_port():
1180    """Returns remote debugging port for Chrome.
1181
1182    Parse chrome process's command line argument to get the remote debugging
1183    port.
1184    """
1185    _, command = get_oldest_by_name('chrome')
1186    matches = re.search('--remote-debugging-port=([0-9]+)', command)
1187    if matches:
1188        return int(matches.group(1))
1189
1190
1191def get_process_list(name, command_line=None):
1192    """
1193    Return the list of pid for matching process |name command_line|.
1194
1195    on a system running
1196      31475 ?    0:06 /opt/google/chrome/chrome --allow-webui-compositing -
1197      31478 ?    0:00 /opt/google/chrome/chrome-sandbox /opt/google/chrome/
1198      31485 ?    0:00 /opt/google/chrome/chrome --type=zygote --log-level=1
1199      31532 ?    1:05 /opt/google/chrome/chrome --type=renderer
1200
1201    get_process_list('chrome')
1202    would return ['31475', '31485', '31532']
1203
1204    get_process_list('chrome', '--type=renderer')
1205    would return ['31532']
1206
1207    Arguments:
1208      name: process name to search for. If command_line is provided, name is
1209            matched against full command line. If command_line is not provided,
1210            name is only matched against the process name.
1211      command line: when command line is passed, the full process command line
1212                    is used for matching.
1213
1214    Returns:
1215      list of PIDs of the matching processes.
1216
1217    """
1218    # TODO(rohitbm) crbug.com/268861
1219    flag = '-x' if not command_line else '-f'
1220    name = '\'%s.*%s\'' % (name, command_line) if command_line else name
1221    str_pid = utils.system_output('pgrep %s %s' % (flag, name),
1222                                  ignore_status=True).rstrip()
1223    return str_pid.split()
1224
1225
1226def nuke_process_by_name(name, with_prejudice=False):
1227    """Tell the oldest process specified by name to exit.
1228
1229    Arguments:
1230      name: process name specifier, as understood by pgrep.
1231      with_prejudice: if True, don't allow for graceful exit.
1232
1233    Raises:
1234      error.AutoservPidAlreadyDeadError: no existing process matches name.
1235    """
1236    try:
1237        pid = get_oldest_pid_by_name(name)
1238    except Exception as e:
1239        logging.error(e)
1240        return
1241    if pid is None:
1242        raise error.AutoservPidAlreadyDeadError('No process matching %s.' %
1243                                                name)
1244    if with_prejudice:
1245        utils.nuke_pid(pid, [signal.SIGKILL])
1246    else:
1247        utils.nuke_pid(pid)
1248
1249
1250def ensure_processes_are_dead_by_name(name, timeout_sec=10):
1251    """Terminate all processes specified by name and ensure they're gone.
1252
1253    Arguments:
1254      name: process name specifier, as understood by pgrep.
1255      timeout_sec: maximum number of seconds to wait for processes to die.
1256
1257    Raises:
1258      error.AutoservPidAlreadyDeadError: no existing process matches name.
1259      utils.TimeoutError: if processes still exist after timeout_sec.
1260    """
1261
1262    def list_and_kill_processes(name):
1263        process_list = get_process_list(name)
1264        try:
1265            for pid in [int(str_pid) for str_pid in process_list]:
1266                utils.nuke_pid(pid)
1267        except error.AutoservPidAlreadyDeadError:
1268            pass
1269        return process_list
1270
1271    utils.poll_for_condition(lambda: list_and_kill_processes(name) == [],
1272                             timeout=timeout_sec)
1273
1274
1275def is_virtual_machine():
1276    return 'QEMU' in platform.processor()
1277
1278
1279def save_vm_state(checkpoint):
1280    """Saves the current state of the virtual machine.
1281
1282    This function is a NOOP if the test is not running under a virtual machine
1283    with the USB serial port redirected.
1284
1285    Arguments:
1286      checkpoint - Name used to identify this state
1287
1288    Returns:
1289      None
1290    """
1291    # The QEMU monitor has been redirected to the guest serial port located at
1292    # /dev/ttyUSB0. To save the state of the VM, we just send the 'savevm'
1293    # command to the serial port.
1294    if is_virtual_machine() and os.path.exists('/dev/ttyUSB0'):
1295        logging.info('Saving VM state "%s"', checkpoint)
1296        serial = open('/dev/ttyUSB0', 'w')
1297        serial.write('savevm %s\r\n' % checkpoint)
1298        logging.info('Done saving VM state "%s"', checkpoint)
1299
1300
1301def check_raw_dmesg(dmesg, message_level, whitelist):
1302    """Checks dmesg for unexpected warnings.
1303
1304    This function parses dmesg for message with message_level <= message_level
1305    which do not appear in the whitelist.
1306
1307    Arguments:
1308      dmesg - string containing raw dmesg buffer
1309      message_level - minimum message priority to check
1310      whitelist - messages to ignore
1311
1312    Returns:
1313      List of unexpected warnings
1314    """
1315    whitelist_re = re.compile(r'(%s)' % '|'.join(whitelist))
1316    unexpected = []
1317    for line in dmesg.splitlines():
1318        if int(line[1]) <= message_level:
1319            stripped_line = line.split('] ', 1)[1]
1320            if whitelist_re.search(stripped_line):
1321                continue
1322            unexpected.append(stripped_line)
1323    return unexpected
1324
1325
1326def verify_mesg_set(mesg, regex, whitelist):
1327    """Verifies that the exact set of messages are present in a text.
1328
1329    This function finds all strings in the text matching a certain regex, and
1330    then verifies that all expected strings are present in the set, and no
1331    unexpected strings are there.
1332
1333    Arguments:
1334      mesg - the mutiline text to be scanned
1335      regex - regular expression to match
1336      whitelist - messages to find in the output, a list of strings
1337          (potentially regexes) to look for in the filtered output. All these
1338          strings must be there, and no other strings should be present in the
1339          filtered output.
1340
1341    Returns:
1342      string of inconsistent findings (i.e. an empty string on success).
1343    """
1344
1345    rv = []
1346
1347    missing_strings = []
1348    present_strings = []
1349    for line in mesg.splitlines():
1350        if not re.search(r'%s' % regex, line):
1351            continue
1352        present_strings.append(line.split('] ', 1)[1])
1353
1354    for string in whitelist:
1355        for present_string in list(present_strings):
1356            if re.search(r'^%s$' % string, present_string):
1357                present_strings.remove(present_string)
1358                break
1359        else:
1360            missing_strings.append(string)
1361
1362    if present_strings:
1363        rv.append('unexpected strings:')
1364        rv.extend(present_strings)
1365    if missing_strings:
1366        rv.append('missing strings:')
1367        rv.extend(missing_strings)
1368
1369    return '\n'.join(rv)
1370
1371
1372def target_is_pie():
1373    """Returns whether the toolchain produces a PIE (position independent
1374    executable) by default.
1375
1376    Arguments:
1377      None
1378
1379    Returns:
1380      True if the target toolchain produces a PIE by default.
1381      False otherwise.
1382    """
1383
1384    command = 'echo | ${CC} -E -dD -P - | grep -i pie'
1385    result = utils.system_output(command,
1386                                 retain_output=True,
1387                                 ignore_status=True)
1388    if re.search('#define __PIE__', result):
1389        return True
1390    else:
1391        return False
1392
1393
1394def target_is_x86():
1395    """Returns whether the toolchain produces an x86 object
1396
1397    Arguments:
1398      None
1399
1400    Returns:
1401      True if the target toolchain produces an x86 object
1402      False otherwise.
1403    """
1404
1405    command = 'echo | ${CC} -E -dD -P - | grep -i 86'
1406    result = utils.system_output(command,
1407                                 retain_output=True,
1408                                 ignore_status=True)
1409    if re.search('__i386__', result) or re.search('__x86_64__', result):
1410        return True
1411    else:
1412        return False
1413
1414
1415def mounts():
1416    ret = []
1417    for line in file('/proc/mounts'):
1418        m = re.match(
1419            r'(?P<src>\S+) (?P<dest>\S+) (?P<type>\S+) (?P<opts>\S+).*', line)
1420        if m:
1421            ret.append(m.groupdict())
1422    return ret
1423
1424
1425def is_mountpoint(path):
1426    return path in [m['dest'] for m in mounts()]
1427
1428
1429def require_mountpoint(path):
1430    """
1431    Raises an exception if path is not a mountpoint.
1432    """
1433    if not is_mountpoint(path):
1434        raise error.TestFail('Path not mounted: "%s"' % path)
1435
1436
1437def random_username():
1438    return str(uuid.uuid4()) + '@example.com'
1439
1440
1441def get_signin_credentials(filepath):
1442    """Returns user_id, password tuple from credentials file at filepath.
1443
1444    File must have one line of the format user_id:password
1445
1446    @param filepath: path of credentials file.
1447    @return user_id, password tuple.
1448    """
1449    user_id, password = None, None
1450    if os.path.isfile(filepath):
1451        with open(filepath) as f:
1452            user_id, password = f.read().rstrip().split(':')
1453    return user_id, password
1454
1455
1456def parse_cmd_output(command, run_method=utils.run):
1457    """Runs a command on a host object to retrieve host attributes.
1458
1459    The command should output to stdout in the format of:
1460    <key> = <value> # <optional_comment>
1461
1462
1463    @param command: Command to execute on the host.
1464    @param run_method: Function to use to execute the command. Defaults to
1465                       utils.run so that the command will be executed locally.
1466                       Can be replace with a host.run call so that it will
1467                       execute on a DUT or external machine. Method must accept
1468                       a command argument, stdout_tee and stderr_tee args and
1469                       return a result object with a string attribute stdout
1470                       which will be parsed.
1471
1472    @returns a dictionary mapping host attributes to their values.
1473    """
1474    result = {}
1475    # Suppresses stdout so that the files are not printed to the logs.
1476    cmd_result = run_method(command, stdout_tee=None, stderr_tee=None)
1477    for line in cmd_result.stdout.splitlines():
1478        # Lines are of the format "<key>     = <value>      # <comment>"
1479        key_value = re.match(r'^\s*(?P<key>[^ ]+)\s*=\s*(?P<value>[^ '
1480                             r']+)(?:\s*#.*)?$', line)
1481        if key_value:
1482            result[key_value.group('key')] = key_value.group('value')
1483    return result
1484
1485
1486def set_from_keyval_output(out, delimiter=' '):
1487    """Parse delimiter-separated key-val output into a set of tuples.
1488
1489    Output is expected to be multiline text output from a command.
1490    Stuffs the key-vals into tuples in a set to be later compared.
1491
1492    e.g.  deactivated 0
1493          disableForceClear 0
1494          ==>  set(('deactivated', '0'), ('disableForceClear', '0'))
1495
1496    @param out: multiple lines of space-separated key-val pairs.
1497    @param delimiter: character that separates key from val. Usually a
1498                      space but may be '=' or something else.
1499    @return set of key-val tuples.
1500    """
1501    results = set()
1502    kv_match_re = re.compile('([^ ]+)%s(.*)' % delimiter)
1503    for linecr in out.splitlines():
1504        match = kv_match_re.match(linecr.strip())
1505        if match:
1506            results.add((match.group(1), match.group(2)))
1507    return results
1508
1509
1510def get_cpu_usage():
1511    """Returns machine's CPU usage.
1512
1513    This function uses /proc/stat to identify CPU usage.
1514    Returns:
1515        A dictionary with 'user', 'nice', 'system' and 'idle' values.
1516        Sample dictionary:
1517        {
1518            'user': 254544,
1519            'nice': 9,
1520            'system': 254768,
1521            'idle': 2859878,
1522        }
1523    """
1524    proc_stat = open('/proc/stat')
1525    cpu_usage_str = proc_stat.readline().split()
1526    proc_stat.close()
1527    return {
1528        'user': int(cpu_usage_str[1]),
1529        'nice': int(cpu_usage_str[2]),
1530        'system': int(cpu_usage_str[3]),
1531        'idle': int(cpu_usage_str[4])
1532    }
1533
1534
1535def compute_active_cpu_time(cpu_usage_start, cpu_usage_end):
1536    """Computes the fraction of CPU time spent non-idling.
1537
1538    This function should be invoked using before/after values from calls to
1539    get_cpu_usage().
1540    """
1541    time_active_end = (
1542        cpu_usage_end['user'] + cpu_usage_end['nice'] + cpu_usage_end['system'])
1543    time_active_start = (cpu_usage_start['user'] + cpu_usage_start['nice'] +
1544                         cpu_usage_start['system'])
1545    total_time_end = (cpu_usage_end['user'] + cpu_usage_end['nice'] +
1546                      cpu_usage_end['system'] + cpu_usage_end['idle'])
1547    total_time_start = (cpu_usage_start['user'] + cpu_usage_start['nice'] +
1548                        cpu_usage_start['system'] + cpu_usage_start['idle'])
1549    return ((float(time_active_end) - time_active_start) /
1550            (total_time_end - total_time_start))
1551
1552
1553def is_pgo_mode():
1554    return 'USE_PGO' in os.environ
1555
1556
1557def wait_for_idle_cpu(timeout, utilization):
1558    """Waits for the CPU to become idle (< utilization).
1559
1560    Args:
1561        timeout: The longest time in seconds to wait before throwing an error.
1562        utilization: The CPU usage below which the system should be considered
1563                idle (between 0 and 1.0 independent of cores/hyperthreads).
1564    """
1565    time_passed = 0.0
1566    fraction_active_time = 1.0
1567    sleep_time = 1
1568    logging.info('Starting to wait up to %.1fs for idle CPU...', timeout)
1569    while fraction_active_time >= utilization:
1570        cpu_usage_start = get_cpu_usage()
1571        # Split timeout interval into not too many chunks to limit log spew.
1572        # Start at 1 second, increase exponentially
1573        time.sleep(sleep_time)
1574        time_passed += sleep_time
1575        sleep_time = min(16.0, 2.0 * sleep_time)
1576        cpu_usage_end = get_cpu_usage()
1577        fraction_active_time = \
1578                compute_active_cpu_time(cpu_usage_start, cpu_usage_end)
1579        logging.info('After waiting %.1fs CPU utilization is %.3f.',
1580                     time_passed, fraction_active_time)
1581        if time_passed > timeout:
1582            logging.warning('CPU did not become idle.')
1583            log_process_activity()
1584            # crosbug.com/37389
1585            if is_pgo_mode():
1586                logging.info('Still continuing because we are in PGO mode.')
1587                return True
1588
1589            return False
1590    logging.info('Wait for idle CPU took %.1fs (utilization = %.3f).',
1591                 time_passed, fraction_active_time)
1592    return True
1593
1594
1595def log_process_activity():
1596    """Logs the output of top.
1597
1598    Useful to debug performance tests and to find runaway processes.
1599    """
1600    logging.info('Logging current process activity using top and ps.')
1601    cmd = 'top -b -n1 -c'
1602    output = utils.run(cmd)
1603    logging.info(output)
1604    output = utils.run('ps axl')
1605    logging.info(output)
1606
1607
1608def wait_for_cool_machine():
1609    """
1610    A simple heuristic to wait for a machine to cool.
1611    The code looks a bit 'magic', but we don't know ambient temperature
1612    nor machine characteristics and still would like to return the caller
1613    a machine that cooled down as much as reasonably possible.
1614    """
1615    temperature = get_current_temperature_max()
1616    # We got here with a cold machine, return immediately. This should be the
1617    # most common case.
1618    if temperature < 50:
1619        return True
1620    logging.info('Got a hot machine of %dC. Sleeping 1 minute.', temperature)
1621    # A modest wait should cool the machine.
1622    time.sleep(60.0)
1623    temperature = get_current_temperature_max()
1624    # Atoms idle below 60 and everyone else should be even lower.
1625    if temperature < 62:
1626        return True
1627    # This should be rare.
1628    logging.info('Did not cool down (%dC). Sleeping 2 minutes.', temperature)
1629    time.sleep(120.0)
1630    temperature = get_current_temperature_max()
1631    # A temperature over 65'C doesn't give us much headroom to the critical
1632    # temperatures that start at 85'C (and PerfControl as of today will fail at
1633    # critical - 10'C).
1634    if temperature < 65:
1635        return True
1636    logging.warning('Did not cool down (%dC), giving up.', temperature)
1637    log_process_activity()
1638    return False
1639
1640
1641# System paths for machine performance state.
1642_CPUINFO = '/proc/cpuinfo'
1643_DIRTY_WRITEBACK_CENTISECS = '/proc/sys/vm/dirty_writeback_centisecs'
1644_KERNEL_MAX = '/sys/devices/system/cpu/kernel_max'
1645_MEMINFO = '/proc/meminfo'
1646_TEMP_SENSOR_RE = 'Reading temperature...([0-9]*)'
1647
1648
1649def _get_line_from_file(path, line):
1650    """
1651    line can be an integer or
1652    line can be a string that matches the beginning of the line
1653    """
1654    with open(path) as f:
1655        if isinstance(line, int):
1656            l = f.readline()
1657            for _ in range(0, line):
1658                l = f.readline()
1659            return l
1660        else:
1661            for l in f:
1662                if l.startswith(line):
1663                    return l
1664    return None
1665
1666
1667def _get_match_from_file(path, line, prefix, postfix):
1668    """
1669    Matches line in path and returns string between first prefix and postfix.
1670    """
1671    match = _get_line_from_file(path, line)
1672    # Strip everything from front of line including prefix.
1673    if prefix:
1674        match = re.split(prefix, match)[1]
1675    # Strip everything from back of string including first occurence of postfix.
1676    if postfix:
1677        match = re.split(postfix, match)[0]
1678    return match
1679
1680
1681def _get_float_from_file(path, line, prefix, postfix):
1682    match = _get_match_from_file(path, line, prefix, postfix)
1683    return float(match)
1684
1685
1686def _get_int_from_file(path, line, prefix, postfix):
1687    match = _get_match_from_file(path, line, prefix, postfix)
1688    return int(match)
1689
1690
1691def _get_hex_from_file(path, line, prefix, postfix):
1692    match = _get_match_from_file(path, line, prefix, postfix)
1693    return int(match, 16)
1694
1695
1696# The paths don't change. Avoid running find all the time.
1697_hwmon_paths = None
1698
1699def _get_hwmon_paths(file_pattern):
1700    """
1701    Returns a list of paths to the temperature sensors.
1702    """
1703    # Some systems like daisy_spring only have the virtual hwmon.
1704    # And other systems like rambi only have coretemp.0. See crbug.com/360249.
1705    #    /sys/class/hwmon/hwmon*/
1706    #    /sys/devices/virtual/hwmon/hwmon*/
1707    #    /sys/devices/platform/coretemp.0/
1708    if not _hwmon_paths:
1709        cmd = 'find /sys/ -name "' + file_pattern + '"'
1710        _hwon_paths = utils.run(cmd, verbose=False).stdout.splitlines()
1711    return _hwon_paths
1712
1713
1714def get_temperature_critical():
1715    """
1716    Returns temperature at which we will see some throttling in the system.
1717    """
1718    min_temperature = 1000.0
1719    paths = _get_hwmon_paths('temp*_crit')
1720    for path in paths:
1721        temperature = _get_float_from_file(path, 0, None, None) * 0.001
1722        # Today typical for Intel is 98'C to 105'C while ARM is 85'C. Clamp to
1723        # the lowest known value.
1724        if (min_temperature < 60.0) or min_temperature > 150.0:
1725            logging.warning('Critical temperature of %.1fC was reset to 85.0C.',
1726                            min_temperature)
1727            min_temperature = 85.0
1728
1729        min_temperature = min(temperature, min_temperature)
1730    return min_temperature
1731
1732
1733def get_temperature_input_max():
1734    """
1735    Returns the maximum currently observed temperature.
1736    """
1737    max_temperature = -1000.0
1738    paths = _get_hwmon_paths('temp*_input')
1739    for path in paths:
1740        temperature = _get_float_from_file(path, 0, None, None) * 0.001
1741        max_temperature = max(temperature, max_temperature)
1742    return max_temperature
1743
1744
1745def get_thermal_zone_temperatures():
1746    """
1747    Returns the maximum currently observered temperature in thermal_zones.
1748    """
1749    temperatures = []
1750    for path in glob.glob('/sys/class/thermal/thermal_zone*/temp'):
1751        try:
1752            temperatures.append(
1753                _get_float_from_file(path, 0, None, None) * 0.001)
1754        except IOError:
1755            # Some devices (e.g. Veyron) may have reserved thermal zones that
1756            # are not active. Trying to read the temperature value would cause a
1757            # EINVAL IO error.
1758            continue
1759    return temperatures
1760
1761
1762def get_ec_temperatures():
1763    """
1764    Uses ectool to return a list of all sensor temperatures in Celsius.
1765    """
1766    temperatures = []
1767    try:
1768        full_cmd = 'ectool temps all'
1769        lines = utils.run(full_cmd, verbose=False).stdout.splitlines()
1770        for line in lines:
1771            temperature = int(line.split(': ')[1]) - 273
1772            temperatures.append(temperature)
1773    except Exception:
1774        logging.warning('Unable to read temperature sensors using ectool.')
1775    for temperature in temperatures:
1776        # Sanity check for real world values.
1777        assert ((temperature > 10.0) and
1778                (temperature < 150.0)), ('Unreasonable temperature %.1fC.' %
1779                                         temperature)
1780
1781    return temperatures
1782
1783
1784def get_current_temperature_max():
1785    """
1786    Returns the highest reported board temperature (all sensors) in Celsius.
1787    """
1788    temperature = max([get_temperature_input_max()] +
1789                      get_thermal_zone_temperatures() +
1790                      get_ec_temperatures())
1791    # Sanity check for real world values.
1792    assert ((temperature > 10.0) and
1793            (temperature < 150.0)), ('Unreasonable temperature %.1fC.' %
1794                                     temperature)
1795    return temperature
1796
1797
1798def get_cpu_cache_size():
1799    """
1800    Returns the last level CPU cache size in kBytes.
1801    """
1802    cache_size = _get_int_from_file(_CPUINFO, 'cache size', ': ', ' KB')
1803    # Sanity check.
1804    assert cache_size >= 64, 'Unreasonably small cache.'
1805    return cache_size
1806
1807
1808def get_cpu_model_frequency():
1809    """
1810    Returns the model frequency from the CPU model name on Intel only. This
1811    might be redundant with get_cpu_max_frequency. Unit is Hz.
1812    """
1813    frequency = _get_float_from_file(_CPUINFO, 'model name', ' @ ', 'GHz')
1814    return 1.e9 * frequency
1815
1816
1817def get_cpu_max_frequency():
1818    """
1819    Returns the largest of the max CPU core frequencies. The unit is Hz.
1820    """
1821    max_frequency = -1
1822    paths = _get_cpufreq_paths('cpuinfo_max_freq')
1823    for path in paths:
1824        # Convert from kHz to Hz.
1825        frequency = 1000 * _get_float_from_file(path, 0, None, None)
1826        max_frequency = max(frequency, max_frequency)
1827    # Sanity check.
1828    assert max_frequency > 1e8, 'Unreasonably low CPU frequency.'
1829    return max_frequency
1830
1831
1832def get_cpu_min_frequency():
1833    """
1834    Returns the smallest of the minimum CPU core frequencies.
1835    """
1836    min_frequency = 1e20
1837    paths = _get_cpufreq_paths('cpuinfo_min_freq')
1838    for path in paths:
1839        frequency = _get_float_from_file(path, 0, None, None)
1840        min_frequency = min(frequency, min_frequency)
1841    # Sanity check.
1842    assert min_frequency > 1e8, 'Unreasonably low CPU frequency.'
1843    return min_frequency
1844
1845
1846def get_cpu_model():
1847    """
1848    Returns the CPU model.
1849    Only works on Intel.
1850    """
1851    cpu_model = _get_int_from_file(_CPUINFO, 'model\t', ': ', None)
1852    return cpu_model
1853
1854
1855def get_cpu_family():
1856    """
1857    Returns the CPU family.
1858    Only works on Intel.
1859    """
1860    cpu_family = _get_int_from_file(_CPUINFO, 'cpu family\t', ': ', None)
1861    return cpu_family
1862
1863
1864def get_board_property(key):
1865    """
1866    Get a specific property from /etc/lsb-release.
1867
1868    @param key: board property to return value for
1869
1870    @return the value or '' if not present
1871    """
1872    with open('/etc/lsb-release') as f:
1873        pattern = '%s=(.*)' % key
1874        pat = re.search(pattern, f.read())
1875        if pat:
1876            return pat.group(1)
1877    return ''
1878
1879
1880def get_board():
1881    """
1882    Get the ChromeOS release board name from /etc/lsb-release.
1883    """
1884    return get_board_property('BOARD')
1885
1886
1887def get_board_type():
1888    """
1889    Get the ChromeOS board type from /etc/lsb-release.
1890
1891    @return device type.
1892    """
1893    return get_board_property('DEVICETYPE')
1894
1895
1896def get_board_with_frequency_and_memory():
1897    """
1898    Returns a board name modified with CPU frequency and memory size to
1899    differentiate between different board variants. For instance
1900    link -> link_1.8GHz_4GB.
1901    """
1902    board_name = get_board()
1903    if is_virtual_machine():
1904        board = '%s_VM' % board_name
1905    else:
1906        # Rounded to nearest GB and GHz.
1907        memory = int(round(get_mem_total() / 1024.0))
1908        # Convert frequency to GHz with 1 digit accuracy after the
1909        # decimal point.
1910        frequency = int(round(get_cpu_max_frequency() * 1e-8)) * 0.1
1911        board = '%s_%1.1fGHz_%dGB' % (board_name, frequency, memory)
1912    return board
1913
1914
1915def get_mem_total():
1916    """
1917    Returns the total memory available in the system in MBytes.
1918    """
1919    mem_total = _get_float_from_file(_MEMINFO, 'MemTotal:', 'MemTotal:', ' kB')
1920    # Sanity check, all Chromebooks have at least 1GB of memory.
1921    assert mem_total > 256 * 1024, 'Unreasonable amount of memory.'
1922    return mem_total / 1024
1923
1924
1925def get_mem_free():
1926    """
1927    Returns the currently free memory in the system in MBytes.
1928    """
1929    mem_free = _get_float_from_file(_MEMINFO, 'MemFree:', 'MemFree:', ' kB')
1930    return mem_free / 1024
1931
1932
1933def get_kernel_max():
1934    """
1935    Returns content of kernel_max.
1936    """
1937    kernel_max = _get_int_from_file(_KERNEL_MAX, 0, None, None)
1938    # Sanity check.
1939    assert ((kernel_max > 0) and (kernel_max < 257)), 'Unreasonable kernel_max.'
1940    return kernel_max
1941
1942
1943def set_high_performance_mode():
1944    """
1945    Sets the kernel governor mode to the highest setting.
1946    Returns previous governor state.
1947    """
1948    original_governors = get_scaling_governor_states()
1949    set_scaling_governors('performance')
1950    return original_governors
1951
1952
1953def set_scaling_governors(value):
1954    """
1955    Sets all scaling governor to string value.
1956    Sample values: 'performance', 'interactive', 'ondemand', 'powersave'.
1957    """
1958    paths = _get_cpufreq_paths('scaling_governor')
1959    for path in paths:
1960        cmd = 'echo %s > %s' % (value, path)
1961        logging.info('Writing scaling governor mode \'%s\' -> %s', value, path)
1962        # On Tegra CPUs can be dynamically enabled/disabled. Ignore failures.
1963        utils.system(cmd, ignore_status=True)
1964
1965
1966def _get_cpufreq_paths(filename):
1967    """
1968    Returns a list of paths to the governors.
1969    """
1970    cmd = 'ls /sys/devices/system/cpu/cpu*/cpufreq/' + filename
1971    paths = utils.run(cmd, verbose=False).stdout.splitlines()
1972    return paths
1973
1974
1975def get_scaling_governor_states():
1976    """
1977    Returns a list of (performance governor path, current state) tuples.
1978    """
1979    paths = _get_cpufreq_paths('scaling_governor')
1980    path_value_list = []
1981    for path in paths:
1982        value = _get_line_from_file(path, 0)
1983        path_value_list.append((path, value))
1984    return path_value_list
1985
1986
1987def restore_scaling_governor_states(path_value_list):
1988    """
1989    Restores governor states. Inverse operation to get_scaling_governor_states.
1990    """
1991    for (path, value) in path_value_list:
1992        cmd = 'echo %s > %s' % (value.rstrip('\n'), path)
1993        # On Tegra CPUs can be dynamically enabled/disabled. Ignore failures.
1994        utils.system(cmd, ignore_status=True)
1995
1996
1997def get_dirty_writeback_centisecs():
1998    """
1999    Reads /proc/sys/vm/dirty_writeback_centisecs.
2000    """
2001    time = _get_int_from_file(_DIRTY_WRITEBACK_CENTISECS, 0, None, None)
2002    return time
2003
2004
2005def set_dirty_writeback_centisecs(time=60000):
2006    """
2007    In hundredths of a second, this is how often pdflush wakes up to write data
2008    to disk. The default wakes up the two (or more) active threads every five
2009    seconds. The ChromeOS default is 10 minutes.
2010
2011    We use this to set as low as 1 second to flush error messages in system
2012    logs earlier to disk.
2013    """
2014    # Flush buffers first to make this function synchronous.
2015    utils.system('sync')
2016    if time >= 0:
2017        cmd = 'echo %d > %s' % (time, _DIRTY_WRITEBACK_CENTISECS)
2018        utils.system(cmd)
2019
2020
2021def wflinfo_cmd():
2022    """
2023    Returns a wflinfo command appropriate to the current graphics platform/api.
2024    """
2025    return 'wflinfo -p %s -a %s' % (graphics_platform(), graphics_api())
2026
2027
2028def has_mali():
2029    """ @return: True if system has a Mali GPU enabled."""
2030    return os.path.exists('/dev/mali0')
2031
2032def get_gpu_family():
2033    """Returns the GPU family name."""
2034    global pciid_to_amd_architecture
2035    global pciid_to_intel_architecture
2036
2037    socfamily = get_cpu_soc_family()
2038    if socfamily == 'exynos5' or socfamily == 'rockchip' or has_mali():
2039        cmd = wflinfo_cmd()
2040        wflinfo = utils.system_output(cmd,
2041                                      retain_output=True,
2042                                      ignore_status=False)
2043        version = re.findall(r'OpenGL renderer string: '
2044                             r'Mali-T([0-9]+)', wflinfo)
2045        if version:
2046            return 'mali-t%s' % version[0]
2047        return 'mali-unrecognized'
2048    if socfamily == 'tegra':
2049        return 'tegra'
2050    if os.path.exists('/sys/kernel/debug/pvr'):
2051        return 'rogue'
2052
2053    pci_vga_device = utils.run("lspci | grep VGA").stdout.rstrip('\n')
2054    bus_device_function = pci_vga_device.partition(' ')[0]
2055    pci_path = '/sys/bus/pci/devices/0000:' + bus_device_function + '/device'
2056
2057    if not os.path.exists(pci_path):
2058        raise error.TestError('PCI device 0000:' + bus_device_function + ' not found')
2059
2060    device_id = utils.read_one_line(pci_path).lower()
2061
2062    if "Advanced Micro Devices" in pci_vga_device:
2063        if not pciid_to_amd_architecture:
2064            with open(_AMD_PCI_IDS_FILE_PATH, 'r') as in_f:
2065                pciid_to_amd_architecture = json.load(in_f)
2066
2067        return pciid_to_amd_architecture[device_id]
2068
2069    if "Intel Corporation" in pci_vga_device:
2070        # Only load Intel PCI ID file once and only if necessary.
2071        if not pciid_to_intel_architecture:
2072            with open(_INTEL_PCI_IDS_FILE_PATH, 'r') as in_f:
2073                pciid_to_intel_architecture = json.load(in_f)
2074
2075        return pciid_to_intel_architecture[device_id]
2076
2077# TODO(ihf): Consider using /etc/lsb-release DEVICETYPE != CHROMEBOOK/CHROMEBASE
2078# for sanity check, but usage seems a bit inconsistent. See
2079# src/third_party/chromiumos-overlay/eclass/appid.eclass
2080_BOARDS_WITHOUT_MONITOR = [
2081    'anglar', 'mccloud', 'monroe', 'ninja', 'rikku', 'guado', 'jecht', 'tidus',
2082    'beltino', 'panther', 'stumpy', 'panther', 'tricky', 'zako', 'veyron_rialto'
2083]
2084
2085
2086def has_no_monitor():
2087    """Returns whether a machine doesn't have a built-in monitor."""
2088    board_name = get_board()
2089    if board_name in _BOARDS_WITHOUT_MONITOR:
2090        return True
2091
2092    return False
2093
2094
2095def get_fixed_dst_drive():
2096    """
2097    Return device name for internal disk.
2098    Example: return /dev/sda for falco booted from usb
2099    """
2100    cmd = ' '.join(['. /usr/sbin/write_gpt.sh;',
2101                    '. /usr/share/misc/chromeos-common.sh;',
2102                    'load_base_vars;',
2103                    'get_fixed_dst_drive'])
2104    return utils.system_output(cmd)
2105
2106
2107def get_root_device():
2108    """
2109    Return root device.
2110    Will return correct disk device even system boot from /dev/dm-0
2111    Example: return /dev/sdb for falco booted from usb
2112    """
2113    return utils.system_output('rootdev -s -d')
2114
2115
2116def get_root_partition():
2117    """
2118    Return current root partition
2119    Example: return /dev/sdb3 for falco booted from usb
2120    """
2121    return utils.system_output('rootdev -s')
2122
2123
2124def get_free_root_partition(root_part=None):
2125    """
2126    Return currently unused root partion
2127    Example: return /dev/sdb5 for falco booted from usb
2128
2129    @param root_part: cuurent root partition
2130    """
2131    spare_root_map = {'3': '5', '5': '3'}
2132    if not root_part:
2133        root_part = get_root_partition()
2134    return root_part[:-1] + spare_root_map[root_part[-1]]
2135
2136
2137def get_kernel_partition(root_part=None):
2138    """
2139    Return current kernel partition
2140    Example: return /dev/sda2 for falco booted from usb
2141
2142    @param root_part: current root partition
2143    """
2144    if not root_part:
2145         root_part = get_root_partition()
2146    current_kernel_map = {'3': '2', '5': '4'}
2147    return root_part[:-1] + current_kernel_map[root_part[-1]]
2148
2149
2150def get_free_kernel_partition(root_part=None):
2151    """
2152    return currently unused kernel partition
2153    Example: return /dev/sda4 for falco booted from usb
2154
2155    @param root_part: current root partition
2156    """
2157    kernel_part = get_kernel_partition(root_part)
2158    spare_kernel_map = {'2': '4', '4': '2'}
2159    return kernel_part[:-1] + spare_kernel_map[kernel_part[-1]]
2160
2161
2162def is_booted_from_internal_disk():
2163    """Return True if boot from internal disk. False, otherwise."""
2164    return get_root_device() == get_fixed_dst_drive()
2165
2166
2167def get_ui_use_flags():
2168    """Parses the USE flags as listed in /etc/ui_use_flags.txt.
2169
2170    @return: A list of flag strings found in the ui use flags file.
2171    """
2172    flags = []
2173    for flag in utils.read_file(_UI_USE_FLAGS_FILE_PATH).splitlines():
2174        # Removes everything after the '#'.
2175        flag_before_comment = flag.split('#')[0].strip()
2176        if len(flag_before_comment) != 0:
2177            flags.append(flag_before_comment)
2178
2179    return flags
2180
2181
2182def graphics_platform():
2183    """
2184    Return a string identifying the graphics platform,
2185    e.g. 'glx' or 'x11_egl' or 'gbm'
2186    """
2187    return 'null'
2188
2189
2190def graphics_api():
2191    """Return a string identifying the graphics api, e.g. gl or gles2."""
2192    use_flags = get_ui_use_flags()
2193    if 'opengles' in use_flags:
2194        return 'gles2'
2195    return 'gl'
2196
2197
2198def is_vm():
2199    """Check if the process is running in a virtual machine.
2200
2201    @return: True if the process is running in a virtual machine, otherwise
2202             return False.
2203    """
2204    try:
2205        virt = utils.run('sudo -n virt-what').stdout.strip()
2206        logging.debug('virt-what output: %s', virt)
2207        return bool(virt)
2208    except error.CmdError:
2209        logging.warn('Package virt-what is not installed, default to assume '
2210                     'it is not a virtual machine.')
2211        return False
2212
2213
2214def is_package_installed(package):
2215    """Check if a package is installed already.
2216
2217    @return: True if the package is already installed, otherwise return False.
2218    """
2219    try:
2220        utils.run(_CHECK_PACKAGE_INSTALLED_COMMAND % package)
2221        return True
2222    except error.CmdError:
2223        logging.warn('Package %s is not installed.', package)
2224        return False
2225
2226
2227def is_python_package_installed(package):
2228    """Check if a Python package is installed already.
2229
2230    @return: True if the package is already installed, otherwise return False.
2231    """
2232    try:
2233        __import__(package)
2234        return True
2235    except ImportError:
2236        logging.warn('Python package %s is not installed.', package)
2237        return False
2238
2239
2240def run_sql_cmd(server, user, password, command, database=''):
2241    """Run the given sql command against the specified database.
2242
2243    @param server: Hostname or IP address of the MySQL server.
2244    @param user: User name to log in the MySQL server.
2245    @param password: Password to log in the MySQL server.
2246    @param command: SQL command to run.
2247    @param database: Name of the database to run the command. Default to empty
2248                     for command that does not require specifying database.
2249
2250    @return: The stdout of the command line.
2251    """
2252    cmd = ('mysql -u%s -p%s --host %s %s -e "%s"' %
2253           (user, password, server, database, command))
2254    # Set verbose to False so the command line won't be logged, as it includes
2255    # database credential.
2256