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