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