blob: 8a34ef17a02856ded4fcd3be9dd341718f8eb5d3 [file] [log] [blame]
mbligh63073c92008-03-31 16:49:32 +00001#
2# Copyright 2008 Google Inc. Released under the GPL v2
3
mbligh849a0f62008-08-28 20:12:19 +00004import os, pickle, random, re, resource, select, shutil, signal, StringIO
mblighb2896192009-07-11 00:12:37 +00005import socket, struct, subprocess, sys, time, textwrap, urlparse
mbligh25284cd2009-06-08 16:17:24 +00006import warnings, smtplib, logging, urllib2
lmrfed221f2010-02-04 03:08:30 +00007try:
8 import hashlib
9except ImportError:
10 import md5, sha
mbligh999fb132010-04-23 17:22:03 +000011from autotest_lib.client.common_lib import error, logging_manager
mbligh81edd792008-08-26 16:54:02 +000012
mbligh849a0f62008-08-28 20:12:19 +000013def deprecated(func):
14 """This is a decorator which can be used to mark functions as deprecated.
15 It will result in a warning being emmitted when the function is used."""
16 def new_func(*args, **dargs):
17 warnings.warn("Call to deprecated function %s." % func.__name__,
18 category=DeprecationWarning)
19 return func(*args, **dargs)
20 new_func.__name__ = func.__name__
21 new_func.__doc__ = func.__doc__
22 new_func.__dict__.update(func.__dict__)
23 return new_func
24
25
showard108d73e2009-06-22 18:14:41 +000026class _NullStream(object):
27 def write(self, data):
28 pass
29
30
31 def flush(self):
32 pass
33
34
35TEE_TO_LOGS = object()
36_the_null_stream = _NullStream()
37
showardb45a4662009-07-15 14:27:56 +000038DEFAULT_STDOUT_LEVEL = logging.DEBUG
39DEFAULT_STDERR_LEVEL = logging.ERROR
40
showard39986a62009-12-10 21:41:53 +000041# prefixes for logging stdout/stderr of commands
42STDOUT_PREFIX = '[stdout] '
43STDERR_PREFIX = '[stderr] '
44
45
46def get_stream_tee_file(stream, level, prefix=''):
showard108d73e2009-06-22 18:14:41 +000047 if stream is None:
48 return _the_null_stream
49 if stream is TEE_TO_LOGS:
showard39986a62009-12-10 21:41:53 +000050 return logging_manager.LoggingFile(level=level, prefix=prefix)
showard108d73e2009-06-22 18:14:41 +000051 return stream
52
53
mbligh849a0f62008-08-28 20:12:19 +000054class BgJob(object):
showard170873e2009-01-07 00:22:26 +000055 def __init__(self, command, stdout_tee=None, stderr_tee=None, verbose=True,
showardb45a4662009-07-15 14:27:56 +000056 stdin=None, stderr_level=DEFAULT_STDERR_LEVEL):
mbligh849a0f62008-08-28 20:12:19 +000057 self.command = command
showard39986a62009-12-10 21:41:53 +000058 self.stdout_tee = get_stream_tee_file(stdout_tee, DEFAULT_STDOUT_LEVEL,
59 prefix=STDOUT_PREFIX)
60 self.stderr_tee = get_stream_tee_file(stderr_tee, stderr_level,
61 prefix=STDERR_PREFIX)
mbligh849a0f62008-08-28 20:12:19 +000062 self.result = CmdResult(command)
jadmanski093a0682009-10-13 14:55:43 +000063
64 # allow for easy stdin input by string, we'll let subprocess create
65 # a pipe for stdin input and we'll write to it in the wait loop
66 if isinstance(stdin, basestring):
67 self.string_stdin = stdin
68 stdin = subprocess.PIPE
69 else:
70 self.string_stdin = None
71
mblighbd96b452008-09-03 23:14:27 +000072 if verbose:
showardb18134f2009-03-20 20:52:18 +000073 logging.debug("Running '%s'" % command)
mbligh849a0f62008-08-28 20:12:19 +000074 self.sp = subprocess.Popen(command, stdout=subprocess.PIPE,
75 stderr=subprocess.PIPE,
76 preexec_fn=self._reset_sigpipe, shell=True,
showard170873e2009-01-07 00:22:26 +000077 executable="/bin/bash",
78 stdin=stdin)
mbligh849a0f62008-08-28 20:12:19 +000079
80
81 def output_prepare(self, stdout_file=None, stderr_file=None):
82 self.stdout_file = stdout_file
83 self.stderr_file = stderr_file
84
mbligh45ffc432008-12-09 23:35:17 +000085
mbligh849a0f62008-08-28 20:12:19 +000086 def process_output(self, stdout=True, final_read=False):
87 """output_prepare must be called prior to calling this"""
88 if stdout:
89 pipe, buf, tee = self.sp.stdout, self.stdout_file, self.stdout_tee
90 else:
91 pipe, buf, tee = self.sp.stderr, self.stderr_file, self.stderr_tee
92
93 if final_read:
94 # read in all the data we can from pipe and then stop
95 data = []
96 while select.select([pipe], [], [], 0)[0]:
97 data.append(os.read(pipe.fileno(), 1024))
98 if len(data[-1]) == 0:
99 break
100 data = "".join(data)
101 else:
102 # perform a single read
103 data = os.read(pipe.fileno(), 1024)
104 buf.write(data)
showard108d73e2009-06-22 18:14:41 +0000105 tee.write(data)
mbligh849a0f62008-08-28 20:12:19 +0000106
107
108 def cleanup(self):
showard108d73e2009-06-22 18:14:41 +0000109 self.stdout_tee.flush()
110 self.stderr_tee.flush()
mbligh849a0f62008-08-28 20:12:19 +0000111 self.sp.stdout.close()
112 self.sp.stderr.close()
113 self.result.stdout = self.stdout_file.getvalue()
114 self.result.stderr = self.stderr_file.getvalue()
115
116
117 def _reset_sigpipe(self):
118 signal.signal(signal.SIGPIPE, signal.SIG_DFL)
119
mbligh81edd792008-08-26 16:54:02 +0000120
121def ip_to_long(ip):
122 # !L is a long in network byte order
123 return struct.unpack('!L', socket.inet_aton(ip))[0]
124
125
126def long_to_ip(number):
127 # See above comment.
128 return socket.inet_ntoa(struct.pack('!L', number))
129
130
131def create_subnet_mask(bits):
mbligh81edd792008-08-26 16:54:02 +0000132 return (1 << 32) - (1 << 32-bits)
133
134
135def format_ip_with_mask(ip, mask_bits):
136 masked_ip = ip_to_long(ip) & create_subnet_mask(mask_bits)
137 return "%s/%s" % (long_to_ip(masked_ip), mask_bits)
mbligh6231cd62008-02-02 19:18:33 +0000138
mblighde0d47e2008-03-28 14:37:18 +0000139
jadmanskie80d4712008-10-03 16:15:59 +0000140def normalize_hostname(alias):
141 ip = socket.gethostbyname(alias)
142 return socket.gethostbyaddr(ip)[0]
143
144
mblighd6d043c2008-09-27 21:00:45 +0000145def get_ip_local_port_range():
146 match = re.match(r'\s*(\d+)\s*(\d+)\s*$',
147 read_one_line('/proc/sys/net/ipv4/ip_local_port_range'))
148 return (int(match.group(1)), int(match.group(2)))
149
150
151def set_ip_local_port_range(lower, upper):
152 write_one_line('/proc/sys/net/ipv4/ip_local_port_range',
153 '%d %d\n' % (lower, upper))
154
mbligh315b9412008-10-01 03:34:11 +0000155
mbligh45ffc432008-12-09 23:35:17 +0000156
157def send_email(mail_from, mail_to, subject, body):
158 """
159 Sends an email via smtp
160
161 mail_from: string with email address of sender
162 mail_to: string or list with email address(es) of recipients
163 subject: string with subject of email
164 body: (multi-line) string with body of email
165 """
166 if isinstance(mail_to, str):
167 mail_to = [mail_to]
168 msg = "From: %s\nTo: %s\nSubject: %s\n\n%s" % (mail_from, ','.join(mail_to),
169 subject, body)
170 try:
171 mailer = smtplib.SMTP('localhost')
172 try:
173 mailer.sendmail(mail_from, mail_to, msg)
174 finally:
175 mailer.quit()
176 except Exception, e:
177 # Emails are non-critical, not errors, but don't raise them
178 print "Sending email failed. Reason: %s" % repr(e)
179
180
jadmanski5182e162008-05-13 21:48:16 +0000181def read_one_line(filename):
mbligh6e8840c2008-07-11 18:05:49 +0000182 return open(filename, 'r').readline().rstrip('\n')
jadmanski5182e162008-05-13 21:48:16 +0000183
184
jamesrenc3940222010-02-19 21:57:37 +0000185def read_file(filename):
186 f = open(filename)
187 try:
188 return f.read()
189 finally:
190 f.close()
191
192
mblighb9d05512008-10-18 13:53:27 +0000193def write_one_line(filename, line):
194 open_write_close(filename, line.rstrip('\n') + '\n')
195
196
197def open_write_close(filename, data):
mbligh618ac9e2008-10-06 17:14:32 +0000198 f = open(filename, 'w')
mblighb9d05512008-10-18 13:53:27 +0000199 try:
200 f.write(data)
201 finally:
202 f.close()
jadmanski5182e162008-05-13 21:48:16 +0000203
204
Eric Li6f27d4f2010-09-29 10:55:17 -0700205def matrix_to_string(matrix, header=None):
206 """
207 Return a pretty, aligned string representation of a nxm matrix.
208
209 This representation can be used to print any tabular data, such as
210 database results. It works by scanning the lengths of each element
211 in each column, and determining the format string dynamically.
212
213 @param matrix: Matrix representation (list with n rows of m elements).
214 @param header: Optional tuple with header elements to be displayed.
215 """
216 lengths = []
217 for row in matrix:
218 for column in row:
219 i = row.index(column)
220 cl = len(column)
221 try:
222 ml = lengths[i]
223 if cl > ml:
224 lengths[i] = cl
225 except IndexError:
226 lengths.append(cl)
227
228 lengths = tuple(lengths)
229 format_string = ""
230 for length in lengths:
231 format_string += "%-" + str(length) + "s "
232 format_string += "\n"
233
234 matrix_str = ""
235 if header:
236 matrix_str += format_string % header
237 for row in matrix:
238 matrix_str += format_string % tuple(row)
239
240 return matrix_str
241
242
mblighde0d47e2008-03-28 14:37:18 +0000243def read_keyval(path):
jadmanski0afbb632008-06-06 21:10:57 +0000244 """
245 Read a key-value pair format file into a dictionary, and return it.
246 Takes either a filename or directory name as input. If it's a
247 directory name, we assume you want the file to be called keyval.
248 """
249 if os.path.isdir(path):
250 path = os.path.join(path, 'keyval')
251 keyval = {}
jadmanski58962982009-04-21 19:54:34 +0000252 if os.path.exists(path):
253 for line in open(path):
254 line = re.sub('#.*', '', line).rstrip()
255 if not re.search(r'^[-\.\w]+=', line):
256 raise ValueError('Invalid format line: %s' % line)
257 key, value = line.split('=', 1)
258 if re.search('^\d+$', value):
259 value = int(value)
260 elif re.search('^(\d+\.)?\d+$', value):
261 value = float(value)
262 keyval[key] = value
jadmanski0afbb632008-06-06 21:10:57 +0000263 return keyval
mblighde0d47e2008-03-28 14:37:18 +0000264
265
jadmanskicc549172008-05-21 18:11:51 +0000266def write_keyval(path, dictionary, type_tag=None):
jadmanski0afbb632008-06-06 21:10:57 +0000267 """
268 Write a key-value pair format file out to a file. This uses append
269 mode to open the file, so existing text will not be overwritten or
270 reparsed.
jadmanskicc549172008-05-21 18:11:51 +0000271
jadmanski0afbb632008-06-06 21:10:57 +0000272 If type_tag is None, then the key must be composed of alphanumeric
273 characters (or dashes+underscores). However, if type-tag is not
274 null then the keys must also have "{type_tag}" as a suffix. At
275 the moment the only valid values of type_tag are "attr" and "perf".
276 """
277 if os.path.isdir(path):
278 path = os.path.join(path, 'keyval')
279 keyval = open(path, 'a')
jadmanskicc549172008-05-21 18:11:51 +0000280
jadmanski0afbb632008-06-06 21:10:57 +0000281 if type_tag is None:
mbligh97227ea2009-03-11 17:09:50 +0000282 key_regex = re.compile(r'^[-\.\w]+$')
jadmanski0afbb632008-06-06 21:10:57 +0000283 else:
284 if type_tag not in ('attr', 'perf'):
285 raise ValueError('Invalid type tag: %s' % type_tag)
286 escaped_tag = re.escape(type_tag)
mbligh97227ea2009-03-11 17:09:50 +0000287 key_regex = re.compile(r'^[-\.\w]+\{%s\}$' % escaped_tag)
jadmanski0afbb632008-06-06 21:10:57 +0000288 try:
mbligh6955e232009-07-11 00:58:47 +0000289 for key in sorted(dictionary.keys()):
jadmanski0afbb632008-06-06 21:10:57 +0000290 if not key_regex.search(key):
291 raise ValueError('Invalid key: %s' % key)
mbligh6955e232009-07-11 00:58:47 +0000292 keyval.write('%s=%s\n' % (key, dictionary[key]))
jadmanski0afbb632008-06-06 21:10:57 +0000293 finally:
294 keyval.close()
mbligh6231cd62008-02-02 19:18:33 +0000295
296
297def is_url(path):
jadmanski0afbb632008-06-06 21:10:57 +0000298 """Return true if path looks like a URL"""
299 # for now, just handle http and ftp
300 url_parts = urlparse.urlparse(path)
301 return (url_parts[0] in ('http', 'ftp'))
mbligh6231cd62008-02-02 19:18:33 +0000302
303
mblighb2896192009-07-11 00:12:37 +0000304def urlopen(url, data=None, timeout=5):
305 """Wrapper to urllib2.urlopen with timeout addition."""
mbligh02ff2d52008-06-03 15:00:21 +0000306
jadmanski0afbb632008-06-06 21:10:57 +0000307 # Save old timeout
308 old_timeout = socket.getdefaulttimeout()
309 socket.setdefaulttimeout(timeout)
310 try:
mblighb2896192009-07-11 00:12:37 +0000311 return urllib2.urlopen(url, data=data)
jadmanski0afbb632008-06-06 21:10:57 +0000312 finally:
313 socket.setdefaulttimeout(old_timeout)
mbligh02ff2d52008-06-03 15:00:21 +0000314
315
mblighb2896192009-07-11 00:12:37 +0000316def urlretrieve(url, filename, data=None, timeout=300):
317 """Retrieve a file from given url."""
318 logging.debug('Fetching %s -> %s', url, filename)
319
320 src_file = urlopen(url, data=data, timeout=timeout)
jadmanski0afbb632008-06-06 21:10:57 +0000321 try:
mblighb2896192009-07-11 00:12:37 +0000322 dest_file = open(filename, 'wb')
323 try:
324 shutil.copyfileobj(src_file, dest_file)
325 finally:
326 dest_file.close()
jadmanski0afbb632008-06-06 21:10:57 +0000327 finally:
mblighb2896192009-07-11 00:12:37 +0000328 src_file.close()
jadmanski0afbb632008-06-06 21:10:57 +0000329
mbligh02ff2d52008-06-03 15:00:21 +0000330
lmrfed221f2010-02-04 03:08:30 +0000331def hash(type, input=None):
332 """
333 Returns an hash object of type md5 or sha1. This function is implemented in
334 order to encapsulate hash objects in a way that is compatible with python
335 2.4 and python 2.6 without warnings.
336
337 Note that even though python 2.6 hashlib supports hash types other than
338 md5 and sha1, we are artificially limiting the input values in order to
339 make the function to behave exactly the same among both python
340 implementations.
341
342 @param input: Optional input string that will be used to update the hash.
343 """
344 if type not in ['md5', 'sha1']:
345 raise ValueError("Unsupported hash type: %s" % type)
346
347 try:
348 hash = hashlib.new(type)
349 except NameError:
350 if type == 'md5':
351 hash = md5.new()
352 elif type == 'sha1':
353 hash = sha.new()
354
355 if input:
356 hash.update(input)
357
358 return hash
359
360
mbligh6231cd62008-02-02 19:18:33 +0000361def get_file(src, dest, permissions=None):
jadmanski0afbb632008-06-06 21:10:57 +0000362 """Get a file from src, which can be local or a remote URL"""
mbligh25284cd2009-06-08 16:17:24 +0000363 if src == dest:
jadmanski0afbb632008-06-06 21:10:57 +0000364 return
mbligh25284cd2009-06-08 16:17:24 +0000365
366 if is_url(src):
mblighb2896192009-07-11 00:12:37 +0000367 urlretrieve(src, dest)
jadmanski0afbb632008-06-06 21:10:57 +0000368 else:
369 shutil.copyfile(src, dest)
mbligh25284cd2009-06-08 16:17:24 +0000370
jadmanski0afbb632008-06-06 21:10:57 +0000371 if permissions:
372 os.chmod(dest, permissions)
373 return dest
mbligh6231cd62008-02-02 19:18:33 +0000374
375
376def unmap_url(srcdir, src, destdir='.'):
jadmanski0afbb632008-06-06 21:10:57 +0000377 """
378 Receives either a path to a local file or a URL.
379 returns either the path to the local file, or the fetched URL
mbligh6231cd62008-02-02 19:18:33 +0000380
jadmanski0afbb632008-06-06 21:10:57 +0000381 unmap_url('/usr/src', 'foo.tar', '/tmp')
382 = '/usr/src/foo.tar'
383 unmap_url('/usr/src', 'http://site/file', '/tmp')
384 = '/tmp/file'
385 (after retrieving it)
386 """
387 if is_url(src):
388 url_parts = urlparse.urlparse(src)
389 filename = os.path.basename(url_parts[2])
390 dest = os.path.join(destdir, filename)
391 return get_file(src, dest)
392 else:
393 return os.path.join(srcdir, src)
mbligh6231cd62008-02-02 19:18:33 +0000394
395
396def update_version(srcdir, preserve_srcdir, new_version, install,
jadmanski0afbb632008-06-06 21:10:57 +0000397 *args, **dargs):
398 """
399 Make sure srcdir is version new_version
mbligh6231cd62008-02-02 19:18:33 +0000400
jadmanski0afbb632008-06-06 21:10:57 +0000401 If not, delete it and install() the new version.
mbligh6231cd62008-02-02 19:18:33 +0000402
jadmanski0afbb632008-06-06 21:10:57 +0000403 In the preserve_srcdir case, we just check it's up to date,
404 and if not, we rerun install, without removing srcdir
405 """
406 versionfile = os.path.join(srcdir, '.version')
407 install_needed = True
mbligh6231cd62008-02-02 19:18:33 +0000408
jadmanski0afbb632008-06-06 21:10:57 +0000409 if os.path.exists(versionfile):
410 old_version = pickle.load(open(versionfile))
411 if old_version == new_version:
412 install_needed = False
mbligh6231cd62008-02-02 19:18:33 +0000413
jadmanski0afbb632008-06-06 21:10:57 +0000414 if install_needed:
415 if not preserve_srcdir and os.path.exists(srcdir):
416 shutil.rmtree(srcdir)
417 install(*args, **dargs)
418 if os.path.exists(srcdir):
419 pickle.dump(new_version, open(versionfile, 'w'))
mbligh462c0152008-03-13 15:37:10 +0000420
421
showardb45a4662009-07-15 14:27:56 +0000422def get_stderr_level(stderr_is_expected):
423 if stderr_is_expected:
424 return DEFAULT_STDOUT_LEVEL
425 return DEFAULT_STDERR_LEVEL
426
427
mbligh63073c92008-03-31 16:49:32 +0000428def run(command, timeout=None, ignore_status=False,
showardb45a4662009-07-15 14:27:56 +0000429 stdout_tee=None, stderr_tee=None, verbose=True, stdin=None,
mblighc823e8d2009-10-02 00:01:35 +0000430 stderr_is_expected=None, args=()):
jadmanski0afbb632008-06-06 21:10:57 +0000431 """
432 Run a command on the host.
mbligh63073c92008-03-31 16:49:32 +0000433
mblighc823e8d2009-10-02 00:01:35 +0000434 @param command: the command line string.
435 @param timeout: time limit in seconds before attempting to kill the
436 running process. The run() function will take a few seconds
437 longer than 'timeout' to complete if it has to kill the process.
438 @param ignore_status: do not raise an exception, no matter what the exit
439 code of the command is.
440 @param stdout_tee: optional file-like object to which stdout data
441 will be written as it is generated (data will still be stored
442 in result.stdout).
443 @param stderr_tee: likewise for stderr.
444 @param verbose: if True, log the command being run.
jadmanski093a0682009-10-13 14:55:43 +0000445 @param stdin: stdin to pass to the executed process (can be a file
446 descriptor, a file object of a real file or a string).
mblighc823e8d2009-10-02 00:01:35 +0000447 @param args: sequence of strings of arguments to be given to the command
448 inside " quotes after they have been escaped for that; each
449 element in the sequence will be given as a separate command
450 argument
mbligh63073c92008-03-31 16:49:32 +0000451
mblighc823e8d2009-10-02 00:01:35 +0000452 @return a CmdResult object
mbligh63073c92008-03-31 16:49:32 +0000453
mblighc823e8d2009-10-02 00:01:35 +0000454 @raise CmdError: the exit code of the command execution was not 0
jadmanski0afbb632008-06-06 21:10:57 +0000455 """
mbligh2c7f7d62009-12-19 05:24:04 +0000456 if isinstance(args, basestring):
457 raise TypeError('Got a string for the "args" keyword argument, '
458 'need a sequence.')
459
mblighc823e8d2009-10-02 00:01:35 +0000460 for arg in args:
461 command += ' "%s"' % sh_escape(arg)
showardb45a4662009-07-15 14:27:56 +0000462 if stderr_is_expected is None:
463 stderr_is_expected = ignore_status
mblighc823e8d2009-10-02 00:01:35 +0000464
showard170873e2009-01-07 00:22:26 +0000465 bg_job = join_bg_jobs(
showardb45a4662009-07-15 14:27:56 +0000466 (BgJob(command, stdout_tee, stderr_tee, verbose, stdin=stdin,
467 stderr_level=get_stderr_level(stderr_is_expected)),),
showard170873e2009-01-07 00:22:26 +0000468 timeout)[0]
mbligh849a0f62008-08-28 20:12:19 +0000469 if not ignore_status and bg_job.result.exit_status:
jadmanski9c1098b2008-09-02 14:18:48 +0000470 raise error.CmdError(command, bg_job.result,
mbligh849a0f62008-08-28 20:12:19 +0000471 "Command returned non-zero exit status")
mbligh63073c92008-03-31 16:49:32 +0000472
mbligh849a0f62008-08-28 20:12:19 +0000473 return bg_job.result
mbligh63073c92008-03-31 16:49:32 +0000474
mbligh45ffc432008-12-09 23:35:17 +0000475
mbligha5630a52008-09-03 22:09:50 +0000476def run_parallel(commands, timeout=None, ignore_status=False,
477 stdout_tee=None, stderr_tee=None):
jadmanski093a0682009-10-13 14:55:43 +0000478 """
479 Behaves the same as run() with the following exceptions:
mbligha5630a52008-09-03 22:09:50 +0000480
481 - commands is a list of commands to run in parallel.
482 - ignore_status toggles whether or not an exception should be raised
483 on any error.
484
jadmanski093a0682009-10-13 14:55:43 +0000485 @return: a list of CmdResult objects
mbligha5630a52008-09-03 22:09:50 +0000486 """
487 bg_jobs = []
488 for command in commands:
showardb45a4662009-07-15 14:27:56 +0000489 bg_jobs.append(BgJob(command, stdout_tee, stderr_tee,
490 stderr_level=get_stderr_level(ignore_status)))
mbligha5630a52008-09-03 22:09:50 +0000491
492 # Updates objects in bg_jobs list with their process information
493 join_bg_jobs(bg_jobs, timeout)
494
495 for bg_job in bg_jobs:
496 if not ignore_status and bg_job.result.exit_status:
497 raise error.CmdError(command, bg_job.result,
498 "Command returned non-zero exit status")
499
500 return [bg_job.result for bg_job in bg_jobs]
501
502
mbligh849a0f62008-08-28 20:12:19 +0000503@deprecated
mbligh63073c92008-03-31 16:49:32 +0000504def run_bg(command):
mbligh849a0f62008-08-28 20:12:19 +0000505 """Function deprecated. Please use BgJob class instead."""
506 bg_job = BgJob(command)
507 return bg_job.sp, bg_job.result
mbligh63073c92008-03-31 16:49:32 +0000508
509
mbligh849a0f62008-08-28 20:12:19 +0000510def join_bg_jobs(bg_jobs, timeout=None):
mbligha5630a52008-09-03 22:09:50 +0000511 """Joins the bg_jobs with the current thread.
512
513 Returns the same list of bg_jobs objects that was passed in.
514 """
mblighae69f262009-04-17 20:14:56 +0000515 ret, timeout_error = 0, False
mbligh849a0f62008-08-28 20:12:19 +0000516 for bg_job in bg_jobs:
517 bg_job.output_prepare(StringIO.StringIO(), StringIO.StringIO())
mbligh63073c92008-03-31 16:49:32 +0000518
jadmanski0afbb632008-06-06 21:10:57 +0000519 try:
520 # We are holding ends to stdin, stdout pipes
521 # hence we need to be sure to close those fds no mater what
522 start_time = time.time()
mbligh849a0f62008-08-28 20:12:19 +0000523 timeout_error = _wait_for_commands(bg_jobs, start_time, timeout)
524
525 for bg_job in bg_jobs:
526 # Process stdout and stderr
527 bg_job.process_output(stdout=True,final_read=True)
528 bg_job.process_output(stdout=False,final_read=True)
jadmanski0afbb632008-06-06 21:10:57 +0000529 finally:
530 # close our ends of the pipes to the sp no matter what
mbligh849a0f62008-08-28 20:12:19 +0000531 for bg_job in bg_jobs:
532 bg_job.cleanup()
mbligh63073c92008-03-31 16:49:32 +0000533
mbligh849a0f62008-08-28 20:12:19 +0000534 if timeout_error:
535 # TODO: This needs to be fixed to better represent what happens when
536 # running in parallel. However this is backwards compatable, so it will
537 # do for the time being.
538 raise error.CmdError(bg_jobs[0].command, bg_jobs[0].result,
539 "Command(s) did not complete within %d seconds"
540 % timeout)
mbligh63073c92008-03-31 16:49:32 +0000541
mbligh63073c92008-03-31 16:49:32 +0000542
mbligh849a0f62008-08-28 20:12:19 +0000543 return bg_jobs
mbligh63073c92008-03-31 16:49:32 +0000544
mbligh849a0f62008-08-28 20:12:19 +0000545
546def _wait_for_commands(bg_jobs, start_time, timeout):
547 # This returns True if it must return due to a timeout, otherwise False.
548
mblighf0b4a0a2008-09-03 20:46:16 +0000549 # To check for processes which terminate without producing any output
550 # a 1 second timeout is used in select.
551 SELECT_TIMEOUT = 1
552
jadmanski093a0682009-10-13 14:55:43 +0000553 read_list = []
554 write_list = []
mbligh849a0f62008-08-28 20:12:19 +0000555 reverse_dict = {}
jadmanski093a0682009-10-13 14:55:43 +0000556
mbligh849a0f62008-08-28 20:12:19 +0000557 for bg_job in bg_jobs:
jadmanski093a0682009-10-13 14:55:43 +0000558 read_list.append(bg_job.sp.stdout)
559 read_list.append(bg_job.sp.stderr)
560 reverse_dict[bg_job.sp.stdout] = (bg_job, True)
561 reverse_dict[bg_job.sp.stderr] = (bg_job, False)
jamesrenf0f9dae2010-02-26 02:21:14 +0000562 if bg_job.string_stdin is not None:
jadmanski093a0682009-10-13 14:55:43 +0000563 write_list.append(bg_job.sp.stdin)
564 reverse_dict[bg_job.sp.stdin] = bg_job
mbligh849a0f62008-08-28 20:12:19 +0000565
jadmanski0afbb632008-06-06 21:10:57 +0000566 if timeout:
567 stop_time = start_time + timeout
568 time_left = stop_time - time.time()
569 else:
570 time_left = None # so that select never times out
jadmanski093a0682009-10-13 14:55:43 +0000571
jadmanski0afbb632008-06-06 21:10:57 +0000572 while not timeout or time_left > 0:
jadmanski093a0682009-10-13 14:55:43 +0000573 # select will return when we may write to stdin or when there is
574 # stdout/stderr output we can read (including when it is
jadmanski0afbb632008-06-06 21:10:57 +0000575 # EOF, that is the process has terminated).
jadmanski093a0682009-10-13 14:55:43 +0000576 read_ready, write_ready, _ = select.select(read_list, write_list, [],
577 SELECT_TIMEOUT)
mbligh849a0f62008-08-28 20:12:19 +0000578
jadmanski0afbb632008-06-06 21:10:57 +0000579 # os.read() has to be used instead of
580 # subproc.stdout.read() which will otherwise block
jadmanski093a0682009-10-13 14:55:43 +0000581 for file_obj in read_ready:
582 bg_job, is_stdout = reverse_dict[file_obj]
583 bg_job.process_output(is_stdout)
mbligh63073c92008-03-31 16:49:32 +0000584
jadmanski093a0682009-10-13 14:55:43 +0000585 for file_obj in write_ready:
586 # we can write PIPE_BUF bytes without blocking
587 # POSIX requires PIPE_BUF is >= 512
588 bg_job = reverse_dict[file_obj]
589 file_obj.write(bg_job.string_stdin[:512])
590 bg_job.string_stdin = bg_job.string_stdin[512:]
591 # no more input data, close stdin, remove it from the select set
592 if not bg_job.string_stdin:
593 file_obj.close()
594 write_list.remove(file_obj)
595 del reverse_dict[file_obj]
596
597 all_jobs_finished = True
598 for bg_job in bg_jobs:
599 if bg_job.result.exit_status is not None:
600 continue
601
mbligh849a0f62008-08-28 20:12:19 +0000602 bg_job.result.exit_status = bg_job.sp.poll()
jadmanski093a0682009-10-13 14:55:43 +0000603 if bg_job.result.exit_status is not None:
604 # process exited, remove its stdout/stdin from the select set
jadmanskidf432e62010-06-11 14:32:28 +0000605 bg_job.result.duration = time.time() - start_time
jadmanski093a0682009-10-13 14:55:43 +0000606 read_list.remove(bg_job.sp.stdout)
607 read_list.remove(bg_job.sp.stderr)
608 del reverse_dict[bg_job.sp.stdout]
609 del reverse_dict[bg_job.sp.stderr]
610 else:
611 all_jobs_finished = False
612
613 if all_jobs_finished:
614 return False
mbligh8ea61e22008-05-09 18:09:37 +0000615
jadmanski0afbb632008-06-06 21:10:57 +0000616 if timeout:
617 time_left = stop_time - time.time()
mbligh63073c92008-03-31 16:49:32 +0000618
mbligh849a0f62008-08-28 20:12:19 +0000619 # Kill all processes which did not complete prior to timeout
jadmanski093a0682009-10-13 14:55:43 +0000620 for bg_job in bg_jobs:
621 if bg_job.result.exit_status is not None:
622 continue
623
624 logging.warn('run process timeout (%s) fired on: %s', timeout,
625 bg_job.command)
mbligh849a0f62008-08-28 20:12:19 +0000626 nuke_subprocess(bg_job.sp)
mbligh095dc642008-10-01 03:41:35 +0000627 bg_job.result.exit_status = bg_job.sp.poll()
jadmanskidf432e62010-06-11 14:32:28 +0000628 bg_job.result.duration = time.time() - start_time
mbligh8ea61e22008-05-09 18:09:37 +0000629
mbligh849a0f62008-08-28 20:12:19 +0000630 return True
mbligh63073c92008-03-31 16:49:32 +0000631
632
showard549afad2009-08-20 23:33:36 +0000633def pid_is_alive(pid):
634 """
635 True if process pid exists and is not yet stuck in Zombie state.
636 Zombies are impossible to move between cgroups, etc.
637 pid can be integer, or text of integer.
638 """
639 path = '/proc/%s/stat' % pid
640
641 try:
642 stat = read_one_line(path)
643 except IOError:
644 if not os.path.exists(path):
645 # file went away
646 return False
647 raise
648
649 return stat.split()[2] != 'Z'
650
651
652def signal_pid(pid, sig):
653 """
654 Sends a signal to a process id. Returns True if the process terminated
655 successfully, False otherwise.
656 """
657 try:
658 os.kill(pid, sig)
659 except OSError:
660 # The process may have died before we could kill it.
661 pass
662
663 for i in range(5):
664 if not pid_is_alive(pid):
665 return True
666 time.sleep(1)
667
668 # The process is still alive
669 return False
670
671
mbligh63073c92008-03-31 16:49:32 +0000672def nuke_subprocess(subproc):
jadmanski09f92032008-09-17 14:05:27 +0000673 # check if the subprocess is still alive, first
674 if subproc.poll() is not None:
675 return subproc.poll()
676
jadmanski0afbb632008-06-06 21:10:57 +0000677 # the process has not terminated within timeout,
678 # kill it via an escalating series of signals.
679 signal_queue = [signal.SIGTERM, signal.SIGKILL]
680 for sig in signal_queue:
showard549afad2009-08-20 23:33:36 +0000681 signal_pid(subproc.pid, sig)
682 if subproc.poll() is not None:
683 return subproc.poll()
mbligh63073c92008-03-31 16:49:32 +0000684
685
showard786da9a2009-10-12 20:31:20 +0000686def nuke_pid(pid, signal_queue=(signal.SIGTERM, signal.SIGKILL)):
jadmanski0afbb632008-06-06 21:10:57 +0000687 # the process has not terminated within timeout,
688 # kill it via an escalating series of signals.
jadmanski0afbb632008-06-06 21:10:57 +0000689 for sig in signal_queue:
showard549afad2009-08-20 23:33:36 +0000690 if signal_pid(pid, sig):
691 return
mbligh63073c92008-03-31 16:49:32 +0000692
showard549afad2009-08-20 23:33:36 +0000693 # no signal successfully terminated the process
694 raise error.AutoservRunError('Could not kill %d' % pid, None)
mbligh298ed592009-06-15 21:52:57 +0000695
696
mbligh63073c92008-03-31 16:49:32 +0000697def system(command, timeout=None, ignore_status=False):
mbligh104a5382010-02-02 18:15:39 +0000698 """
699 Run a command
700
701 @param timeout: timeout in seconds
702 @param ignore_status: if ignore_status=False, throw an exception if the
703 command's exit code is non-zero
704 if ignore_stauts=True, return the exit code.
lmrfed221f2010-02-04 03:08:30 +0000705
mbligh104a5382010-02-02 18:15:39 +0000706 @return exit status of command
707 (note, this will always be zero unless ignore_status=True)
708 """
mblighf8dffb12008-10-29 16:45:26 +0000709 return run(command, timeout=timeout, ignore_status=ignore_status,
showard108d73e2009-06-22 18:14:41 +0000710 stdout_tee=TEE_TO_LOGS, stderr_tee=TEE_TO_LOGS).exit_status
mbligh63073c92008-03-31 16:49:32 +0000711
712
mbligha5630a52008-09-03 22:09:50 +0000713def system_parallel(commands, timeout=None, ignore_status=False):
714 """This function returns a list of exit statuses for the respective
715 list of commands."""
716 return [bg_jobs.exit_status for bg_jobs in
mblighf8dffb12008-10-29 16:45:26 +0000717 run_parallel(commands, timeout=timeout, ignore_status=ignore_status,
showard108d73e2009-06-22 18:14:41 +0000718 stdout_tee=TEE_TO_LOGS, stderr_tee=TEE_TO_LOGS)]
mbligh849a0f62008-08-28 20:12:19 +0000719
720
mbligh8ea61e22008-05-09 18:09:37 +0000721def system_output(command, timeout=None, ignore_status=False,
mblighc823e8d2009-10-02 00:01:35 +0000722 retain_output=False, args=()):
723 """
724 Run a command and return the stdout output.
725
726 @param command: command string to execute.
727 @param timeout: time limit in seconds before attempting to kill the
728 running process. The function will take a few seconds longer
729 than 'timeout' to complete if it has to kill the process.
730 @param ignore_status: do not raise an exception, no matter what the exit
731 code of the command is.
732 @param retain_output: set to True to make stdout/stderr of the command
733 output to be also sent to the logging system
734 @param args: sequence of strings of arguments to be given to the command
735 inside " quotes after they have been escaped for that; each
736 element in the sequence will be given as a separate command
737 argument
738
739 @return a string with the stdout output of the command.
740 """
jadmanski0afbb632008-06-06 21:10:57 +0000741 if retain_output:
mblighf8dffb12008-10-29 16:45:26 +0000742 out = run(command, timeout=timeout, ignore_status=ignore_status,
mblighc823e8d2009-10-02 00:01:35 +0000743 stdout_tee=TEE_TO_LOGS, stderr_tee=TEE_TO_LOGS,
744 args=args).stdout
jadmanski0afbb632008-06-06 21:10:57 +0000745 else:
mblighc823e8d2009-10-02 00:01:35 +0000746 out = run(command, timeout=timeout, ignore_status=ignore_status,
747 args=args).stdout
748 if out[-1:] == '\n':
749 out = out[:-1]
jadmanski0afbb632008-06-06 21:10:57 +0000750 return out
mbligh63073c92008-03-31 16:49:32 +0000751
mbligh849a0f62008-08-28 20:12:19 +0000752
mbligha5630a52008-09-03 22:09:50 +0000753def system_output_parallel(commands, timeout=None, ignore_status=False,
754 retain_output=False):
755 if retain_output:
showard108d73e2009-06-22 18:14:41 +0000756 out = [bg_job.stdout for bg_job
757 in run_parallel(commands, timeout=timeout,
758 ignore_status=ignore_status,
759 stdout_tee=TEE_TO_LOGS, stderr_tee=TEE_TO_LOGS)]
mbligha5630a52008-09-03 22:09:50 +0000760 else:
mblighf8dffb12008-10-29 16:45:26 +0000761 out = [bg_job.stdout for bg_job in run_parallel(commands,
762 timeout=timeout, ignore_status=ignore_status)]
mbligha5630a52008-09-03 22:09:50 +0000763 for x in out:
764 if out[-1:] == '\n': out = out[:-1]
765 return out
766
767
mbligh98467952008-11-19 00:25:45 +0000768def strip_unicode(input):
769 if type(input) == list:
770 return [strip_unicode(i) for i in input]
771 elif type(input) == dict:
772 output = {}
773 for key in input.keys():
774 output[str(key)] = strip_unicode(input[key])
775 return output
776 elif type(input) == unicode:
777 return str(input)
778 else:
779 return input
780
781
mbligha5630a52008-09-03 22:09:50 +0000782def get_cpu_percentage(function, *args, **dargs):
783 """Returns a tuple containing the CPU% and return value from function call.
784
785 This function calculates the usage time by taking the difference of
786 the user and system times both before and after the function call.
787 """
788 child_pre = resource.getrusage(resource.RUSAGE_CHILDREN)
789 self_pre = resource.getrusage(resource.RUSAGE_SELF)
790 start = time.time()
791 to_return = function(*args, **dargs)
792 elapsed = time.time() - start
793 self_post = resource.getrusage(resource.RUSAGE_SELF)
794 child_post = resource.getrusage(resource.RUSAGE_CHILDREN)
795
796 # Calculate CPU Percentage
797 s_user, s_system = [a - b for a, b in zip(self_post, self_pre)[:2]]
798 c_user, c_system = [a - b for a, b in zip(child_post, child_pre)[:2]]
799 cpu_percent = (s_user + c_user + s_system + c_system) / elapsed
800
801 return cpu_percent, to_return
802
803
mblighc5ddfd12008-08-04 17:15:00 +0000804def get_arch(run_function=run):
805 """
806 Get the hardware architecture of the machine.
807 run_function is used to execute the commands. It defaults to
808 utils.run() but a custom method (if provided) should be of the
809 same schema as utils.run. It should return a CmdResult object and
810 throw a CmdError exception.
811 """
812 arch = run_function('/bin/uname -m').stdout.rstrip()
813 if re.match(r'i\d86$', arch):
814 arch = 'i386'
815 return arch
816
817
showard4745ecd2009-05-26 19:34:56 +0000818def get_num_logical_cpus_per_socket(run_function=run):
mbligh9fd9afe2009-04-28 18:27:25 +0000819 """
820 Get the number of cores (including hyperthreading) per cpu.
821 run_function is used to execute the commands. It defaults to
822 utils.run() but a custom method (if provided) should be of the
823 same schema as utils.run. It should return a CmdResult object and
824 throw a CmdError exception.
825 """
showard4745ecd2009-05-26 19:34:56 +0000826 siblings = run_function('grep "^siblings" /proc/cpuinfo').stdout.rstrip()
827 num_siblings = map(int,
828 re.findall(r'^siblings\s*:\s*(\d+)\s*$',
829 siblings, re.M))
830 if len(num_siblings) == 0:
831 raise error.TestError('Unable to find siblings info in /proc/cpuinfo')
832 if min(num_siblings) != max(num_siblings):
833 raise error.TestError('Number of siblings differ %r' %
834 num_siblings)
835 return num_siblings[0]
mbligh9fd9afe2009-04-28 18:27:25 +0000836
837
jadmanski4f909252008-12-01 20:47:10 +0000838def merge_trees(src, dest):
839 """
840 Merges a source directory tree at 'src' into a destination tree at
841 'dest'. If a path is a file in both trees than the file in the source
842 tree is APPENDED to the one in the destination tree. If a path is
843 a directory in both trees then the directories are recursively merged
844 with this function. In any other case, the function will skip the
845 paths that cannot be merged (instead of failing).
846 """
847 if not os.path.exists(src):
848 return # exists only in dest
849 elif not os.path.exists(dest):
850 if os.path.isfile(src):
851 shutil.copy2(src, dest) # file only in src
852 else:
853 shutil.copytree(src, dest, symlinks=True) # dir only in src
854 return
855 elif os.path.isfile(src) and os.path.isfile(dest):
856 # src & dest are files in both trees, append src to dest
857 destfile = open(dest, "a")
858 try:
859 srcfile = open(src)
860 try:
861 destfile.write(srcfile.read())
862 finally:
863 srcfile.close()
864 finally:
865 destfile.close()
866 elif os.path.isdir(src) and os.path.isdir(dest):
867 # src & dest are directories in both trees, so recursively merge
868 for name in os.listdir(src):
869 merge_trees(os.path.join(src, name), os.path.join(dest, name))
870 else:
871 # src & dest both exist, but are incompatible
872 return
873
874
mbligh63073c92008-03-31 16:49:32 +0000875class CmdResult(object):
jadmanski0afbb632008-06-06 21:10:57 +0000876 """
877 Command execution result.
mbligh63073c92008-03-31 16:49:32 +0000878
jadmanski0afbb632008-06-06 21:10:57 +0000879 command: String containing the command line itself
880 exit_status: Integer exit code of the process
881 stdout: String containing stdout of the process
882 stderr: String containing stderr of the process
883 duration: Elapsed wall clock time running the process
884 """
mbligh63073c92008-03-31 16:49:32 +0000885
886
mblighcd63a212009-05-01 23:04:38 +0000887 def __init__(self, command="", stdout="", stderr="",
jadmanski0afbb632008-06-06 21:10:57 +0000888 exit_status=None, duration=0):
889 self.command = command
890 self.exit_status = exit_status
891 self.stdout = stdout
892 self.stderr = stderr
893 self.duration = duration
mbligh63073c92008-03-31 16:49:32 +0000894
895
jadmanski0afbb632008-06-06 21:10:57 +0000896 def __repr__(self):
897 wrapper = textwrap.TextWrapper(width = 78,
898 initial_indent="\n ",
899 subsequent_indent=" ")
900
901 stdout = self.stdout.rstrip()
902 if stdout:
903 stdout = "\nstdout:\n%s" % stdout
904
905 stderr = self.stderr.rstrip()
906 if stderr:
907 stderr = "\nstderr:\n%s" % stderr
908
909 return ("* Command: %s\n"
910 "Exit status: %s\n"
911 "Duration: %s\n"
912 "%s"
913 "%s"
914 % (wrapper.fill(self.command), self.exit_status,
915 self.duration, stdout, stderr))
mbligh63073c92008-03-31 16:49:32 +0000916
917
mbligh462c0152008-03-13 15:37:10 +0000918class run_randomly:
jadmanski0afbb632008-06-06 21:10:57 +0000919 def __init__(self, run_sequentially=False):
920 # Run sequentially is for debugging control files
921 self.test_list = []
922 self.run_sequentially = run_sequentially
mbligh462c0152008-03-13 15:37:10 +0000923
924
jadmanski0afbb632008-06-06 21:10:57 +0000925 def add(self, *args, **dargs):
926 test = (args, dargs)
927 self.test_list.append(test)
mbligh462c0152008-03-13 15:37:10 +0000928
929
jadmanski0afbb632008-06-06 21:10:57 +0000930 def run(self, fn):
931 while self.test_list:
932 test_index = random.randint(0, len(self.test_list)-1)
933 if self.run_sequentially:
934 test_index = 0
935 (args, dargs) = self.test_list.pop(test_index)
936 fn(*args, **dargs)
mbligha7007722009-01-13 00:37:11 +0000937
938
showard5deef7f2009-09-09 18:16:58 +0000939def import_site_module(path, module, dummy=None, modulefile=None):
940 """
941 Try to import the site specific module if it exists.
942
943 @param path full filename of the source file calling this (ie __file__)
944 @param module full module name
945 @param dummy dummy value to return in case there is no symbol to import
946 @param modulefile module filename
947
948 @return site specific module or dummy
949
950 @raises ImportError if the site file exists but imports fails
951 """
952 short_module = module[module.rfind(".") + 1:]
953
954 if not modulefile:
955 modulefile = short_module + ".py"
956
957 if os.path.exists(os.path.join(os.path.dirname(path), modulefile)):
958 return __import__(module, {}, {}, [short_module])
959 return dummy
960
961
mblighdd669372009-02-03 21:57:18 +0000962def import_site_symbol(path, module, name, dummy=None, modulefile=None):
963 """
964 Try to import site specific symbol from site specific file if it exists
965
966 @param path full filename of the source file calling this (ie __file__)
967 @param module full module name
968 @param name symbol name to be imported from the site file
969 @param dummy dummy value to return in case there is no symbol to import
970 @param modulefile module filename
971
972 @return site specific symbol or dummy
973
showard5deef7f2009-09-09 18:16:58 +0000974 @raises ImportError if the site file exists but imports fails
mblighdd669372009-02-03 21:57:18 +0000975 """
showard5deef7f2009-09-09 18:16:58 +0000976 module = import_site_module(path, module, modulefile=modulefile)
977 if not module:
978 return dummy
mbligha7007722009-01-13 00:37:11 +0000979
showard5deef7f2009-09-09 18:16:58 +0000980 # special unique value to tell us if the symbol can't be imported
981 cant_import = object()
mbligha7007722009-01-13 00:37:11 +0000982
showard5deef7f2009-09-09 18:16:58 +0000983 obj = getattr(module, name, cant_import)
984 if obj is cant_import:
mblighf989e582010-04-01 17:10:35 +0000985 logging.debug("unable to import site symbol '%s', using non-site "
showard5deef7f2009-09-09 18:16:58 +0000986 "implementation", name)
987 return dummy
mbligh062ed152009-01-13 00:57:14 +0000988
989 return obj
990
991
992def import_site_class(path, module, classname, baseclass, modulefile=None):
993 """
994 Try to import site specific class from site specific file if it exists
995
996 Args:
997 path: full filename of the source file calling this (ie __file__)
998 module: full module name
999 classname: class name to be loaded from site file
mbligh0a8c3322009-04-28 18:32:19 +00001000 baseclass: base class object to return when no site file present or
1001 to mixin when site class exists but is not inherited from baseclass
mbligh062ed152009-01-13 00:57:14 +00001002 modulefile: module filename
1003
mbligh0a8c3322009-04-28 18:32:19 +00001004 Returns: baseclass if site specific class does not exist, the site specific
1005 class if it exists and is inherited from baseclass or a mixin of the
1006 site specific class and baseclass when the site specific class exists
1007 and is not inherited from baseclass
mbligh062ed152009-01-13 00:57:14 +00001008
1009 Raises: ImportError if the site file exists but imports fails
1010 """
1011
mblighdd669372009-02-03 21:57:18 +00001012 res = import_site_symbol(path, module, classname, None, modulefile)
mbligh0a8c3322009-04-28 18:32:19 +00001013 if res:
1014 if not issubclass(res, baseclass):
1015 # if not a subclass of baseclass then mix in baseclass with the
1016 # site specific class object and return the result
1017 res = type(classname, (res, baseclass), {})
1018 else:
1019 res = baseclass
mbligha7007722009-01-13 00:37:11 +00001020
mbligh062ed152009-01-13 00:57:14 +00001021 return res
1022
1023
1024def import_site_function(path, module, funcname, dummy, modulefile=None):
1025 """
1026 Try to import site specific function from site specific file if it exists
1027
1028 Args:
1029 path: full filename of the source file calling this (ie __file__)
1030 module: full module name
1031 funcname: function name to be imported from site file
1032 dummy: dummy function to return in case there is no function to import
1033 modulefile: module filename
1034
1035 Returns: site specific function object or dummy
1036
1037 Raises: ImportError if the site file exists but imports fails
1038 """
1039
mblighdd669372009-02-03 21:57:18 +00001040 return import_site_symbol(path, module, funcname, dummy, modulefile)
mblighfb676032009-04-01 18:25:38 +00001041
1042
showard549afad2009-08-20 23:33:36 +00001043def _get_pid_path(program_name):
1044 my_path = os.path.dirname(__file__)
1045 return os.path.abspath(os.path.join(my_path, "..", "..",
1046 "%s.pid" % program_name))
1047
1048
mblighfb676032009-04-01 18:25:38 +00001049def write_pid(program_name):
1050 """
1051 Try to drop <program_name>.pid in the main autotest directory.
1052
1053 Args:
1054 program_name: prefix for file name
1055 """
showard549afad2009-08-20 23:33:36 +00001056 pidfile = open(_get_pid_path(program_name), "w")
1057 try:
1058 pidfile.write("%s\n" % os.getpid())
1059 finally:
1060 pidfile.close()
mblighfb676032009-04-01 18:25:38 +00001061
showard549afad2009-08-20 23:33:36 +00001062
1063def delete_pid_file_if_exists(program_name):
1064 """
1065 Tries to remove <program_name>.pid from the main autotest directory.
1066 """
1067 pidfile_path = _get_pid_path(program_name)
1068
1069 try:
1070 os.remove(pidfile_path)
1071 except OSError:
1072 if not os.path.exists(pidfile_path):
1073 return
1074 raise
1075
1076
1077def get_pid_from_file(program_name):
1078 """
1079 Reads the pid from <program_name>.pid in the autotest directory.
1080
1081 @param program_name the name of the program
1082 @return the pid if the file exists, None otherwise.
1083 """
1084 pidfile_path = _get_pid_path(program_name)
1085 if not os.path.exists(pidfile_path):
1086 return None
1087
1088 pidfile = open(_get_pid_path(program_name), 'r')
1089
1090 try:
1091 try:
1092 pid = int(pidfile.readline())
1093 except IOError:
1094 if not os.path.exists(pidfile_path):
1095 return None
1096 raise
1097 finally:
1098 pidfile.close()
1099
1100 return pid
1101
1102
showard8de37132009-08-31 18:33:08 +00001103def program_is_alive(program_name):
showard549afad2009-08-20 23:33:36 +00001104 """
1105 Checks if the process is alive and not in Zombie state.
1106
1107 @param program_name the name of the program
1108 @return True if still alive, False otherwise
1109 """
1110 pid = get_pid_from_file(program_name)
1111 if pid is None:
1112 return False
1113 return pid_is_alive(pid)
1114
1115
showard8de37132009-08-31 18:33:08 +00001116def signal_program(program_name, sig=signal.SIGTERM):
showard549afad2009-08-20 23:33:36 +00001117 """
1118 Sends a signal to the process listed in <program_name>.pid
1119
1120 @param program_name the name of the program
1121 @param sig signal to send
1122 """
1123 pid = get_pid_from_file(program_name)
1124 if pid:
1125 signal_pid(pid, sig)
mbligh45561782009-05-11 21:14:34 +00001126
1127
1128def get_relative_path(path, reference):
1129 """Given 2 absolute paths "path" and "reference", compute the path of
1130 "path" as relative to the directory "reference".
1131
1132 @param path the absolute path to convert to a relative path
1133 @param reference an absolute directory path to which the relative
1134 path will be computed
1135 """
1136 # normalize the paths (remove double slashes, etc)
1137 assert(os.path.isabs(path))
1138 assert(os.path.isabs(reference))
1139
1140 path = os.path.normpath(path)
1141 reference = os.path.normpath(reference)
1142
1143 # we could use os.path.split() but it splits from the end
1144 path_list = path.split(os.path.sep)[1:]
1145 ref_list = reference.split(os.path.sep)[1:]
1146
1147 # find the longest leading common path
1148 for i in xrange(min(len(path_list), len(ref_list))):
1149 if path_list[i] != ref_list[i]:
1150 # decrement i so when exiting this loop either by no match or by
1151 # end of range we are one step behind
1152 i -= 1
1153 break
1154 i += 1
1155 # drop the common part of the paths, not interested in that anymore
1156 del path_list[:i]
1157
1158 # for each uncommon component in the reference prepend a ".."
1159 path_list[:0] = ['..'] * (len(ref_list) - i)
1160
1161 return os.path.join(*path_list)
mbligh277a0e42009-07-11 00:11:45 +00001162
1163
1164def sh_escape(command):
1165 """
1166 Escape special characters from a command so that it can be passed
1167 as a double quoted (" ") string in a (ba)sh command.
1168
1169 Args:
1170 command: the command string to escape.
1171
1172 Returns:
1173 The escaped command string. The required englobing double
1174 quotes are NOT added and so should be added at some point by
1175 the caller.
1176
1177 See also: http://www.tldp.org/LDP/abs/html/escapingsection.html
1178 """
1179 command = command.replace("\\", "\\\\")
1180 command = command.replace("$", r'\$')
1181 command = command.replace('"', r'\"')
1182 command = command.replace('`', r'\`')
1183 return command
mblighab88fbb2010-04-08 18:31:13 +00001184
1185
1186def configure(extra=None, configure='./configure'):
1187 """
1188 Run configure passing in the correct host, build, and target options.
1189
1190 @param extra: extra command line arguments to pass to configure
1191 @param configure: which configure script to use
1192 """
1193 args = []
1194 if 'CHOST' in os.environ:
1195 args.append('--host=' + os.environ['CHOST'])
1196 if 'CBUILD' in os.environ:
1197 args.append('--build=' + os.environ['CBUILD'])
1198 if 'CTARGET' in os.environ:
1199 args.append('--target=' + os.environ['CTARGET'])
1200 if extra:
1201 args.append(extra)
1202
1203 system('%s %s' % (configure, ' '.join(args)))
1204
mbligh777db852010-04-22 21:59:57 +00001205
jadmanski82e11962010-08-17 22:59:08 +00001206def make(extra='', make='make', timeout=None, ignore_status=False):
1207 """
1208 Run make, adding MAKEOPTS to the list of options.
1209
1210 @param extra: extra command line arguments to pass to make.
1211 """
1212 cmd = '%s %s %s' % (make, os.environ.get('MAKEOPTS', ''), extra)
1213 return system(cmd, timeout=timeout, ignore_status=ignore_status)
1214
1215
mbligh777db852010-04-22 21:59:57 +00001216def compare_versions(ver1, ver2):
mbligh58609322010-05-03 17:58:20 +00001217 """Version number comparison between ver1 and ver2 strings.
mbligh777db852010-04-22 21:59:57 +00001218
mbligh58609322010-05-03 17:58:20 +00001219 >>> compare_tuple("1", "2")
1220 -1
1221 >>> compare_tuple("foo-1.1", "foo-1.2")
1222 -1
1223 >>> compare_tuple("1.2", "1.2a")
1224 -1
1225 >>> compare_tuple("1.2b", "1.2a")
1226 1
1227 >>> compare_tuple("1.3.5.3a", "1.3.5.3b")
1228 -1
mbligh777db852010-04-22 21:59:57 +00001229
1230 Args:
mbligh58609322010-05-03 17:58:20 +00001231 ver1: version string
1232 ver2: version string
mbligh777db852010-04-22 21:59:57 +00001233
1234 Returns:
1235 int: 1 if ver1 > ver2
1236 0 if ver1 == ver2
1237 -1 if ver1 < ver2
1238 """
mbligh58609322010-05-03 17:58:20 +00001239 ax = re.split('[.-]', ver1)
1240 ay = re.split('[.-]', ver2)
1241 while len(ax) > 0 and len(ay) > 0:
1242 cx = ax.pop(0)
1243 cy = ay.pop(0)
1244 maxlen = max(len(cx), len(cy))
1245 c = cmp(cx.zfill(maxlen), cy.zfill(maxlen))
1246 if c != 0:
1247 return c
1248 return cmp(len(ax), len(ay))
jadmanski4ceb7c22010-06-11 14:32:04 +00001249
1250
1251def args_to_dict(args):
1252 """Convert autoserv extra arguments in the form of key=val or key:val to a
1253 dictionary. Each argument key is converted to lowercase dictionary key.
1254
1255 Args:
1256 args - list of autoserv extra arguments.
1257
1258 Returns:
1259 dictionary
1260 """
1261 arg_re = re.compile(r'(\w+)[:=](.*)$')
1262 dict = {}
1263 for arg in args:
1264 match = arg_re.match(arg)
1265 if match:
1266 dict[match.group(1).lower()] = match.group(2)
1267 else:
1268 logging.warning("args_to_dict: argument '%s' doesn't match "
1269 "'%s' pattern. Ignored." % (arg, arg_re.pattern))
1270 return dict
Eric Li6f27d4f2010-09-29 10:55:17 -07001271
1272
1273def get_unused_port():
1274 """
1275 Finds a semi-random available port. A race condition is still
1276 possible after the port number is returned, if another process
1277 happens to bind it.
1278
1279 Returns:
1280 A port number that is unused on both TCP and UDP.
1281 """
1282
1283 def try_bind(port, socket_type, socket_proto):
1284 s = socket.socket(socket.AF_INET, socket_type, socket_proto)
1285 try:
1286 try:
1287 s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
1288 s.bind(('', port))
1289 return s.getsockname()[1]
1290 except socket.error:
1291 return None
1292 finally:
1293 s.close()
1294
1295 # On the 2.6 kernel, calling try_bind() on UDP socket returns the
1296 # same port over and over. So always try TCP first.
1297 while True:
1298 # Ask the OS for an unused port.
1299 port = try_bind(0, socket.SOCK_STREAM, socket.IPPROTO_TCP)
1300 # Check if this port is unused on the other protocol.
1301 if port and try_bind(port, socket.SOCK_DGRAM, socket.IPPROTO_UDP):
1302 return port