Home | History | Annotate | Download | only in common_lib
      1 # Copyright (c) 2017 The Chromium 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 """
      6 Convenience functions for use by tests or whomever.
      7 
      8 There's no really good way to do this, as this isn't a class we can do
      9 inheritance with, just a collection of static methods.
     10 """
     11 
     12 # pylint: disable=missing-docstring
     13 
     14 import StringIO
     15 import collections
     16 import datetime
     17 import errno
     18 import inspect
     19 import itertools
     20 import logging
     21 import os
     22 import pickle
     23 import Queue
     24 import random
     25 import re
     26 import resource
     27 import select
     28 import shutil
     29 import signal
     30 import socket
     31 import string
     32 import struct
     33 import subprocess
     34 import textwrap
     35 import threading
     36 import time
     37 import urllib2
     38 import urlparse
     39 import uuid
     40 import warnings
     41 
     42 try:
     43     import hashlib
     44 except ImportError:
     45     import md5
     46     import sha
     47 
     48 import common
     49 
     50 from autotest_lib.client.common_lib import env
     51 from autotest_lib.client.common_lib import error
     52 from autotest_lib.client.common_lib import global_config
     53 from autotest_lib.client.common_lib import logging_manager
     54 from autotest_lib.client.common_lib import metrics_mock_class
     55 from autotest_lib.client.cros import constants
     56 
     57 # pylint: disable=wildcard-import
     58 from autotest_lib.client.common_lib.lsbrelease_utils import *
     59 
     60 
     61 def deprecated(func):
     62     """This is a decorator which can be used to mark functions as deprecated.
     63     It will result in a warning being emmitted when the function is used."""
     64     def new_func(*args, **dargs):
     65         warnings.warn("Call to deprecated function %s." % func.__name__,
     66                       category=DeprecationWarning)
     67         return func(*args, **dargs)
     68     new_func.__name__ = func.__name__
     69     new_func.__doc__ = func.__doc__
     70     new_func.__dict__.update(func.__dict__)
     71     return new_func
     72 
     73 
     74 class _NullStream(object):
     75     def write(self, data):
     76         pass
     77 
     78 
     79     def flush(self):
     80         pass
     81 
     82 
     83 TEE_TO_LOGS = object()
     84 _the_null_stream = _NullStream()
     85 
     86 DEVNULL = object()
     87 
     88 DEFAULT_STDOUT_LEVEL = logging.DEBUG
     89 DEFAULT_STDERR_LEVEL = logging.ERROR
     90 
     91 # prefixes for logging stdout/stderr of commands
     92 STDOUT_PREFIX = '[stdout] '
     93 STDERR_PREFIX = '[stderr] '
     94 
     95 # safe characters for the shell (do not need quoting)
     96 SHELL_QUOTING_WHITELIST = frozenset(string.ascii_letters +
     97                                     string.digits +
     98                                     '_-+=')
     99 
    100 def custom_warning_handler(message, category, filename, lineno, file=None,
    101                            line=None):
    102     """Custom handler to log at the WARNING error level. Ignores |file|."""
    103     logging.warning(warnings.formatwarning(message, category, filename, lineno,
    104                                            line))
    105 
    106 warnings.showwarning = custom_warning_handler
    107 
    108 def get_stream_tee_file(stream, level, prefix=''):
    109     if stream is None:
    110         return _the_null_stream
    111     if stream is DEVNULL:
    112         return None
    113     if stream is TEE_TO_LOGS:
    114         return logging_manager.LoggingFile(level=level, prefix=prefix)
    115     return stream
    116 
    117 
    118 def _join_with_nickname(base_string, nickname):
    119     if nickname:
    120         return '%s BgJob "%s" ' % (base_string, nickname)
    121     return base_string
    122 
    123 
    124 # TODO: Cleanup and possibly eliminate |unjoinable|, which is only used in our
    125 # master-ssh connection process, while fixing underlying
    126 # semantics problem in BgJob. See crbug.com/279312
    127 class BgJob(object):
    128     def __init__(self, command, stdout_tee=None, stderr_tee=None, verbose=True,
    129                  stdin=None, stdout_level=DEFAULT_STDOUT_LEVEL,
    130                  stderr_level=DEFAULT_STDERR_LEVEL, nickname=None,
    131                  unjoinable=False, env=None, extra_paths=None):
    132         """Create and start a new BgJob.
    133 
    134         This constructor creates a new BgJob, and uses Popen to start a new
    135         subprocess with given command. It returns without blocking on execution
    136         of the subprocess.
    137 
    138         After starting a new BgJob, use output_prepare to connect the process's
    139         stdout and stderr pipes to the stream of your choice.
    140 
    141         When the job is running, the jobs's output streams are only read from
    142         when process_output is called.
    143 
    144         @param command: command to be executed in new subprocess. May be either
    145                         a list, or a string (in which case Popen will be called
    146                         with shell=True)
    147         @param stdout_tee: (Optional) a file like object, TEE_TO_LOGS or
    148                            DEVNULL.
    149                            If not given, after finishing the process, the
    150                            stdout data from subprocess is available in
    151                            result.stdout.
    152                            If a file like object is given, in process_output(),
    153                            the stdout data from the subprocess will be handled
    154                            by the given file like object.
    155                            If TEE_TO_LOGS is given, in process_output(), the
    156                            stdout data from the subprocess will be handled by
    157                            the standard logging_manager.
    158                            If DEVNULL is given, the stdout of the subprocess
    159                            will be just discarded. In addition, even after
    160                            cleanup(), result.stdout will be just an empty
    161                            string (unlike the case where stdout_tee is not
    162                            given).
    163         @param stderr_tee: Same as stdout_tee, but for stderr.
    164         @param verbose: Boolean, make BgJob logging more verbose.
    165         @param stdin: Stream object, will be passed to Popen as the new
    166                       process's stdin.
    167         @param stdout_level: A logging level value. If stdout_tee was set to
    168                              TEE_TO_LOGS, sets the level that tee'd
    169                              stdout output will be logged at. Ignored
    170                              otherwise.
    171         @param stderr_level: Same as stdout_level, but for stderr.
    172         @param nickname: Optional string, to be included in logging messages
    173         @param unjoinable: Optional bool, default False.
    174                            This should be True for BgJobs running in background
    175                            and will never be joined with join_bg_jobs(), such
    176                            as the master-ssh connection. Instead, it is
    177                            caller's responsibility to terminate the subprocess
    178                            correctly, e.g. by calling nuke_subprocess().
    179                            This will lead that, calling join_bg_jobs(),
    180                            process_output() or cleanup() will result in an
    181                            InvalidBgJobCall exception.
    182                            Also, |stdout_tee| and |stderr_tee| must be set to
    183                            DEVNULL, otherwise InvalidBgJobCall is raised.
    184         @param env: Dict containing environment variables used in subprocess.
    185         @param extra_paths: Optional string list, to be prepended to the PATH
    186                             env variable in env (or os.environ dict if env is
    187                             not specified).
    188         """
    189         self.command = command
    190         self.unjoinable = unjoinable
    191         if (unjoinable and (stdout_tee != DEVNULL or stderr_tee != DEVNULL)):
    192             raise error.InvalidBgJobCall(
    193                 'stdout_tee and stderr_tee must be DEVNULL for '
    194                 'unjoinable BgJob')
    195         self._stdout_tee = get_stream_tee_file(
    196                 stdout_tee, stdout_level,
    197                 prefix=_join_with_nickname(STDOUT_PREFIX, nickname))
    198         self._stderr_tee = get_stream_tee_file(
    199                 stderr_tee, stderr_level,
    200                 prefix=_join_with_nickname(STDERR_PREFIX, nickname))
    201         self.result = CmdResult(command)
    202 
    203         # allow for easy stdin input by string, we'll let subprocess create
    204         # a pipe for stdin input and we'll write to it in the wait loop
    205         if isinstance(stdin, basestring):
    206             self.string_stdin = stdin
    207             stdin = subprocess.PIPE
    208         else:
    209             self.string_stdin = None
    210 
    211         # Prepend extra_paths to env['PATH'] if necessary.
    212         if extra_paths:
    213             env = (os.environ if env is None else env).copy()
    214             oldpath = env.get('PATH')
    215             env['PATH'] = os.pathsep.join(
    216                     extra_paths + ([oldpath] if oldpath else []))
    217 
    218         if verbose:
    219             logging.debug("Running '%s'", command)
    220 
    221         if type(command) == list:
    222             shell = False
    223             executable = None
    224         else:
    225             shell = True
    226             executable = '/bin/bash'
    227 
    228         with open('/dev/null', 'w') as devnull:
    229             self.sp = subprocess.Popen(
    230                 command,
    231                 stdin=stdin,
    232                 stdout=devnull if stdout_tee == DEVNULL else subprocess.PIPE,
    233                 stderr=devnull if stderr_tee == DEVNULL else subprocess.PIPE,
    234                 preexec_fn=self._reset_sigpipe,
    235                 shell=shell, executable=executable,
    236                 env=env, close_fds=True)
    237 
    238         self._cleanup_called = False
    239         self._stdout_file = (
    240             None if stdout_tee == DEVNULL else StringIO.StringIO())
    241         self._stderr_file = (
    242             None if stderr_tee == DEVNULL else StringIO.StringIO())
    243 
    244     def process_output(self, stdout=True, final_read=False):
    245         """Read from process's output stream, and write data to destinations.
    246 
    247         This function reads up to 1024 bytes from the background job's
    248         stdout or stderr stream, and writes the resulting data to the BgJob's
    249         output tee and to the stream set up in output_prepare.
    250 
    251         Warning: Calls to process_output will block on reads from the
    252         subprocess stream, and will block on writes to the configured
    253         destination stream.
    254 
    255         @param stdout: True = read and process data from job's stdout.
    256                        False = from stderr.
    257                        Default: True
    258         @param final_read: Do not read only 1024 bytes from stream. Instead,
    259                            read and process all data until end of the stream.
    260 
    261         """
    262         if self.unjoinable:
    263             raise error.InvalidBgJobCall('Cannot call process_output on '
    264                                          'a job with unjoinable BgJob')
    265         if stdout:
    266             pipe, buf, tee = (
    267                 self.sp.stdout, self._stdout_file, self._stdout_tee)
    268         else:
    269             pipe, buf, tee = (
    270                 self.sp.stderr, self._stderr_file, self._stderr_tee)
    271 
    272         if not pipe:
    273             return
    274 
    275         if final_read:
    276             # read in all the data we can from pipe and then stop
    277             data = []
    278             while select.select([pipe], [], [], 0)[0]:
    279                 data.append(os.read(pipe.fileno(), 1024))
    280                 if len(data[-1]) == 0:
    281                     break
    282             data = "".join(data)
    283         else:
    284             # perform a single read
    285             data = os.read(pipe.fileno(), 1024)
    286         buf.write(data)
    287         tee.write(data)
    288 
    289     def cleanup(self):
    290         """Clean up after BgJob.
    291 
    292         Flush the stdout_tee and stderr_tee buffers, close the
    293         subprocess stdout and stderr buffers, and saves data from
    294         the configured stdout and stderr destination streams to
    295         self.result. Duplicate calls ignored with a warning.
    296         """
    297         if self.unjoinable:
    298             raise error.InvalidBgJobCall('Cannot call cleanup on '
    299                                          'a job with a unjoinable BgJob')
    300         if self._cleanup_called:
    301             logging.warning('BgJob [%s] received a duplicate call to '
    302                             'cleanup. Ignoring.', self.command)
    303             return
    304         try:
    305             if self.sp.stdout:
    306                 self._stdout_tee.flush()
    307                 self.sp.stdout.close()
    308                 self.result.stdout = self._stdout_file.getvalue()
    309 
    310             if self.sp.stderr:
    311                 self._stderr_tee.flush()
    312                 self.sp.stderr.close()
    313                 self.result.stderr = self._stderr_file.getvalue()
    314         finally:
    315             self._cleanup_called = True
    316 
    317     def _reset_sigpipe(self):
    318         if not env.IN_MOD_WSGI:
    319             signal.signal(signal.SIGPIPE, signal.SIG_DFL)
    320 
    321 
    322 def ip_to_long(ip):
    323     # !L is a long in network byte order
    324     return struct.unpack('!L', socket.inet_aton(ip))[0]
    325 
    326 
    327 def long_to_ip(number):
    328     # See above comment.
    329     return socket.inet_ntoa(struct.pack('!L', number))
    330 
    331 
    332 def create_subnet_mask(bits):
    333     return (1 << 32) - (1 << 32-bits)
    334 
    335 
    336 def format_ip_with_mask(ip, mask_bits):
    337     masked_ip = ip_to_long(ip) & create_subnet_mask(mask_bits)
    338     return "%s/%s" % (long_to_ip(masked_ip), mask_bits)
    339 
    340 
    341 def normalize_hostname(alias):
    342     ip = socket.gethostbyname(alias)
    343     return socket.gethostbyaddr(ip)[0]
    344 
    345 
    346 def get_ip_local_port_range():
    347     match = re.match(r'\s*(\d+)\s*(\d+)\s*$',
    348                      read_one_line('/proc/sys/net/ipv4/ip_local_port_range'))
    349     return (int(match.group(1)), int(match.group(2)))
    350 
    351 
    352 def set_ip_local_port_range(lower, upper):
    353     write_one_line('/proc/sys/net/ipv4/ip_local_port_range',
    354                    '%d %d\n' % (lower, upper))
    355 
    356 
    357 def read_one_line(filename):
    358     f = open(filename, 'r')
    359     try:
    360         return f.readline().rstrip('\n')
    361     finally:
    362         f.close()
    363 
    364 
    365 def read_file(filename):
    366     f = open(filename)
    367     try:
    368         return f.read()
    369     finally:
    370         f.close()
    371 
    372 
    373 def get_field(data, param, linestart="", sep=" "):
    374     """
    375     Parse data from string.
    376     @param data: Data to parse.
    377         example:
    378           data:
    379              cpu   324 345 34  5 345
    380              cpu0  34  11  34 34  33
    381              ^^^^
    382              start of line
    383              params 0   1   2  3   4
    384     @param param: Position of parameter after linestart marker.
    385     @param linestart: String to which start line with parameters.
    386     @param sep: Separator between parameters regular expression.
    387     """
    388     search = re.compile(r"(?<=^%s)\s*(.*)" % linestart, re.MULTILINE)
    389     find = search.search(data)
    390     if find != None:
    391         return re.split("%s" % sep, find.group(1))[param]
    392     else:
    393         print "There is no line which starts with %s in data." % linestart
    394         return None
    395 
    396 
    397 def write_one_line(filename, line):
    398     open_write_close(filename, str(line).rstrip('\n') + '\n')
    399 
    400 
    401 def open_write_close(filename, data):
    402     f = open(filename, 'w')
    403     try:
    404         f.write(data)
    405     finally:
    406         f.close()
    407 
    408 
    409 def locate_file(path, base_dir=None):
    410     """Locates a file.
    411 
    412     @param path: The path of the file being located. Could be absolute or
    413         relative path. For relative path, it tries to locate the file from
    414         base_dir.
    415 
    416     @param base_dir (optional): Base directory of the relative path.
    417 
    418     @returns Absolute path of the file if found. None if path is None.
    419     @raises error.TestFail if the file is not found.
    420     """
    421     if path is None:
    422         return None
    423 
    424     if not os.path.isabs(path) and base_dir is not None:
    425         # Assume the relative path is based in autotest directory.
    426         path = os.path.join(base_dir, path)
    427     if not os.path.isfile(path):
    428         raise error.TestFail('ERROR: Unable to find %s' % path)
    429     return path
    430 
    431 
    432 def matrix_to_string(matrix, header=None):
    433     """
    434     Return a pretty, aligned string representation of a nxm matrix.
    435 
    436     This representation can be used to print any tabular data, such as
    437     database results. It works by scanning the lengths of each element
    438     in each column, and determining the format string dynamically.
    439 
    440     @param matrix: Matrix representation (list with n rows of m elements).
    441     @param header: Optional tuple or list with header elements to be displayed.
    442     """
    443     if type(header) is list:
    444         header = tuple(header)
    445     lengths = []
    446     if header:
    447         for column in header:
    448             lengths.append(len(column))
    449     for row in matrix:
    450         for i, column in enumerate(row):
    451             column = unicode(column).encode("utf-8")
    452             cl = len(column)
    453             try:
    454                 ml = lengths[i]
    455                 if cl > ml:
    456                     lengths[i] = cl
    457             except IndexError:
    458                 lengths.append(cl)
    459 
    460     lengths = tuple(lengths)
    461     format_string = ""
    462     for length in lengths:
    463         format_string += "%-" + str(length) + "s "
    464     format_string += "\n"
    465 
    466     matrix_str = ""
    467     if header:
    468         matrix_str += format_string % header
    469     for row in matrix:
    470         matrix_str += format_string % tuple(row)
    471 
    472     return matrix_str
    473 
    474 
    475 def read_keyval(path, type_tag=None):
    476     """
    477     Read a key-value pair format file into a dictionary, and return it.
    478     Takes either a filename or directory name as input. If it's a
    479     directory name, we assume you want the file to be called keyval.
    480 
    481     @param path: Full path of the file to read from.
    482     @param type_tag: If not None, only keyvals with key ending
    483                      in a suffix {type_tag} will be collected.
    484     """
    485     if os.path.isdir(path):
    486         path = os.path.join(path, 'keyval')
    487     if not os.path.exists(path):
    488         return {}
    489 
    490     if type_tag:
    491         pattern = r'^([-\.\w]+)\{%s\}=(.*)$' % type_tag
    492     else:
    493         pattern = r'^([-\.\w]+)=(.*)$'
    494 
    495     keyval = {}
    496     f = open(path)
    497     for line in f:
    498         line = re.sub('#.*', '', line).rstrip()
    499         if not line:
    500             continue
    501         match = re.match(pattern, line)
    502         if match:
    503             key = match.group(1)
    504             value = match.group(2)
    505             if re.search('^\d+$', value):
    506                 value = int(value)
    507             elif re.search('^(\d+\.)?\d+$', value):
    508                 value = float(value)
    509             keyval[key] = value
    510         else:
    511             raise ValueError('Invalid format line: %s' % line)
    512     f.close()
    513     return keyval
    514 
    515 
    516 def write_keyval(path, dictionary, type_tag=None):
    517     """
    518     Write a key-value pair format file out to a file. This uses append
    519     mode to open the file, so existing text will not be overwritten or
    520     reparsed.
    521 
    522     If type_tag is None, then the key must be composed of alphanumeric
    523     characters (or dashes+underscores). However, if type-tag is not
    524     null then the keys must also have "{type_tag}" as a suffix. At
    525     the moment the only valid values of type_tag are "attr" and "perf".
    526 
    527     @param path: full path of the file to be written
    528     @param dictionary: the items to write
    529     @param type_tag: see text above
    530     """
    531     if os.path.isdir(path):
    532         path = os.path.join(path, 'keyval')
    533     keyval = open(path, 'a')
    534 
    535     if type_tag is None:
    536         key_regex = re.compile(r'^[-\.\w]+$')
    537     else:
    538         if type_tag not in ('attr', 'perf'):
    539             raise ValueError('Invalid type tag: %s' % type_tag)
    540         escaped_tag = re.escape(type_tag)
    541         key_regex = re.compile(r'^[-\.\w]+\{%s\}$' % escaped_tag)
    542     try:
    543         for key in sorted(dictionary.keys()):
    544             if not key_regex.search(key):
    545                 raise ValueError('Invalid key: %s' % key)
    546             keyval.write('%s=%s\n' % (key, dictionary[key]))
    547     finally:
    548         keyval.close()
    549 
    550 
    551 def is_url(path):
    552     """Return true if path looks like a URL"""
    553     # for now, just handle http and ftp
    554     url_parts = urlparse.urlparse(path)
    555     return (url_parts[0] in ('http', 'ftp'))
    556 
    557 
    558 def urlopen(url, data=None, timeout=5):
    559     """Wrapper to urllib2.urlopen with timeout addition."""
    560 
    561     # Save old timeout
    562     old_timeout = socket.getdefaulttimeout()
    563     socket.setdefaulttimeout(timeout)
    564     try:
    565         return urllib2.urlopen(url, data=data)
    566     finally:
    567         socket.setdefaulttimeout(old_timeout)
    568 
    569 
    570 def urlretrieve(url, filename, data=None, timeout=300):
    571     """Retrieve a file from given url."""
    572     logging.debug('Fetching %s -> %s', url, filename)
    573 
    574     src_file = urlopen(url, data=data, timeout=timeout)
    575     try:
    576         dest_file = open(filename, 'wb')
    577         try:
    578             shutil.copyfileobj(src_file, dest_file)
    579         finally:
    580             dest_file.close()
    581     finally:
    582         src_file.close()
    583 
    584 
    585 def hash(hashtype, input=None):
    586     """
    587     Returns an hash object of type md5 or sha1. This function is implemented in
    588     order to encapsulate hash objects in a way that is compatible with python
    589     2.4 and python 2.6 without warnings.
    590 
    591     Note that even though python 2.6 hashlib supports hash types other than
    592     md5 and sha1, we are artificially limiting the input values in order to
    593     make the function to behave exactly the same among both python
    594     implementations.
    595 
    596     @param input: Optional input string that will be used to update the hash.
    597     """
    598     # pylint: disable=redefined-builtin
    599     if hashtype not in ['md5', 'sha1']:
    600         raise ValueError("Unsupported hash type: %s" % hashtype)
    601 
    602     try:
    603         computed_hash = hashlib.new(hashtype)
    604     except NameError:
    605         if hashtype == 'md5':
    606             computed_hash = md5.new()
    607         elif hashtype == 'sha1':
    608             computed_hash = sha.new()
    609 
    610     if input:
    611         computed_hash.update(input)
    612 
    613     return computed_hash
    614 
    615 
    616 def get_file(src, dest, permissions=None):
    617     """Get a file from src, which can be local or a remote URL"""
    618     if src == dest:
    619         return
    620 
    621     if is_url(src):
    622         urlretrieve(src, dest)
    623     else:
    624         shutil.copyfile(src, dest)
    625 
    626     if permissions:
    627         os.chmod(dest, permissions)
    628     return dest
    629 
    630 
    631 def unmap_url(srcdir, src, destdir='.'):
    632     """
    633     Receives either a path to a local file or a URL.
    634     returns either the path to the local file, or the fetched URL
    635 
    636     unmap_url('/usr/src', 'foo.tar', '/tmp')
    637                             = '/usr/src/foo.tar'
    638     unmap_url('/usr/src', 'http://site/file', '/tmp')
    639                             = '/tmp/file'
    640                             (after retrieving it)
    641     """
    642     if is_url(src):
    643         url_parts = urlparse.urlparse(src)
    644         filename = os.path.basename(url_parts[2])
    645         dest = os.path.join(destdir, filename)
    646         return get_file(src, dest)
    647     else:
    648         return os.path.join(srcdir, src)
    649 
    650 
    651 def update_version(srcdir, preserve_srcdir, new_version, install,
    652                    *args, **dargs):
    653     """
    654     Make sure srcdir is version new_version
    655 
    656     If not, delete it and install() the new version.
    657 
    658     In the preserve_srcdir case, we just check it's up to date,
    659     and if not, we rerun install, without removing srcdir
    660     """
    661     versionfile = os.path.join(srcdir, '.version')
    662     install_needed = True
    663 
    664     if os.path.exists(versionfile):
    665         old_version = pickle.load(open(versionfile))
    666         if old_version == new_version:
    667             install_needed = False
    668 
    669     if install_needed:
    670         if not preserve_srcdir and os.path.exists(srcdir):
    671             shutil.rmtree(srcdir)
    672         install(*args, **dargs)
    673         if os.path.exists(srcdir):
    674             pickle.dump(new_version, open(versionfile, 'w'))
    675 
    676 
    677 def get_stderr_level(stderr_is_expected, stdout_level=DEFAULT_STDOUT_LEVEL):
    678     if stderr_is_expected:
    679         return stdout_level
    680     return DEFAULT_STDERR_LEVEL
    681 
    682 
    683 def run(command, timeout=None, ignore_status=False, stdout_tee=None,
    684         stderr_tee=None, verbose=True, stdin=None, stderr_is_expected=None,
    685         stdout_level=None, stderr_level=None, args=(), nickname=None,
    686         ignore_timeout=False, env=None, extra_paths=None):
    687     """
    688     Run a command on the host.
    689 
    690     @param command: the command line string.
    691     @param timeout: time limit in seconds before attempting to kill the
    692             running process. The run() function will take a few seconds
    693             longer than 'timeout' to complete if it has to kill the process.
    694     @param ignore_status: do not raise an exception, no matter what the exit
    695             code of the command is.
    696     @param stdout_tee: optional file-like object to which stdout data
    697             will be written as it is generated (data will still be stored
    698             in result.stdout unless this is DEVNULL).
    699     @param stderr_tee: likewise for stderr.
    700     @param verbose: if True, log the command being run.
    701     @param stdin: stdin to pass to the executed process (can be a file
    702             descriptor, a file object of a real file or a string).
    703     @param stderr_is_expected: if True, stderr will be logged at the same level
    704             as stdout
    705     @param stdout_level: logging level used if stdout_tee is TEE_TO_LOGS;
    706             if None, a default is used.
    707     @param stderr_level: like stdout_level but for stderr.
    708     @param args: sequence of strings of arguments to be given to the command
    709             inside " quotes after they have been escaped for that; each
    710             element in the sequence will be given as a separate command
    711             argument
    712     @param nickname: Short string that will appear in logging messages
    713                      associated with this command.
    714     @param ignore_timeout: If True, timeouts are ignored otherwise if a
    715             timeout occurs it will raise CmdTimeoutError.
    716     @param env: Dict containing environment variables used in a subprocess.
    717     @param extra_paths: Optional string list, to be prepended to the PATH
    718                         env variable in env (or os.environ dict if env is
    719                         not specified).
    720 
    721     @return a CmdResult object or None if the command timed out and
    722             ignore_timeout is True
    723 
    724     @raise CmdError: the exit code of the command execution was not 0
    725     @raise CmdTimeoutError: the command timed out and ignore_timeout is False.
    726     """
    727     if isinstance(args, basestring):
    728         raise TypeError('Got a string for the "args" keyword argument, '
    729                         'need a sequence.')
    730 
    731     # In some cases, command will actually be a list
    732     # (For example, see get_user_hash in client/cros/cryptohome.py.)
    733     # So, to cover that case, detect if it's a string or not and convert it
    734     # into one if necessary.
    735     if not isinstance(command, basestring):
    736         command = ' '.join([sh_quote_word(arg) for arg in command])
    737 
    738     command = ' '.join([command] + [sh_quote_word(arg) for arg in args])
    739 
    740     if stderr_is_expected is None:
    741         stderr_is_expected = ignore_status
    742     if stdout_level is None:
    743         stdout_level = DEFAULT_STDOUT_LEVEL
    744     if stderr_level is None:
    745         stderr_level = get_stderr_level(stderr_is_expected, stdout_level)
    746 
    747     try:
    748         bg_job = join_bg_jobs(
    749             (BgJob(command, stdout_tee, stderr_tee, verbose, stdin=stdin,
    750                    stdout_level=stdout_level, stderr_level=stderr_level,
    751                    nickname=nickname, env=env, extra_paths=extra_paths),),
    752             timeout)[0]
    753     except error.CmdTimeoutError:
    754         if not ignore_timeout:
    755             raise
    756         return None
    757 
    758     if not ignore_status and bg_job.result.exit_status:
    759         raise error.CmdError(command, bg_job.result,
    760                              "Command returned non-zero exit status")
    761 
    762     return bg_job.result
    763 
    764 
    765 def run_parallel(commands, timeout=None, ignore_status=False,
    766                  stdout_tee=None, stderr_tee=None,
    767                  nicknames=None):
    768     """
    769     Behaves the same as run() with the following exceptions:
    770 
    771     - commands is a list of commands to run in parallel.
    772     - ignore_status toggles whether or not an exception should be raised
    773       on any error.
    774 
    775     @return: a list of CmdResult objects
    776     """
    777     bg_jobs = []
    778     if nicknames is None:
    779         nicknames = []
    780     for (command, nickname) in itertools.izip_longest(commands, nicknames):
    781         bg_jobs.append(BgJob(command, stdout_tee, stderr_tee,
    782                              stderr_level=get_stderr_level(ignore_status),
    783                              nickname=nickname))
    784 
    785     # Updates objects in bg_jobs list with their process information
    786     join_bg_jobs(bg_jobs, timeout)
    787 
    788     for bg_job in bg_jobs:
    789         if not ignore_status and bg_job.result.exit_status:
    790             raise error.CmdError(command, bg_job.result,
    791                                  "Command returned non-zero exit status")
    792 
    793     return [bg_job.result for bg_job in bg_jobs]
    794 
    795 
    796 @deprecated
    797 def run_bg(command):
    798     """Function deprecated. Please use BgJob class instead."""
    799     bg_job = BgJob(command)
    800     return bg_job.sp, bg_job.result
    801 
    802 
    803 def join_bg_jobs(bg_jobs, timeout=None):
    804     """Joins the bg_jobs with the current thread.
    805 
    806     Returns the same list of bg_jobs objects that was passed in.
    807     """
    808     if any(bg_job.unjoinable for bg_job in bg_jobs):
    809         raise error.InvalidBgJobCall(
    810                 'join_bg_jobs cannot be called for unjoinable bg_job')
    811 
    812     timeout_error = False
    813     try:
    814         # We are holding ends to stdin, stdout pipes
    815         # hence we need to be sure to close those fds no mater what
    816         start_time = time.time()
    817         timeout_error = _wait_for_commands(bg_jobs, start_time, timeout)
    818 
    819         for bg_job in bg_jobs:
    820             # Process stdout and stderr
    821             bg_job.process_output(stdout=True,final_read=True)
    822             bg_job.process_output(stdout=False,final_read=True)
    823     finally:
    824         # close our ends of the pipes to the sp no matter what
    825         for bg_job in bg_jobs:
    826             bg_job.cleanup()
    827 
    828     if timeout_error:
    829         # TODO: This needs to be fixed to better represent what happens when
    830         # running in parallel. However this is backwards compatable, so it will
    831         # do for the time being.
    832         raise error.CmdTimeoutError(
    833                 bg_jobs[0].command, bg_jobs[0].result,
    834                 "Command(s) did not complete within %d seconds" % timeout)
    835 
    836 
    837     return bg_jobs
    838 
    839 
    840 def _wait_for_commands(bg_jobs, start_time, timeout):
    841     """Waits for background jobs by select polling their stdout/stderr.
    842 
    843     @param bg_jobs: A list of background jobs to wait on.
    844     @param start_time: Time used to calculate the timeout lifetime of a job.
    845     @param timeout: The timeout of the list of bg_jobs.
    846 
    847     @return: True if the return was due to a timeout, False otherwise.
    848     """
    849 
    850     # To check for processes which terminate without producing any output
    851     # a 1 second timeout is used in select.
    852     SELECT_TIMEOUT = 1
    853 
    854     read_list = []
    855     write_list = []
    856     reverse_dict = {}
    857 
    858     for bg_job in bg_jobs:
    859         if bg_job.sp.stdout:
    860             read_list.append(bg_job.sp.stdout)
    861             reverse_dict[bg_job.sp.stdout] = (bg_job, True)
    862         if bg_job.sp.stderr:
    863             read_list.append(bg_job.sp.stderr)
    864             reverse_dict[bg_job.sp.stderr] = (bg_job, False)
    865         if bg_job.string_stdin is not None:
    866             write_list.append(bg_job.sp.stdin)
    867             reverse_dict[bg_job.sp.stdin] = bg_job
    868 
    869     if timeout:
    870         stop_time = start_time + timeout
    871         time_left = stop_time - time.time()
    872     else:
    873         time_left = None # so that select never times out
    874 
    875     while not timeout or time_left > 0:
    876         # select will return when we may write to stdin, when there is
    877         # stdout/stderr output we can read (including when it is
    878         # EOF, that is the process has terminated) or when a non-fatal
    879         # signal was sent to the process. In the last case the select returns
    880         # EINTR, and we continue waiting for the job if the signal handler for
    881         # the signal that interrupted the call allows us to.
    882         try:
    883             read_ready, write_ready, _ = select.select(read_list, write_list,
    884                                                        [], SELECT_TIMEOUT)
    885         except select.error as v:
    886             if v[0] == errno.EINTR:
    887                 logging.warning(v)
    888                 continue
    889             else:
    890                 raise
    891         # os.read() has to be used instead of
    892         # subproc.stdout.read() which will otherwise block
    893         for file_obj in read_ready:
    894             bg_job, is_stdout = reverse_dict[file_obj]
    895             bg_job.process_output(is_stdout)
    896 
    897         for file_obj in write_ready:
    898             # we can write PIPE_BUF bytes without blocking
    899             # POSIX requires PIPE_BUF is >= 512
    900             bg_job = reverse_dict[file_obj]
    901             file_obj.write(bg_job.string_stdin[:512])
    902             bg_job.string_stdin = bg_job.string_stdin[512:]
    903             # no more input data, close stdin, remove it from the select set
    904             if not bg_job.string_stdin:
    905                 file_obj.close()
    906                 write_list.remove(file_obj)
    907                 del reverse_dict[file_obj]
    908 
    909         all_jobs_finished = True
    910         for bg_job in bg_jobs:
    911             if bg_job.result.exit_status is not None:
    912                 continue
    913 
    914             bg_job.result.exit_status = bg_job.sp.poll()
    915             if bg_job.result.exit_status is not None:
    916                 # process exited, remove its stdout/stdin from the select set
    917                 bg_job.result.duration = time.time() - start_time
    918                 if bg_job.sp.stdout:
    919                     read_list.remove(bg_job.sp.stdout)
    920                     del reverse_dict[bg_job.sp.stdout]
    921                 if bg_job.sp.stderr:
    922                     read_list.remove(bg_job.sp.stderr)
    923                     del reverse_dict[bg_job.sp.stderr]
    924             else:
    925                 all_jobs_finished = False
    926 
    927         if all_jobs_finished:
    928             return False
    929 
    930         if timeout:
    931             time_left = stop_time - time.time()
    932 
    933     # Kill all processes which did not complete prior to timeout
    934     for bg_job in bg_jobs:
    935         if bg_job.result.exit_status is not None:
    936             continue
    937 
    938         logging.warning('run process timeout (%s) fired on: %s', timeout,
    939                         bg_job.command)
    940         if nuke_subprocess(bg_job.sp) is None:
    941             # If process could not be SIGKILL'd, log kernel stack.
    942             logging.warning(read_file('/proc/%d/stack' % bg_job.sp.pid))
    943         bg_job.result.exit_status = bg_job.sp.poll()
    944         bg_job.result.duration = time.time() - start_time
    945 
    946     return True
    947 
    948 
    949 def pid_is_alive(pid):
    950     """
    951     True if process pid exists and is not yet stuck in Zombie state.
    952     Zombies are impossible to move between cgroups, etc.
    953     pid can be integer, or text of integer.
    954     """
    955     path = '/proc/%s/stat' % pid
    956 
    957     try:
    958         stat = read_one_line(path)
    959     except IOError:
    960         if not os.path.exists(path):
    961             # file went away
    962             return False
    963         raise
    964 
    965     return stat.split()[2] != 'Z'
    966 
    967 
    968 def signal_pid(pid, sig):
    969     """
    970     Sends a signal to a process id. Returns True if the process terminated
    971     successfully, False otherwise.
    972     """
    973     try:
    974         os.kill(pid, sig)
    975     except OSError:
    976         # The process may have died before we could kill it.
    977         pass
    978 
    979     for _ in range(5):
    980         if not pid_is_alive(pid):
    981             return True
    982         time.sleep(1)
    983 
    984     # The process is still alive
    985     return False
    986 
    987 
    988 def nuke_subprocess(subproc):
    989     # check if the subprocess is still alive, first
    990     if subproc.poll() is not None:
    991         return subproc.poll()
    992 
    993     # the process has not terminated within timeout,
    994     # kill it via an escalating series of signals.
    995     signal_queue = [signal.SIGTERM, signal.SIGKILL]
    996     for sig in signal_queue:
    997         signal_pid(subproc.pid, sig)
    998         if subproc.poll() is not None:
    999             return subproc.poll()
   1000 
   1001 
   1002 def nuke_pid(pid, signal_queue=(signal.SIGTERM, signal.SIGKILL)):
   1003     # the process has not terminated within timeout,
   1004     # kill it via an escalating series of signals.
   1005     pid_path = '/proc/%d/'
   1006     if not os.path.exists(pid_path % pid):
   1007         # Assume that if the pid does not exist in proc it is already dead.
   1008         logging.error('No listing in /proc for pid:%d.', pid)
   1009         raise error.AutoservPidAlreadyDeadError('Could not kill nonexistant '
   1010                                                 'pid: %s.', pid)
   1011     for sig in signal_queue:
   1012         if signal_pid(pid, sig):
   1013             return
   1014 
   1015     # no signal successfully terminated the process
   1016     raise error.AutoservRunError('Could not kill %d for process name: %s' % (
   1017             pid, get_process_name(pid)), None)
   1018 
   1019 
   1020 def system(command, timeout=None, ignore_status=False):
   1021     """
   1022     Run a command
   1023 
   1024     @param timeout: timeout in seconds
   1025     @param ignore_status: if ignore_status=False, throw an exception if the
   1026             command's exit code is non-zero
   1027             if ignore_stauts=True, return the exit code.
   1028 
   1029     @return exit status of command
   1030             (note, this will always be zero unless ignore_status=True)
   1031     """
   1032     return run(command, timeout=timeout, ignore_status=ignore_status,
   1033                stdout_tee=TEE_TO_LOGS, stderr_tee=TEE_TO_LOGS).exit_status
   1034 
   1035 
   1036 def system_parallel(commands, timeout=None, ignore_status=False):
   1037     """This function returns a list of exit statuses for the respective
   1038     list of commands."""
   1039     return [bg_jobs.exit_status for bg_jobs in
   1040             run_parallel(commands, timeout=timeout, ignore_status=ignore_status,
   1041                          stdout_tee=TEE_TO_LOGS, stderr_tee=TEE_TO_LOGS)]
   1042 
   1043 
   1044 def system_output(command, timeout=None, ignore_status=False,
   1045                   retain_output=False, args=()):
   1046     """
   1047     Run a command and return the stdout output.
   1048 
   1049     @param command: command string to execute.
   1050     @param timeout: time limit in seconds before attempting to kill the
   1051             running process. The function will take a few seconds longer
   1052             than 'timeout' to complete if it has to kill the process.
   1053     @param ignore_status: do not raise an exception, no matter what the exit
   1054             code of the command is.
   1055     @param retain_output: set to True to make stdout/stderr of the command
   1056             output to be also sent to the logging system
   1057     @param args: sequence of strings of arguments to be given to the command
   1058             inside " quotes after they have been escaped for that; each
   1059             element in the sequence will be given as a separate command
   1060             argument
   1061 
   1062     @return a string with the stdout output of the command.
   1063     """
   1064     if retain_output:
   1065         out = run(command, timeout=timeout, ignore_status=ignore_status,
   1066                   stdout_tee=TEE_TO_LOGS, stderr_tee=TEE_TO_LOGS,
   1067                   args=args).stdout
   1068     else:
   1069         out = run(command, timeout=timeout, ignore_status=ignore_status,
   1070                   args=args).stdout
   1071     if out[-1:] == '\n':
   1072         out = out[:-1]
   1073     return out
   1074 
   1075 
   1076 def system_output_parallel(commands, timeout=None, ignore_status=False,
   1077                            retain_output=False):
   1078     if retain_output:
   1079         out = [bg_job.stdout for bg_job
   1080                in run_parallel(commands, timeout=timeout,
   1081                                ignore_status=ignore_status,
   1082                                stdout_tee=TEE_TO_LOGS, stderr_tee=TEE_TO_LOGS)]
   1083     else:
   1084         out = [bg_job.stdout for bg_job in run_parallel(commands,
   1085                                   timeout=timeout, ignore_status=ignore_status)]
   1086     for _ in out:
   1087         if out[-1:] == '\n':
   1088             out = out[:-1]
   1089     return out
   1090 
   1091 
   1092 def strip_unicode(input_obj):
   1093     if type(input_obj) == list:
   1094         return [strip_unicode(i) for i in input_obj]
   1095     elif type(input_obj) == dict:
   1096         output = {}
   1097         for key in input_obj.keys():
   1098             output[str(key)] = strip_unicode(input_obj[key])
   1099         return output
   1100     elif type(input_obj) == unicode:
   1101         return str(input_obj)
   1102     else:
   1103         return input_obj
   1104 
   1105 
   1106 def get_cpu_percentage(function, *args, **dargs):
   1107     """Returns a tuple containing the CPU% and return value from function call.
   1108 
   1109     This function calculates the usage time by taking the difference of
   1110     the user and system times both before and after the function call.
   1111     """
   1112     child_pre = resource.getrusage(resource.RUSAGE_CHILDREN)
   1113     self_pre = resource.getrusage(resource.RUSAGE_SELF)
   1114     start = time.time()
   1115     to_return = function(*args, **dargs)
   1116     elapsed = time.time() - start
   1117     self_post = resource.getrusage(resource.RUSAGE_SELF)
   1118     child_post = resource.getrusage(resource.RUSAGE_CHILDREN)
   1119 
   1120     # Calculate CPU Percentage
   1121     s_user, s_system = [a - b for a, b in zip(self_post, self_pre)[:2]]
   1122     c_user, c_system = [a - b for a, b in zip(child_post, child_pre)[:2]]
   1123     cpu_percent = (s_user + c_user + s_system + c_system) / elapsed
   1124 
   1125     return cpu_percent, to_return
   1126 
   1127 
   1128 def get_arch(run_function=run):
   1129     """
   1130     Get the hardware architecture of the machine.
   1131     If specified, run_function should return a CmdResult object and throw a
   1132     CmdError exception.
   1133     If run_function is anything other than utils.run(), it is used to
   1134     execute the commands. By default (when set to utils.run()) this will
   1135     just examine os.uname()[4].
   1136     """
   1137 
   1138     # Short circuit from the common case.
   1139     if run_function == run:
   1140         return re.sub(r'i\d86$', 'i386', os.uname()[4])
   1141 
   1142     # Otherwise, use the run_function in case it hits a remote machine.
   1143     arch = run_function('/bin/uname -m').stdout.rstrip()
   1144     if re.match(r'i\d86$', arch):
   1145         arch = 'i386'
   1146     return arch
   1147 
   1148 def get_arch_userspace(run_function=run):
   1149     """
   1150     Get the architecture by userspace (possibly different from kernel).
   1151     """
   1152     archs = {
   1153         'arm': 'ELF 32-bit.*, ARM,',
   1154         'arm64': 'ELF 64-bit.*, ARM aarch64,',
   1155         'i386': 'ELF 32-bit.*, Intel 80386,',
   1156         'x86_64': 'ELF 64-bit.*, x86-64,',
   1157     }
   1158 
   1159     cmd = 'file --brief --dereference /bin/sh'
   1160     filestr = run_function(cmd).stdout.rstrip()
   1161     for a, regex in archs.iteritems():
   1162         if re.match(regex, filestr):
   1163             return a
   1164 
   1165     return get_arch()
   1166 
   1167 
   1168 def get_num_logical_cpus_per_socket(run_function=run):
   1169     """
   1170     Get the number of cores (including hyperthreading) per cpu.
   1171     run_function is used to execute the commands. It defaults to
   1172     utils.run() but a custom method (if provided) should be of the
   1173     same schema as utils.run. It should return a CmdResult object and
   1174     throw a CmdError exception.
   1175     """
   1176     siblings = run_function('grep "^siblings" /proc/cpuinfo').stdout.rstrip()
   1177     num_siblings = map(int,
   1178                        re.findall(r'^siblings\s*:\s*(\d+)\s*$',
   1179                                   siblings, re.M))
   1180     if len(num_siblings) == 0:
   1181         raise error.TestError('Unable to find siblings info in /proc/cpuinfo')
   1182     if min(num_siblings) != max(num_siblings):
   1183         raise error.TestError('Number of siblings differ %r' %
   1184                               num_siblings)
   1185     return num_siblings[0]
   1186 
   1187 
   1188 def set_high_performance_mode(host=None):
   1189     """
   1190     Sets the kernel governor mode to the highest setting.
   1191     Returns previous governor state.
   1192     """
   1193     original_governors = get_scaling_governor_states(host)
   1194     set_scaling_governors('performance', host)
   1195     return original_governors
   1196 
   1197 
   1198 def set_scaling_governors(value, host=None):
   1199     """
   1200     Sets all scaling governor to string value.
   1201     Sample values: 'performance', 'interactive', 'ondemand', 'powersave'.
   1202     """
   1203     paths = _get_cpufreq_paths('scaling_governor', host)
   1204     if not paths:
   1205         logging.info("Could not set governor states, as no files of the form "
   1206                      "'/sys/devices/system/cpu/cpu*/cpufreq/scaling_governor' "
   1207                      "were found.")
   1208     run_func = host.run if host else system
   1209     for path in paths:
   1210         cmd = 'echo %s > %s' % (value, path)
   1211         logging.info('Writing scaling governor mode \'%s\' -> %s', value, path)
   1212         # On Tegra CPUs can be dynamically enabled/disabled. Ignore failures.
   1213         run_func(cmd, ignore_status=True)
   1214 
   1215 
   1216 def _get_cpufreq_paths(filename, host=None):
   1217     """
   1218     Returns a list of paths to the governors.
   1219     """
   1220     run_func = host.run if host else run
   1221     glob = '/sys/devices/system/cpu/cpu*/cpufreq/' + filename
   1222     # Simple glob expansion; note that CPUs may come and go, causing these
   1223     # paths to change at any time.
   1224     cmd = 'echo ' + glob
   1225     try:
   1226         paths = run_func(cmd, verbose=False).stdout.split()
   1227     except error.CmdError:
   1228         return []
   1229     # If the glob result equals itself, then we likely didn't match any real
   1230     # paths (assuming 'cpu*' is not a real path).
   1231     if paths == [glob]:
   1232         return []
   1233     return paths
   1234 
   1235 
   1236 def get_scaling_governor_states(host=None):
   1237     """
   1238     Returns a list of (performance governor path, current state) tuples.
   1239     """
   1240     paths = _get_cpufreq_paths('scaling_governor', host)
   1241     path_value_list = []
   1242     run_func = host.run if host else run
   1243     for path in paths:
   1244         value = run_func('head -n 1 %s' % path, verbose=False).stdout
   1245         path_value_list.append((path, value))
   1246     return path_value_list
   1247 
   1248 
   1249 def restore_scaling_governor_states(path_value_list, host=None):
   1250     """
   1251     Restores governor states. Inverse operation to get_scaling_governor_states.
   1252     """
   1253     run_func = host.run if host else system
   1254     for (path, value) in path_value_list:
   1255         cmd = 'echo %s > %s' % (value.rstrip('\n'), path)
   1256         # On Tegra CPUs can be dynamically enabled/disabled. Ignore failures.
   1257         run_func(cmd, ignore_status=True)
   1258 
   1259 
   1260 def merge_trees(src, dest):
   1261     """
   1262     Merges a source directory tree at 'src' into a destination tree at
   1263     'dest'. If a path is a file in both trees than the file in the source
   1264     tree is APPENDED to the one in the destination tree. If a path is
   1265     a directory in both trees then the directories are recursively merged
   1266     with this function. In any other case, the function will skip the
   1267     paths that cannot be merged (instead of failing).
   1268     """
   1269     if not os.path.exists(src):
   1270         return # exists only in dest
   1271     elif not os.path.exists(dest):
   1272         if os.path.isfile(src):
   1273             shutil.copy2(src, dest) # file only in src
   1274         else:
   1275             shutil.copytree(src, dest, symlinks=True) # dir only in src
   1276         return
   1277     elif os.path.isfile(src) and os.path.isfile(dest):
   1278         # src & dest are files in both trees, append src to dest
   1279         destfile = open(dest, "a")
   1280         try:
   1281             srcfile = open(src)
   1282             try:
   1283                 destfile.write(srcfile.read())
   1284             finally:
   1285                 srcfile.close()
   1286         finally:
   1287             destfile.close()
   1288     elif os.path.isdir(src) and os.path.isdir(dest):
   1289         # src & dest are directories in both trees, so recursively merge
   1290         for name in os.listdir(src):
   1291             merge_trees(os.path.join(src, name), os.path.join(dest, name))
   1292     else:
   1293         # src & dest both exist, but are incompatible
   1294         return
   1295 
   1296 
   1297 class CmdResult(object):
   1298     """
   1299     Command execution result.
   1300 
   1301     command:     String containing the command line itself
   1302     exit_status: Integer exit code of the process
   1303     stdout:      String containing stdout of the process
   1304     stderr:      String containing stderr of the process
   1305     duration:    Elapsed wall clock time running the process
   1306     """
   1307 
   1308 
   1309     def __init__(self, command="", stdout="", stderr="",
   1310                  exit_status=None, duration=0):
   1311         self.command = command
   1312         self.exit_status = exit_status
   1313         self.stdout = stdout
   1314         self.stderr = stderr
   1315         self.duration = duration
   1316 
   1317 
   1318     def __eq__(self, other):
   1319         if type(self) == type(other):
   1320             return (self.command == other.command
   1321                     and self.exit_status == other.exit_status
   1322                     and self.stdout == other.stdout
   1323                     and self.stderr == other.stderr
   1324                     and self.duration == other.duration)
   1325         else:
   1326             return NotImplemented
   1327 
   1328 
   1329     def __repr__(self):
   1330         wrapper = textwrap.TextWrapper(width = 78,
   1331                                        initial_indent="\n    ",
   1332                                        subsequent_indent="    ")
   1333 
   1334         stdout = self.stdout.rstrip()
   1335         if stdout:
   1336             stdout = "\nstdout:\n%s" % stdout
   1337 
   1338         stderr = self.stderr.rstrip()
   1339         if stderr:
   1340             stderr = "\nstderr:\n%s" % stderr
   1341 
   1342         return ("* Command: %s\n"
   1343                 "Exit status: %s\n"
   1344                 "Duration: %s\n"
   1345                 "%s"
   1346                 "%s"
   1347                 % (wrapper.fill(str(self.command)), self.exit_status,
   1348                 self.duration, stdout, stderr))
   1349 
   1350 
   1351 class run_randomly:
   1352     def __init__(self, run_sequentially=False):
   1353         # Run sequentially is for debugging control files
   1354         self.test_list = []
   1355         self.run_sequentially = run_sequentially
   1356 
   1357 
   1358     def add(self, *args, **dargs):
   1359         test = (args, dargs)
   1360         self.test_list.append(test)
   1361 
   1362 
   1363     def run(self, fn):
   1364         while self.test_list:
   1365             test_index = random.randint(0, len(self.test_list)-1)
   1366             if self.run_sequentially:
   1367                 test_index = 0
   1368             (args, dargs) = self.test_list.pop(test_index)
   1369             fn(*args, **dargs)
   1370 
   1371 
   1372 def import_site_module(path, module, dummy=None, modulefile=None):
   1373     """
   1374     Try to import the site specific module if it exists.
   1375 
   1376     @param path full filename of the source file calling this (ie __file__)
   1377     @param module full module name
   1378     @param dummy dummy value to return in case there is no symbol to import
   1379     @param modulefile module filename
   1380 
   1381     @return site specific module or dummy
   1382 
   1383     @raises ImportError if the site file exists but imports fails
   1384     """
   1385     short_module = module[module.rfind(".") + 1:]
   1386 
   1387     if not modulefile:
   1388         modulefile = short_module + ".py"
   1389 
   1390     if os.path.exists(os.path.join(os.path.dirname(path), modulefile)):
   1391         return __import__(module, {}, {}, [short_module])
   1392     return dummy
   1393 
   1394 
   1395 def import_site_symbol(path, module, name, dummy=None, modulefile=None):
   1396     """
   1397     Try to import site specific symbol from site specific file if it exists
   1398 
   1399     @param path full filename of the source file calling this (ie __file__)
   1400     @param module full module name
   1401     @param name symbol name to be imported from the site file
   1402     @param dummy dummy value to return in case there is no symbol to import
   1403     @param modulefile module filename
   1404 
   1405     @return site specific symbol or dummy
   1406 
   1407     @raises ImportError if the site file exists but imports fails
   1408     """
   1409     module = import_site_module(path, module, modulefile=modulefile)
   1410     if not module:
   1411         return dummy
   1412 
   1413     # special unique value to tell us if the symbol can't be imported
   1414     cant_import = object()
   1415 
   1416     obj = getattr(module, name, cant_import)
   1417     if obj is cant_import:
   1418         return dummy
   1419 
   1420     return obj
   1421 
   1422 
   1423 def import_site_class(path, module, classname, baseclass, modulefile=None):
   1424     """
   1425     Try to import site specific class from site specific file if it exists
   1426 
   1427     Args:
   1428         path: full filename of the source file calling this (ie __file__)
   1429         module: full module name
   1430         classname: class name to be loaded from site file
   1431         baseclass: base class object to return when no site file present or
   1432             to mixin when site class exists but is not inherited from baseclass
   1433         modulefile: module filename
   1434 
   1435     Returns: baseclass if site specific class does not exist, the site specific
   1436         class if it exists and is inherited from baseclass or a mixin of the
   1437         site specific class and baseclass when the site specific class exists
   1438         and is not inherited from baseclass
   1439 
   1440     Raises: ImportError if the site file exists but imports fails
   1441     """
   1442 
   1443     res = import_site_symbol(path, module, classname, None, modulefile)
   1444     if res:
   1445         if not issubclass(res, baseclass):
   1446             # if not a subclass of baseclass then mix in baseclass with the
   1447             # site specific class object and return the result
   1448             res = type(classname, (res, baseclass), {})
   1449     else:
   1450         res = baseclass
   1451 
   1452     return res
   1453 
   1454 
   1455 def import_site_function(path, module, funcname, dummy, modulefile=None):
   1456     """
   1457     Try to import site specific function from site specific file if it exists
   1458 
   1459     Args:
   1460         path: full filename of the source file calling this (ie __file__)
   1461         module: full module name
   1462         funcname: function name to be imported from site file
   1463         dummy: dummy function to return in case there is no function to import
   1464         modulefile: module filename
   1465 
   1466     Returns: site specific function object or dummy
   1467 
   1468     Raises: ImportError if the site file exists but imports fails
   1469     """
   1470 
   1471     return import_site_symbol(path, module, funcname, dummy, modulefile)
   1472 
   1473 
   1474 def _get_pid_path(program_name):
   1475     my_path = os.path.dirname(__file__)
   1476     return os.path.abspath(os.path.join(my_path, "..", "..",
   1477                                         "%s.pid" % program_name))
   1478 
   1479 
   1480 def write_pid(program_name):
   1481     """
   1482     Try to drop <program_name>.pid in the main autotest directory.
   1483 
   1484     Args:
   1485       program_name: prefix for file name
   1486     """
   1487     pidfile = open(_get_pid_path(program_name), "w")
   1488     try:
   1489         pidfile.write("%s\n" % os.getpid())
   1490     finally:
   1491         pidfile.close()
   1492 
   1493 
   1494 def delete_pid_file_if_exists(program_name):
   1495     """
   1496     Tries to remove <program_name>.pid from the main autotest directory.
   1497     """
   1498     pidfile_path = _get_pid_path(program_name)
   1499 
   1500     try:
   1501         os.remove(pidfile_path)
   1502     except OSError:
   1503         if not os.path.exists(pidfile_path):
   1504             return
   1505         raise
   1506 
   1507 
   1508 def get_pid_from_file(program_name):
   1509     """
   1510     Reads the pid from <program_name>.pid in the autotest directory.
   1511 
   1512     @param program_name the name of the program
   1513     @return the pid if the file exists, None otherwise.
   1514     """
   1515     pidfile_path = _get_pid_path(program_name)
   1516     if not os.path.exists(pidfile_path):
   1517         return None
   1518 
   1519     pidfile = open(_get_pid_path(program_name), 'r')
   1520 
   1521     try:
   1522         try:
   1523             pid = int(pidfile.readline())
   1524         except IOError:
   1525             if not os.path.exists(pidfile_path):
   1526                 return None
   1527             raise
   1528     finally:
   1529         pidfile.close()
   1530 
   1531     return pid
   1532 
   1533 
   1534 def get_process_name(pid):
   1535     """
   1536     Get process name from PID.
   1537     @param pid: PID of process.
   1538     @return: Process name if PID stat file exists or 'Dead PID' if it does not.
   1539     """
   1540     pid_stat_path = "/proc/%d/stat"
   1541     if not os.path.exists(pid_stat_path % pid):
   1542         return "Dead Pid"
   1543     return get_field(read_file(pid_stat_path % pid), 1)[1:-1]
   1544 
   1545 
   1546 def program_is_alive(program_name):
   1547     """
   1548     Checks if the process is alive and not in Zombie state.
   1549 
   1550     @param program_name the name of the program
   1551     @return True if still alive, False otherwise
   1552     """
   1553     pid = get_pid_from_file(program_name)
   1554     if pid is None:
   1555         return False
   1556     return pid_is_alive(pid)
   1557 
   1558 
   1559 def signal_program(program_name, sig=signal.SIGTERM):
   1560     """
   1561     Sends a signal to the process listed in <program_name>.pid
   1562 
   1563     @param program_name the name of the program
   1564     @param sig signal to send
   1565     """
   1566     pid = get_pid_from_file(program_name)
   1567     if pid:
   1568         signal_pid(pid, sig)
   1569 
   1570 
   1571 def get_relative_path(path, reference):
   1572     """Given 2 absolute paths "path" and "reference", compute the path of
   1573     "path" as relative to the directory "reference".
   1574 
   1575     @param path the absolute path to convert to a relative path
   1576     @param reference an absolute directory path to which the relative
   1577         path will be computed
   1578     """
   1579     # normalize the paths (remove double slashes, etc)
   1580     assert(os.path.isabs(path))
   1581     assert(os.path.isabs(reference))
   1582 
   1583     path = os.path.normpath(path)
   1584     reference = os.path.normpath(reference)
   1585 
   1586     # we could use os.path.split() but it splits from the end
   1587     path_list = path.split(os.path.sep)[1:]
   1588     ref_list = reference.split(os.path.sep)[1:]
   1589 
   1590     # find the longest leading common path
   1591     for i in xrange(min(len(path_list), len(ref_list))):
   1592         if path_list[i] != ref_list[i]:
   1593             # decrement i so when exiting this loop either by no match or by
   1594             # end of range we are one step behind
   1595             i -= 1
   1596             break
   1597     i += 1
   1598     # drop the common part of the paths, not interested in that anymore
   1599     del path_list[:i]
   1600 
   1601     # for each uncommon component in the reference prepend a ".."
   1602     path_list[:0] = ['..'] * (len(ref_list) - i)
   1603 
   1604     return os.path.join(*path_list)
   1605 
   1606 
   1607 def sh_escape(command):
   1608     """
   1609     Escape special characters from a command so that it can be passed
   1610     as a double quoted (" ") string in a (ba)sh command.
   1611 
   1612     Args:
   1613             command: the command string to escape.
   1614 
   1615     Returns:
   1616             The escaped command string. The required englobing double
   1617             quotes are NOT added and so should be added at some point by
   1618             the caller.
   1619 
   1620     See also: http://www.tldp.org/LDP/abs/html/escapingsection.html
   1621     """
   1622     command = command.replace("\\", "\\\\")
   1623     command = command.replace("$", r'\$')
   1624     command = command.replace('"', r'\"')
   1625     command = command.replace('`', r'\`')
   1626     return command
   1627 
   1628 
   1629 def sh_quote_word(text, whitelist=SHELL_QUOTING_WHITELIST):
   1630     r"""Quote a string to make it safe as a single word in a shell command.
   1631 
   1632     POSIX shell syntax recognizes no escape characters inside a single-quoted
   1633     string.  So, single quotes can safely quote any string of characters except
   1634     a string with a single quote character.  A single quote character must be
   1635     quoted with the sequence '\'' which translates to:
   1636         '  -> close current quote
   1637         \' -> insert a literal single quote
   1638         '  -> reopen quoting again.
   1639 
   1640     This is safe for all combinations of characters, including embedded and
   1641     trailing backslashes in odd or even numbers.
   1642 
   1643     This is also safe for nesting, e.g. the following is a valid use:
   1644 
   1645         adb_command = 'adb shell %s' % (
   1646                 sh_quote_word('echo %s' % sh_quote_word('hello world')))
   1647 
   1648     @param text: The string to be quoted into a single word for the shell.
   1649     @param whitelist: Optional list of characters that do not need quoting.
   1650                       Defaults to a known good list of characters.
   1651 
   1652     @return A string, possibly quoted, safe as a single word for a shell.
   1653     """
   1654     if all(c in whitelist for c in text):
   1655         return text
   1656     return "'" + text.replace("'", r"'\''") + "'"
   1657 
   1658 
   1659 def configure(extra=None, configure='./configure'):
   1660     """
   1661     Run configure passing in the correct host, build, and target options.
   1662 
   1663     @param extra: extra command line arguments to pass to configure
   1664     @param configure: which configure script to use
   1665     """
   1666     args = []
   1667     if 'CHOST' in os.environ:
   1668         args.append('--host=' + os.environ['CHOST'])
   1669     if 'CBUILD' in os.environ:
   1670         args.append('--build=' + os.environ['CBUILD'])
   1671     if 'CTARGET' in os.environ:
   1672         args.append('--target=' + os.environ['CTARGET'])
   1673     if extra:
   1674         args.append(extra)
   1675 
   1676     system('%s %s' % (configure, ' '.join(args)))
   1677 
   1678 
   1679 def make(extra='', make='make', timeout=None, ignore_status=False):
   1680     """
   1681     Run make, adding MAKEOPTS to the list of options.
   1682 
   1683     @param extra: extra command line arguments to pass to make.
   1684     """
   1685     cmd = '%s %s %s' % (make, os.environ.get('MAKEOPTS', ''), extra)
   1686     return system(cmd, timeout=timeout, ignore_status=ignore_status)
   1687 
   1688 
   1689 def compare_versions(ver1, ver2):
   1690     """Version number comparison between ver1 and ver2 strings.
   1691 
   1692     >>> compare_tuple("1", "2")
   1693     -1
   1694     >>> compare_tuple("foo-1.1", "foo-1.2")
   1695     -1
   1696     >>> compare_tuple("1.2", "1.2a")
   1697     -1
   1698     >>> compare_tuple("1.2b", "1.2a")
   1699     1
   1700     >>> compare_tuple("1.3.5.3a", "1.3.5.3b")
   1701     -1
   1702 
   1703     Args:
   1704         ver1: version string
   1705         ver2: version string
   1706 
   1707     Returns:
   1708         int:  1 if ver1 >  ver2
   1709               0 if ver1 == ver2
   1710              -1 if ver1 <  ver2
   1711     """
   1712     ax = re.split('[.-]', ver1)
   1713     ay = re.split('[.-]', ver2)
   1714     while len(ax) > 0 and len(ay) > 0:
   1715         cx = ax.pop(0)
   1716         cy = ay.pop(0)
   1717         maxlen = max(len(cx), len(cy))
   1718         c = cmp(cx.zfill(maxlen), cy.zfill(maxlen))
   1719         if c != 0:
   1720             return c
   1721     return cmp(len(ax), len(ay))
   1722 
   1723 
   1724 def args_to_dict(args):
   1725     """Convert autoserv extra arguments in the form of key=val or key:val to a
   1726     dictionary.  Each argument key is converted to lowercase dictionary key.
   1727 
   1728     Args:
   1729         args - list of autoserv extra arguments.
   1730 
   1731     Returns:
   1732         dictionary
   1733     """
   1734     arg_re = re.compile(r'(\w+)[:=](.*)$')
   1735     args_dict = {}
   1736     for arg in args:
   1737         match = arg_re.match(arg)
   1738         if match:
   1739             args_dict[match.group(1).lower()] = match.group(2)
   1740         else:
   1741             logging.warning("args_to_dict: argument '%s' doesn't match "
   1742                             "'%s' pattern. Ignored.", arg, arg_re.pattern)
   1743     return args_dict
   1744 
   1745 
   1746 def get_unused_port():
   1747     """
   1748     Finds a semi-random available port. A race condition is still
   1749     possible after the port number is returned, if another process
   1750     happens to bind it.
   1751 
   1752     Returns:
   1753         A port number that is unused on both TCP and UDP.
   1754     """
   1755 
   1756     def try_bind(port, socket_type, socket_proto):
   1757         s = socket.socket(socket.AF_INET, socket_type, socket_proto)
   1758         try:
   1759             try:
   1760                 s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
   1761                 s.bind(('', port))
   1762                 return s.getsockname()[1]
   1763             except socket.error:
   1764                 return None
   1765         finally:
   1766             s.close()
   1767 
   1768     # On the 2.6 kernel, calling try_bind() on UDP socket returns the
   1769     # same port over and over. So always try TCP first.
   1770     while True:
   1771         # Ask the OS for an unused port.
   1772         port = try_bind(0, socket.SOCK_STREAM, socket.IPPROTO_TCP)
   1773         # Check if this port is unused on the other protocol.
   1774         if port and try_bind(port, socket.SOCK_DGRAM, socket.IPPROTO_UDP):
   1775             return port
   1776 
   1777 
   1778 def ask(question, auto=False):
   1779     """
   1780     Raw input with a prompt that emulates logging.
   1781 
   1782     @param question: Question to be asked
   1783     @param auto: Whether to return "y" instead of asking the question
   1784     """
   1785     if auto:
   1786         logging.info("%s (y/n) y", question)
   1787         return "y"
   1788     return raw_input("%s INFO | %s (y/n) " %
   1789                      (time.strftime("%H:%M:%S", time.localtime()), question))
   1790 
   1791 
   1792 def rdmsr(address, cpu=0):
   1793     """
   1794     Reads an x86 MSR from the specified CPU, returns as long integer.
   1795     """
   1796     with open('/dev/cpu/%s/msr' % cpu, 'r', 0) as fd:
   1797         fd.seek(address)
   1798         return struct.unpack('=Q', fd.read(8))[0]
   1799 
   1800 
   1801 def wait_for_value(func,
   1802                    expected_value=None,
   1803                    min_threshold=None,
   1804                    max_threshold=None,
   1805                    timeout_sec=10):
   1806     """
   1807     Returns the value of func().  If |expected_value|, |min_threshold|, and
   1808     |max_threshold| are not set, returns immediately.
   1809 
   1810     If |expected_value| is set, polls the return value until |expected_value| is
   1811     reached, and returns that value.
   1812 
   1813     If either |max_threshold| or |min_threshold| is set, this function will
   1814     will repeatedly call func() until the return value reaches or exceeds one of
   1815     these thresholds.
   1816 
   1817     Polling will stop after |timeout_sec| regardless of these thresholds.
   1818 
   1819     @param func: function whose return value is to be waited on.
   1820     @param expected_value: wait for func to return this value.
   1821     @param min_threshold: wait for func value to reach or fall below this value.
   1822     @param max_threshold: wait for func value to reach or rise above this value.
   1823     @param timeout_sec: Number of seconds to wait before giving up and
   1824                         returning whatever value func() last returned.
   1825 
   1826     Return value:
   1827         The most recent return value of func().
   1828     """
   1829     value = None
   1830     start_time_sec = time.time()
   1831     while True:
   1832         value = func()
   1833         if (expected_value is None and \
   1834             min_threshold is None and \
   1835             max_threshold is None) or \
   1836            (expected_value is not None and value == expected_value) or \
   1837            (min_threshold is not None and value <= min_threshold) or \
   1838            (max_threshold is not None and value >= max_threshold):
   1839             break
   1840 
   1841         if time.time() - start_time_sec >= timeout_sec:
   1842             break
   1843         time.sleep(0.1)
   1844 
   1845     return value
   1846 
   1847 
   1848 def wait_for_value_changed(func,
   1849                            old_value=None,
   1850                            timeout_sec=10):
   1851     """
   1852     Returns the value of func().
   1853 
   1854     The function polls the return value until it is different from |old_value|,
   1855     and returns that value.
   1856 
   1857     Polling will stop after |timeout_sec|.
   1858 
   1859     @param func: function whose return value is to be waited on.
   1860     @param old_value: wait for func to return a value different from this.
   1861     @param timeout_sec: Number of seconds to wait before giving up and
   1862                         returning whatever value func() last returned.
   1863 
   1864     @returns The most recent return value of func().
   1865     """
   1866     value = None
   1867     start_time_sec = time.time()
   1868     while True:
   1869         value = func()
   1870         if value != old_value:
   1871             break
   1872 
   1873         if time.time() - start_time_sec >= timeout_sec:
   1874             break
   1875         time.sleep(0.1)
   1876 
   1877     return value
   1878 
   1879 
   1880 CONFIG = global_config.global_config
   1881 
   1882 # Keep checking if the pid is alive every second until the timeout (in seconds)
   1883 CHECK_PID_IS_ALIVE_TIMEOUT = 6
   1884 
   1885 _LOCAL_HOST_LIST = ('localhost', '127.0.0.1')
   1886 
   1887 # The default address of a vm gateway.
   1888 DEFAULT_VM_GATEWAY = '10.0.2.2'
   1889 
   1890 # Google Storage bucket URI to store results in.
   1891 DEFAULT_OFFLOAD_GSURI = CONFIG.get_config_value(
   1892         'CROS', 'results_storage_server', default=None)
   1893 
   1894 # Default Moblab Ethernet Interface.
   1895 _MOBLAB_ETH_0 = 'eth0'
   1896 _MOBLAB_ETH_1 = 'eth1'
   1897 
   1898 # A list of subnets that requires dedicated devserver and drone in the same
   1899 # subnet. Each item is a tuple of (subnet_ip, mask_bits), e.g.,
   1900 # ('192.168.0.0', 24))
   1901 RESTRICTED_SUBNETS = []
   1902 
   1903 def _setup_restricted_subnets():
   1904     restricted_subnets_list = CONFIG.get_config_value(
   1905             'CROS', 'restricted_subnets', type=list, default=[])
   1906     # TODO(dshi): Remove the code to split subnet with `:` after R51 is
   1907     # off stable channel, and update shadow config to use `/` as
   1908     # delimiter for consistency.
   1909     for subnet in restricted_subnets_list:
   1910         ip, mask_bits = subnet.split('/') if '/' in subnet \
   1911                         else subnet.split(':')
   1912         RESTRICTED_SUBNETS.append((ip, int(mask_bits)))
   1913 
   1914 _setup_restricted_subnets()
   1915 
   1916 # regex pattern for CLIENT/wireless_ssid_ config. For example, global config
   1917 # can have following config in CLIENT section to indicate that hosts in subnet
   1918 # 192.168.0.1/24 should use wireless ssid of `ssid_1`
   1919 # wireless_ssid_192.168.0.1/24: ssid_1
   1920 WIRELESS_SSID_PATTERN = 'wireless_ssid_(.*)/(\d+)'
   1921 
   1922 
   1923 def get_moblab_serial_number():
   1924     """Gets a unique identifier for the moblab.
   1925 
   1926     Serial number is the prefered identifier, use it if
   1927     present, however fallback is the ethernet mac address.
   1928     """
   1929     for vpd_key in ['serial_number', 'ethernet_mac']:
   1930       try:
   1931           cmd_result = run('sudo vpd -g %s' % vpd_key)
   1932           if cmd_result and cmd_result.stdout:
   1933             return cmd_result.stdout
   1934       except error.CmdError as e:
   1935           logging.error(str(e))
   1936           logging.info(vpd_key)
   1937     return 'NoSerialNumber'
   1938 
   1939 
   1940 def ping(host, deadline=None, tries=None, timeout=60, user=None):
   1941     """Attempt to ping |host|.
   1942 
   1943     Shell out to 'ping' if host is an IPv4 addres or 'ping6' if host is an
   1944     IPv6 address to try to reach |host| for |timeout| seconds.
   1945     Returns exit code of ping.
   1946 
   1947     Per 'man ping', if you specify BOTH |deadline| and |tries|, ping only
   1948     returns 0 if we get responses to |tries| pings within |deadline| seconds.
   1949 
   1950     Specifying |deadline| or |count| alone should return 0 as long as
   1951     some packets receive responses.
   1952 
   1953     Note that while this works with literal IPv6 addresses it will not work
   1954     with hostnames that resolve to IPv6 only.
   1955 
   1956     @param host: the host to ping.
   1957     @param deadline: seconds within which |tries| pings must succeed.
   1958     @param tries: number of pings to send.
   1959     @param timeout: number of seconds after which to kill 'ping' command.
   1960     @return exit code of ping command.
   1961     """
   1962     args = [host]
   1963     cmd = 'ping6' if re.search(r':.*:', host) else 'ping'
   1964 
   1965     if deadline:
   1966         args.append('-w%d' % deadline)
   1967     if tries:
   1968         args.append('-c%d' % tries)
   1969 
   1970     if user != None:
   1971         args = [user, '-c', ' '.join([cmd] + args)]
   1972         cmd = 'su'
   1973 
   1974     return run(cmd, args=args, verbose=True,
   1975                           ignore_status=True, timeout=timeout,
   1976                           stdout_tee=TEE_TO_LOGS,
   1977                           stderr_tee=TEE_TO_LOGS).exit_status
   1978 
   1979 
   1980 def host_is_in_lab_zone(hostname):
   1981     """Check if the host is in the CLIENT.dns_zone.
   1982 
   1983     @param hostname: The hostname to check.
   1984     @returns True if hostname.dns_zone resolves, otherwise False.
   1985     """
   1986     host_parts = hostname.split('.')
   1987     dns_zone = CONFIG.get_config_value('CLIENT', 'dns_zone', default=None)
   1988     fqdn = '%s.%s' % (host_parts[0], dns_zone)
   1989     logging.debug('Checking if host %s is in lab zone.', fqdn)
   1990     try:
   1991         socket.gethostbyname(fqdn)
   1992         return True
   1993     except socket.gaierror:
   1994         return False
   1995 
   1996 
   1997 def in_moblab_ssp():
   1998     """Detects if this execution is inside an SSP container on moblab."""
   1999     config_is_moblab = CONFIG.get_config_value('SSP', 'is_moblab', type=bool,
   2000                                                default=False)
   2001     return is_in_container() and config_is_moblab
   2002 
   2003 
   2004 def get_chrome_version(job_views):
   2005     """
   2006     Retrieves the version of the chrome binary associated with a job.
   2007 
   2008     When a test runs we query the chrome binary for it's version and drop
   2009     that value into a client keyval. To retrieve the chrome version we get all
   2010     the views associated with a test from the db, including those of the
   2011     server and client jobs, and parse the version out of the first test view
   2012     that has it. If we never ran a single test in the suite the job_views
   2013     dictionary will not contain a chrome version.
   2014 
   2015     This method cannot retrieve the chrome version from a dictionary that
   2016     does not conform to the structure of an autotest tko view.
   2017 
   2018     @param job_views: a list of a job's result views, as returned by
   2019                       the get_detailed_test_views method in rpc_interface.
   2020     @return: The chrome version string, or None if one can't be found.
   2021     """
   2022 
   2023     # Aborted jobs have no views.
   2024     if not job_views:
   2025         return None
   2026 
   2027     for view in job_views:
   2028         if (view.get('attributes')
   2029             and constants.CHROME_VERSION in view['attributes'].keys()):
   2030 
   2031             return view['attributes'].get(constants.CHROME_VERSION)
   2032 
   2033     logging.warning('Could not find chrome version for failure.')
   2034     return None
   2035 
   2036 
   2037 def get_moblab_id():
   2038     """Gets the moblab random id.
   2039 
   2040     The random id file is cached on disk. If it does not exist, a new file is
   2041     created the first time.
   2042 
   2043     @returns the moblab random id.
   2044     """
   2045     moblab_id_filepath = '/home/moblab/.moblab_id'
   2046     try:
   2047         if os.path.exists(moblab_id_filepath):
   2048             with open(moblab_id_filepath, 'r') as moblab_id_file:
   2049                 random_id = moblab_id_file.read()
   2050         else:
   2051             random_id = uuid.uuid1().hex
   2052             with open(moblab_id_filepath, 'w') as moblab_id_file:
   2053                 moblab_id_file.write('%s' % random_id)
   2054     except IOError as e:
   2055         # Possible race condition, another process has created the file.
   2056         # Sleep a second to make sure the file gets closed.
   2057         logging.info(e)
   2058         time.sleep(1)
   2059         with open(moblab_id_filepath, 'r') as moblab_id_file:
   2060             random_id = moblab_id_file.read()
   2061     return random_id
   2062 
   2063 
   2064 def get_offload_gsuri():
   2065     """Return the GSURI to offload test results to.
   2066 
   2067     For the normal use case this is the results_storage_server in the
   2068     global_config.
   2069 
   2070     However partners using Moblab will be offloading their results to a
   2071     subdirectory of their image storage buckets. The subdirectory is
   2072     determined by the MAC Address of the Moblab device.
   2073 
   2074     @returns gsuri to offload test results to.
   2075     """
   2076     # For non-moblab, use results_storage_server or default.
   2077     if not is_moblab():  # pylint: disable=undefined-variable
   2078         return DEFAULT_OFFLOAD_GSURI
   2079 
   2080     # For moblab, use results_storage_server or image_storage_server as bucket
   2081     # name and mac-address/moblab_id as path.
   2082     gsuri = DEFAULT_OFFLOAD_GSURI
   2083     if not gsuri:
   2084         gsuri = "%sresults/" % CONFIG.get_config_value('CROS',
   2085                                                        'image_storage_server')
   2086 
   2087     return '%s%s/%s/' % (gsuri, get_moblab_serial_number(), get_moblab_id())
   2088 
   2089 
   2090 # TODO(petermayo): crosbug.com/31826 Share this with _GsUpload in
   2091 # //chromite.git/buildbot/prebuilt.py somewhere/somehow
   2092 def gs_upload(local_file, remote_file, acl, result_dir=None,
   2093               transfer_timeout=300, acl_timeout=300):
   2094     """Upload to GS bucket.
   2095 
   2096     @param local_file: Local file to upload
   2097     @param remote_file: Remote location to upload the local_file to.
   2098     @param acl: name or file used for controlling access to the uploaded
   2099                 file.
   2100     @param result_dir: Result directory if you want to add tracing to the
   2101                        upload.
   2102     @param transfer_timeout: Timeout for this upload call.
   2103     @param acl_timeout: Timeout for the acl call needed to confirm that
   2104                         the uploader has permissions to execute the upload.
   2105 
   2106     @raise CmdError: the exit code of the gsutil call was not 0.
   2107 
   2108     @returns True/False - depending on if the upload succeeded or failed.
   2109     """
   2110     # https://developers.google.com/storage/docs/accesscontrol#extension
   2111     CANNED_ACLS = ['project-private', 'private', 'public-read',
   2112                    'public-read-write', 'authenticated-read',
   2113                    'bucket-owner-read', 'bucket-owner-full-control']
   2114     _GSUTIL_BIN = 'gsutil'
   2115     acl_cmd = None
   2116     if acl in CANNED_ACLS:
   2117         cmd = '%s cp -a %s %s %s' % (_GSUTIL_BIN, acl, local_file, remote_file)
   2118     else:
   2119         # For private uploads we assume that the overlay board is set up
   2120         # properly and a googlestore_acl.xml is present, if not this script
   2121         # errors
   2122         cmd = '%s cp -a private %s %s' % (_GSUTIL_BIN, local_file, remote_file)
   2123         if not os.path.exists(acl):
   2124             logging.error('Unable to find ACL File %s.', acl)
   2125             return False
   2126         acl_cmd = '%s setacl %s %s' % (_GSUTIL_BIN, acl, remote_file)
   2127     if not result_dir:
   2128         run(cmd, timeout=transfer_timeout, verbose=True)
   2129         if acl_cmd:
   2130             run(acl_cmd, timeout=acl_timeout, verbose=True)
   2131         return True
   2132     with open(os.path.join(result_dir, 'tracing'), 'w') as ftrace:
   2133         ftrace.write('Preamble\n')
   2134         run(cmd, timeout=transfer_timeout, verbose=True,
   2135                        stdout_tee=ftrace, stderr_tee=ftrace)
   2136         if acl_cmd:
   2137             ftrace.write('\nACL setting\n')
   2138             # Apply the passed in ACL xml file to the uploaded object.
   2139             run(acl_cmd, timeout=acl_timeout, verbose=True,
   2140                            stdout_tee=ftrace, stderr_tee=ftrace)
   2141         ftrace.write('Postamble\n')
   2142         return True
   2143 
   2144 
   2145 def gs_ls(uri_pattern):
   2146     """Returns a list of URIs that match a given pattern.
   2147 
   2148     @param uri_pattern: a GS URI pattern, may contain wildcards
   2149 
   2150     @return A list of URIs matching the given pattern.
   2151 
   2152     @raise CmdError: the gsutil command failed.
   2153 
   2154     """
   2155     gs_cmd = ' '.join(['gsutil', 'ls', uri_pattern])
   2156     result = system_output(gs_cmd).splitlines()
   2157     return [path.rstrip() for path in result if path]
   2158 
   2159 
   2160 def nuke_pids(pid_list, signal_queue=None):
   2161     """
   2162     Given a list of pid's, kill them via an esclating series of signals.
   2163 
   2164     @param pid_list: List of PID's to kill.
   2165     @param signal_queue: Queue of signals to send the PID's to terminate them.
   2166 
   2167     @return: A mapping of the signal name to the number of processes it
   2168         was sent to.
   2169     """
   2170     if signal_queue is None:
   2171         signal_queue = [signal.SIGTERM, signal.SIGKILL]
   2172     sig_count = {}
   2173     # Though this is slightly hacky it beats hardcoding names anyday.
   2174     sig_names = dict((k, v) for v, k in signal.__dict__.iteritems()
   2175                      if v.startswith('SIG'))
   2176     for sig in signal_queue:
   2177         logging.debug('Sending signal %s to the following pids:', sig)
   2178         sig_count[sig_names.get(sig, 'unknown_signal')] = len(pid_list)
   2179         for pid in pid_list:
   2180             logging.debug('Pid %d', pid)
   2181             try:
   2182                 os.kill(pid, sig)
   2183             except OSError:
   2184                 # The process may have died from a previous signal before we
   2185                 # could kill it.
   2186                 pass
   2187         if sig == signal.SIGKILL:
   2188             return sig_count
   2189         pid_list = [pid for pid in pid_list if pid_is_alive(pid)]
   2190         if not pid_list:
   2191             break
   2192         time.sleep(CHECK_PID_IS_ALIVE_TIMEOUT)
   2193     failed_list = []
   2194     for pid in pid_list:
   2195         if pid_is_alive(pid):
   2196             failed_list.append('Could not kill %d for process name: %s.' % pid,
   2197                                get_process_name(pid))
   2198     if failed_list:
   2199         raise error.AutoservRunError('Following errors occured: %s' %
   2200                                      failed_list, None)
   2201     return sig_count
   2202 
   2203 
   2204 def externalize_host(host):
   2205     """Returns an externally accessible host name.
   2206 
   2207     @param host: a host name or address (string)
   2208 
   2209     @return An externally visible host name or address
   2210 
   2211     """
   2212     return socket.gethostname() if host in _LOCAL_HOST_LIST else host
   2213 
   2214 
   2215 def urlopen_socket_timeout(url, data=None, timeout=5):
   2216     """
   2217     Wrapper to urllib2.urlopen with a socket timeout.
   2218 
   2219     This method will convert all socket timeouts to
   2220     TimeoutExceptions, so we can use it in conjunction
   2221     with the rpc retry decorator and continue to handle
   2222     other URLErrors as we see fit.
   2223 
   2224     @param url: The url to open.
   2225     @param data: The data to send to the url (eg: the urlencoded dictionary
   2226                  used with a POST call).
   2227     @param timeout: The timeout for this urlopen call.
   2228 
   2229     @return: The response of the urlopen call.
   2230 
   2231     @raises: error.TimeoutException when a socket timeout occurs.
   2232              urllib2.URLError for errors that not caused by timeout.
   2233              urllib2.HTTPError for errors like 404 url not found.
   2234     """
   2235     old_timeout = socket.getdefaulttimeout()
   2236     socket.setdefaulttimeout(timeout)
   2237     try:
   2238         return urllib2.urlopen(url, data=data)
   2239     except urllib2.URLError as e:
   2240         if type(e.reason) is socket.timeout:
   2241             raise error.TimeoutException(str(e))
   2242         raise
   2243     finally:
   2244         socket.setdefaulttimeout(old_timeout)
   2245 
   2246 
   2247 def parse_chrome_version(version_string):
   2248     """
   2249     Parse a chrome version string and return version and milestone.
   2250 
   2251     Given a chrome version of the form "W.X.Y.Z", return "W.X.Y.Z" as
   2252     the version and "W" as the milestone.
   2253 
   2254     @param version_string: Chrome version string.
   2255     @return: a tuple (chrome_version, milestone). If the incoming version
   2256              string is not of the form "W.X.Y.Z", chrome_version will
   2257              be set to the incoming "version_string" argument and the
   2258              milestone will be set to the empty string.
   2259     """
   2260     match = re.search('(\d+)\.\d+\.\d+\.\d+', version_string)
   2261     ver = match.group(0) if match else version_string
   2262     milestone = match.group(1) if match else ''
   2263     return ver, milestone
   2264 
   2265 
   2266 def is_localhost(server):
   2267     """Check if server is equivalent to localhost.
   2268 
   2269     @param server: Name of the server to check.
   2270 
   2271     @return: True if given server is equivalent to localhost.
   2272 
   2273     @raise socket.gaierror: If server name failed to be resolved.
   2274     """
   2275     if server in _LOCAL_HOST_LIST:
   2276         return True
   2277     try:
   2278         return (socket.gethostbyname(socket.gethostname()) ==
   2279                 socket.gethostbyname(server))
   2280     except socket.gaierror:
   2281         logging.error('Failed to resolve server name %s.', server)
   2282         return False
   2283 
   2284 
   2285 def get_function_arg_value(func, arg_name, args, kwargs):
   2286     """Get the value of the given argument for the function.
   2287 
   2288     @param func: Function being called with given arguments.
   2289     @param arg_name: Name of the argument to look for value.
   2290     @param args: arguments for function to be called.
   2291     @param kwargs: keyword arguments for function to be called.
   2292 
   2293     @return: The value of the given argument for the function.
   2294 
   2295     @raise ValueError: If the argument is not listed function arguemnts.
   2296     @raise KeyError: If no value is found for the given argument.
   2297     """
   2298     if arg_name in kwargs:
   2299         return kwargs[arg_name]
   2300 
   2301     argspec = inspect.getargspec(func)
   2302     index = argspec.args.index(arg_name)
   2303     try:
   2304         return args[index]
   2305     except IndexError:
   2306         try:
   2307             # The argument can use a default value. Reverse the default value
   2308             # so argument with default value can be counted from the last to
   2309             # the first.
   2310             return argspec.defaults[::-1][len(argspec.args) - index - 1]
   2311         except IndexError:
   2312             raise KeyError('Argument %s is not given a value. argspec: %s, '
   2313                            'args:%s, kwargs:%s' %
   2314                            (arg_name, argspec, args, kwargs))
   2315 
   2316 
   2317 def has_systemd():
   2318     """Check if the host is running systemd.
   2319 
   2320     @return: True if the host uses systemd, otherwise returns False.
   2321     """
   2322     return os.path.basename(os.readlink('/proc/1/exe')) == 'systemd'
   2323 
   2324 
   2325 def get_real_user():
   2326     """Get the real user that runs the script.
   2327 
   2328     The function check environment variable SUDO_USER for the user if the
   2329     script is run with sudo. Otherwise, it returns the value of environment
   2330     variable USER.
   2331 
   2332     @return: The user name that runs the script.
   2333 
   2334     """
   2335     user = os.environ.get('SUDO_USER')
   2336     if not user:
   2337         user = os.environ.get('USER')
   2338     return user
   2339 
   2340 
   2341 def get_service_pid(service_name):
   2342     """Return pid of service.
   2343 
   2344     @param service_name: string name of service.
   2345 
   2346     @return: pid or 0 if service is not running.
   2347     """
   2348     if has_systemd():
   2349         # systemctl show prints 'MainPID=0' if the service is not running.
   2350         cmd_result = run('systemctl show -p MainPID %s' %
   2351                                     service_name, ignore_status=True)
   2352         return int(cmd_result.stdout.split('=')[1])
   2353     else:
   2354         cmd_result = run('status %s' % service_name,
   2355                                         ignore_status=True)
   2356         if 'start/running' in cmd_result.stdout:
   2357             return int(cmd_result.stdout.split()[3])
   2358         return 0
   2359 
   2360 
   2361 def control_service(service_name, action='start', ignore_status=True):
   2362     """Controls a service. It can be used to start, stop or restart
   2363     a service.
   2364 
   2365     @param service_name: string service to be restarted.
   2366 
   2367     @param action: string choice of action to control command.
   2368 
   2369     @param ignore_status: boolean ignore if system command fails.
   2370 
   2371     @return: status code of the executed command.
   2372     """
   2373     if action not in ('start', 'stop', 'restart'):
   2374         raise ValueError('Unknown action supplied as parameter.')
   2375 
   2376     control_cmd = action + ' ' + service_name
   2377     if has_systemd():
   2378         control_cmd = 'systemctl ' + control_cmd
   2379     return system(control_cmd, ignore_status=ignore_status)
   2380 
   2381 
   2382 def restart_service(service_name, ignore_status=True):
   2383     """Restarts a service
   2384 
   2385     @param service_name: string service to be restarted.
   2386 
   2387     @param ignore_status: boolean ignore if system command fails.
   2388 
   2389     @return: status code of the executed command.
   2390     """
   2391     return control_service(service_name, action='restart',
   2392                            ignore_status=ignore_status)
   2393 
   2394 
   2395 def start_service(service_name, ignore_status=True):
   2396     """Starts a service
   2397 
   2398     @param service_name: string service to be started.
   2399 
   2400     @param ignore_status: boolean ignore if system command fails.
   2401 
   2402     @return: status code of the executed command.
   2403     """
   2404     return control_service(service_name, action='start',
   2405                            ignore_status=ignore_status)
   2406 
   2407 
   2408 def stop_service(service_name, ignore_status=True):
   2409     """Stops a service
   2410 
   2411     @param service_name: string service to be stopped.
   2412 
   2413     @param ignore_status: boolean ignore if system command fails.
   2414 
   2415     @return: status code of the executed command.
   2416     """
   2417     return control_service(service_name, action='stop',
   2418                            ignore_status=ignore_status)
   2419 
   2420 
   2421 def sudo_require_password():
   2422     """Test if the process can run sudo command without using password.
   2423 
   2424     @return: True if the process needs password to run sudo command.
   2425 
   2426     """
   2427     try:
   2428         run('sudo -n true')
   2429         return False
   2430     except error.CmdError:
   2431         logging.warn('sudo command requires password.')
   2432         return True
   2433 
   2434 
   2435 def is_in_container():
   2436     """Check if the process is running inside a container.
   2437 
   2438     @return: True if the process is running inside a container, otherwise False.
   2439     """
   2440     result = run('grep -q "/lxc/" /proc/1/cgroup',
   2441                             verbose=False, ignore_status=True)
   2442     if result.exit_status == 0:
   2443         return True
   2444 
   2445     # Check "container" environment variable for lxd/lxc containers.
   2446     if os.environ.get('container') == 'lxc':
   2447         return True
   2448 
   2449     return False
   2450 
   2451 
   2452 def is_flash_installed():
   2453     """
   2454     The Adobe Flash binary is only distributed with internal builds.
   2455     """
   2456     return (os.path.exists('/opt/google/chrome/pepper/libpepflashplayer.so')
   2457         and os.path.exists('/opt/google/chrome/pepper/pepper-flash.info'))
   2458 
   2459 
   2460 def verify_flash_installed():
   2461     """
   2462     The Adobe Flash binary is only distributed with internal builds.
   2463     Warn users of public builds of the extra dependency.
   2464     """
   2465     if not is_flash_installed():
   2466         raise error.TestNAError('No Adobe Flash binary installed.')
   2467 
   2468 
   2469 def is_in_same_subnet(ip_1, ip_2, mask_bits=24):
   2470     """Check if two IP addresses are in the same subnet with given mask bits.
   2471 
   2472     The two IP addresses are string of IPv4, e.g., '192.168.0.3'.
   2473 
   2474     @param ip_1: First IP address to compare.
   2475     @param ip_2: Second IP address to compare.
   2476     @param mask_bits: Number of mask bits for subnet comparison. Default to 24.
   2477 
   2478     @return: True if the two IP addresses are in the same subnet.
   2479 
   2480     """
   2481     mask = ((2L<<mask_bits-1) -1)<<(32-mask_bits)
   2482     ip_1_num = struct.unpack('!I', socket.inet_aton(ip_1))[0]
   2483     ip_2_num = struct.unpack('!I', socket.inet_aton(ip_2))[0]
   2484     return ip_1_num & mask == ip_2_num & mask
   2485 
   2486 
   2487 def get_ip_address(hostname):
   2488     """Get the IP address of given hostname.
   2489 
   2490     @param hostname: Hostname of a DUT.
   2491 
   2492     @return: The IP address of given hostname. None if failed to resolve
   2493              hostname.
   2494     """
   2495     try:
   2496         if hostname:
   2497             return socket.gethostbyname(hostname)
   2498     except socket.gaierror as e:
   2499         logging.error('Failed to get IP address of %s, error: %s.', hostname, e)
   2500 
   2501 
   2502 def get_servers_in_same_subnet(host_ip, mask_bits, servers=None,
   2503                                server_ip_map=None):
   2504     """Get the servers in the same subnet of the given host ip.
   2505 
   2506     @param host_ip: The IP address of a dut to look for devserver.
   2507     @param mask_bits: Number of mask bits.
   2508     @param servers: A list of servers to be filtered by subnet specified by
   2509                     host_ip and mask_bits.
   2510     @param server_ip_map: A map between the server name and its IP address.
   2511             The map can be pre-built for better performance, e.g., when
   2512             allocating a drone for an agent task.
   2513 
   2514     @return: A list of servers in the same subnet of the given host ip.
   2515 
   2516     """
   2517     matched_servers = []
   2518     if not servers and not server_ip_map:
   2519         raise ValueError('Either `servers` or `server_ip_map` must be given.')
   2520     if not servers:
   2521         servers = server_ip_map.keys()
   2522     # Make sure server_ip_map is an empty dict if it's not set.
   2523     if not server_ip_map:
   2524         server_ip_map = {}
   2525     for server in servers:
   2526         server_ip = server_ip_map.get(server, get_ip_address(server))
   2527         if server_ip and is_in_same_subnet(server_ip, host_ip, mask_bits):
   2528             matched_servers.append(server)
   2529     return matched_servers
   2530 
   2531 
   2532 def get_restricted_subnet(hostname, restricted_subnets=None):
   2533     """Get the restricted subnet of given hostname.
   2534 
   2535     @param hostname: Name of the host to look for matched restricted subnet.
   2536     @param restricted_subnets: A list of restricted subnets, default is set to
   2537             RESTRICTED_SUBNETS.
   2538 
   2539     @return: A tuple of (subnet_ip, mask_bits), which defines a restricted
   2540              subnet.
   2541     """
   2542     if restricted_subnets is None:
   2543         restricted_subnets=RESTRICTED_SUBNETS
   2544     host_ip = get_ip_address(hostname)
   2545     if not host_ip:
   2546         return
   2547     for subnet_ip, mask_bits in restricted_subnets:
   2548         if is_in_same_subnet(subnet_ip, host_ip, mask_bits):
   2549             return subnet_ip, mask_bits
   2550 
   2551 
   2552 def get_wireless_ssid(hostname):
   2553     """Get the wireless ssid based on given hostname.
   2554 
   2555     The method tries to locate the wireless ssid in the same subnet of given
   2556     hostname first. If none is found, it returns the default setting in
   2557     CLIENT/wireless_ssid.
   2558 
   2559     @param hostname: Hostname of the test device.
   2560 
   2561     @return: wireless ssid for the test device.
   2562     """
   2563     default_ssid = CONFIG.get_config_value('CLIENT', 'wireless_ssid',
   2564                                            default=None)
   2565     host_ip = get_ip_address(hostname)
   2566     if not host_ip:
   2567         return default_ssid
   2568 
   2569     # Get all wireless ssid in the global config.
   2570     ssids = CONFIG.get_config_value_regex('CLIENT', WIRELESS_SSID_PATTERN)
   2571 
   2572     # There could be multiple subnet matches, pick the one with most strict
   2573     # match, i.e., the one with highest maskbit.
   2574     matched_ssid = default_ssid
   2575     matched_maskbit = -1
   2576     for key, value in ssids.items():
   2577         # The config key filtered by regex WIRELESS_SSID_PATTERN has a format of
   2578         # wireless_ssid_[subnet_ip]/[maskbit], for example:
   2579         # wireless_ssid_192.168.0.1/24
   2580         # Following line extract the subnet ip and mask bit from the key name.
   2581         match = re.match(WIRELESS_SSID_PATTERN, key)
   2582         subnet_ip, maskbit = match.groups()
   2583         maskbit = int(maskbit)
   2584         if (is_in_same_subnet(subnet_ip, host_ip, maskbit) and
   2585             maskbit > matched_maskbit):
   2586             matched_ssid = value
   2587             matched_maskbit = maskbit
   2588     return matched_ssid
   2589 
   2590 
   2591 def parse_launch_control_build(build_name):
   2592     """Get branch, target, build_id from the given Launch Control build_name.
   2593 
   2594     @param build_name: Name of a Launch Control build, should be formated as
   2595                        branch/target/build_id
   2596 
   2597     @return: Tuple of branch, target, build_id
   2598     @raise ValueError: If the build_name is not correctly formated.
   2599     """
   2600     branch, target, build_id = build_name.split('/')
   2601     return branch, target, build_id
   2602 
   2603 
   2604 def parse_android_target(target):
   2605     """Get board and build type from the given target.
   2606 
   2607     @param target: Name of an Android build target, e.g., shamu-eng.
   2608 
   2609     @return: Tuple of board, build_type
   2610     @raise ValueError: If the target is not correctly formated.
   2611     """
   2612     board, build_type = target.split('-')
   2613     return board, build_type
   2614 
   2615 
   2616 def parse_launch_control_target(target):
   2617     """Parse the build target and type from a Launch Control target.
   2618 
   2619     The Launch Control target has the format of build_target-build_type, e.g.,
   2620     shamu-eng or dragonboard-userdebug. This method extracts the build target
   2621     and type from the target name.
   2622 
   2623     @param target: Name of a Launch Control target, e.g., shamu-eng.
   2624 
   2625     @return: (build_target, build_type), e.g., ('shamu', 'userdebug')
   2626     """
   2627     match = re.match('(?P<build_target>.+)-(?P<build_type>[^-]+)', target)
   2628     if match:
   2629         return match.group('build_target'), match.group('build_type')
   2630     else:
   2631         return None, None
   2632 
   2633 
   2634 def is_launch_control_build(build):
   2635     """Check if a given build is a Launch Control build.
   2636 
   2637     @param build: Name of a build, e.g.,
   2638                   ChromeOS build: daisy-release/R50-1234.0.0
   2639                   Launch Control build: git_mnc_release/shamu-eng
   2640 
   2641     @return: True if the build name matches the pattern of a Launch Control
   2642              build, False otherwise.
   2643     """
   2644     try:
   2645         _, target, _ = parse_launch_control_build(build)
   2646         build_target, _ = parse_launch_control_target(target)
   2647         if build_target:
   2648             return True
   2649     except ValueError:
   2650         # parse_launch_control_build or parse_launch_control_target failed.
   2651         pass
   2652     return False
   2653 
   2654 
   2655 def which(exec_file):
   2656     """Finds an executable file.
   2657 
   2658     If the file name contains a path component, it is checked as-is.
   2659     Otherwise, we check with each of the path components found in the system
   2660     PATH prepended. This behavior is similar to the 'which' command-line tool.
   2661 
   2662     @param exec_file: Name or path to desired executable.
   2663 
   2664     @return: An actual path to the executable, or None if not found.
   2665     """
   2666     if os.path.dirname(exec_file):
   2667         return exec_file if os.access(exec_file, os.X_OK) else None
   2668     sys_path = os.environ.get('PATH')
   2669     prefix_list = sys_path.split(os.pathsep) if sys_path else []
   2670     for prefix in prefix_list:
   2671         path = os.path.join(prefix, exec_file)
   2672         if os.access(path, os.X_OK):
   2673             return path
   2674 
   2675 
   2676 class TimeoutError(error.TestError):
   2677     """Error raised when poll_for_condition() failed to poll within time.
   2678 
   2679     It may embed a reason (either a string or an exception object) so that
   2680     the caller of poll_for_condition() can handle failure better.
   2681     """
   2682 
   2683     def __init__(self, message=None, reason=None):
   2684         """Constructor.
   2685 
   2686         It supports three invocations:
   2687         1) TimeoutError()
   2688         2) TimeoutError(message): with customized message.
   2689         3) TimeoutError(message, reason): with message and reason for timeout.
   2690         """
   2691         self.reason = reason
   2692         if self.reason:
   2693             reason_str = 'Reason: ' + repr(self.reason)
   2694             if message:
   2695                 message += '. ' + reason_str
   2696             else:
   2697                 message = reason_str
   2698 
   2699         if message:
   2700             super(TimeoutError, self).__init__(message)
   2701         else:
   2702             super(TimeoutError, self).__init__()
   2703 
   2704 
   2705 class Timer(object):
   2706     """A synchronous timer to evaluate if timout is reached.
   2707 
   2708     Usage:
   2709       timer = Timer(timeout_sec)
   2710       while timer.sleep(sleep_interval):
   2711         # do something...
   2712     """
   2713     def __init__(self, timeout):
   2714         """Constructor.
   2715 
   2716         Note that timer won't start until next() is called.
   2717 
   2718         @param timeout: timer timeout in seconds.
   2719         """
   2720         self.timeout = timeout
   2721         self.deadline = 0
   2722 
   2723     def sleep(self, interval):
   2724         """Checks if it has sufficient time to sleep; sleeps if so.
   2725 
   2726         It blocks for |interval| seconds if it has time to sleep.
   2727         If timer is not ticked yet, kicks it off and returns True without
   2728         sleep.
   2729 
   2730         @param interval: sleep interval in seconds.
   2731         @return True if it has sleeped or just kicked off the timer. False
   2732                 otherwise.
   2733         """
   2734         now = time.time()
   2735         if not self.deadline:
   2736             self.deadline = now + self.timeout
   2737             return True
   2738         if now + interval < self.deadline:
   2739             time.sleep(interval)
   2740             return True
   2741         return False
   2742 
   2743 
   2744 def poll_for_condition(condition,
   2745                        exception=None,
   2746                        timeout=10,
   2747                        sleep_interval=0.1,
   2748                        desc=None):
   2749     """Polls until a condition is evaluated to true.
   2750 
   2751     @param condition: function taking no args and returning anything that will
   2752                       evaluate to True in a conditional check
   2753     @param exception: exception to throw if condition doesn't evaluate to true
   2754     @param timeout: maximum number of seconds to wait
   2755     @param sleep_interval: time to sleep between polls
   2756     @param desc: description of default TimeoutError used if 'exception' is
   2757                  None
   2758 
   2759     @return The evaluated value that caused the poll loop to terminate.
   2760 
   2761     @raise 'exception' arg if supplied; TimeoutError otherwise
   2762     """
   2763     start_time = time.time()
   2764     while True:
   2765         value = condition()
   2766         if value:
   2767             return value
   2768         if time.time() + sleep_interval - start_time > timeout:
   2769             if exception:
   2770                 logging.error('Will raise error %r due to unexpected return: '
   2771                               '%r', exception, value)
   2772                 raise exception # pylint: disable=raising-bad-type
   2773 
   2774             if desc:
   2775                 desc = 'Timed out waiting for condition: ' + desc
   2776             else:
   2777                 desc = 'Timed out waiting for unnamed condition'
   2778             logging.error(desc)
   2779             raise TimeoutError(message=desc)
   2780 
   2781         time.sleep(sleep_interval)
   2782 
   2783 
   2784 def poll_for_condition_ex(condition, timeout=10, sleep_interval=0.1, desc=None):
   2785     """Polls until a condition is evaluated to true or until timeout.
   2786 
   2787     Similiar to poll_for_condition, except that it handles exceptions
   2788     condition() raises. If timeout is not reached, the exception is dropped and
   2789     poll for condition after a sleep; otherwise, the exception is embedded into
   2790     TimeoutError to raise.
   2791 
   2792     @param condition: function taking no args and returning anything that will
   2793                       evaluate to True in a conditional check
   2794     @param timeout: maximum number of seconds to wait
   2795     @param sleep_interval: time to sleep between polls
   2796     @param desc: description of the condition
   2797 
   2798     @return The evaluated value that caused the poll loop to terminate.
   2799 
   2800     @raise TimeoutError. If condition() raised exception, it is embedded in
   2801            raised TimeoutError.
   2802     """
   2803     timer = Timer(timeout)
   2804     while timer.sleep(sleep_interval):
   2805         reason = None
   2806         try:
   2807             value = condition()
   2808             if value:
   2809                 return value
   2810         except BaseException as e:
   2811             reason = e
   2812 
   2813     if desc is None:
   2814         desc = 'unamed condition'
   2815     if reason is None:
   2816         reason = 'condition evaluted as false'
   2817     to_raise = TimeoutError(message='Timed out waiting for ' + desc,
   2818                             reason=reason)
   2819     logging.error(str(to_raise))
   2820     raise to_raise
   2821 
   2822 
   2823 def threaded_return(function):
   2824     """
   2825     Decorator to add to a function to get that function to return a thread
   2826     object, but with the added benefit of storing its return value.
   2827 
   2828     @param function: function object to be run in the thread
   2829 
   2830     @return a threading.Thread object, that has already been started, is
   2831             recording its result, and can be completed and its result
   2832             fetched by calling .finish()
   2833     """
   2834     def wrapped_t(queue, *args, **kwargs):
   2835         """
   2836         Calls the decorated function as normal, but appends the output into
   2837         the passed-in threadsafe queue.
   2838         """
   2839         ret = function(*args, **kwargs)
   2840         queue.put(ret)
   2841 
   2842     def wrapped_finish(threaded_object):
   2843         """
   2844         Provides a utility to this thread object, getting its result while
   2845         simultaneously joining the thread.
   2846         """
   2847         ret = threaded_object.get()
   2848         threaded_object.join()
   2849         return ret
   2850 
   2851     def wrapper(*args, **kwargs):
   2852         """
   2853         Creates the queue and starts the thread, then assigns extra attributes
   2854         to the thread to give it result-storing capability.
   2855         """
   2856         q = Queue.Queue()
   2857         t = threading.Thread(target=wrapped_t, args=(q,) + args, kwargs=kwargs)
   2858         t.start()
   2859         t.result_queue = q
   2860         t.get = t.result_queue.get
   2861         t.finish = lambda: wrapped_finish(t)
   2862         return t
   2863 
   2864     # for the decorator
   2865     return wrapper
   2866 
   2867 
   2868 @threaded_return
   2869 def background_sample_until_condition(
   2870         function,
   2871         condition=lambda: True,
   2872         timeout=10,
   2873         sleep_interval=1):
   2874     """
   2875     Records the value of the function until the condition is False or the
   2876     timeout is reached. Runs as a background thread, so it's nonblocking.
   2877     Usage might look something like:
   2878 
   2879     def function():
   2880         return get_value()
   2881     def condition():
   2882         return self._keep_sampling
   2883 
   2884     # main thread
   2885     sample_thread = utils.background_sample_until_condition(
   2886         function=function,condition=condition)
   2887     # do other work
   2888     # ...
   2889     self._keep_sampling = False
   2890     # blocking call to get result and join the thread
   2891     result = sample_thread.finish()
   2892 
   2893     @param function: function object, 0 args, to be continually polled
   2894     @param condition: function object, 0 args, to say when to stop polling
   2895     @param timeout: maximum number of seconds to wait
   2896     @param number of seconds to wait in between polls
   2897 
   2898     @return a thread object that has already been started and is running in
   2899             the background, whose run must be stopped with .finish(), which
   2900             also returns a list of the results from the sample function
   2901     """
   2902     log = []
   2903 
   2904     end_time = datetime.datetime.now() + datetime.timedelta(
   2905             seconds = timeout + sleep_interval)
   2906 
   2907     while condition() and datetime.datetime.now() < end_time:
   2908         log.append(function())
   2909         time.sleep(sleep_interval)
   2910     return log
   2911 
   2912 
   2913 class metrics_mock(metrics_mock_class.mock_class_base):
   2914     """mock class for metrics in case chromite is not installed."""
   2915     pass
   2916 
   2917 
   2918 MountInfo = collections.namedtuple('MountInfo', ['root', 'mount_point', 'tags'])
   2919 
   2920 
   2921 def get_mount_info(process='self', mount_point=None):
   2922     """Retrieves information about currently mounted file systems.
   2923 
   2924     @param mount_point: (optional) The mount point (a path).  If this is
   2925                         provided, only information about the given mount point
   2926                         is returned.  If this is omitted, info about all mount
   2927                         points is returned.
   2928     @param process: (optional) The process id (or the string 'self') of the
   2929                     process whose mountinfo will be obtained.  If this is
   2930                     omitted, info about the current process is returned.
   2931 
   2932     @return A generator yielding one MountInfo object for each relevant mount
   2933             found in /proc/PID/mountinfo.
   2934     """
   2935     with open('/proc/{}/mountinfo'.format(process)) as f:
   2936         for line in f.readlines():
   2937             # These lines are formatted according to the proc(5) manpage.
   2938             # Sample line:
   2939             # 36 35 98:0 /mnt1 /mnt2 rw,noatime master:1 - ext3 /dev/root \
   2940             #     rw,errors=continue
   2941             # Fields (descriptions omitted for fields we don't care about)
   2942             # 3: the root of the mount.
   2943             # 4: the mount point.
   2944             # 5: mount options.
   2945             # 6: tags.  There can be more than one of these.  This is where
   2946             #    shared mounts are indicated.
   2947             # 7: a dash separator marking the end of the tags.
   2948             mountinfo = line.split()
   2949             if mount_point is None or mountinfo[4] == mount_point:
   2950                 tags = []
   2951                 for field in mountinfo[6:]:
   2952                     if field == '-':
   2953                         break
   2954                     tags.append(field.split(':')[0])
   2955                 yield MountInfo(root = mountinfo[3],
   2956                                 mount_point = mountinfo[4],
   2957                                 tags = tags)
   2958 
   2959 
   2960 # Appended suffix for chart tablet naming convention in test lab
   2961 CHART_ADDRESS_SUFFIX = '-tablet'
   2962 
   2963 
   2964 def get_lab_chart_address(hostname):
   2965     """Convert lab DUT hostname to address of camera box chart tablet"""
   2966     return hostname + CHART_ADDRESS_SUFFIX if is_in_container() else None
   2967