blob: b42415e5473ad171ec148b939535139365a2c855 [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
showard108d73e2009-06-22 18:14:41 +000011from autotest_lib.client.common_lib import error, barrier, 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
mblighb9d05512008-10-18 13:53:27 +0000185def write_one_line(filename, line):
186 open_write_close(filename, line.rstrip('\n') + '\n')
187
188
189def open_write_close(filename, data):
mbligh618ac9e2008-10-06 17:14:32 +0000190 f = open(filename, 'w')
mblighb9d05512008-10-18 13:53:27 +0000191 try:
192 f.write(data)
193 finally:
194 f.close()
jadmanski5182e162008-05-13 21:48:16 +0000195
196
mblighde0d47e2008-03-28 14:37:18 +0000197def read_keyval(path):
jadmanski0afbb632008-06-06 21:10:57 +0000198 """
199 Read a key-value pair format file into a dictionary, and return it.
200 Takes either a filename or directory name as input. If it's a
201 directory name, we assume you want the file to be called keyval.
202 """
203 if os.path.isdir(path):
204 path = os.path.join(path, 'keyval')
205 keyval = {}
jadmanski58962982009-04-21 19:54:34 +0000206 if os.path.exists(path):
207 for line in open(path):
208 line = re.sub('#.*', '', line).rstrip()
209 if not re.search(r'^[-\.\w]+=', line):
210 raise ValueError('Invalid format line: %s' % line)
211 key, value = line.split('=', 1)
212 if re.search('^\d+$', value):
213 value = int(value)
214 elif re.search('^(\d+\.)?\d+$', value):
215 value = float(value)
216 keyval[key] = value
jadmanski0afbb632008-06-06 21:10:57 +0000217 return keyval
mblighde0d47e2008-03-28 14:37:18 +0000218
219
jadmanskicc549172008-05-21 18:11:51 +0000220def write_keyval(path, dictionary, type_tag=None):
jadmanski0afbb632008-06-06 21:10:57 +0000221 """
222 Write a key-value pair format file out to a file. This uses append
223 mode to open the file, so existing text will not be overwritten or
224 reparsed.
jadmanskicc549172008-05-21 18:11:51 +0000225
jadmanski0afbb632008-06-06 21:10:57 +0000226 If type_tag is None, then the key must be composed of alphanumeric
227 characters (or dashes+underscores). However, if type-tag is not
228 null then the keys must also have "{type_tag}" as a suffix. At
229 the moment the only valid values of type_tag are "attr" and "perf".
230 """
231 if os.path.isdir(path):
232 path = os.path.join(path, 'keyval')
233 keyval = open(path, 'a')
jadmanskicc549172008-05-21 18:11:51 +0000234
jadmanski0afbb632008-06-06 21:10:57 +0000235 if type_tag is None:
mbligh97227ea2009-03-11 17:09:50 +0000236 key_regex = re.compile(r'^[-\.\w]+$')
jadmanski0afbb632008-06-06 21:10:57 +0000237 else:
238 if type_tag not in ('attr', 'perf'):
239 raise ValueError('Invalid type tag: %s' % type_tag)
240 escaped_tag = re.escape(type_tag)
mbligh97227ea2009-03-11 17:09:50 +0000241 key_regex = re.compile(r'^[-\.\w]+\{%s\}$' % escaped_tag)
jadmanski0afbb632008-06-06 21:10:57 +0000242 try:
mbligh6955e232009-07-11 00:58:47 +0000243 for key in sorted(dictionary.keys()):
jadmanski0afbb632008-06-06 21:10:57 +0000244 if not key_regex.search(key):
245 raise ValueError('Invalid key: %s' % key)
mbligh6955e232009-07-11 00:58:47 +0000246 keyval.write('%s=%s\n' % (key, dictionary[key]))
jadmanski0afbb632008-06-06 21:10:57 +0000247 finally:
248 keyval.close()
mbligh6231cd62008-02-02 19:18:33 +0000249
250
251def is_url(path):
jadmanski0afbb632008-06-06 21:10:57 +0000252 """Return true if path looks like a URL"""
253 # for now, just handle http and ftp
254 url_parts = urlparse.urlparse(path)
255 return (url_parts[0] in ('http', 'ftp'))
mbligh6231cd62008-02-02 19:18:33 +0000256
257
mblighb2896192009-07-11 00:12:37 +0000258def urlopen(url, data=None, timeout=5):
259 """Wrapper to urllib2.urlopen with timeout addition."""
mbligh02ff2d52008-06-03 15:00:21 +0000260
jadmanski0afbb632008-06-06 21:10:57 +0000261 # Save old timeout
262 old_timeout = socket.getdefaulttimeout()
263 socket.setdefaulttimeout(timeout)
264 try:
mblighb2896192009-07-11 00:12:37 +0000265 return urllib2.urlopen(url, data=data)
jadmanski0afbb632008-06-06 21:10:57 +0000266 finally:
267 socket.setdefaulttimeout(old_timeout)
mbligh02ff2d52008-06-03 15:00:21 +0000268
269
mblighb2896192009-07-11 00:12:37 +0000270def urlretrieve(url, filename, data=None, timeout=300):
271 """Retrieve a file from given url."""
272 logging.debug('Fetching %s -> %s', url, filename)
273
274 src_file = urlopen(url, data=data, timeout=timeout)
jadmanski0afbb632008-06-06 21:10:57 +0000275 try:
mblighb2896192009-07-11 00:12:37 +0000276 dest_file = open(filename, 'wb')
277 try:
278 shutil.copyfileobj(src_file, dest_file)
279 finally:
280 dest_file.close()
jadmanski0afbb632008-06-06 21:10:57 +0000281 finally:
mblighb2896192009-07-11 00:12:37 +0000282 src_file.close()
jadmanski0afbb632008-06-06 21:10:57 +0000283
mbligh02ff2d52008-06-03 15:00:21 +0000284
lmrfed221f2010-02-04 03:08:30 +0000285def hash(type, input=None):
286 """
287 Returns an hash object of type md5 or sha1. This function is implemented in
288 order to encapsulate hash objects in a way that is compatible with python
289 2.4 and python 2.6 without warnings.
290
291 Note that even though python 2.6 hashlib supports hash types other than
292 md5 and sha1, we are artificially limiting the input values in order to
293 make the function to behave exactly the same among both python
294 implementations.
295
296 @param input: Optional input string that will be used to update the hash.
297 """
298 if type not in ['md5', 'sha1']:
299 raise ValueError("Unsupported hash type: %s" % type)
300
301 try:
302 hash = hashlib.new(type)
303 except NameError:
304 if type == 'md5':
305 hash = md5.new()
306 elif type == 'sha1':
307 hash = sha.new()
308
309 if input:
310 hash.update(input)
311
312 return hash
313
314
mbligh6231cd62008-02-02 19:18:33 +0000315def get_file(src, dest, permissions=None):
jadmanski0afbb632008-06-06 21:10:57 +0000316 """Get a file from src, which can be local or a remote URL"""
mbligh25284cd2009-06-08 16:17:24 +0000317 if src == dest:
jadmanski0afbb632008-06-06 21:10:57 +0000318 return
mbligh25284cd2009-06-08 16:17:24 +0000319
320 if is_url(src):
mblighb2896192009-07-11 00:12:37 +0000321 urlretrieve(src, dest)
jadmanski0afbb632008-06-06 21:10:57 +0000322 else:
323 shutil.copyfile(src, dest)
mbligh25284cd2009-06-08 16:17:24 +0000324
jadmanski0afbb632008-06-06 21:10:57 +0000325 if permissions:
326 os.chmod(dest, permissions)
327 return dest
mbligh6231cd62008-02-02 19:18:33 +0000328
329
330def unmap_url(srcdir, src, destdir='.'):
jadmanski0afbb632008-06-06 21:10:57 +0000331 """
332 Receives either a path to a local file or a URL.
333 returns either the path to the local file, or the fetched URL
mbligh6231cd62008-02-02 19:18:33 +0000334
jadmanski0afbb632008-06-06 21:10:57 +0000335 unmap_url('/usr/src', 'foo.tar', '/tmp')
336 = '/usr/src/foo.tar'
337 unmap_url('/usr/src', 'http://site/file', '/tmp')
338 = '/tmp/file'
339 (after retrieving it)
340 """
341 if is_url(src):
342 url_parts = urlparse.urlparse(src)
343 filename = os.path.basename(url_parts[2])
344 dest = os.path.join(destdir, filename)
345 return get_file(src, dest)
346 else:
347 return os.path.join(srcdir, src)
mbligh6231cd62008-02-02 19:18:33 +0000348
349
350def update_version(srcdir, preserve_srcdir, new_version, install,
jadmanski0afbb632008-06-06 21:10:57 +0000351 *args, **dargs):
352 """
353 Make sure srcdir is version new_version
mbligh6231cd62008-02-02 19:18:33 +0000354
jadmanski0afbb632008-06-06 21:10:57 +0000355 If not, delete it and install() the new version.
mbligh6231cd62008-02-02 19:18:33 +0000356
jadmanski0afbb632008-06-06 21:10:57 +0000357 In the preserve_srcdir case, we just check it's up to date,
358 and if not, we rerun install, without removing srcdir
359 """
360 versionfile = os.path.join(srcdir, '.version')
361 install_needed = True
mbligh6231cd62008-02-02 19:18:33 +0000362
jadmanski0afbb632008-06-06 21:10:57 +0000363 if os.path.exists(versionfile):
364 old_version = pickle.load(open(versionfile))
365 if old_version == new_version:
366 install_needed = False
mbligh6231cd62008-02-02 19:18:33 +0000367
jadmanski0afbb632008-06-06 21:10:57 +0000368 if install_needed:
369 if not preserve_srcdir and os.path.exists(srcdir):
370 shutil.rmtree(srcdir)
371 install(*args, **dargs)
372 if os.path.exists(srcdir):
373 pickle.dump(new_version, open(versionfile, 'w'))
mbligh462c0152008-03-13 15:37:10 +0000374
375
showardb45a4662009-07-15 14:27:56 +0000376def get_stderr_level(stderr_is_expected):
377 if stderr_is_expected:
378 return DEFAULT_STDOUT_LEVEL
379 return DEFAULT_STDERR_LEVEL
380
381
mbligh63073c92008-03-31 16:49:32 +0000382def run(command, timeout=None, ignore_status=False,
showardb45a4662009-07-15 14:27:56 +0000383 stdout_tee=None, stderr_tee=None, verbose=True, stdin=None,
mblighc823e8d2009-10-02 00:01:35 +0000384 stderr_is_expected=None, args=()):
jadmanski0afbb632008-06-06 21:10:57 +0000385 """
386 Run a command on the host.
mbligh63073c92008-03-31 16:49:32 +0000387
mblighc823e8d2009-10-02 00:01:35 +0000388 @param command: the command line string.
389 @param timeout: time limit in seconds before attempting to kill the
390 running process. The run() function will take a few seconds
391 longer than 'timeout' to complete if it has to kill the process.
392 @param ignore_status: do not raise an exception, no matter what the exit
393 code of the command is.
394 @param stdout_tee: optional file-like object to which stdout data
395 will be written as it is generated (data will still be stored
396 in result.stdout).
397 @param stderr_tee: likewise for stderr.
398 @param verbose: if True, log the command being run.
jadmanski093a0682009-10-13 14:55:43 +0000399 @param stdin: stdin to pass to the executed process (can be a file
400 descriptor, a file object of a real file or a string).
mblighc823e8d2009-10-02 00:01:35 +0000401 @param args: sequence of strings of arguments to be given to the command
402 inside " quotes after they have been escaped for that; each
403 element in the sequence will be given as a separate command
404 argument
mbligh63073c92008-03-31 16:49:32 +0000405
mblighc823e8d2009-10-02 00:01:35 +0000406 @return a CmdResult object
mbligh63073c92008-03-31 16:49:32 +0000407
mblighc823e8d2009-10-02 00:01:35 +0000408 @raise CmdError: the exit code of the command execution was not 0
jadmanski0afbb632008-06-06 21:10:57 +0000409 """
mbligh2c7f7d62009-12-19 05:24:04 +0000410 if isinstance(args, basestring):
411 raise TypeError('Got a string for the "args" keyword argument, '
412 'need a sequence.')
413
mblighc823e8d2009-10-02 00:01:35 +0000414 for arg in args:
415 command += ' "%s"' % sh_escape(arg)
showardb45a4662009-07-15 14:27:56 +0000416 if stderr_is_expected is None:
417 stderr_is_expected = ignore_status
mblighc823e8d2009-10-02 00:01:35 +0000418
showard170873e2009-01-07 00:22:26 +0000419 bg_job = join_bg_jobs(
showardb45a4662009-07-15 14:27:56 +0000420 (BgJob(command, stdout_tee, stderr_tee, verbose, stdin=stdin,
421 stderr_level=get_stderr_level(stderr_is_expected)),),
showard170873e2009-01-07 00:22:26 +0000422 timeout)[0]
mbligh849a0f62008-08-28 20:12:19 +0000423 if not ignore_status and bg_job.result.exit_status:
jadmanski9c1098b2008-09-02 14:18:48 +0000424 raise error.CmdError(command, bg_job.result,
mbligh849a0f62008-08-28 20:12:19 +0000425 "Command returned non-zero exit status")
mbligh63073c92008-03-31 16:49:32 +0000426
mbligh849a0f62008-08-28 20:12:19 +0000427 return bg_job.result
mbligh63073c92008-03-31 16:49:32 +0000428
mbligh45ffc432008-12-09 23:35:17 +0000429
mbligha5630a52008-09-03 22:09:50 +0000430def run_parallel(commands, timeout=None, ignore_status=False,
431 stdout_tee=None, stderr_tee=None):
jadmanski093a0682009-10-13 14:55:43 +0000432 """
433 Behaves the same as run() with the following exceptions:
mbligha5630a52008-09-03 22:09:50 +0000434
435 - commands is a list of commands to run in parallel.
436 - ignore_status toggles whether or not an exception should be raised
437 on any error.
438
jadmanski093a0682009-10-13 14:55:43 +0000439 @return: a list of CmdResult objects
mbligha5630a52008-09-03 22:09:50 +0000440 """
441 bg_jobs = []
442 for command in commands:
showardb45a4662009-07-15 14:27:56 +0000443 bg_jobs.append(BgJob(command, stdout_tee, stderr_tee,
444 stderr_level=get_stderr_level(ignore_status)))
mbligha5630a52008-09-03 22:09:50 +0000445
446 # Updates objects in bg_jobs list with their process information
447 join_bg_jobs(bg_jobs, timeout)
448
449 for bg_job in bg_jobs:
450 if not ignore_status and bg_job.result.exit_status:
451 raise error.CmdError(command, bg_job.result,
452 "Command returned non-zero exit status")
453
454 return [bg_job.result for bg_job in bg_jobs]
455
456
mbligh849a0f62008-08-28 20:12:19 +0000457@deprecated
mbligh63073c92008-03-31 16:49:32 +0000458def run_bg(command):
mbligh849a0f62008-08-28 20:12:19 +0000459 """Function deprecated. Please use BgJob class instead."""
460 bg_job = BgJob(command)
461 return bg_job.sp, bg_job.result
mbligh63073c92008-03-31 16:49:32 +0000462
463
mbligh849a0f62008-08-28 20:12:19 +0000464def join_bg_jobs(bg_jobs, timeout=None):
mbligha5630a52008-09-03 22:09:50 +0000465 """Joins the bg_jobs with the current thread.
466
467 Returns the same list of bg_jobs objects that was passed in.
468 """
mblighae69f262009-04-17 20:14:56 +0000469 ret, timeout_error = 0, False
mbligh849a0f62008-08-28 20:12:19 +0000470 for bg_job in bg_jobs:
471 bg_job.output_prepare(StringIO.StringIO(), StringIO.StringIO())
mbligh63073c92008-03-31 16:49:32 +0000472
jadmanski0afbb632008-06-06 21:10:57 +0000473 try:
474 # We are holding ends to stdin, stdout pipes
475 # hence we need to be sure to close those fds no mater what
476 start_time = time.time()
mbligh849a0f62008-08-28 20:12:19 +0000477 timeout_error = _wait_for_commands(bg_jobs, start_time, timeout)
478
479 for bg_job in bg_jobs:
480 # Process stdout and stderr
481 bg_job.process_output(stdout=True,final_read=True)
482 bg_job.process_output(stdout=False,final_read=True)
jadmanski0afbb632008-06-06 21:10:57 +0000483 finally:
484 # close our ends of the pipes to the sp no matter what
mbligh849a0f62008-08-28 20:12:19 +0000485 for bg_job in bg_jobs:
486 bg_job.cleanup()
mbligh63073c92008-03-31 16:49:32 +0000487
mbligh849a0f62008-08-28 20:12:19 +0000488 if timeout_error:
489 # TODO: This needs to be fixed to better represent what happens when
490 # running in parallel. However this is backwards compatable, so it will
491 # do for the time being.
492 raise error.CmdError(bg_jobs[0].command, bg_jobs[0].result,
493 "Command(s) did not complete within %d seconds"
494 % timeout)
mbligh63073c92008-03-31 16:49:32 +0000495
mbligh63073c92008-03-31 16:49:32 +0000496
mbligh849a0f62008-08-28 20:12:19 +0000497 return bg_jobs
mbligh63073c92008-03-31 16:49:32 +0000498
mbligh849a0f62008-08-28 20:12:19 +0000499
500def _wait_for_commands(bg_jobs, start_time, timeout):
501 # This returns True if it must return due to a timeout, otherwise False.
502
mblighf0b4a0a2008-09-03 20:46:16 +0000503 # To check for processes which terminate without producing any output
504 # a 1 second timeout is used in select.
505 SELECT_TIMEOUT = 1
506
jadmanski093a0682009-10-13 14:55:43 +0000507 read_list = []
508 write_list = []
mbligh849a0f62008-08-28 20:12:19 +0000509 reverse_dict = {}
jadmanski093a0682009-10-13 14:55:43 +0000510
mbligh849a0f62008-08-28 20:12:19 +0000511 for bg_job in bg_jobs:
jadmanski093a0682009-10-13 14:55:43 +0000512 read_list.append(bg_job.sp.stdout)
513 read_list.append(bg_job.sp.stderr)
514 reverse_dict[bg_job.sp.stdout] = (bg_job, True)
515 reverse_dict[bg_job.sp.stderr] = (bg_job, False)
516 if bg_job.string_stdin:
517 write_list.append(bg_job.sp.stdin)
518 reverse_dict[bg_job.sp.stdin] = bg_job
mbligh849a0f62008-08-28 20:12:19 +0000519
jadmanski0afbb632008-06-06 21:10:57 +0000520 if timeout:
521 stop_time = start_time + timeout
522 time_left = stop_time - time.time()
523 else:
524 time_left = None # so that select never times out
jadmanski093a0682009-10-13 14:55:43 +0000525
jadmanski0afbb632008-06-06 21:10:57 +0000526 while not timeout or time_left > 0:
jadmanski093a0682009-10-13 14:55:43 +0000527 # select will return when we may write to stdin or when there is
528 # stdout/stderr output we can read (including when it is
jadmanski0afbb632008-06-06 21:10:57 +0000529 # EOF, that is the process has terminated).
jadmanski093a0682009-10-13 14:55:43 +0000530 read_ready, write_ready, _ = select.select(read_list, write_list, [],
531 SELECT_TIMEOUT)
mbligh849a0f62008-08-28 20:12:19 +0000532
jadmanski0afbb632008-06-06 21:10:57 +0000533 # os.read() has to be used instead of
534 # subproc.stdout.read() which will otherwise block
jadmanski093a0682009-10-13 14:55:43 +0000535 for file_obj in read_ready:
536 bg_job, is_stdout = reverse_dict[file_obj]
537 bg_job.process_output(is_stdout)
mbligh63073c92008-03-31 16:49:32 +0000538
jadmanski093a0682009-10-13 14:55:43 +0000539 for file_obj in write_ready:
540 # we can write PIPE_BUF bytes without blocking
541 # POSIX requires PIPE_BUF is >= 512
542 bg_job = reverse_dict[file_obj]
543 file_obj.write(bg_job.string_stdin[:512])
544 bg_job.string_stdin = bg_job.string_stdin[512:]
545 # no more input data, close stdin, remove it from the select set
546 if not bg_job.string_stdin:
547 file_obj.close()
548 write_list.remove(file_obj)
549 del reverse_dict[file_obj]
550
551 all_jobs_finished = True
552 for bg_job in bg_jobs:
553 if bg_job.result.exit_status is not None:
554 continue
555
mbligh849a0f62008-08-28 20:12:19 +0000556 bg_job.result.exit_status = bg_job.sp.poll()
jadmanski093a0682009-10-13 14:55:43 +0000557 if bg_job.result.exit_status is not None:
558 # process exited, remove its stdout/stdin from the select set
559 read_list.remove(bg_job.sp.stdout)
560 read_list.remove(bg_job.sp.stderr)
561 del reverse_dict[bg_job.sp.stdout]
562 del reverse_dict[bg_job.sp.stderr]
563 else:
564 all_jobs_finished = False
565
566 if all_jobs_finished:
567 return False
mbligh8ea61e22008-05-09 18:09:37 +0000568
jadmanski0afbb632008-06-06 21:10:57 +0000569 if timeout:
570 time_left = stop_time - time.time()
mbligh63073c92008-03-31 16:49:32 +0000571
mbligh849a0f62008-08-28 20:12:19 +0000572 # Kill all processes which did not complete prior to timeout
jadmanski093a0682009-10-13 14:55:43 +0000573 for bg_job in bg_jobs:
574 if bg_job.result.exit_status is not None:
575 continue
576
577 logging.warn('run process timeout (%s) fired on: %s', timeout,
578 bg_job.command)
mbligh849a0f62008-08-28 20:12:19 +0000579 nuke_subprocess(bg_job.sp)
mbligh095dc642008-10-01 03:41:35 +0000580 bg_job.result.exit_status = bg_job.sp.poll()
mbligh8ea61e22008-05-09 18:09:37 +0000581
mbligh849a0f62008-08-28 20:12:19 +0000582 return True
mbligh63073c92008-03-31 16:49:32 +0000583
584
showard549afad2009-08-20 23:33:36 +0000585def pid_is_alive(pid):
586 """
587 True if process pid exists and is not yet stuck in Zombie state.
588 Zombies are impossible to move between cgroups, etc.
589 pid can be integer, or text of integer.
590 """
591 path = '/proc/%s/stat' % pid
592
593 try:
594 stat = read_one_line(path)
595 except IOError:
596 if not os.path.exists(path):
597 # file went away
598 return False
599 raise
600
601 return stat.split()[2] != 'Z'
602
603
604def signal_pid(pid, sig):
605 """
606 Sends a signal to a process id. Returns True if the process terminated
607 successfully, False otherwise.
608 """
609 try:
610 os.kill(pid, sig)
611 except OSError:
612 # The process may have died before we could kill it.
613 pass
614
615 for i in range(5):
616 if not pid_is_alive(pid):
617 return True
618 time.sleep(1)
619
620 # The process is still alive
621 return False
622
623
mbligh63073c92008-03-31 16:49:32 +0000624def nuke_subprocess(subproc):
jadmanski09f92032008-09-17 14:05:27 +0000625 # check if the subprocess is still alive, first
626 if subproc.poll() is not None:
627 return subproc.poll()
628
jadmanski0afbb632008-06-06 21:10:57 +0000629 # the process has not terminated within timeout,
630 # kill it via an escalating series of signals.
631 signal_queue = [signal.SIGTERM, signal.SIGKILL]
632 for sig in signal_queue:
showard549afad2009-08-20 23:33:36 +0000633 signal_pid(subproc.pid, sig)
634 if subproc.poll() is not None:
635 return subproc.poll()
mbligh63073c92008-03-31 16:49:32 +0000636
637
showard786da9a2009-10-12 20:31:20 +0000638def nuke_pid(pid, signal_queue=(signal.SIGTERM, signal.SIGKILL)):
jadmanski0afbb632008-06-06 21:10:57 +0000639 # the process has not terminated within timeout,
640 # kill it via an escalating series of signals.
jadmanski0afbb632008-06-06 21:10:57 +0000641 for sig in signal_queue:
showard549afad2009-08-20 23:33:36 +0000642 if signal_pid(pid, sig):
643 return
mbligh63073c92008-03-31 16:49:32 +0000644
showard549afad2009-08-20 23:33:36 +0000645 # no signal successfully terminated the process
646 raise error.AutoservRunError('Could not kill %d' % pid, None)
mbligh298ed592009-06-15 21:52:57 +0000647
648
mbligh63073c92008-03-31 16:49:32 +0000649def system(command, timeout=None, ignore_status=False):
mbligh104a5382010-02-02 18:15:39 +0000650 """
651 Run a command
652
653 @param timeout: timeout in seconds
654 @param ignore_status: if ignore_status=False, throw an exception if the
655 command's exit code is non-zero
656 if ignore_stauts=True, return the exit code.
lmrfed221f2010-02-04 03:08:30 +0000657
mbligh104a5382010-02-02 18:15:39 +0000658 @return exit status of command
659 (note, this will always be zero unless ignore_status=True)
660 """
mblighf8dffb12008-10-29 16:45:26 +0000661 return run(command, timeout=timeout, ignore_status=ignore_status,
showard108d73e2009-06-22 18:14:41 +0000662 stdout_tee=TEE_TO_LOGS, stderr_tee=TEE_TO_LOGS).exit_status
mbligh63073c92008-03-31 16:49:32 +0000663
664
mbligha5630a52008-09-03 22:09:50 +0000665def system_parallel(commands, timeout=None, ignore_status=False):
666 """This function returns a list of exit statuses for the respective
667 list of commands."""
668 return [bg_jobs.exit_status for bg_jobs in
mblighf8dffb12008-10-29 16:45:26 +0000669 run_parallel(commands, timeout=timeout, ignore_status=ignore_status,
showard108d73e2009-06-22 18:14:41 +0000670 stdout_tee=TEE_TO_LOGS, stderr_tee=TEE_TO_LOGS)]
mbligh849a0f62008-08-28 20:12:19 +0000671
672
mbligh8ea61e22008-05-09 18:09:37 +0000673def system_output(command, timeout=None, ignore_status=False,
mblighc823e8d2009-10-02 00:01:35 +0000674 retain_output=False, args=()):
675 """
676 Run a command and return the stdout output.
677
678 @param command: command string to execute.
679 @param timeout: time limit in seconds before attempting to kill the
680 running process. The function will take a few seconds longer
681 than 'timeout' to complete if it has to kill the process.
682 @param ignore_status: do not raise an exception, no matter what the exit
683 code of the command is.
684 @param retain_output: set to True to make stdout/stderr of the command
685 output to be also sent to the logging system
686 @param args: sequence of strings of arguments to be given to the command
687 inside " quotes after they have been escaped for that; each
688 element in the sequence will be given as a separate command
689 argument
690
691 @return a string with the stdout output of the command.
692 """
jadmanski0afbb632008-06-06 21:10:57 +0000693 if retain_output:
mblighf8dffb12008-10-29 16:45:26 +0000694 out = run(command, timeout=timeout, ignore_status=ignore_status,
mblighc823e8d2009-10-02 00:01:35 +0000695 stdout_tee=TEE_TO_LOGS, stderr_tee=TEE_TO_LOGS,
696 args=args).stdout
jadmanski0afbb632008-06-06 21:10:57 +0000697 else:
mblighc823e8d2009-10-02 00:01:35 +0000698 out = run(command, timeout=timeout, ignore_status=ignore_status,
699 args=args).stdout
700 if out[-1:] == '\n':
701 out = out[:-1]
jadmanski0afbb632008-06-06 21:10:57 +0000702 return out
mbligh63073c92008-03-31 16:49:32 +0000703
mbligh849a0f62008-08-28 20:12:19 +0000704
mbligha5630a52008-09-03 22:09:50 +0000705def system_output_parallel(commands, timeout=None, ignore_status=False,
706 retain_output=False):
707 if retain_output:
showard108d73e2009-06-22 18:14:41 +0000708 out = [bg_job.stdout for bg_job
709 in run_parallel(commands, timeout=timeout,
710 ignore_status=ignore_status,
711 stdout_tee=TEE_TO_LOGS, stderr_tee=TEE_TO_LOGS)]
mbligha5630a52008-09-03 22:09:50 +0000712 else:
mblighf8dffb12008-10-29 16:45:26 +0000713 out = [bg_job.stdout for bg_job in run_parallel(commands,
714 timeout=timeout, ignore_status=ignore_status)]
mbligha5630a52008-09-03 22:09:50 +0000715 for x in out:
716 if out[-1:] == '\n': out = out[:-1]
717 return out
718
719
mbligh98467952008-11-19 00:25:45 +0000720def strip_unicode(input):
721 if type(input) == list:
722 return [strip_unicode(i) for i in input]
723 elif type(input) == dict:
724 output = {}
725 for key in input.keys():
726 output[str(key)] = strip_unicode(input[key])
727 return output
728 elif type(input) == unicode:
729 return str(input)
730 else:
731 return input
732
733
mbligha5630a52008-09-03 22:09:50 +0000734def get_cpu_percentage(function, *args, **dargs):
735 """Returns a tuple containing the CPU% and return value from function call.
736
737 This function calculates the usage time by taking the difference of
738 the user and system times both before and after the function call.
739 """
740 child_pre = resource.getrusage(resource.RUSAGE_CHILDREN)
741 self_pre = resource.getrusage(resource.RUSAGE_SELF)
742 start = time.time()
743 to_return = function(*args, **dargs)
744 elapsed = time.time() - start
745 self_post = resource.getrusage(resource.RUSAGE_SELF)
746 child_post = resource.getrusage(resource.RUSAGE_CHILDREN)
747
748 # Calculate CPU Percentage
749 s_user, s_system = [a - b for a, b in zip(self_post, self_pre)[:2]]
750 c_user, c_system = [a - b for a, b in zip(child_post, child_pre)[:2]]
751 cpu_percent = (s_user + c_user + s_system + c_system) / elapsed
752
753 return cpu_percent, to_return
754
755
mblighc1cbc992008-05-27 20:01:45 +0000756"""
757This function is used when there is a need to run more than one
758job simultaneously starting exactly at the same time. It basically returns
759a modified control file (containing the synchronization code prepended)
760whenever it is ready to run the control file. The synchronization
761is done using barriers to make sure that the jobs start at the same time.
762
763Here is how the synchronization is done to make sure that the tests
764start at exactly the same time on the client.
765sc_bar is a server barrier and s_bar, c_bar are the normal barriers
766
767 Job1 Job2 ...... JobN
768 Server: | sc_bar
769 Server: | s_bar ...... s_bar
770 Server: | at.run() at.run() ...... at.run()
771 ----------|------------------------------------------------------
772 Client | sc_bar
773 Client | c_bar c_bar ...... c_bar
774 Client | <run test> <run test> ...... <run test>
775
776
777PARAMS:
778 control_file : The control file which to which the above synchronization
779 code would be prepended to
780 host_name : The host name on which the job is going to run
781 host_num (non negative) : A number to identify the machine so that we have
782 different sets of s_bar_ports for each of the machines.
783 instance : The number of the job
784 num_jobs : Total number of jobs that are going to run in parallel with
785 this job starting at the same time
786 port_base : Port number that is used to derive the actual barrier ports.
787
788RETURN VALUE:
789 The modified control file.
790
791"""
792def get_sync_control_file(control, host_name, host_num,
jadmanski0afbb632008-06-06 21:10:57 +0000793 instance, num_jobs, port_base=63100):
794 sc_bar_port = port_base
795 c_bar_port = port_base
796 if host_num < 0:
797 print "Please provide a non negative number for the host"
798 return None
799 s_bar_port = port_base + 1 + host_num # The set of s_bar_ports are
800 # the same for a given machine
mblighc1cbc992008-05-27 20:01:45 +0000801
jadmanski0afbb632008-06-06 21:10:57 +0000802 sc_bar_timeout = 180
803 s_bar_timeout = c_bar_timeout = 120
mblighc1cbc992008-05-27 20:01:45 +0000804
jadmanski0afbb632008-06-06 21:10:57 +0000805 # The barrier code snippet is prepended into the conrol file
806 # dynamically before at.run() is called finally.
807 control_new = []
mblighc1cbc992008-05-27 20:01:45 +0000808
jadmanski0afbb632008-06-06 21:10:57 +0000809 # jobid is the unique name used to identify the processes
810 # trying to reach the barriers
811 jobid = "%s#%d" % (host_name, instance)
mblighc1cbc992008-05-27 20:01:45 +0000812
jadmanski0afbb632008-06-06 21:10:57 +0000813 rendv = []
814 # rendvstr is a temp holder for the rendezvous list of the processes
815 for n in range(num_jobs):
816 rendv.append("'%s#%d'" % (host_name, n))
817 rendvstr = ",".join(rendv)
mblighc1cbc992008-05-27 20:01:45 +0000818
jadmanski0afbb632008-06-06 21:10:57 +0000819 if instance == 0:
820 # Do the setup and wait at the server barrier
821 # Clean up the tmp and the control dirs for the first instance
822 control_new.append('if os.path.exists(job.tmpdir):')
823 control_new.append("\t system('umount -f %s > /dev/null"
824 "2> /dev/null' % job.tmpdir,"
825 "ignore_status=True)")
826 control_new.append("\t system('rm -rf ' + job.tmpdir)")
827 control_new.append(
828 'b0 = job.barrier("%s", "sc_bar", %d, port=%d)'
829 % (jobid, sc_bar_timeout, sc_bar_port))
830 control_new.append(
mbligh9c12f772009-06-22 19:03:55 +0000831 'b0.rendezvous_servers("PARALLEL_MASTER", "%s")'
jadmanski0afbb632008-06-06 21:10:57 +0000832 % jobid)
mblighc1cbc992008-05-27 20:01:45 +0000833
jadmanski0afbb632008-06-06 21:10:57 +0000834 elif instance == 1:
835 # Wait at the server barrier to wait for instance=0
836 # process to complete setup
837 b0 = barrier.barrier("PARALLEL_MASTER", "sc_bar", sc_bar_timeout,
838 port=sc_bar_port)
mbligh9c12f772009-06-22 19:03:55 +0000839 b0.rendezvous_servers("PARALLEL_MASTER", jobid)
mblighc1cbc992008-05-27 20:01:45 +0000840
jadmanski0afbb632008-06-06 21:10:57 +0000841 if(num_jobs > 2):
842 b1 = barrier.barrier(jobid, "s_bar", s_bar_timeout,
843 port=s_bar_port)
mbligh9c12f772009-06-22 19:03:55 +0000844 b1.rendezvous(rendvstr)
mblighc1cbc992008-05-27 20:01:45 +0000845
jadmanski0afbb632008-06-06 21:10:57 +0000846 else:
847 # For the rest of the clients
848 b2 = barrier.barrier(jobid, "s_bar", s_bar_timeout, port=s_bar_port)
mbligh9c12f772009-06-22 19:03:55 +0000849 b2.rendezvous(rendvstr)
mblighc1cbc992008-05-27 20:01:45 +0000850
jadmanski0afbb632008-06-06 21:10:57 +0000851 # Client side barrier for all the tests to start at the same time
852 control_new.append('b1 = job.barrier("%s", "c_bar", %d, port=%d)'
853 % (jobid, c_bar_timeout, c_bar_port))
mbligh9c12f772009-06-22 19:03:55 +0000854 control_new.append("b1.rendezvous(%s)" % rendvstr)
mblighc1cbc992008-05-27 20:01:45 +0000855
jadmanski0afbb632008-06-06 21:10:57 +0000856 # Stick in the rest of the control file
857 control_new.append(control)
mblighc1cbc992008-05-27 20:01:45 +0000858
jadmanski0afbb632008-06-06 21:10:57 +0000859 return "\n".join(control_new)
mblighc1cbc992008-05-27 20:01:45 +0000860
mbligh63073c92008-03-31 16:49:32 +0000861
mblighc5ddfd12008-08-04 17:15:00 +0000862def get_arch(run_function=run):
863 """
864 Get the hardware architecture of the machine.
865 run_function is used to execute the commands. It defaults to
866 utils.run() but a custom method (if provided) should be of the
867 same schema as utils.run. It should return a CmdResult object and
868 throw a CmdError exception.
869 """
870 arch = run_function('/bin/uname -m').stdout.rstrip()
871 if re.match(r'i\d86$', arch):
872 arch = 'i386'
873 return arch
874
875
showard4745ecd2009-05-26 19:34:56 +0000876def get_num_logical_cpus_per_socket(run_function=run):
mbligh9fd9afe2009-04-28 18:27:25 +0000877 """
878 Get the number of cores (including hyperthreading) per cpu.
879 run_function is used to execute the commands. It defaults to
880 utils.run() but a custom method (if provided) should be of the
881 same schema as utils.run. It should return a CmdResult object and
882 throw a CmdError exception.
883 """
showard4745ecd2009-05-26 19:34:56 +0000884 siblings = run_function('grep "^siblings" /proc/cpuinfo').stdout.rstrip()
885 num_siblings = map(int,
886 re.findall(r'^siblings\s*:\s*(\d+)\s*$',
887 siblings, re.M))
888 if len(num_siblings) == 0:
889 raise error.TestError('Unable to find siblings info in /proc/cpuinfo')
890 if min(num_siblings) != max(num_siblings):
891 raise error.TestError('Number of siblings differ %r' %
892 num_siblings)
893 return num_siblings[0]
mbligh9fd9afe2009-04-28 18:27:25 +0000894
895
jadmanski4f909252008-12-01 20:47:10 +0000896def merge_trees(src, dest):
897 """
898 Merges a source directory tree at 'src' into a destination tree at
899 'dest'. If a path is a file in both trees than the file in the source
900 tree is APPENDED to the one in the destination tree. If a path is
901 a directory in both trees then the directories are recursively merged
902 with this function. In any other case, the function will skip the
903 paths that cannot be merged (instead of failing).
904 """
905 if not os.path.exists(src):
906 return # exists only in dest
907 elif not os.path.exists(dest):
908 if os.path.isfile(src):
909 shutil.copy2(src, dest) # file only in src
910 else:
911 shutil.copytree(src, dest, symlinks=True) # dir only in src
912 return
913 elif os.path.isfile(src) and os.path.isfile(dest):
914 # src & dest are files in both trees, append src to dest
915 destfile = open(dest, "a")
916 try:
917 srcfile = open(src)
918 try:
919 destfile.write(srcfile.read())
920 finally:
921 srcfile.close()
922 finally:
923 destfile.close()
924 elif os.path.isdir(src) and os.path.isdir(dest):
925 # src & dest are directories in both trees, so recursively merge
926 for name in os.listdir(src):
927 merge_trees(os.path.join(src, name), os.path.join(dest, name))
928 else:
929 # src & dest both exist, but are incompatible
930 return
931
932
mbligh63073c92008-03-31 16:49:32 +0000933class CmdResult(object):
jadmanski0afbb632008-06-06 21:10:57 +0000934 """
935 Command execution result.
mbligh63073c92008-03-31 16:49:32 +0000936
jadmanski0afbb632008-06-06 21:10:57 +0000937 command: String containing the command line itself
938 exit_status: Integer exit code of the process
939 stdout: String containing stdout of the process
940 stderr: String containing stderr of the process
941 duration: Elapsed wall clock time running the process
942 """
mbligh63073c92008-03-31 16:49:32 +0000943
944
mblighcd63a212009-05-01 23:04:38 +0000945 def __init__(self, command="", stdout="", stderr="",
jadmanski0afbb632008-06-06 21:10:57 +0000946 exit_status=None, duration=0):
947 self.command = command
948 self.exit_status = exit_status
949 self.stdout = stdout
950 self.stderr = stderr
951 self.duration = duration
mbligh63073c92008-03-31 16:49:32 +0000952
953
jadmanski0afbb632008-06-06 21:10:57 +0000954 def __repr__(self):
955 wrapper = textwrap.TextWrapper(width = 78,
956 initial_indent="\n ",
957 subsequent_indent=" ")
958
959 stdout = self.stdout.rstrip()
960 if stdout:
961 stdout = "\nstdout:\n%s" % stdout
962
963 stderr = self.stderr.rstrip()
964 if stderr:
965 stderr = "\nstderr:\n%s" % stderr
966
967 return ("* Command: %s\n"
968 "Exit status: %s\n"
969 "Duration: %s\n"
970 "%s"
971 "%s"
972 % (wrapper.fill(self.command), self.exit_status,
973 self.duration, stdout, stderr))
mbligh63073c92008-03-31 16:49:32 +0000974
975
mbligh462c0152008-03-13 15:37:10 +0000976class run_randomly:
jadmanski0afbb632008-06-06 21:10:57 +0000977 def __init__(self, run_sequentially=False):
978 # Run sequentially is for debugging control files
979 self.test_list = []
980 self.run_sequentially = run_sequentially
mbligh462c0152008-03-13 15:37:10 +0000981
982
jadmanski0afbb632008-06-06 21:10:57 +0000983 def add(self, *args, **dargs):
984 test = (args, dargs)
985 self.test_list.append(test)
mbligh462c0152008-03-13 15:37:10 +0000986
987
jadmanski0afbb632008-06-06 21:10:57 +0000988 def run(self, fn):
989 while self.test_list:
990 test_index = random.randint(0, len(self.test_list)-1)
991 if self.run_sequentially:
992 test_index = 0
993 (args, dargs) = self.test_list.pop(test_index)
994 fn(*args, **dargs)
mbligha7007722009-01-13 00:37:11 +0000995
996
showard5deef7f2009-09-09 18:16:58 +0000997def import_site_module(path, module, dummy=None, modulefile=None):
998 """
999 Try to import the site specific module if it exists.
1000
1001 @param path full filename of the source file calling this (ie __file__)
1002 @param module full module name
1003 @param dummy dummy value to return in case there is no symbol to import
1004 @param modulefile module filename
1005
1006 @return site specific module or dummy
1007
1008 @raises ImportError if the site file exists but imports fails
1009 """
1010 short_module = module[module.rfind(".") + 1:]
1011
1012 if not modulefile:
1013 modulefile = short_module + ".py"
1014
1015 if os.path.exists(os.path.join(os.path.dirname(path), modulefile)):
1016 return __import__(module, {}, {}, [short_module])
1017 return dummy
1018
1019
mblighdd669372009-02-03 21:57:18 +00001020def import_site_symbol(path, module, name, dummy=None, modulefile=None):
1021 """
1022 Try to import site specific symbol from site specific file if it exists
1023
1024 @param path full filename of the source file calling this (ie __file__)
1025 @param module full module name
1026 @param name symbol name to be imported from the site file
1027 @param dummy dummy value to return in case there is no symbol to import
1028 @param modulefile module filename
1029
1030 @return site specific symbol or dummy
1031
showard5deef7f2009-09-09 18:16:58 +00001032 @raises ImportError if the site file exists but imports fails
mblighdd669372009-02-03 21:57:18 +00001033 """
showard5deef7f2009-09-09 18:16:58 +00001034 module = import_site_module(path, module, modulefile=modulefile)
1035 if not module:
1036 return dummy
mbligha7007722009-01-13 00:37:11 +00001037
showard5deef7f2009-09-09 18:16:58 +00001038 # special unique value to tell us if the symbol can't be imported
1039 cant_import = object()
mbligha7007722009-01-13 00:37:11 +00001040
showard5deef7f2009-09-09 18:16:58 +00001041 obj = getattr(module, name, cant_import)
1042 if obj is cant_import:
1043 logging.error("unable to import site symbol '%s', using non-site "
1044 "implementation", name)
1045 return dummy
mbligh062ed152009-01-13 00:57:14 +00001046
1047 return obj
1048
1049
1050def import_site_class(path, module, classname, baseclass, modulefile=None):
1051 """
1052 Try to import site specific class from site specific file if it exists
1053
1054 Args:
1055 path: full filename of the source file calling this (ie __file__)
1056 module: full module name
1057 classname: class name to be loaded from site file
mbligh0a8c3322009-04-28 18:32:19 +00001058 baseclass: base class object to return when no site file present or
1059 to mixin when site class exists but is not inherited from baseclass
mbligh062ed152009-01-13 00:57:14 +00001060 modulefile: module filename
1061
mbligh0a8c3322009-04-28 18:32:19 +00001062 Returns: baseclass if site specific class does not exist, the site specific
1063 class if it exists and is inherited from baseclass or a mixin of the
1064 site specific class and baseclass when the site specific class exists
1065 and is not inherited from baseclass
mbligh062ed152009-01-13 00:57:14 +00001066
1067 Raises: ImportError if the site file exists but imports fails
1068 """
1069
mblighdd669372009-02-03 21:57:18 +00001070 res = import_site_symbol(path, module, classname, None, modulefile)
mbligh0a8c3322009-04-28 18:32:19 +00001071 if res:
1072 if not issubclass(res, baseclass):
1073 # if not a subclass of baseclass then mix in baseclass with the
1074 # site specific class object and return the result
1075 res = type(classname, (res, baseclass), {})
1076 else:
1077 res = baseclass
mbligha7007722009-01-13 00:37:11 +00001078
mbligh062ed152009-01-13 00:57:14 +00001079 return res
1080
1081
1082def import_site_function(path, module, funcname, dummy, modulefile=None):
1083 """
1084 Try to import site specific function from site specific file if it exists
1085
1086 Args:
1087 path: full filename of the source file calling this (ie __file__)
1088 module: full module name
1089 funcname: function name to be imported from site file
1090 dummy: dummy function to return in case there is no function to import
1091 modulefile: module filename
1092
1093 Returns: site specific function object or dummy
1094
1095 Raises: ImportError if the site file exists but imports fails
1096 """
1097
mblighdd669372009-02-03 21:57:18 +00001098 return import_site_symbol(path, module, funcname, dummy, modulefile)
mblighfb676032009-04-01 18:25:38 +00001099
1100
showard549afad2009-08-20 23:33:36 +00001101def _get_pid_path(program_name):
1102 my_path = os.path.dirname(__file__)
1103 return os.path.abspath(os.path.join(my_path, "..", "..",
1104 "%s.pid" % program_name))
1105
1106
mblighfb676032009-04-01 18:25:38 +00001107def write_pid(program_name):
1108 """
1109 Try to drop <program_name>.pid in the main autotest directory.
1110
1111 Args:
1112 program_name: prefix for file name
1113 """
showard549afad2009-08-20 23:33:36 +00001114 pidfile = open(_get_pid_path(program_name), "w")
1115 try:
1116 pidfile.write("%s\n" % os.getpid())
1117 finally:
1118 pidfile.close()
mblighfb676032009-04-01 18:25:38 +00001119
showard549afad2009-08-20 23:33:36 +00001120
1121def delete_pid_file_if_exists(program_name):
1122 """
1123 Tries to remove <program_name>.pid from the main autotest directory.
1124 """
1125 pidfile_path = _get_pid_path(program_name)
1126
1127 try:
1128 os.remove(pidfile_path)
1129 except OSError:
1130 if not os.path.exists(pidfile_path):
1131 return
1132 raise
1133
1134
1135def get_pid_from_file(program_name):
1136 """
1137 Reads the pid from <program_name>.pid in the autotest directory.
1138
1139 @param program_name the name of the program
1140 @return the pid if the file exists, None otherwise.
1141 """
1142 pidfile_path = _get_pid_path(program_name)
1143 if not os.path.exists(pidfile_path):
1144 return None
1145
1146 pidfile = open(_get_pid_path(program_name), 'r')
1147
1148 try:
1149 try:
1150 pid = int(pidfile.readline())
1151 except IOError:
1152 if not os.path.exists(pidfile_path):
1153 return None
1154 raise
1155 finally:
1156 pidfile.close()
1157
1158 return pid
1159
1160
showard8de37132009-08-31 18:33:08 +00001161def program_is_alive(program_name):
showard549afad2009-08-20 23:33:36 +00001162 """
1163 Checks if the process is alive and not in Zombie state.
1164
1165 @param program_name the name of the program
1166 @return True if still alive, False otherwise
1167 """
1168 pid = get_pid_from_file(program_name)
1169 if pid is None:
1170 return False
1171 return pid_is_alive(pid)
1172
1173
showard8de37132009-08-31 18:33:08 +00001174def signal_program(program_name, sig=signal.SIGTERM):
showard549afad2009-08-20 23:33:36 +00001175 """
1176 Sends a signal to the process listed in <program_name>.pid
1177
1178 @param program_name the name of the program
1179 @param sig signal to send
1180 """
1181 pid = get_pid_from_file(program_name)
1182 if pid:
1183 signal_pid(pid, sig)
mbligh45561782009-05-11 21:14:34 +00001184
1185
1186def get_relative_path(path, reference):
1187 """Given 2 absolute paths "path" and "reference", compute the path of
1188 "path" as relative to the directory "reference".
1189
1190 @param path the absolute path to convert to a relative path
1191 @param reference an absolute directory path to which the relative
1192 path will be computed
1193 """
1194 # normalize the paths (remove double slashes, etc)
1195 assert(os.path.isabs(path))
1196 assert(os.path.isabs(reference))
1197
1198 path = os.path.normpath(path)
1199 reference = os.path.normpath(reference)
1200
1201 # we could use os.path.split() but it splits from the end
1202 path_list = path.split(os.path.sep)[1:]
1203 ref_list = reference.split(os.path.sep)[1:]
1204
1205 # find the longest leading common path
1206 for i in xrange(min(len(path_list), len(ref_list))):
1207 if path_list[i] != ref_list[i]:
1208 # decrement i so when exiting this loop either by no match or by
1209 # end of range we are one step behind
1210 i -= 1
1211 break
1212 i += 1
1213 # drop the common part of the paths, not interested in that anymore
1214 del path_list[:i]
1215
1216 # for each uncommon component in the reference prepend a ".."
1217 path_list[:0] = ['..'] * (len(ref_list) - i)
1218
1219 return os.path.join(*path_list)
mbligh277a0e42009-07-11 00:11:45 +00001220
1221
1222def sh_escape(command):
1223 """
1224 Escape special characters from a command so that it can be passed
1225 as a double quoted (" ") string in a (ba)sh command.
1226
1227 Args:
1228 command: the command string to escape.
1229
1230 Returns:
1231 The escaped command string. The required englobing double
1232 quotes are NOT added and so should be added at some point by
1233 the caller.
1234
1235 See also: http://www.tldp.org/LDP/abs/html/escapingsection.html
1236 """
1237 command = command.replace("\\", "\\\\")
1238 command = command.replace("$", r'\$')
1239 command = command.replace('"', r'\"')
1240 command = command.replace('`', r'\`')
1241 return command