1#
2# Copyright 2008 Google Inc. Released under the GPL v2
3
4# pylint: disable-msg=C0111
5
6import StringIO
7import errno
8import itertools
9import logging
10import os
11import pickle
12import random
13import re
14import resource
15import select
16import shutil
17import signal
18import smtplib
19import socket
20import string
21import struct
22import subprocess
23import textwrap
24import time
25import urllib2
26import urlparse
27import warnings
28
29from threading import Thread, Event
30
31try:
32    import hashlib
33except ImportError:
34    import md5
35    import sha
36
37from autotest_lib.client.common_lib import error, logging_manager
38
39
40def deprecated(func):
41    """This is a decorator which can be used to mark functions as deprecated.
42    It will result in a warning being emmitted when the function is used."""
43    def new_func(*args, **dargs):
44        warnings.warn("Call to deprecated function %s." % func.__name__,
45                      category=DeprecationWarning)
46        return func(*args, **dargs)
47    new_func.__name__ = func.__name__
48    new_func.__doc__ = func.__doc__
49    new_func.__dict__.update(func.__dict__)
50    return new_func
51
52
53class _NullStream(object):
54    def write(self, data):
55        pass
56
57
58    def flush(self):
59        pass
60
61
62TEE_TO_LOGS = object()
63_the_null_stream = _NullStream()
64
65DEFAULT_STDOUT_LEVEL = logging.DEBUG
66DEFAULT_STDERR_LEVEL = logging.ERROR
67
68# prefixes for logging stdout/stderr of commands
69STDOUT_PREFIX = '[stdout] '
70STDERR_PREFIX = '[stderr] '
71
72# safe characters for the shell (do not need quoting)
73SHELL_QUOTING_WHITELIST = frozenset(string.ascii_letters +
74                                    string.digits +
75                                    '_-+=')
76
77
78def custom_warning_handler(message, category, filename, lineno, file=None,
79                           line=None):
80    """Custom handler to log at the WARNING error level. Ignores |file|."""
81    logging.warning(warnings.formatwarning(message, category, filename, lineno,
82                                           line))
83
84warnings.showwarning = custom_warning_handler
85
86def get_stream_tee_file(stream, level, prefix=''):
87    if stream is None:
88        return _the_null_stream
89    if stream is TEE_TO_LOGS:
90        return logging_manager.LoggingFile(level=level, prefix=prefix)
91    return stream
92
93
94def _join_with_nickname(base_string, nickname):
95    if nickname:
96        return '%s BgJob "%s" ' % (base_string, nickname)
97    return base_string
98
99
100# TODO: Cleanup and possibly eliminate no_pipes, which is only used
101# in our master-ssh connection process, while fixing underlying
102# semantics problem in BgJob. See crbug.com/279312
103class BgJob(object):
104    def __init__(self, command, stdout_tee=None, stderr_tee=None, verbose=True,
105                 stdin=None, stderr_level=DEFAULT_STDERR_LEVEL, nickname=None,
106                 no_pipes=False):
107        """Create and start a new BgJob.
108
109        This constructor creates a new BgJob, and uses Popen to start a new
110        subprocess with given command. It returns without blocking on execution
111        of the subprocess.
112
113        After starting a new BgJob, use output_prepare to connect the process's
114        stdout and stderr pipes to the stream of your choice.
115
116        When the job is running, the jobs's output streams are only read from
117        when process_output is called.
118
119        @param command: command to be executed in new subprocess. May be either
120                        a list, or a string (in which case Popen will be called
121                        with shell=True)
122        @param stdout_tee: Optional additional stream that the process's stdout
123                           stream output will be written to. Or, specify
124                           base_utils.TEE_TO_LOGS and the output will handled by
125                           the standard logging_manager.
126        @param stderr_tee: Same as stdout_tee, but for stderr.
127        @param verbose: Boolean, make BgJob logging more verbose.
128        @param stdin: Stream object, will be passed to Popen as the new
129                      process's stdin.
130        @param stderr_level: A logging level value. If stderr_tee was set to
131                             base_utils.TEE_TO_LOGS, sets the level that tee'd
132                             stderr output will be logged at. Ignored
133                             otherwise.
134        @param nickname: Optional string, to be included in logging messages
135        @param no_pipes: Boolean, default False. If True, this subprocess
136                         created by this BgJob does NOT use subprocess.PIPE
137                         for its stdin or stderr streams. Instead, these
138                         streams are connected to the logging manager
139                         (regardless of the values of stdout_tee and
140                         stderr_tee).
141                         If no_pipes is True, then calls to output_prepare,
142                         process_output, and cleanup will result in an
143                         InvalidBgJobCall exception. no_pipes should be
144                         True for BgJobs that do not interact via stdout/stderr
145                         with other BgJobs, or long runing background jobs that
146                         will never be joined with join_bg_jobs, such as the
147                         master-ssh connection BgJob.
148        """
149        self.command = command
150        self._no_pipes = no_pipes
151        if no_pipes:
152            stdout_tee = TEE_TO_LOGS
153            stderr_tee = TEE_TO_LOGS
154        self.stdout_tee = get_stream_tee_file(stdout_tee, DEFAULT_STDOUT_LEVEL,
155                prefix=_join_with_nickname(STDOUT_PREFIX, nickname))
156        self.stderr_tee = get_stream_tee_file(stderr_tee, stderr_level,
157                prefix=_join_with_nickname(STDERR_PREFIX, nickname))
158        self.result = CmdResult(command)
159
160        # allow for easy stdin input by string, we'll let subprocess create
161        # a pipe for stdin input and we'll write to it in the wait loop
162        if isinstance(stdin, basestring):
163            self.string_stdin = stdin
164            stdin = subprocess.PIPE
165        else:
166            self.string_stdin = None
167
168
169        if no_pipes:
170            stdout_param = self.stdout_tee
171            stderr_param = self.stderr_tee
172        else:
173            stdout_param = subprocess.PIPE
174            stderr_param = subprocess.PIPE
175
176        if verbose:
177            logging.debug("Running '%s'", command)
178        if type(command) == list:
179            self.sp = subprocess.Popen(command,
180                                       stdout=stdout_param,
181                                       stderr=stderr_param,
182                                       preexec_fn=self._reset_sigpipe,
183                                       stdin=stdin)
184        else:
185            self.sp = subprocess.Popen(command, stdout=stdout_param,
186                                       stderr=stderr_param,
187                                       preexec_fn=self._reset_sigpipe, shell=True,
188                                       executable="/bin/bash",
189                                       stdin=stdin)
190
191        self._output_prepare_called = False
192        self._process_output_warned = False
193        self._cleanup_called = False
194        self.stdout_file = _the_null_stream
195        self.stderr_file = _the_null_stream
196
197    def output_prepare(self, stdout_file=_the_null_stream,
198                       stderr_file=_the_null_stream):
199        """Connect the subprocess's stdout and stderr to streams.
200
201        Subsequent calls to output_prepare are permitted, and will reassign
202        the streams. However, this will have the side effect that the ultimate
203        call to cleanup() will only remember the stdout and stderr data up to
204        the last output_prepare call when saving this data to BgJob.result.
205
206        @param stdout_file: Stream that output from the process's stdout pipe
207                            will be written to. Default: a null stream.
208        @param stderr_file: Stream that output from the process's stdout pipe
209                            will be written to. Default: a null stream.
210        """
211        if self._no_pipes:
212            raise error.InvalidBgJobCall('Cannot call output_prepare on a '
213                                         'job with no_pipes=True.')
214        if self._output_prepare_called:
215            logging.warning('BgJob [%s] received a duplicate call to '
216                            'output prepare. Allowing, but this may result '
217                            'in data missing from BgJob.result.')
218        self.stdout_file = stdout_file
219        self.stderr_file = stderr_file
220        self._output_prepare_called = True
221
222
223    def process_output(self, stdout=True, final_read=False):
224        """Read from process's output stream, and write data to destinations.
225
226        This function reads up to 1024 bytes from the background job's
227        stdout or stderr stream, and writes the resulting data to the BgJob's
228        output tee and to the stream set up in output_prepare.
229
230        Warning: Calls to process_output will block on reads from the
231        subprocess stream, and will block on writes to the configured
232        destination stream.
233
234        @param stdout: True = read and process data from job's stdout.
235                       False = from stderr.
236                       Default: True
237        @param final_read: Do not read only 1024 bytes from stream. Instead,
238                           read and process all data until end of the stream.
239
240        """
241        if self._no_pipes:
242            raise error.InvalidBgJobCall('Cannot call process_output on '
243                                         'a job with no_pipes=True')
244        if not self._output_prepare_called and not self._process_output_warned:
245            logging.warning('BgJob with command [%s] handled a process_output '
246                            'call before output_prepare was called. '
247                            'Some output data discarded. '
248                            'Future warnings suppressed.',
249                            self.command)
250            self._process_output_warned = True
251        if stdout:
252            pipe, buf, tee = self.sp.stdout, self.stdout_file, self.stdout_tee
253        else:
254            pipe, buf, tee = self.sp.stderr, self.stderr_file, self.stderr_tee
255
256        if final_read:
257            # read in all the data we can from pipe and then stop
258            data = []
259            while select.select([pipe], [], [], 0)[0]:
260                data.append(os.read(pipe.fileno(), 1024))
261                if len(data[-1]) == 0:
262                    break
263            data = "".join(data)
264        else:
265            # perform a single read
266            data = os.read(pipe.fileno(), 1024)
267        buf.write(data)
268        tee.write(data)
269
270
271    def cleanup(self):
272        """Clean up after BgJob.
273
274        Flush the stdout_tee and stderr_tee buffers, close the
275        subprocess stdout and stderr buffers, and saves data from
276        the configured stdout and stderr destination streams to
277        self.result. Duplicate calls ignored with a warning.
278        """
279        if self._no_pipes:
280            raise error.InvalidBgJobCall('Cannot call cleanup on '
281                                         'a job with no_pipes=True')
282        if self._cleanup_called:
283            logging.warning('BgJob [%s] received a duplicate call to '
284                            'cleanup. Ignoring.', self.command)
285            return
286        try:
287            self.stdout_tee.flush()
288            self.stderr_tee.flush()
289            self.sp.stdout.close()
290            self.sp.stderr.close()
291            self.result.stdout = self.stdout_file.getvalue()
292            self.result.stderr = self.stderr_file.getvalue()
293        finally:
294            self._cleanup_called = True
295
296
297    def _reset_sigpipe(self):
298        signal.signal(signal.SIGPIPE, signal.SIG_DFL)
299
300
301def ip_to_long(ip):
302    # !L is a long in network byte order
303    return struct.unpack('!L', socket.inet_aton(ip))[0]
304
305
306def long_to_ip(number):
307    # See above comment.
308    return socket.inet_ntoa(struct.pack('!L', number))
309
310
311def create_subnet_mask(bits):
312    return (1 << 32) - (1 << 32-bits)
313
314
315def format_ip_with_mask(ip, mask_bits):
316    masked_ip = ip_to_long(ip) & create_subnet_mask(mask_bits)
317    return "%s/%s" % (long_to_ip(masked_ip), mask_bits)
318
319
320def normalize_hostname(alias):
321    ip = socket.gethostbyname(alias)
322    return socket.gethostbyaddr(ip)[0]
323
324
325def get_ip_local_port_range():
326    match = re.match(r'\s*(\d+)\s*(\d+)\s*$',
327                     read_one_line('/proc/sys/net/ipv4/ip_local_port_range'))
328    return (int(match.group(1)), int(match.group(2)))
329
330
331def set_ip_local_port_range(lower, upper):
332    write_one_line('/proc/sys/net/ipv4/ip_local_port_range',
333                   '%d %d\n' % (lower, upper))
334
335
336def send_email(mail_from, mail_to, subject, body):
337    """
338    Sends an email via smtp
339
340    mail_from: string with email address of sender
341    mail_to: string or list with email address(es) of recipients
342    subject: string with subject of email
343    body: (multi-line) string with body of email
344    """
345    if isinstance(mail_to, str):
346        mail_to = [mail_to]
347    msg = "From: %s\nTo: %s\nSubject: %s\n\n%s" % (mail_from, ','.join(mail_to),
348                                                   subject, body)
349    try:
350        mailer = smtplib.SMTP('localhost')
351        try:
352            mailer.sendmail(mail_from, mail_to, msg)
353        finally:
354            mailer.quit()
355    except Exception, e:
356        # Emails are non-critical, not errors, but don't raise them
357        print "Sending email failed. Reason: %s" % repr(e)
358
359
360def read_one_line(filename):
361    return open(filename, 'r').readline().rstrip('\n')
362
363
364def read_file(filename):
365    f = open(filename)
366    try:
367        return f.read()
368    finally:
369        f.close()
370
371
372def get_field(data, param, linestart="", sep=" "):
373    """
374    Parse data from string.
375    @param data: Data to parse.
376        example:
377          data:
378             cpu   324 345 34  5 345
379             cpu0  34  11  34 34  33
380             ^^^^
381             start of line
382             params 0   1   2  3   4
383    @param param: Position of parameter after linestart marker.
384    @param linestart: String to which start line with parameters.
385    @param sep: Separator between parameters regular expression.
386    """
387    search = re.compile(r"(?<=^%s)\s*(.*)" % linestart, re.MULTILINE)
388    find = search.search(data)
389    if find != None:
390        return re.split("%s" % sep, find.group(1))[param]
391    else:
392        print "There is no line which starts with %s in data." % linestart
393        return None
394
395
396def write_one_line(filename, line):
397    open_write_close(filename, str(line).rstrip('\n') + '\n')
398
399
400def open_write_close(filename, data):
401    f = open(filename, 'w')
402    try:
403        f.write(data)
404    finally:
405        f.close()
406
407
408def locate_file(path, base_dir=None):
409    """Locates a file.
410
411    @param path: The path of the file being located. Could be absolute or relative
412        path. For relative path, it tries to locate the file from base_dir.
413    @param base_dir (optional): Base directory of the relative path.
414
415    @returns Absolute path of the file if found. None if path is None.
416    @raises error.TestFail if the file is not found.
417    """
418    if path is None:
419        return None
420
421    if not os.path.isabs(path) and base_dir is not None:
422        # Assume the relative path is based in autotest directory.
423        path = os.path.join(base_dir, path)
424    if not os.path.isfile(path):
425        raise error.TestFail('ERROR: Unable to find %s' % path)
426    return path
427
428
429def matrix_to_string(matrix, header=None):
430    """
431    Return a pretty, aligned string representation of a nxm matrix.
432
433    This representation can be used to print any tabular data, such as
434    database results. It works by scanning the lengths of each element
435    in each column, and determining the format string dynamically.
436
437    @param matrix: Matrix representation (list with n rows of m elements).
438    @param header: Optional tuple or list with header elements to be displayed.
439    """
440    if type(header) is list:
441        header = tuple(header)
442    lengths = []
443    if header:
444        for column in header:
445            lengths.append(len(column))
446    for row in matrix:
447        for i, column in enumerate(row):
448            column = unicode(column).encode("utf-8")
449            cl = len(column)
450            try:
451                ml = lengths[i]
452                if cl > ml:
453                    lengths[i] = cl
454            except IndexError:
455                lengths.append(cl)
456
457    lengths = tuple(lengths)
458    format_string = ""
459    for length in lengths:
460        format_string += "%-" + str(length) + "s "
461    format_string += "\n"
462
463    matrix_str = ""
464    if header:
465        matrix_str += format_string % header
466    for row in matrix:
467        matrix_str += format_string % tuple(row)
468
469    return matrix_str
470
471
472def read_keyval(path, type_tag=None):
473    """
474    Read a key-value pair format file into a dictionary, and return it.
475    Takes either a filename or directory name as input. If it's a
476    directory name, we assume you want the file to be called keyval.
477
478    @param path: Full path of the file to read from.
479    @param type_tag: If not None, only keyvals with key ending
480                     in a suffix {type_tag} will be collected.
481    """
482    if os.path.isdir(path):
483        path = os.path.join(path, 'keyval')
484    if not os.path.exists(path):
485        return {}
486
487    if type_tag:
488        pattern = r'^([-\.\w]+)\{%s\}=(.*)$' % type_tag
489    else:
490        pattern = r'^([-\.\w]+)=(.*)$'
491
492    keyval = {}
493    f = open(path)
494    for line in f:
495        line = re.sub('#.*', '', line).rstrip()
496        if not line:
497            continue
498        match = re.match(pattern, line)
499        if match:
500            key = match.group(1)
501            value = match.group(2)
502            if re.search('^\d+$', value):
503                value = int(value)
504            elif re.search('^(\d+\.)?\d+$', value):
505                value = float(value)
506            keyval[key] = value
507        else:
508            raise ValueError('Invalid format line: %s' % line)
509    f.close()
510    return keyval
511
512
513def write_keyval(path, dictionary, type_tag=None, tap_report=None):
514    """
515    Write a key-value pair format file out to a file. This uses append
516    mode to open the file, so existing text will not be overwritten or
517    reparsed.
518
519    If type_tag is None, then the key must be composed of alphanumeric
520    characters (or dashes+underscores). However, if type-tag is not
521    null then the keys must also have "{type_tag}" as a suffix. At
522    the moment the only valid values of type_tag are "attr" and "perf".
523
524    @param path: full path of the file to be written
525    @param dictionary: the items to write
526    @param type_tag: see text above
527    """
528    if os.path.isdir(path):
529        path = os.path.join(path, 'keyval')
530    keyval = open(path, 'a')
531
532    if type_tag is None:
533        key_regex = re.compile(r'^[-\.\w]+$')
534    else:
535        if type_tag not in ('attr', 'perf'):
536            raise ValueError('Invalid type tag: %s' % type_tag)
537        escaped_tag = re.escape(type_tag)
538        key_regex = re.compile(r'^[-\.\w]+\{%s\}$' % escaped_tag)
539    try:
540        for key in sorted(dictionary.keys()):
541            if not key_regex.search(key):
542                raise ValueError('Invalid key: %s' % key)
543            keyval.write('%s=%s\n' % (key, dictionary[key]))
544    finally:
545        keyval.close()
546
547    # same for tap
548    if tap_report is not None and tap_report.do_tap_report:
549        tap_report.record_keyval(path, dictionary, type_tag=type_tag)
550
551class FileFieldMonitor(object):
552    """
553    Monitors the information from the file and reports it's values.
554
555    It gather the information at start and stop of the measurement or
556    continuously during the measurement.
557    """
558    class Monitor(Thread):
559        """
560        Internal monitor class to ensure continuous monitor of monitored file.
561        """
562        def __init__(self, master):
563            """
564            @param master: Master class which control Monitor
565            """
566            Thread.__init__(self)
567            self.master = master
568
569        def run(self):
570            """
571            Start monitor in thread mode
572            """
573            while not self.master.end_event.isSet():
574                self.master._get_value(self.master.logging)
575                time.sleep(self.master.time_step)
576
577
578    def __init__(self, status_file, data_to_read, mode_diff, continuously=False,
579                 contlogging=False, separator=" +", time_step=0.1):
580        """
581        Initialize variables.
582        @param status_file: File contain status.
583        @param mode_diff: If True make a difference of value, else average.
584        @param data_to_read: List of tuples with data position.
585            format: [(start_of_line,position in params)]
586            example:
587              data:
588                 cpu   324 345 34  5 345
589                 cpu0  34  11  34 34  33
590                 ^^^^
591                 start of line
592                 params 0   1   2  3   4
593        @param mode_diff: True to subtract old value from new value,
594            False make average of the values.
595        @parma continuously: Start the monitoring thread using the time_step
596            as the measurement period.
597        @param contlogging: Log data in continuous run.
598        @param separator: Regular expression of separator.
599        @param time_step: Time period of the monitoring value.
600        """
601        self.end_event = Event()
602        self.start_time = 0
603        self.end_time = 0
604        self.test_time = 0
605
606        self.status_file = status_file
607        self.separator = separator
608        self.data_to_read = data_to_read
609        self.num_of_params = len(self.data_to_read)
610        self.mode_diff = mode_diff
611        self.continuously = continuously
612        self.time_step = time_step
613
614        self.value = [0 for i in range(self.num_of_params)]
615        self.old_value = [0 for i in range(self.num_of_params)]
616        self.log = []
617        self.logging = contlogging
618
619        self.started = False
620        self.num_of_get_value = 0
621        self.monitor = None
622
623
624    def _get_value(self, logging=True):
625        """
626        Return current values.
627        @param logging: If true log value in memory. There can be problem
628          with long run.
629        """
630        data = read_file(self.status_file)
631        value = []
632        for i in range(self.num_of_params):
633            value.append(int(get_field(data,
634                             self.data_to_read[i][1],
635                             self.data_to_read[i][0],
636                             self.separator)))
637
638        if logging:
639            self.log.append(value)
640        if not self.mode_diff:
641            value = map(lambda x, y: x + y, value, self.old_value)
642
643        self.old_value = value
644        self.num_of_get_value += 1
645        return value
646
647
648    def start(self):
649        """
650        Start value monitor.
651        """
652        if self.started:
653            self.stop()
654        self.old_value = [0 for i in range(self.num_of_params)]
655        self.num_of_get_value = 0
656        self.log = []
657        self.end_event.clear()
658        self.start_time = time.time()
659        self._get_value()
660        self.started = True
661        if (self.continuously):
662            self.monitor = FileFieldMonitor.Monitor(self)
663            self.monitor.start()
664
665
666    def stop(self):
667        """
668        Stop value monitor.
669        """
670        if self.started:
671            self.started = False
672            self.end_time = time.time()
673            self.test_time = self.end_time - self.start_time
674            self.value = self._get_value()
675            if (self.continuously):
676                self.end_event.set()
677                self.monitor.join()
678            if (self.mode_diff):
679                self.value = map(lambda x, y: x - y, self.log[-1], self.log[0])
680            else:
681                self.value = map(lambda x: x / self.num_of_get_value,
682                                 self.value)
683
684
685    def get_status(self):
686        """
687        @return: Status of monitored process average value,
688            time of test and array of monitored values and time step of
689            continuous run.
690        """
691        if self.started:
692            self.stop()
693        if self.mode_diff:
694            for i in range(len(self.log) - 1):
695                self.log[i] = (map(lambda x, y: x - y,
696                                   self.log[i + 1], self.log[i]))
697            self.log.pop()
698        return (self.value, self.test_time, self.log, self.time_step)
699
700
701def is_url(path):
702    """Return true if path looks like a URL"""
703    # for now, just handle http and ftp
704    url_parts = urlparse.urlparse(path)
705    return (url_parts[0] in ('http', 'ftp'))
706
707
708def urlopen(url, data=None, timeout=5):
709    """Wrapper to urllib2.urlopen with timeout addition."""
710
711    # Save old timeout
712    old_timeout = socket.getdefaulttimeout()
713    socket.setdefaulttimeout(timeout)
714    try:
715        return urllib2.urlopen(url, data=data)
716    finally:
717        socket.setdefaulttimeout(old_timeout)
718
719
720def urlretrieve(url, filename, data=None, timeout=300):
721    """Retrieve a file from given url."""
722    logging.debug('Fetching %s -> %s', url, filename)
723
724    src_file = urlopen(url, data=data, timeout=timeout)
725    try:
726        dest_file = open(filename, 'wb')
727        try:
728            shutil.copyfileobj(src_file, dest_file)
729        finally:
730            dest_file.close()
731    finally:
732        src_file.close()
733
734
735def hash(type, input=None):
736    """
737    Returns an hash object of type md5 or sha1. This function is implemented in
738    order to encapsulate hash objects in a way that is compatible with python
739    2.4 and python 2.6 without warnings.
740
741    Note that even though python 2.6 hashlib supports hash types other than
742    md5 and sha1, we are artificially limiting the input values in order to
743    make the function to behave exactly the same among both python
744    implementations.
745
746    @param input: Optional input string that will be used to update the hash.
747    """
748    if type not in ['md5', 'sha1']:
749        raise ValueError("Unsupported hash type: %s" % type)
750
751    try:
752        hash = hashlib.new(type)
753    except NameError:
754        if type == 'md5':
755            hash = md5.new()
756        elif type == 'sha1':
757            hash = sha.new()
758
759    if input:
760        hash.update(input)
761
762    return hash
763
764
765def get_file(src, dest, permissions=None):
766    """Get a file from src, which can be local or a remote URL"""
767    if src == dest:
768        return
769
770    if is_url(src):
771        urlretrieve(src, dest)
772    else:
773        shutil.copyfile(src, dest)
774
775    if permissions:
776        os.chmod(dest, permissions)
777    return dest
778
779
780def unmap_url(srcdir, src, destdir='.'):
781    """
782    Receives either a path to a local file or a URL.
783    returns either the path to the local file, or the fetched URL
784
785    unmap_url('/usr/src', 'foo.tar', '/tmp')
786                            = '/usr/src/foo.tar'
787    unmap_url('/usr/src', 'http://site/file', '/tmp')
788                            = '/tmp/file'
789                            (after retrieving it)
790    """
791    if is_url(src):
792        url_parts = urlparse.urlparse(src)
793        filename = os.path.basename(url_parts[2])
794        dest = os.path.join(destdir, filename)
795        return get_file(src, dest)
796    else:
797        return os.path.join(srcdir, src)
798
799
800def update_version(srcdir, preserve_srcdir, new_version, install,
801                   *args, **dargs):
802    """
803    Make sure srcdir is version new_version
804
805    If not, delete it and install() the new version.
806
807    In the preserve_srcdir case, we just check it's up to date,
808    and if not, we rerun install, without removing srcdir
809    """
810    versionfile = os.path.join(srcdir, '.version')
811    install_needed = True
812
813    if os.path.exists(versionfile):
814        old_version = pickle.load(open(versionfile))
815        if old_version == new_version:
816            install_needed = False
817
818    if install_needed:
819        if not preserve_srcdir and os.path.exists(srcdir):
820            shutil.rmtree(srcdir)
821        install(*args, **dargs)
822        if os.path.exists(srcdir):
823            pickle.dump(new_version, open(versionfile, 'w'))
824
825
826def get_stderr_level(stderr_is_expected):
827    if stderr_is_expected:
828        return DEFAULT_STDOUT_LEVEL
829    return DEFAULT_STDERR_LEVEL
830
831
832def run(command, timeout=None, ignore_status=False,
833        stdout_tee=None, stderr_tee=None, verbose=True, stdin=None,
834        stderr_is_expected=None, args=(), nickname=None, ignore_timeout=False):
835    """
836    Run a command on the host.
837
838    @param command: the command line string.
839    @param timeout: time limit in seconds before attempting to kill the
840            running process. The run() function will take a few seconds
841            longer than 'timeout' to complete if it has to kill the process.
842    @param ignore_status: do not raise an exception, no matter what the exit
843            code of the command is.
844    @param ignore_timeout: If True, timeouts are ignored otherwise if a
845            timeout occurs it will raise CmdTimeoutError.
846    @param stdout_tee: optional file-like object to which stdout data
847            will be written as it is generated (data will still be stored
848            in result.stdout).
849    @param stderr_tee: likewise for stderr.
850    @param verbose: if True, log the command being run.
851    @param stdin: stdin to pass to the executed process (can be a file
852            descriptor, a file object of a real file or a string).
853    @param args: sequence of strings of arguments to be given to the command
854            inside " quotes after they have been escaped for that; each
855            element in the sequence will be given as a separate command
856            argument
857    @param nickname: Short string that will appear in logging messages
858                     associated with this command.
859
860    @return a CmdResult object or None if the command timed out and
861            ignore_timeout is True
862
863    @raise CmdError: the exit code of the command execution was not 0
864    @raise CmdTimeoutError: the command timed out and ignore_timeout is False.
865    """
866    if isinstance(args, basestring):
867        raise TypeError('Got a string for the "args" keyword argument, '
868                        'need a sequence.')
869
870    # In some cases, command will actually be a list
871    # (For example, see get_user_hash in client/cros/cryptohome.py.)
872    # So, to cover that case, detect if it's a string or not and convert it
873    # into one if necessary.
874    if not isinstance(command, basestring):
875        command = ' '.join([sh_quote_word(arg) for arg in command])
876
877    command = ' '.join([command] + [sh_quote_word(arg) for arg in args])
878    if stderr_is_expected is None:
879        stderr_is_expected = ignore_status
880
881    try:
882        bg_job = join_bg_jobs(
883            (BgJob(command, stdout_tee, stderr_tee, verbose, stdin=stdin,
884                   stderr_level=get_stderr_level(stderr_is_expected),
885                   nickname=nickname),), timeout)[0]
886    except error.CmdTimeoutError:
887        if not ignore_timeout:
888            raise
889        return None
890
891    if not ignore_status and bg_job.result.exit_status:
892        raise error.CmdError(command, bg_job.result,
893                             "Command returned non-zero exit status")
894
895    return bg_job.result
896
897
898def run_parallel(commands, timeout=None, ignore_status=False,
899                 stdout_tee=None, stderr_tee=None,
900                 nicknames=[]):
901    """
902    Behaves the same as run() with the following exceptions:
903
904    - commands is a list of commands to run in parallel.
905    - ignore_status toggles whether or not an exception should be raised
906      on any error.
907
908    @return: a list of CmdResult objects
909    """
910    bg_jobs = []
911    for (command, nickname) in itertools.izip_longest(commands, nicknames):
912        bg_jobs.append(BgJob(command, stdout_tee, stderr_tee,
913                             stderr_level=get_stderr_level(ignore_status),
914                             nickname=nickname))
915
916    # Updates objects in bg_jobs list with their process information
917    join_bg_jobs(bg_jobs, timeout)
918
919    for bg_job in bg_jobs:
920        if not ignore_status and bg_job.result.exit_status:
921            raise error.CmdError(command, bg_job.result,
922                                 "Command returned non-zero exit status")
923
924    return [bg_job.result for bg_job in bg_jobs]
925
926
927@deprecated
928def run_bg(command):
929    """Function deprecated. Please use BgJob class instead."""
930    bg_job = BgJob(command)
931    return bg_job.sp, bg_job.result
932
933
934def join_bg_jobs(bg_jobs, timeout=None):
935    """Joins the bg_jobs with the current thread.
936
937    Returns the same list of bg_jobs objects that was passed in.
938    """
939    ret, timeout_error = 0, False
940    for bg_job in bg_jobs:
941        bg_job.output_prepare(StringIO.StringIO(), StringIO.StringIO())
942
943    try:
944        # We are holding ends to stdin, stdout pipes
945        # hence we need to be sure to close those fds no mater what
946        start_time = time.time()
947        timeout_error = _wait_for_commands(bg_jobs, start_time, timeout)
948
949        for bg_job in bg_jobs:
950            # Process stdout and stderr
951            bg_job.process_output(stdout=True,final_read=True)
952            bg_job.process_output(stdout=False,final_read=True)
953    finally:
954        # close our ends of the pipes to the sp no matter what
955        for bg_job in bg_jobs:
956            bg_job.cleanup()
957
958    if timeout_error:
959        # TODO: This needs to be fixed to better represent what happens when
960        # running in parallel. However this is backwards compatable, so it will
961        # do for the time being.
962        raise error.CmdTimeoutError(
963                bg_jobs[0].command, bg_jobs[0].result,
964                "Command(s) did not complete within %d seconds" % timeout)
965
966
967    return bg_jobs
968
969
970def _wait_for_commands(bg_jobs, start_time, timeout):
971    """Waits for background jobs by select polling their stdout/stderr.
972
973    @param bg_jobs: A list of background jobs to wait on.
974    @param start_time: Time used to calculate the timeout lifetime of a job.
975    @param timeout: The timeout of the list of bg_jobs.
976
977    @return: True if the return was due to a timeout, False otherwise.
978    """
979
980    # To check for processes which terminate without producing any output
981    # a 1 second timeout is used in select.
982    SELECT_TIMEOUT = 1
983
984    read_list = []
985    write_list = []
986    reverse_dict = {}
987
988    for bg_job in bg_jobs:
989        read_list.append(bg_job.sp.stdout)
990        read_list.append(bg_job.sp.stderr)
991        reverse_dict[bg_job.sp.stdout] = (bg_job, True)
992        reverse_dict[bg_job.sp.stderr] = (bg_job, False)
993        if bg_job.string_stdin is not None:
994            write_list.append(bg_job.sp.stdin)
995            reverse_dict[bg_job.sp.stdin] = bg_job
996
997    if timeout:
998        stop_time = start_time + timeout
999        time_left = stop_time - time.time()
1000    else:
1001        time_left = None # so that select never times out
1002
1003    while not timeout or time_left > 0:
1004        # select will return when we may write to stdin, when there is
1005        # stdout/stderr output we can read (including when it is
1006        # EOF, that is the process has terminated) or when a non-fatal
1007        # signal was sent to the process. In the last case the select returns
1008        # EINTR, and we continue waiting for the job if the signal handler for
1009        # the signal that interrupted the call allows us to.
1010        try:
1011            read_ready, write_ready, _ = select.select(read_list, write_list,
1012                                                       [], SELECT_TIMEOUT)
1013        except select.error as v:
1014            if v[0] == errno.EINTR:
1015                logging.warning(v)
1016                continue
1017            else:
1018                raise
1019        # os.read() has to be used instead of
1020        # subproc.stdout.read() which will otherwise block
1021        for file_obj in read_ready:
1022            bg_job, is_stdout = reverse_dict[file_obj]
1023            bg_job.process_output(is_stdout)
1024
1025        for file_obj in write_ready:
1026            # we can write PIPE_BUF bytes without blocking
1027            # POSIX requires PIPE_BUF is >= 512
1028            bg_job = reverse_dict[file_obj]
1029            file_obj.write(bg_job.string_stdin[:512])
1030            bg_job.string_stdin = bg_job.string_stdin[512:]
1031            # no more input data, close stdin, remove it from the select set
1032            if not bg_job.string_stdin:
1033                file_obj.close()
1034                write_list.remove(file_obj)
1035                del reverse_dict[file_obj]
1036
1037        all_jobs_finished = True
1038        for bg_job in bg_jobs:
1039            if bg_job.result.exit_status is not None:
1040                continue
1041
1042            bg_job.result.exit_status = bg_job.sp.poll()
1043            if bg_job.result.exit_status is not None:
1044                # process exited, remove its stdout/stdin from the select set
1045                bg_job.result.duration = time.time() - start_time
1046                read_list.remove(bg_job.sp.stdout)
1047                read_list.remove(bg_job.sp.stderr)
1048                del reverse_dict[bg_job.sp.stdout]
1049                del reverse_dict[bg_job.sp.stderr]
1050            else:
1051                all_jobs_finished = False
1052
1053        if all_jobs_finished:
1054            return False
1055
1056        if timeout:
1057            time_left = stop_time - time.time()
1058
1059    # Kill all processes which did not complete prior to timeout
1060    for bg_job in bg_jobs:
1061        if bg_job.result.exit_status is not None:
1062            continue
1063
1064        logging.warning('run process timeout (%s) fired on: %s', timeout,
1065                        bg_job.command)
1066        if nuke_subprocess(bg_job.sp) is None:
1067            # If process could not be SIGKILL'd, log kernel stack.
1068            logging.warning(read_file('/proc/%d/stack' % bg_job.sp.pid))
1069        bg_job.result.exit_status = bg_job.sp.poll()
1070        bg_job.result.duration = time.time() - start_time
1071
1072    return True
1073
1074
1075def pid_is_alive(pid):
1076    """
1077    True if process pid exists and is not yet stuck in Zombie state.
1078    Zombies are impossible to move between cgroups, etc.
1079    pid can be integer, or text of integer.
1080    """
1081    path = '/proc/%s/stat' % pid
1082
1083    try:
1084        stat = read_one_line(path)
1085    except IOError:
1086        if not os.path.exists(path):
1087            # file went away
1088            return False
1089        raise
1090
1091    return stat.split()[2] != 'Z'
1092
1093
1094def signal_pid(pid, sig):
1095    """
1096    Sends a signal to a process id. Returns True if the process terminated
1097    successfully, False otherwise.
1098    """
1099    try:
1100        os.kill(pid, sig)
1101    except OSError:
1102        # The process may have died before we could kill it.
1103        pass
1104
1105    for i in range(5):
1106        if not pid_is_alive(pid):
1107            return True
1108        time.sleep(1)
1109
1110    # The process is still alive
1111    return False
1112
1113
1114def nuke_subprocess(subproc):
1115    # check if the subprocess is still alive, first
1116    if subproc.poll() is not None:
1117        return subproc.poll()
1118
1119    # the process has not terminated within timeout,
1120    # kill it via an escalating series of signals.
1121    signal_queue = [signal.SIGTERM, signal.SIGKILL]
1122    for sig in signal_queue:
1123        signal_pid(subproc.pid, sig)
1124        if subproc.poll() is not None:
1125            return subproc.poll()
1126
1127
1128def nuke_pid(pid, signal_queue=(signal.SIGTERM, signal.SIGKILL)):
1129    # the process has not terminated within timeout,
1130    # kill it via an escalating series of signals.
1131    pid_path = '/proc/%d/'
1132    if not os.path.exists(pid_path % pid):
1133        # Assume that if the pid does not exist in proc it is already dead.
1134        logging.error('No listing in /proc for pid:%d.', pid)
1135        raise error.AutoservPidAlreadyDeadError('Could not kill nonexistant '
1136                                                'pid: %s.', pid)
1137    for sig in signal_queue:
1138        if signal_pid(pid, sig):
1139            return
1140
1141    # no signal successfully terminated the process
1142    raise error.AutoservRunError('Could not kill %d for process name: %s' % (
1143            pid, get_process_name(pid)), None)
1144
1145
1146def system(command, timeout=None, ignore_status=False):
1147    """
1148    Run a command
1149
1150    @param timeout: timeout in seconds
1151    @param ignore_status: if ignore_status=False, throw an exception if the
1152            command's exit code is non-zero
1153            if ignore_stauts=True, return the exit code.
1154
1155    @return exit status of command
1156            (note, this will always be zero unless ignore_status=True)
1157    """
1158    return run(command, timeout=timeout, ignore_status=ignore_status,
1159               stdout_tee=TEE_TO_LOGS, stderr_tee=TEE_TO_LOGS).exit_status
1160
1161
1162def system_parallel(commands, timeout=None, ignore_status=False):
1163    """This function returns a list of exit statuses for the respective
1164    list of commands."""
1165    return [bg_jobs.exit_status for bg_jobs in
1166            run_parallel(commands, timeout=timeout, ignore_status=ignore_status,
1167                         stdout_tee=TEE_TO_LOGS, stderr_tee=TEE_TO_LOGS)]
1168
1169
1170def system_output(command, timeout=None, ignore_status=False,
1171                  retain_output=False, args=()):
1172    """
1173    Run a command and return the stdout output.
1174
1175    @param command: command string to execute.
1176    @param timeout: time limit in seconds before attempting to kill the
1177            running process. The function will take a few seconds longer
1178            than 'timeout' to complete if it has to kill the process.
1179    @param ignore_status: do not raise an exception, no matter what the exit
1180            code of the command is.
1181    @param retain_output: set to True to make stdout/stderr of the command
1182            output to be also sent to the logging system
1183    @param args: sequence of strings of arguments to be given to the command
1184            inside " quotes after they have been escaped for that; each
1185            element in the sequence will be given as a separate command
1186            argument
1187
1188    @return a string with the stdout output of the command.
1189    """
1190    if retain_output:
1191        out = run(command, timeout=timeout, ignore_status=ignore_status,
1192                  stdout_tee=TEE_TO_LOGS, stderr_tee=TEE_TO_LOGS,
1193                  args=args).stdout
1194    else:
1195        out = run(command, timeout=timeout, ignore_status=ignore_status,
1196                  args=args).stdout
1197    if out[-1:] == '\n':
1198        out = out[:-1]
1199    return out
1200
1201
1202def system_output_parallel(commands, timeout=None, ignore_status=False,
1203                           retain_output=False):
1204    if retain_output:
1205        out = [bg_job.stdout for bg_job
1206               in run_parallel(commands, timeout=timeout,
1207                               ignore_status=ignore_status,
1208                               stdout_tee=TEE_TO_LOGS, stderr_tee=TEE_TO_LOGS)]
1209    else:
1210        out = [bg_job.stdout for bg_job in run_parallel(commands,
1211                                  timeout=timeout, ignore_status=ignore_status)]
1212    for x in out:
1213        if out[-1:] == '\n': out = out[:-1]
1214    return out
1215
1216
1217def strip_unicode(input):
1218    if type(input) == list:
1219        return [strip_unicode(i) for i in input]
1220    elif type(input) == dict:
1221        output = {}
1222        for key in input.keys():
1223            output[str(key)] = strip_unicode(input[key])
1224        return output
1225    elif type(input) == unicode:
1226        return str(input)
1227    else:
1228        return input
1229
1230
1231def get_cpu_percentage(function, *args, **dargs):
1232    """Returns a tuple containing the CPU% and return value from function call.
1233
1234    This function calculates the usage time by taking the difference of
1235    the user and system times both before and after the function call.
1236    """
1237    child_pre = resource.getrusage(resource.RUSAGE_CHILDREN)
1238    self_pre = resource.getrusage(resource.RUSAGE_SELF)
1239    start = time.time()
1240    to_return = function(*args, **dargs)
1241    elapsed = time.time() - start
1242    self_post = resource.getrusage(resource.RUSAGE_SELF)
1243    child_post = resource.getrusage(resource.RUSAGE_CHILDREN)
1244
1245    # Calculate CPU Percentage
1246    s_user, s_system = [a - b for a, b in zip(self_post, self_pre)[:2]]
1247    c_user, c_system = [a - b for a, b in zip(child_post, child_pre)[:2]]
1248    cpu_percent = (s_user + c_user + s_system + c_system) / elapsed
1249
1250    return cpu_percent, to_return
1251
1252
1253class SystemLoad(object):
1254    """
1255    Get system and/or process values and return average value of load.
1256    """
1257    def __init__(self, pids, advanced=False, time_step=0.1, cpu_cont=False,
1258                 use_log=False):
1259        """
1260        @param pids: List of pids to be monitored. If pid = 0 whole system will
1261          be monitored. pid == 0 means whole system.
1262        @param advanced: monitor add value for system irq count and softirq
1263          for process minor and maior page fault
1264        @param time_step: Time step for continuous monitoring.
1265        @param cpu_cont: If True monitor CPU load continuously.
1266        @param use_log: If true every monitoring is logged for dump.
1267        """
1268        self.pids = []
1269        self.stats = {}
1270        for pid in pids:
1271            if pid == 0:
1272                cpu = FileFieldMonitor("/proc/stat",
1273                                       [("cpu", 0), # User Time
1274                                        ("cpu", 2), # System Time
1275                                        ("intr", 0), # IRQ Count
1276                                        ("softirq", 0)], # Soft IRQ Count
1277                                       True,
1278                                       cpu_cont,
1279                                       use_log,
1280                                       " +",
1281                                       time_step)
1282                mem = FileFieldMonitor("/proc/meminfo",
1283                                       [("MemTotal:", 0), # Mem Total
1284                                        ("MemFree:", 0), # Mem Free
1285                                        ("Buffers:", 0), # Buffers
1286                                        ("Cached:", 0)], # Cached
1287                                       False,
1288                                       True,
1289                                       use_log,
1290                                       " +",
1291                                       time_step)
1292                self.stats[pid] = ["TOTAL", cpu, mem]
1293                self.pids.append(pid)
1294            else:
1295                name = ""
1296                if (type(pid) is int):
1297                    self.pids.append(pid)
1298                    name = get_process_name(pid)
1299                else:
1300                    self.pids.append(pid[0])
1301                    name = pid[1]
1302
1303                cpu = FileFieldMonitor("/proc/%d/stat" %
1304                                       self.pids[-1],
1305                                       [("", 13), # User Time
1306                                        ("", 14), # System Time
1307                                        ("", 9), # Minority Page Fault
1308                                        ("", 11)], # Majority Page Fault
1309                                       True,
1310                                       cpu_cont,
1311                                       use_log,
1312                                       " +",
1313                                       time_step)
1314                mem = FileFieldMonitor("/proc/%d/status" %
1315                                       self.pids[-1],
1316                                       [("VmSize:", 0), # Virtual Memory Size
1317                                        ("VmRSS:", 0), # Resident Set Size
1318                                        ("VmPeak:", 0), # Peak VM Size
1319                                        ("VmSwap:", 0)], # VM in Swap
1320                                       False,
1321                                       True,
1322                                       use_log,
1323                                       " +",
1324                                       time_step)
1325                self.stats[self.pids[-1]] = [name, cpu, mem]
1326
1327        self.advanced = advanced
1328
1329
1330    def __str__(self):
1331        """
1332        Define format how to print
1333        """
1334        out = ""
1335        for pid in self.pids:
1336            for stat in self.stats[pid][1:]:
1337                out += str(stat.get_status()) + "\n"
1338        return out
1339
1340
1341    def start(self, pids=[]):
1342        """
1343        Start monitoring of the process system usage.
1344        @param pids: List of PIDs you intend to control. Use pids=[] to control
1345            all defined PIDs.
1346        """
1347        if pids == []:
1348            pids = self.pids
1349
1350        for pid in pids:
1351            for stat in self.stats[pid][1:]:
1352                stat.start()
1353
1354
1355    def stop(self, pids=[]):
1356        """
1357        Stop monitoring of the process system usage.
1358        @param pids: List of PIDs you intend to control. Use pids=[] to control
1359            all defined PIDs.
1360        """
1361        if pids == []:
1362            pids = self.pids
1363
1364        for pid in pids:
1365            for stat in self.stats[pid][1:]:
1366                stat.stop()
1367
1368
1369    def dump(self, pids=[]):
1370        """
1371        Get the status of monitoring.
1372        @param pids: List of PIDs you intend to control. Use pids=[] to control
1373            all defined PIDs.
1374         @return:
1375            tuple([cpu load], [memory load]):
1376                ([(PID1, (PID1_cpu_meas)), (PID2, (PID2_cpu_meas)), ...],
1377                 [(PID1, (PID1_mem_meas)), (PID2, (PID2_mem_meas)), ...])
1378
1379            PID1_cpu_meas:
1380                average_values[], test_time, cont_meas_values[[]], time_step
1381            PID1_mem_meas:
1382                average_values[], test_time, cont_meas_values[[]], time_step
1383            where average_values[] are the measured values (mem_free,swap,...)
1384            which are described in SystemLoad.__init__()-FileFieldMonitor.
1385            cont_meas_values[[]] is a list of average_values in the sampling
1386            times.
1387        """
1388        if pids == []:
1389            pids = self.pids
1390
1391        cpus = []
1392        memory = []
1393        for pid in pids:
1394            stat = (pid, self.stats[pid][1].get_status())
1395            cpus.append(stat)
1396        for pid in pids:
1397            stat = (pid, self.stats[pid][2].get_status())
1398            memory.append(stat)
1399
1400        return (cpus, memory)
1401
1402
1403    def get_cpu_status_string(self, pids=[]):
1404        """
1405        Convert status to string array.
1406        @param pids: List of PIDs you intend to control. Use pids=[] to control
1407            all defined PIDs.
1408        @return: String format to table.
1409        """
1410        if pids == []:
1411            pids = self.pids
1412
1413        headers = ["NAME",
1414                   ("%7s") % "PID",
1415                   ("%5s") % "USER",
1416                   ("%5s") % "SYS",
1417                   ("%5s") % "SUM"]
1418        if self.advanced:
1419            headers.extend(["MINFLT/IRQC",
1420                            "MAJFLT/SOFTIRQ"])
1421        headers.append(("%11s") % "TIME")
1422        textstatus = []
1423        for pid in pids:
1424            stat = self.stats[pid][1].get_status()
1425            time = stat[1]
1426            stat = stat[0]
1427            textstatus.append(["%s" % self.stats[pid][0],
1428                               "%7s" % pid,
1429                               "%4.0f%%" % (stat[0] / time),
1430                               "%4.0f%%" % (stat[1] / time),
1431                               "%4.0f%%" % ((stat[0] + stat[1]) / time),
1432                               "%10.3fs" % time])
1433            if self.advanced:
1434                textstatus[-1].insert(-1, "%11d" % stat[2])
1435                textstatus[-1].insert(-1, "%14d" % stat[3])
1436
1437        return matrix_to_string(textstatus, tuple(headers))
1438
1439
1440    def get_mem_status_string(self, pids=[]):
1441        """
1442        Convert status to string array.
1443        @param pids: List of PIDs you intend to control. Use pids=[] to control
1444            all defined PIDs.
1445        @return: String format to table.
1446        """
1447        if pids == []:
1448            pids = self.pids
1449
1450        headers = ["NAME",
1451                   ("%7s") % "PID",
1452                   ("%8s") % "TOTAL/VMSIZE",
1453                   ("%8s") % "FREE/VMRSS",
1454                   ("%8s") % "BUFFERS/VMPEAK",
1455                   ("%8s") % "CACHED/VMSWAP",
1456                   ("%11s") % "TIME"]
1457        textstatus = []
1458        for pid in pids:
1459            stat = self.stats[pid][2].get_status()
1460            time = stat[1]
1461            stat = stat[0]
1462            textstatus.append(["%s" % self.stats[pid][0],
1463                               "%7s" % pid,
1464                               "%10dMB" % (stat[0] / 1024),
1465                               "%8dMB" % (stat[1] / 1024),
1466                               "%12dMB" % (stat[2] / 1024),
1467                               "%11dMB" % (stat[3] / 1024),
1468                               "%10.3fs" % time])
1469
1470        return matrix_to_string(textstatus, tuple(headers))
1471
1472
1473def get_arch(run_function=run):
1474    """
1475    Get the hardware architecture of the machine.
1476    If specified, run_function should return a CmdResult object and throw a
1477    CmdError exception.
1478    If run_function is anything other than utils.run(), it is used to
1479    execute the commands. By default (when set to utils.run()) this will
1480    just examine os.uname()[4].
1481    """
1482
1483    # Short circuit from the common case.
1484    if run_function == run:
1485        return re.sub(r'i\d86$', 'i386', os.uname()[4])
1486
1487    # Otherwise, use the run_function in case it hits a remote machine.
1488    arch = run_function('/bin/uname -m').stdout.rstrip()
1489    if re.match(r'i\d86$', arch):
1490        arch = 'i386'
1491    return arch
1492
1493def get_arch_userspace(run_function=run):
1494    """
1495    Get the architecture by userspace (possibly different from kernel).
1496    """
1497    archs = {
1498        'arm': 'ELF 32-bit.*, ARM,',
1499        'i386': 'ELF 32-bit.*, Intel 80386,',
1500        'x86_64': 'ELF 64-bit.*, x86-64,',
1501    }
1502
1503    cmd = 'file --brief --dereference /bin/sh'
1504    filestr = run_function(cmd).stdout.rstrip()
1505    for a, regex in archs.iteritems():
1506        if re.match(regex, filestr):
1507            return a
1508
1509    return get_arch()
1510
1511
1512def get_num_logical_cpus_per_socket(run_function=run):
1513    """
1514    Get the number of cores (including hyperthreading) per cpu.
1515    run_function is used to execute the commands. It defaults to
1516    utils.run() but a custom method (if provided) should be of the
1517    same schema as utils.run. It should return a CmdResult object and
1518    throw a CmdError exception.
1519    """
1520    siblings = run_function('grep "^siblings" /proc/cpuinfo').stdout.rstrip()
1521    num_siblings = map(int,
1522                       re.findall(r'^siblings\s*:\s*(\d+)\s*$',
1523                                  siblings, re.M))
1524    if len(num_siblings) == 0:
1525        raise error.TestError('Unable to find siblings info in /proc/cpuinfo')
1526    if min(num_siblings) != max(num_siblings):
1527        raise error.TestError('Number of siblings differ %r' %
1528                              num_siblings)
1529    return num_siblings[0]
1530
1531
1532def merge_trees(src, dest):
1533    """
1534    Merges a source directory tree at 'src' into a destination tree at
1535    'dest'. If a path is a file in both trees than the file in the source
1536    tree is APPENDED to the one in the destination tree. If a path is
1537    a directory in both trees then the directories are recursively merged
1538    with this function. In any other case, the function will skip the
1539    paths that cannot be merged (instead of failing).
1540    """
1541    if not os.path.exists(src):
1542        return # exists only in dest
1543    elif not os.path.exists(dest):
1544        if os.path.isfile(src):
1545            shutil.copy2(src, dest) # file only in src
1546        else:
1547            shutil.copytree(src, dest, symlinks=True) # dir only in src
1548        return
1549    elif os.path.isfile(src) and os.path.isfile(dest):
1550        # src & dest are files in both trees, append src to dest
1551        destfile = open(dest, "a")
1552        try:
1553            srcfile = open(src)
1554            try:
1555                destfile.write(srcfile.read())
1556            finally:
1557                srcfile.close()
1558        finally:
1559            destfile.close()
1560    elif os.path.isdir(src) and os.path.isdir(dest):
1561        # src & dest are directories in both trees, so recursively merge
1562        for name in os.listdir(src):
1563            merge_trees(os.path.join(src, name), os.path.join(dest, name))
1564    else:
1565        # src & dest both exist, but are incompatible
1566        return
1567
1568
1569class CmdResult(object):
1570    """
1571    Command execution result.
1572
1573    command:     String containing the command line itself
1574    exit_status: Integer exit code of the process
1575    stdout:      String containing stdout of the process
1576    stderr:      String containing stderr of the process
1577    duration:    Elapsed wall clock time running the process
1578    """
1579
1580
1581    def __init__(self, command="", stdout="", stderr="",
1582                 exit_status=None, duration=0):
1583        self.command = command
1584        self.exit_status = exit_status
1585        self.stdout = stdout
1586        self.stderr = stderr
1587        self.duration = duration
1588
1589
1590    def __repr__(self):
1591        wrapper = textwrap.TextWrapper(width = 78,
1592                                       initial_indent="\n    ",
1593                                       subsequent_indent="    ")
1594
1595        stdout = self.stdout.rstrip()
1596        if stdout:
1597            stdout = "\nstdout:\n%s" % stdout
1598
1599        stderr = self.stderr.rstrip()
1600        if stderr:
1601            stderr = "\nstderr:\n%s" % stderr
1602
1603        return ("* Command: %s\n"
1604                "Exit status: %s\n"
1605                "Duration: %s\n"
1606                "%s"
1607                "%s"
1608                % (wrapper.fill(str(self.command)), self.exit_status,
1609                self.duration, stdout, stderr))
1610
1611
1612class run_randomly:
1613    def __init__(self, run_sequentially=False):
1614        # Run sequentially is for debugging control files
1615        self.test_list = []
1616        self.run_sequentially = run_sequentially
1617
1618
1619    def add(self, *args, **dargs):
1620        test = (args, dargs)
1621        self.test_list.append(test)
1622
1623
1624    def run(self, fn):
1625        while self.test_list:
1626            test_index = random.randint(0, len(self.test_list)-1)
1627            if self.run_sequentially:
1628                test_index = 0
1629            (args, dargs) = self.test_list.pop(test_index)
1630            fn(*args, **dargs)
1631
1632
1633def import_site_module(path, module, dummy=None, modulefile=None):
1634    """
1635    Try to import the site specific module if it exists.
1636
1637    @param path full filename of the source file calling this (ie __file__)
1638    @param module full module name
1639    @param dummy dummy value to return in case there is no symbol to import
1640    @param modulefile module filename
1641
1642    @return site specific module or dummy
1643
1644    @raises ImportError if the site file exists but imports fails
1645    """
1646    short_module = module[module.rfind(".") + 1:]
1647
1648    if not modulefile:
1649        modulefile = short_module + ".py"
1650
1651    if os.path.exists(os.path.join(os.path.dirname(path), modulefile)):
1652        return __import__(module, {}, {}, [short_module])
1653    return dummy
1654
1655
1656def import_site_symbol(path, module, name, dummy=None, modulefile=None):
1657    """
1658    Try to import site specific symbol from site specific file if it exists
1659
1660    @param path full filename of the source file calling this (ie __file__)
1661    @param module full module name
1662    @param name symbol name to be imported from the site file
1663    @param dummy dummy value to return in case there is no symbol to import
1664    @param modulefile module filename
1665
1666    @return site specific symbol or dummy
1667
1668    @raises ImportError if the site file exists but imports fails
1669    """
1670    module = import_site_module(path, module, modulefile=modulefile)
1671    if not module:
1672        return dummy
1673
1674    # special unique value to tell us if the symbol can't be imported
1675    cant_import = object()
1676
1677    obj = getattr(module, name, cant_import)
1678    if obj is cant_import:
1679        return dummy
1680
1681    return obj
1682
1683
1684def import_site_class(path, module, classname, baseclass, modulefile=None):
1685    """
1686    Try to import site specific class from site specific file if it exists
1687
1688    Args:
1689        path: full filename of the source file calling this (ie __file__)
1690        module: full module name
1691        classname: class name to be loaded from site file
1692        baseclass: base class object to return when no site file present or
1693            to mixin when site class exists but is not inherited from baseclass
1694        modulefile: module filename
1695
1696    Returns: baseclass if site specific class does not exist, the site specific
1697        class if it exists and is inherited from baseclass or a mixin of the
1698        site specific class and baseclass when the site specific class exists
1699        and is not inherited from baseclass
1700
1701    Raises: ImportError if the site file exists but imports fails
1702    """
1703
1704    res = import_site_symbol(path, module, classname, None, modulefile)
1705    if res:
1706        if not issubclass(res, baseclass):
1707            # if not a subclass of baseclass then mix in baseclass with the
1708            # site specific class object and return the result
1709            res = type(classname, (res, baseclass), {})
1710    else:
1711        res = baseclass
1712
1713    return res
1714
1715
1716def import_site_function(path, module, funcname, dummy, modulefile=None):
1717    """
1718    Try to import site specific function from site specific file if it exists
1719
1720    Args:
1721        path: full filename of the source file calling this (ie __file__)
1722        module: full module name
1723        funcname: function name to be imported from site file
1724        dummy: dummy function to return in case there is no function to import
1725        modulefile: module filename
1726
1727    Returns: site specific function object or dummy
1728
1729    Raises: ImportError if the site file exists but imports fails
1730    """
1731
1732    return import_site_symbol(path, module, funcname, dummy, modulefile)
1733
1734
1735def _get_pid_path(program_name):
1736    my_path = os.path.dirname(__file__)
1737    return os.path.abspath(os.path.join(my_path, "..", "..",
1738                                        "%s.pid" % program_name))
1739
1740
1741def write_pid(program_name):
1742    """
1743    Try to drop <program_name>.pid in the main autotest directory.
1744
1745    Args:
1746      program_name: prefix for file name
1747    """
1748    pidfile = open(_get_pid_path(program_name), "w")
1749    try:
1750        pidfile.write("%s\n" % os.getpid())
1751    finally:
1752        pidfile.close()
1753
1754
1755def delete_pid_file_if_exists(program_name):
1756    """
1757    Tries to remove <program_name>.pid from the main autotest directory.
1758    """
1759    pidfile_path = _get_pid_path(program_name)
1760
1761    try:
1762        os.remove(pidfile_path)
1763    except OSError:
1764        if not os.path.exists(pidfile_path):
1765            return
1766        raise
1767
1768
1769def get_pid_from_file(program_name):
1770    """
1771    Reads the pid from <program_name>.pid in the autotest directory.
1772
1773    @param program_name the name of the program
1774    @return the pid if the file exists, None otherwise.
1775    """
1776    pidfile_path = _get_pid_path(program_name)
1777    if not os.path.exists(pidfile_path):
1778        return None
1779
1780    pidfile = open(_get_pid_path(program_name), 'r')
1781
1782    try:
1783        try:
1784            pid = int(pidfile.readline())
1785        except IOError:
1786            if not os.path.exists(pidfile_path):
1787                return None
1788            raise
1789    finally:
1790        pidfile.close()
1791
1792    return pid
1793
1794
1795def get_process_name(pid):
1796    """
1797    Get process name from PID.
1798    @param pid: PID of process.
1799    @return: Process name if PID stat file exists or 'Dead PID' if it does not.
1800    """
1801    pid_stat_path = "/proc/%d/stat"
1802    if not os.path.exists(pid_stat_path % pid):
1803        return "Dead Pid"
1804    return get_field(read_file(pid_stat_path % pid), 1)[1:-1]
1805
1806
1807def program_is_alive(program_name):
1808    """
1809    Checks if the process is alive and not in Zombie state.
1810
1811    @param program_name the name of the program
1812    @return True if still alive, False otherwise
1813    """
1814    pid = get_pid_from_file(program_name)
1815    if pid is None:
1816        return False
1817    return pid_is_alive(pid)
1818
1819
1820def signal_program(program_name, sig=signal.SIGTERM):
1821    """
1822    Sends a signal to the process listed in <program_name>.pid
1823
1824    @param program_name the name of the program
1825    @param sig signal to send
1826    """
1827    pid = get_pid_from_file(program_name)
1828    if pid:
1829        signal_pid(pid, sig)
1830
1831
1832def get_relative_path(path, reference):
1833    """Given 2 absolute paths "path" and "reference", compute the path of
1834    "path" as relative to the directory "reference".
1835
1836    @param path the absolute path to convert to a relative path
1837    @param reference an absolute directory path to which the relative
1838        path will be computed
1839    """
1840    # normalize the paths (remove double slashes, etc)
1841    assert(os.path.isabs(path))
1842    assert(os.path.isabs(reference))
1843
1844    path = os.path.normpath(path)
1845    reference = os.path.normpath(reference)
1846
1847    # we could use os.path.split() but it splits from the end
1848    path_list = path.split(os.path.sep)[1:]
1849    ref_list = reference.split(os.path.sep)[1:]
1850
1851    # find the longest leading common path
1852    for i in xrange(min(len(path_list), len(ref_list))):
1853        if path_list[i] != ref_list[i]:
1854            # decrement i so when exiting this loop either by no match or by
1855            # end of range we are one step behind
1856            i -= 1
1857            break
1858    i += 1
1859    # drop the common part of the paths, not interested in that anymore
1860    del path_list[:i]
1861
1862    # for each uncommon component in the reference prepend a ".."
1863    path_list[:0] = ['..'] * (len(ref_list) - i)
1864
1865    return os.path.join(*path_list)
1866
1867
1868def sh_escape(command):
1869    """
1870    Escape special characters from a command so that it can be passed
1871    as a double quoted (" ") string in a (ba)sh command.
1872
1873    Args:
1874            command: the command string to escape.
1875
1876    Returns:
1877            The escaped command string. The required englobing double
1878            quotes are NOT added and so should be added at some point by
1879            the caller.
1880
1881    See also: http://www.tldp.org/LDP/abs/html/escapingsection.html
1882    """
1883    command = command.replace("\\", "\\\\")
1884    command = command.replace("$", r'\$')
1885    command = command.replace('"', r'\"')
1886    command = command.replace('`', r'\`')
1887    return command
1888
1889
1890def sh_quote_word(text, whitelist=SHELL_QUOTING_WHITELIST):
1891    r"""Quote a string to make it safe as a single word in a shell command.
1892
1893    POSIX shell syntax recognizes no escape characters inside a single-quoted
1894    string.  So, single quotes can safely quote any string of characters except
1895    a string with a single quote character.  A single quote character must be
1896    quoted with the sequence '\'' which translates to:
1897        '  -> close current quote
1898        \' -> insert a literal single quote
1899        '  -> reopen quoting again.
1900
1901    This is safe for all combinations of characters, including embedded and
1902    trailing backslashes in odd or even numbers.
1903
1904    This is also safe for nesting, e.g. the following is a valid use:
1905
1906        adb_command = 'adb shell %s' % (
1907                sh_quote_word('echo %s' % sh_quote_word('hello world')))
1908
1909    @param text: The string to be quoted into a single word for the shell.
1910    @param whitelist: Optional list of characters that do not need quoting.
1911                      Defaults to a known good list of characters.
1912
1913    @return A string, possibly quoted, safe as a single word for a shell.
1914    """
1915    if all(c in whitelist for c in text):
1916        return text
1917    return "'" + text.replace("'", r"'\''") + "'"
1918
1919
1920def configure(extra=None, configure='./configure'):
1921    """
1922    Run configure passing in the correct host, build, and target options.
1923
1924    @param extra: extra command line arguments to pass to configure
1925    @param configure: which configure script to use
1926    """
1927    args = []
1928    if 'CHOST' in os.environ:
1929        args.append('--host=' + os.environ['CHOST'])
1930    if 'CBUILD' in os.environ:
1931        args.append('--build=' + os.environ['CBUILD'])
1932    if 'CTARGET' in os.environ:
1933        args.append('--target=' + os.environ['CTARGET'])
1934    if extra:
1935        args.append(extra)
1936
1937    system('%s %s' % (configure, ' '.join(args)))
1938
1939
1940def make(extra='', make='make', timeout=None, ignore_status=False):
1941    """
1942    Run make, adding MAKEOPTS to the list of options.
1943
1944    @param extra: extra command line arguments to pass to make.
1945    """
1946    cmd = '%s %s %s' % (make, os.environ.get('MAKEOPTS', ''), extra)
1947    return system(cmd, timeout=timeout, ignore_status=ignore_status)
1948
1949
1950def compare_versions(ver1, ver2):
1951    """Version number comparison between ver1 and ver2 strings.
1952
1953    >>> compare_tuple("1", "2")
1954    -1
1955    >>> compare_tuple("foo-1.1", "foo-1.2")
1956    -1
1957    >>> compare_tuple("1.2", "1.2a")
1958    -1
1959    >>> compare_tuple("1.2b", "1.2a")
1960    1
1961    >>> compare_tuple("1.3.5.3a", "1.3.5.3b")
1962    -1
1963
1964    Args:
1965        ver1: version string
1966        ver2: version string
1967
1968    Returns:
1969        int:  1 if ver1 >  ver2
1970              0 if ver1 == ver2
1971             -1 if ver1 <  ver2
1972    """
1973    ax = re.split('[.-]', ver1)
1974    ay = re.split('[.-]', ver2)
1975    while len(ax) > 0 and len(ay) > 0:
1976        cx = ax.pop(0)
1977        cy = ay.pop(0)
1978        maxlen = max(len(cx), len(cy))
1979        c = cmp(cx.zfill(maxlen), cy.zfill(maxlen))
1980        if c != 0:
1981            return c
1982    return cmp(len(ax), len(ay))
1983
1984
1985def args_to_dict(args):
1986    """Convert autoserv extra arguments in the form of key=val or key:val to a
1987    dictionary.  Each argument key is converted to lowercase dictionary key.
1988
1989    Args:
1990        args - list of autoserv extra arguments.
1991
1992    Returns:
1993        dictionary
1994    """
1995    arg_re = re.compile(r'(\w+)[:=](.*)$')
1996    dict = {}
1997    for arg in args:
1998        match = arg_re.match(arg)
1999        if match:
2000            dict[match.group(1).lower()] = match.group(2)
2001        else:
2002            logging.warning("args_to_dict: argument '%s' doesn't match "
2003                            "'%s' pattern. Ignored.", arg, arg_re.pattern)
2004    return dict
2005
2006
2007def get_unused_port():
2008    """
2009    Finds a semi-random available port. A race condition is still
2010    possible after the port number is returned, if another process
2011    happens to bind it.
2012
2013    Returns:
2014        A port number that is unused on both TCP and UDP.
2015    """
2016
2017    def try_bind(port, socket_type, socket_proto):
2018        s = socket.socket(socket.AF_INET, socket_type, socket_proto)
2019        try:
2020            try:
2021                s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
2022                s.bind(('', port))
2023                return s.getsockname()[1]
2024            except socket.error:
2025                return None
2026        finally:
2027            s.close()
2028
2029    # On the 2.6 kernel, calling try_bind() on UDP socket returns the
2030    # same port over and over. So always try TCP first.
2031    while True:
2032        # Ask the OS for an unused port.
2033        port = try_bind(0, socket.SOCK_STREAM, socket.IPPROTO_TCP)
2034        # Check if this port is unused on the other protocol.
2035        if port and try_bind(port, socket.SOCK_DGRAM, socket.IPPROTO_UDP):
2036            return port
2037
2038
2039def ask(question, auto=False):
2040    """
2041    Raw input with a prompt that emulates logging.
2042
2043    @param question: Question to be asked
2044    @param auto: Whether to return "y" instead of asking the question
2045    """
2046    if auto:
2047        logging.info("%s (y/n) y", question)
2048        return "y"
2049    return raw_input("%s INFO | %s (y/n) " %
2050                     (time.strftime("%H:%M:%S", time.localtime()), question))
2051
2052
2053def rdmsr(address, cpu=0):
2054    """
2055    Reads an x86 MSR from the specified CPU, returns as long integer.
2056    """
2057    with open('/dev/cpu/%s/msr' % cpu, 'r', 0) as fd:
2058        fd.seek(address)
2059        return struct.unpack('=Q', fd.read(8))[0]
2060
2061
2062def wait_for_value(func,
2063                   expected_value=None,
2064                   min_threshold=None,
2065                   max_threshold=None,
2066                   timeout_sec=10):
2067    """
2068    Returns the value of func().  If |expected_value|, |min_threshold|, and
2069    |max_threshold| are not set, returns immediately.
2070
2071    If |expected_value| is set, polls the return value until |expected_value| is
2072    reached, and returns that value.
2073
2074    If either |max_threshold| or |min_threshold| is set, this function will
2075    will repeatedly call func() until the return value reaches or exceeds one of
2076    these thresholds.
2077
2078    Polling will stop after |timeout_sec| regardless of these thresholds.
2079
2080    @param func: function whose return value is to be waited on.
2081    @param expected_value: wait for func to return this value.
2082    @param min_threshold: wait for func value to reach or fall below this value.
2083    @param max_threshold: wait for func value to reach or rise above this value.
2084    @param timeout_sec: Number of seconds to wait before giving up and
2085                        returning whatever value func() last returned.
2086
2087    Return value:
2088        The most recent return value of func().
2089    """
2090    value = None
2091    start_time_sec = time.time()
2092    while True:
2093        value = func()
2094        if (expected_value is None and \
2095            min_threshold is None and \
2096            max_threshold is None) or \
2097           (expected_value is not None and value == expected_value) or \
2098           (min_threshold is not None and value <= min_threshold) or \
2099           (max_threshold is not None and value >= max_threshold):
2100            break
2101
2102        if time.time() - start_time_sec >= timeout_sec:
2103            break
2104        time.sleep(0.1)
2105
2106    return value
2107
2108
2109def wait_for_value_changed(func,
2110                           old_value=None,
2111                           timeout_sec=10):
2112    """
2113    Returns the value of func().
2114
2115    The function polls the return value until it is different from |old_value|,
2116    and returns that value.
2117
2118    Polling will stop after |timeout_sec|.
2119
2120    @param func: function whose return value is to be waited on.
2121    @param old_value: wait for func to return a value different from this.
2122    @param timeout_sec: Number of seconds to wait before giving up and
2123                        returning whatever value func() last returned.
2124
2125    @returns The most recent return value of func().
2126    """
2127    value = None
2128    start_time_sec = time.time()
2129    while True:
2130        value = func()
2131        if value != old_value:
2132            break
2133
2134        if time.time() - start_time_sec >= timeout_sec:
2135            break
2136        time.sleep(0.1)
2137
2138    return value
2139
2140
2141def restart_job(name):
2142    """
2143    Restarts an upstart job if it's running.
2144    If it's not running, start it.
2145    """
2146
2147    if system_output('status %s' % name).find('start/running') != -1:
2148        system_output('restart %s' % name)
2149    else:
2150        system_output('start %s' % name)
2151
2152