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