blob: a36b1d55e79f0402c211eef23ba3edc60a80364f [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
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
mblighde0d47e2008-03-28 14:37:18 +0000205def read_keyval(path):
jadmanski0afbb632008-06-06 21:10:57 +0000206 """
207 Read a key-value pair format file into a dictionary, and return it.
208 Takes either a filename or directory name as input. If it's a
209 directory name, we assume you want the file to be called keyval.
210 """
211 if os.path.isdir(path):
212 path = os.path.join(path, 'keyval')
213 keyval = {}
jadmanski58962982009-04-21 19:54:34 +0000214 if os.path.exists(path):
215 for line in open(path):
216 line = re.sub('#.*', '', line).rstrip()
217 if not re.search(r'^[-\.\w]+=', line):
218 raise ValueError('Invalid format line: %s' % line)
219 key, value = line.split('=', 1)
220 if re.search('^\d+$', value):
221 value = int(value)
222 elif re.search('^(\d+\.)?\d+$', value):
223 value = float(value)
224 keyval[key] = value
jadmanski0afbb632008-06-06 21:10:57 +0000225 return keyval
mblighde0d47e2008-03-28 14:37:18 +0000226
227
jadmanskicc549172008-05-21 18:11:51 +0000228def write_keyval(path, dictionary, type_tag=None):
jadmanski0afbb632008-06-06 21:10:57 +0000229 """
230 Write a key-value pair format file out to a file. This uses append
231 mode to open the file, so existing text will not be overwritten or
232 reparsed.
jadmanskicc549172008-05-21 18:11:51 +0000233
jadmanski0afbb632008-06-06 21:10:57 +0000234 If type_tag is None, then the key must be composed of alphanumeric
235 characters (or dashes+underscores). However, if type-tag is not
236 null then the keys must also have "{type_tag}" as a suffix. At
237 the moment the only valid values of type_tag are "attr" and "perf".
238 """
239 if os.path.isdir(path):
240 path = os.path.join(path, 'keyval')
241 keyval = open(path, 'a')
jadmanskicc549172008-05-21 18:11:51 +0000242
jadmanski0afbb632008-06-06 21:10:57 +0000243 if type_tag is None:
mbligh97227ea2009-03-11 17:09:50 +0000244 key_regex = re.compile(r'^[-\.\w]+$')
jadmanski0afbb632008-06-06 21:10:57 +0000245 else:
246 if type_tag not in ('attr', 'perf'):
247 raise ValueError('Invalid type tag: %s' % type_tag)
248 escaped_tag = re.escape(type_tag)
mbligh97227ea2009-03-11 17:09:50 +0000249 key_regex = re.compile(r'^[-\.\w]+\{%s\}$' % escaped_tag)
jadmanski0afbb632008-06-06 21:10:57 +0000250 try:
mbligh6955e232009-07-11 00:58:47 +0000251 for key in sorted(dictionary.keys()):
jadmanski0afbb632008-06-06 21:10:57 +0000252 if not key_regex.search(key):
253 raise ValueError('Invalid key: %s' % key)
mbligh6955e232009-07-11 00:58:47 +0000254 keyval.write('%s=%s\n' % (key, dictionary[key]))
jadmanski0afbb632008-06-06 21:10:57 +0000255 finally:
256 keyval.close()
mbligh6231cd62008-02-02 19:18:33 +0000257
258
259def is_url(path):
jadmanski0afbb632008-06-06 21:10:57 +0000260 """Return true if path looks like a URL"""
261 # for now, just handle http and ftp
262 url_parts = urlparse.urlparse(path)
263 return (url_parts[0] in ('http', 'ftp'))
mbligh6231cd62008-02-02 19:18:33 +0000264
265
mblighb2896192009-07-11 00:12:37 +0000266def urlopen(url, data=None, timeout=5):
267 """Wrapper to urllib2.urlopen with timeout addition."""
mbligh02ff2d52008-06-03 15:00:21 +0000268
jadmanski0afbb632008-06-06 21:10:57 +0000269 # Save old timeout
270 old_timeout = socket.getdefaulttimeout()
271 socket.setdefaulttimeout(timeout)
272 try:
mblighb2896192009-07-11 00:12:37 +0000273 return urllib2.urlopen(url, data=data)
jadmanski0afbb632008-06-06 21:10:57 +0000274 finally:
275 socket.setdefaulttimeout(old_timeout)
mbligh02ff2d52008-06-03 15:00:21 +0000276
277
mblighb2896192009-07-11 00:12:37 +0000278def urlretrieve(url, filename, data=None, timeout=300):
279 """Retrieve a file from given url."""
280 logging.debug('Fetching %s -> %s', url, filename)
281
282 src_file = urlopen(url, data=data, timeout=timeout)
jadmanski0afbb632008-06-06 21:10:57 +0000283 try:
mblighb2896192009-07-11 00:12:37 +0000284 dest_file = open(filename, 'wb')
285 try:
286 shutil.copyfileobj(src_file, dest_file)
287 finally:
288 dest_file.close()
jadmanski0afbb632008-06-06 21:10:57 +0000289 finally:
mblighb2896192009-07-11 00:12:37 +0000290 src_file.close()
jadmanski0afbb632008-06-06 21:10:57 +0000291
mbligh02ff2d52008-06-03 15:00:21 +0000292
lmrfed221f2010-02-04 03:08:30 +0000293def hash(type, input=None):
294 """
295 Returns an hash object of type md5 or sha1. This function is implemented in
296 order to encapsulate hash objects in a way that is compatible with python
297 2.4 and python 2.6 without warnings.
298
299 Note that even though python 2.6 hashlib supports hash types other than
300 md5 and sha1, we are artificially limiting the input values in order to
301 make the function to behave exactly the same among both python
302 implementations.
303
304 @param input: Optional input string that will be used to update the hash.
305 """
306 if type not in ['md5', 'sha1']:
307 raise ValueError("Unsupported hash type: %s" % type)
308
309 try:
310 hash = hashlib.new(type)
311 except NameError:
312 if type == 'md5':
313 hash = md5.new()
314 elif type == 'sha1':
315 hash = sha.new()
316
317 if input:
318 hash.update(input)
319
320 return hash
321
322
mbligh6231cd62008-02-02 19:18:33 +0000323def get_file(src, dest, permissions=None):
jadmanski0afbb632008-06-06 21:10:57 +0000324 """Get a file from src, which can be local or a remote URL"""
mbligh25284cd2009-06-08 16:17:24 +0000325 if src == dest:
jadmanski0afbb632008-06-06 21:10:57 +0000326 return
mbligh25284cd2009-06-08 16:17:24 +0000327
328 if is_url(src):
mblighb2896192009-07-11 00:12:37 +0000329 urlretrieve(src, dest)
jadmanski0afbb632008-06-06 21:10:57 +0000330 else:
331 shutil.copyfile(src, dest)
mbligh25284cd2009-06-08 16:17:24 +0000332
jadmanski0afbb632008-06-06 21:10:57 +0000333 if permissions:
334 os.chmod(dest, permissions)
335 return dest
mbligh6231cd62008-02-02 19:18:33 +0000336
337
338def unmap_url(srcdir, src, destdir='.'):
jadmanski0afbb632008-06-06 21:10:57 +0000339 """
340 Receives either a path to a local file or a URL.
341 returns either the path to the local file, or the fetched URL
mbligh6231cd62008-02-02 19:18:33 +0000342
jadmanski0afbb632008-06-06 21:10:57 +0000343 unmap_url('/usr/src', 'foo.tar', '/tmp')
344 = '/usr/src/foo.tar'
345 unmap_url('/usr/src', 'http://site/file', '/tmp')
346 = '/tmp/file'
347 (after retrieving it)
348 """
349 if is_url(src):
350 url_parts = urlparse.urlparse(src)
351 filename = os.path.basename(url_parts[2])
352 dest = os.path.join(destdir, filename)
353 return get_file(src, dest)
354 else:
355 return os.path.join(srcdir, src)
mbligh6231cd62008-02-02 19:18:33 +0000356
357
358def update_version(srcdir, preserve_srcdir, new_version, install,
jadmanski0afbb632008-06-06 21:10:57 +0000359 *args, **dargs):
360 """
361 Make sure srcdir is version new_version
mbligh6231cd62008-02-02 19:18:33 +0000362
jadmanski0afbb632008-06-06 21:10:57 +0000363 If not, delete it and install() the new version.
mbligh6231cd62008-02-02 19:18:33 +0000364
jadmanski0afbb632008-06-06 21:10:57 +0000365 In the preserve_srcdir case, we just check it's up to date,
366 and if not, we rerun install, without removing srcdir
367 """
368 versionfile = os.path.join(srcdir, '.version')
369 install_needed = True
mbligh6231cd62008-02-02 19:18:33 +0000370
jadmanski0afbb632008-06-06 21:10:57 +0000371 if os.path.exists(versionfile):
372 old_version = pickle.load(open(versionfile))
373 if old_version == new_version:
374 install_needed = False
mbligh6231cd62008-02-02 19:18:33 +0000375
jadmanski0afbb632008-06-06 21:10:57 +0000376 if install_needed:
377 if not preserve_srcdir and os.path.exists(srcdir):
378 shutil.rmtree(srcdir)
379 install(*args, **dargs)
380 if os.path.exists(srcdir):
381 pickle.dump(new_version, open(versionfile, 'w'))
mbligh462c0152008-03-13 15:37:10 +0000382
383
showardb45a4662009-07-15 14:27:56 +0000384def get_stderr_level(stderr_is_expected):
385 if stderr_is_expected:
386 return DEFAULT_STDOUT_LEVEL
387 return DEFAULT_STDERR_LEVEL
388
389
mbligh63073c92008-03-31 16:49:32 +0000390def run(command, timeout=None, ignore_status=False,
showardb45a4662009-07-15 14:27:56 +0000391 stdout_tee=None, stderr_tee=None, verbose=True, stdin=None,
mblighc823e8d2009-10-02 00:01:35 +0000392 stderr_is_expected=None, args=()):
jadmanski0afbb632008-06-06 21:10:57 +0000393 """
394 Run a command on the host.
mbligh63073c92008-03-31 16:49:32 +0000395
mblighc823e8d2009-10-02 00:01:35 +0000396 @param command: the command line string.
397 @param timeout: time limit in seconds before attempting to kill the
398 running process. The run() function will take a few seconds
399 longer than 'timeout' to complete if it has to kill the process.
400 @param ignore_status: do not raise an exception, no matter what the exit
401 code of the command is.
402 @param stdout_tee: optional file-like object to which stdout data
403 will be written as it is generated (data will still be stored
404 in result.stdout).
405 @param stderr_tee: likewise for stderr.
406 @param verbose: if True, log the command being run.
jadmanski093a0682009-10-13 14:55:43 +0000407 @param stdin: stdin to pass to the executed process (can be a file
408 descriptor, a file object of a real file or a string).
mblighc823e8d2009-10-02 00:01:35 +0000409 @param args: sequence of strings of arguments to be given to the command
410 inside " quotes after they have been escaped for that; each
411 element in the sequence will be given as a separate command
412 argument
mbligh63073c92008-03-31 16:49:32 +0000413
mblighc823e8d2009-10-02 00:01:35 +0000414 @return a CmdResult object
mbligh63073c92008-03-31 16:49:32 +0000415
mblighc823e8d2009-10-02 00:01:35 +0000416 @raise CmdError: the exit code of the command execution was not 0
jadmanski0afbb632008-06-06 21:10:57 +0000417 """
mbligh2c7f7d62009-12-19 05:24:04 +0000418 if isinstance(args, basestring):
419 raise TypeError('Got a string for the "args" keyword argument, '
420 'need a sequence.')
421
mblighc823e8d2009-10-02 00:01:35 +0000422 for arg in args:
423 command += ' "%s"' % sh_escape(arg)
showardb45a4662009-07-15 14:27:56 +0000424 if stderr_is_expected is None:
425 stderr_is_expected = ignore_status
mblighc823e8d2009-10-02 00:01:35 +0000426
showard170873e2009-01-07 00:22:26 +0000427 bg_job = join_bg_jobs(
showardb45a4662009-07-15 14:27:56 +0000428 (BgJob(command, stdout_tee, stderr_tee, verbose, stdin=stdin,
429 stderr_level=get_stderr_level(stderr_is_expected)),),
showard170873e2009-01-07 00:22:26 +0000430 timeout)[0]
mbligh849a0f62008-08-28 20:12:19 +0000431 if not ignore_status and bg_job.result.exit_status:
jadmanski9c1098b2008-09-02 14:18:48 +0000432 raise error.CmdError(command, bg_job.result,
mbligh849a0f62008-08-28 20:12:19 +0000433 "Command returned non-zero exit status")
mbligh63073c92008-03-31 16:49:32 +0000434
mbligh849a0f62008-08-28 20:12:19 +0000435 return bg_job.result
mbligh63073c92008-03-31 16:49:32 +0000436
mbligh45ffc432008-12-09 23:35:17 +0000437
mbligha5630a52008-09-03 22:09:50 +0000438def run_parallel(commands, timeout=None, ignore_status=False,
439 stdout_tee=None, stderr_tee=None):
jadmanski093a0682009-10-13 14:55:43 +0000440 """
441 Behaves the same as run() with the following exceptions:
mbligha5630a52008-09-03 22:09:50 +0000442
443 - commands is a list of commands to run in parallel.
444 - ignore_status toggles whether or not an exception should be raised
445 on any error.
446
jadmanski093a0682009-10-13 14:55:43 +0000447 @return: a list of CmdResult objects
mbligha5630a52008-09-03 22:09:50 +0000448 """
449 bg_jobs = []
450 for command in commands:
showardb45a4662009-07-15 14:27:56 +0000451 bg_jobs.append(BgJob(command, stdout_tee, stderr_tee,
452 stderr_level=get_stderr_level(ignore_status)))
mbligha5630a52008-09-03 22:09:50 +0000453
454 # Updates objects in bg_jobs list with their process information
455 join_bg_jobs(bg_jobs, timeout)
456
457 for bg_job in bg_jobs:
458 if not ignore_status and bg_job.result.exit_status:
459 raise error.CmdError(command, bg_job.result,
460 "Command returned non-zero exit status")
461
462 return [bg_job.result for bg_job in bg_jobs]
463
464
mbligh849a0f62008-08-28 20:12:19 +0000465@deprecated
mbligh63073c92008-03-31 16:49:32 +0000466def run_bg(command):
mbligh849a0f62008-08-28 20:12:19 +0000467 """Function deprecated. Please use BgJob class instead."""
468 bg_job = BgJob(command)
469 return bg_job.sp, bg_job.result
mbligh63073c92008-03-31 16:49:32 +0000470
471
mbligh849a0f62008-08-28 20:12:19 +0000472def join_bg_jobs(bg_jobs, timeout=None):
mbligha5630a52008-09-03 22:09:50 +0000473 """Joins the bg_jobs with the current thread.
474
475 Returns the same list of bg_jobs objects that was passed in.
476 """
mblighae69f262009-04-17 20:14:56 +0000477 ret, timeout_error = 0, False
mbligh849a0f62008-08-28 20:12:19 +0000478 for bg_job in bg_jobs:
479 bg_job.output_prepare(StringIO.StringIO(), StringIO.StringIO())
mbligh63073c92008-03-31 16:49:32 +0000480
jadmanski0afbb632008-06-06 21:10:57 +0000481 try:
482 # We are holding ends to stdin, stdout pipes
483 # hence we need to be sure to close those fds no mater what
484 start_time = time.time()
mbligh849a0f62008-08-28 20:12:19 +0000485 timeout_error = _wait_for_commands(bg_jobs, start_time, timeout)
486
487 for bg_job in bg_jobs:
488 # Process stdout and stderr
489 bg_job.process_output(stdout=True,final_read=True)
490 bg_job.process_output(stdout=False,final_read=True)
jadmanski0afbb632008-06-06 21:10:57 +0000491 finally:
492 # close our ends of the pipes to the sp no matter what
mbligh849a0f62008-08-28 20:12:19 +0000493 for bg_job in bg_jobs:
494 bg_job.cleanup()
mbligh63073c92008-03-31 16:49:32 +0000495
mbligh849a0f62008-08-28 20:12:19 +0000496 if timeout_error:
497 # TODO: This needs to be fixed to better represent what happens when
498 # running in parallel. However this is backwards compatable, so it will
499 # do for the time being.
500 raise error.CmdError(bg_jobs[0].command, bg_jobs[0].result,
501 "Command(s) did not complete within %d seconds"
502 % timeout)
mbligh63073c92008-03-31 16:49:32 +0000503
mbligh63073c92008-03-31 16:49:32 +0000504
mbligh849a0f62008-08-28 20:12:19 +0000505 return bg_jobs
mbligh63073c92008-03-31 16:49:32 +0000506
mbligh849a0f62008-08-28 20:12:19 +0000507
508def _wait_for_commands(bg_jobs, start_time, timeout):
509 # This returns True if it must return due to a timeout, otherwise False.
510
mblighf0b4a0a2008-09-03 20:46:16 +0000511 # To check for processes which terminate without producing any output
512 # a 1 second timeout is used in select.
513 SELECT_TIMEOUT = 1
514
jadmanski093a0682009-10-13 14:55:43 +0000515 read_list = []
516 write_list = []
mbligh849a0f62008-08-28 20:12:19 +0000517 reverse_dict = {}
jadmanski093a0682009-10-13 14:55:43 +0000518
mbligh849a0f62008-08-28 20:12:19 +0000519 for bg_job in bg_jobs:
jadmanski093a0682009-10-13 14:55:43 +0000520 read_list.append(bg_job.sp.stdout)
521 read_list.append(bg_job.sp.stderr)
522 reverse_dict[bg_job.sp.stdout] = (bg_job, True)
523 reverse_dict[bg_job.sp.stderr] = (bg_job, False)
524 if bg_job.string_stdin:
525 write_list.append(bg_job.sp.stdin)
526 reverse_dict[bg_job.sp.stdin] = bg_job
mbligh849a0f62008-08-28 20:12:19 +0000527
jadmanski0afbb632008-06-06 21:10:57 +0000528 if timeout:
529 stop_time = start_time + timeout
530 time_left = stop_time - time.time()
531 else:
532 time_left = None # so that select never times out
jadmanski093a0682009-10-13 14:55:43 +0000533
jadmanski0afbb632008-06-06 21:10:57 +0000534 while not timeout or time_left > 0:
jadmanski093a0682009-10-13 14:55:43 +0000535 # select will return when we may write to stdin or when there is
536 # stdout/stderr output we can read (including when it is
jadmanski0afbb632008-06-06 21:10:57 +0000537 # EOF, that is the process has terminated).
jadmanski093a0682009-10-13 14:55:43 +0000538 read_ready, write_ready, _ = select.select(read_list, write_list, [],
539 SELECT_TIMEOUT)
mbligh849a0f62008-08-28 20:12:19 +0000540
jadmanski0afbb632008-06-06 21:10:57 +0000541 # os.read() has to be used instead of
542 # subproc.stdout.read() which will otherwise block
jadmanski093a0682009-10-13 14:55:43 +0000543 for file_obj in read_ready:
544 bg_job, is_stdout = reverse_dict[file_obj]
545 bg_job.process_output(is_stdout)
mbligh63073c92008-03-31 16:49:32 +0000546
jadmanski093a0682009-10-13 14:55:43 +0000547 for file_obj in write_ready:
548 # we can write PIPE_BUF bytes without blocking
549 # POSIX requires PIPE_BUF is >= 512
550 bg_job = reverse_dict[file_obj]
551 file_obj.write(bg_job.string_stdin[:512])
552 bg_job.string_stdin = bg_job.string_stdin[512:]
553 # no more input data, close stdin, remove it from the select set
554 if not bg_job.string_stdin:
555 file_obj.close()
556 write_list.remove(file_obj)
557 del reverse_dict[file_obj]
558
559 all_jobs_finished = True
560 for bg_job in bg_jobs:
561 if bg_job.result.exit_status is not None:
562 continue
563
mbligh849a0f62008-08-28 20:12:19 +0000564 bg_job.result.exit_status = bg_job.sp.poll()
jadmanski093a0682009-10-13 14:55:43 +0000565 if bg_job.result.exit_status is not None:
566 # process exited, remove its stdout/stdin from the select set
567 read_list.remove(bg_job.sp.stdout)
568 read_list.remove(bg_job.sp.stderr)
569 del reverse_dict[bg_job.sp.stdout]
570 del reverse_dict[bg_job.sp.stderr]
571 else:
572 all_jobs_finished = False
573
574 if all_jobs_finished:
575 return False
mbligh8ea61e22008-05-09 18:09:37 +0000576
jadmanski0afbb632008-06-06 21:10:57 +0000577 if timeout:
578 time_left = stop_time - time.time()
mbligh63073c92008-03-31 16:49:32 +0000579
mbligh849a0f62008-08-28 20:12:19 +0000580 # Kill all processes which did not complete prior to timeout
jadmanski093a0682009-10-13 14:55:43 +0000581 for bg_job in bg_jobs:
582 if bg_job.result.exit_status is not None:
583 continue
584
585 logging.warn('run process timeout (%s) fired on: %s', timeout,
586 bg_job.command)
mbligh849a0f62008-08-28 20:12:19 +0000587 nuke_subprocess(bg_job.sp)
mbligh095dc642008-10-01 03:41:35 +0000588 bg_job.result.exit_status = bg_job.sp.poll()
mbligh8ea61e22008-05-09 18:09:37 +0000589
mbligh849a0f62008-08-28 20:12:19 +0000590 return True
mbligh63073c92008-03-31 16:49:32 +0000591
592
showard549afad2009-08-20 23:33:36 +0000593def pid_is_alive(pid):
594 """
595 True if process pid exists and is not yet stuck in Zombie state.
596 Zombies are impossible to move between cgroups, etc.
597 pid can be integer, or text of integer.
598 """
599 path = '/proc/%s/stat' % pid
600
601 try:
602 stat = read_one_line(path)
603 except IOError:
604 if not os.path.exists(path):
605 # file went away
606 return False
607 raise
608
609 return stat.split()[2] != 'Z'
610
611
612def signal_pid(pid, sig):
613 """
614 Sends a signal to a process id. Returns True if the process terminated
615 successfully, False otherwise.
616 """
617 try:
618 os.kill(pid, sig)
619 except OSError:
620 # The process may have died before we could kill it.
621 pass
622
623 for i in range(5):
624 if not pid_is_alive(pid):
625 return True
626 time.sleep(1)
627
628 # The process is still alive
629 return False
630
631
mbligh63073c92008-03-31 16:49:32 +0000632def nuke_subprocess(subproc):
jadmanski09f92032008-09-17 14:05:27 +0000633 # check if the subprocess is still alive, first
634 if subproc.poll() is not None:
635 return subproc.poll()
636
jadmanski0afbb632008-06-06 21:10:57 +0000637 # the process has not terminated within timeout,
638 # kill it via an escalating series of signals.
639 signal_queue = [signal.SIGTERM, signal.SIGKILL]
640 for sig in signal_queue:
showard549afad2009-08-20 23:33:36 +0000641 signal_pid(subproc.pid, sig)
642 if subproc.poll() is not None:
643 return subproc.poll()
mbligh63073c92008-03-31 16:49:32 +0000644
645
showard786da9a2009-10-12 20:31:20 +0000646def nuke_pid(pid, signal_queue=(signal.SIGTERM, signal.SIGKILL)):
jadmanski0afbb632008-06-06 21:10:57 +0000647 # the process has not terminated within timeout,
648 # kill it via an escalating series of signals.
jadmanski0afbb632008-06-06 21:10:57 +0000649 for sig in signal_queue:
showard549afad2009-08-20 23:33:36 +0000650 if signal_pid(pid, sig):
651 return
mbligh63073c92008-03-31 16:49:32 +0000652
showard549afad2009-08-20 23:33:36 +0000653 # no signal successfully terminated the process
654 raise error.AutoservRunError('Could not kill %d' % pid, None)
mbligh298ed592009-06-15 21:52:57 +0000655
656
mbligh63073c92008-03-31 16:49:32 +0000657def system(command, timeout=None, ignore_status=False):
mbligh104a5382010-02-02 18:15:39 +0000658 """
659 Run a command
660
661 @param timeout: timeout in seconds
662 @param ignore_status: if ignore_status=False, throw an exception if the
663 command's exit code is non-zero
664 if ignore_stauts=True, return the exit code.
lmrfed221f2010-02-04 03:08:30 +0000665
mbligh104a5382010-02-02 18:15:39 +0000666 @return exit status of command
667 (note, this will always be zero unless ignore_status=True)
668 """
mblighf8dffb12008-10-29 16:45:26 +0000669 return run(command, timeout=timeout, ignore_status=ignore_status,
showard108d73e2009-06-22 18:14:41 +0000670 stdout_tee=TEE_TO_LOGS, stderr_tee=TEE_TO_LOGS).exit_status
mbligh63073c92008-03-31 16:49:32 +0000671
672
mbligha5630a52008-09-03 22:09:50 +0000673def system_parallel(commands, timeout=None, ignore_status=False):
674 """This function returns a list of exit statuses for the respective
675 list of commands."""
676 return [bg_jobs.exit_status for bg_jobs in
mblighf8dffb12008-10-29 16:45:26 +0000677 run_parallel(commands, timeout=timeout, ignore_status=ignore_status,
showard108d73e2009-06-22 18:14:41 +0000678 stdout_tee=TEE_TO_LOGS, stderr_tee=TEE_TO_LOGS)]
mbligh849a0f62008-08-28 20:12:19 +0000679
680
mbligh8ea61e22008-05-09 18:09:37 +0000681def system_output(command, timeout=None, ignore_status=False,
mblighc823e8d2009-10-02 00:01:35 +0000682 retain_output=False, args=()):
683 """
684 Run a command and return the stdout output.
685
686 @param command: command string to execute.
687 @param timeout: time limit in seconds before attempting to kill the
688 running process. The function will take a few seconds longer
689 than 'timeout' to complete if it has to kill the process.
690 @param ignore_status: do not raise an exception, no matter what the exit
691 code of the command is.
692 @param retain_output: set to True to make stdout/stderr of the command
693 output to be also sent to the logging system
694 @param args: sequence of strings of arguments to be given to the command
695 inside " quotes after they have been escaped for that; each
696 element in the sequence will be given as a separate command
697 argument
698
699 @return a string with the stdout output of the command.
700 """
jadmanski0afbb632008-06-06 21:10:57 +0000701 if retain_output:
mblighf8dffb12008-10-29 16:45:26 +0000702 out = run(command, timeout=timeout, ignore_status=ignore_status,
mblighc823e8d2009-10-02 00:01:35 +0000703 stdout_tee=TEE_TO_LOGS, stderr_tee=TEE_TO_LOGS,
704 args=args).stdout
jadmanski0afbb632008-06-06 21:10:57 +0000705 else:
mblighc823e8d2009-10-02 00:01:35 +0000706 out = run(command, timeout=timeout, ignore_status=ignore_status,
707 args=args).stdout
708 if out[-1:] == '\n':
709 out = out[:-1]
jadmanski0afbb632008-06-06 21:10:57 +0000710 return out
mbligh63073c92008-03-31 16:49:32 +0000711
mbligh849a0f62008-08-28 20:12:19 +0000712
mbligha5630a52008-09-03 22:09:50 +0000713def system_output_parallel(commands, timeout=None, ignore_status=False,
714 retain_output=False):
715 if retain_output:
showard108d73e2009-06-22 18:14:41 +0000716 out = [bg_job.stdout for bg_job
717 in run_parallel(commands, timeout=timeout,
718 ignore_status=ignore_status,
719 stdout_tee=TEE_TO_LOGS, stderr_tee=TEE_TO_LOGS)]
mbligha5630a52008-09-03 22:09:50 +0000720 else:
mblighf8dffb12008-10-29 16:45:26 +0000721 out = [bg_job.stdout for bg_job in run_parallel(commands,
722 timeout=timeout, ignore_status=ignore_status)]
mbligha5630a52008-09-03 22:09:50 +0000723 for x in out:
724 if out[-1:] == '\n': out = out[:-1]
725 return out
726
727
mbligh98467952008-11-19 00:25:45 +0000728def strip_unicode(input):
729 if type(input) == list:
730 return [strip_unicode(i) for i in input]
731 elif type(input) == dict:
732 output = {}
733 for key in input.keys():
734 output[str(key)] = strip_unicode(input[key])
735 return output
736 elif type(input) == unicode:
737 return str(input)
738 else:
739 return input
740
741
mbligha5630a52008-09-03 22:09:50 +0000742def get_cpu_percentage(function, *args, **dargs):
743 """Returns a tuple containing the CPU% and return value from function call.
744
745 This function calculates the usage time by taking the difference of
746 the user and system times both before and after the function call.
747 """
748 child_pre = resource.getrusage(resource.RUSAGE_CHILDREN)
749 self_pre = resource.getrusage(resource.RUSAGE_SELF)
750 start = time.time()
751 to_return = function(*args, **dargs)
752 elapsed = time.time() - start
753 self_post = resource.getrusage(resource.RUSAGE_SELF)
754 child_post = resource.getrusage(resource.RUSAGE_CHILDREN)
755
756 # Calculate CPU Percentage
757 s_user, s_system = [a - b for a, b in zip(self_post, self_pre)[:2]]
758 c_user, c_system = [a - b for a, b in zip(child_post, child_pre)[:2]]
759 cpu_percent = (s_user + c_user + s_system + c_system) / elapsed
760
761 return cpu_percent, to_return
762
763
mblighc1cbc992008-05-27 20:01:45 +0000764"""
765This function is used when there is a need to run more than one
766job simultaneously starting exactly at the same time. It basically returns
767a modified control file (containing the synchronization code prepended)
768whenever it is ready to run the control file. The synchronization
769is done using barriers to make sure that the jobs start at the same time.
770
771Here is how the synchronization is done to make sure that the tests
772start at exactly the same time on the client.
773sc_bar is a server barrier and s_bar, c_bar are the normal barriers
774
775 Job1 Job2 ...... JobN
776 Server: | sc_bar
777 Server: | s_bar ...... s_bar
778 Server: | at.run() at.run() ...... at.run()
779 ----------|------------------------------------------------------
780 Client | sc_bar
781 Client | c_bar c_bar ...... c_bar
782 Client | <run test> <run test> ...... <run test>
783
784
785PARAMS:
786 control_file : The control file which to which the above synchronization
787 code would be prepended to
788 host_name : The host name on which the job is going to run
789 host_num (non negative) : A number to identify the machine so that we have
790 different sets of s_bar_ports for each of the machines.
791 instance : The number of the job
792 num_jobs : Total number of jobs that are going to run in parallel with
793 this job starting at the same time
794 port_base : Port number that is used to derive the actual barrier ports.
795
796RETURN VALUE:
797 The modified control file.
798
799"""
800def get_sync_control_file(control, host_name, host_num,
jadmanski0afbb632008-06-06 21:10:57 +0000801 instance, num_jobs, port_base=63100):
802 sc_bar_port = port_base
803 c_bar_port = port_base
804 if host_num < 0:
805 print "Please provide a non negative number for the host"
806 return None
807 s_bar_port = port_base + 1 + host_num # The set of s_bar_ports are
808 # the same for a given machine
mblighc1cbc992008-05-27 20:01:45 +0000809
jadmanski0afbb632008-06-06 21:10:57 +0000810 sc_bar_timeout = 180
811 s_bar_timeout = c_bar_timeout = 120
mblighc1cbc992008-05-27 20:01:45 +0000812
jadmanski0afbb632008-06-06 21:10:57 +0000813 # The barrier code snippet is prepended into the conrol file
814 # dynamically before at.run() is called finally.
815 control_new = []
mblighc1cbc992008-05-27 20:01:45 +0000816
jadmanski0afbb632008-06-06 21:10:57 +0000817 # jobid is the unique name used to identify the processes
818 # trying to reach the barriers
819 jobid = "%s#%d" % (host_name, instance)
mblighc1cbc992008-05-27 20:01:45 +0000820
jadmanski0afbb632008-06-06 21:10:57 +0000821 rendv = []
822 # rendvstr is a temp holder for the rendezvous list of the processes
823 for n in range(num_jobs):
824 rendv.append("'%s#%d'" % (host_name, n))
825 rendvstr = ",".join(rendv)
mblighc1cbc992008-05-27 20:01:45 +0000826
jadmanski0afbb632008-06-06 21:10:57 +0000827 if instance == 0:
828 # Do the setup and wait at the server barrier
829 # Clean up the tmp and the control dirs for the first instance
830 control_new.append('if os.path.exists(job.tmpdir):')
831 control_new.append("\t system('umount -f %s > /dev/null"
832 "2> /dev/null' % job.tmpdir,"
833 "ignore_status=True)")
834 control_new.append("\t system('rm -rf ' + job.tmpdir)")
835 control_new.append(
836 'b0 = job.barrier("%s", "sc_bar", %d, port=%d)'
837 % (jobid, sc_bar_timeout, sc_bar_port))
838 control_new.append(
mbligh9c12f772009-06-22 19:03:55 +0000839 'b0.rendezvous_servers("PARALLEL_MASTER", "%s")'
jadmanski0afbb632008-06-06 21:10:57 +0000840 % jobid)
mblighc1cbc992008-05-27 20:01:45 +0000841
jadmanski0afbb632008-06-06 21:10:57 +0000842 elif instance == 1:
843 # Wait at the server barrier to wait for instance=0
844 # process to complete setup
845 b0 = barrier.barrier("PARALLEL_MASTER", "sc_bar", sc_bar_timeout,
846 port=sc_bar_port)
mbligh9c12f772009-06-22 19:03:55 +0000847 b0.rendezvous_servers("PARALLEL_MASTER", jobid)
mblighc1cbc992008-05-27 20:01:45 +0000848
jadmanski0afbb632008-06-06 21:10:57 +0000849 if(num_jobs > 2):
850 b1 = barrier.barrier(jobid, "s_bar", s_bar_timeout,
851 port=s_bar_port)
mbligh9c12f772009-06-22 19:03:55 +0000852 b1.rendezvous(rendvstr)
mblighc1cbc992008-05-27 20:01:45 +0000853
jadmanski0afbb632008-06-06 21:10:57 +0000854 else:
855 # For the rest of the clients
856 b2 = barrier.barrier(jobid, "s_bar", s_bar_timeout, port=s_bar_port)
mbligh9c12f772009-06-22 19:03:55 +0000857 b2.rendezvous(rendvstr)
mblighc1cbc992008-05-27 20:01:45 +0000858
jadmanski0afbb632008-06-06 21:10:57 +0000859 # Client side barrier for all the tests to start at the same time
860 control_new.append('b1 = job.barrier("%s", "c_bar", %d, port=%d)'
861 % (jobid, c_bar_timeout, c_bar_port))
mbligh9c12f772009-06-22 19:03:55 +0000862 control_new.append("b1.rendezvous(%s)" % rendvstr)
mblighc1cbc992008-05-27 20:01:45 +0000863
jadmanski0afbb632008-06-06 21:10:57 +0000864 # Stick in the rest of the control file
865 control_new.append(control)
mblighc1cbc992008-05-27 20:01:45 +0000866
jadmanski0afbb632008-06-06 21:10:57 +0000867 return "\n".join(control_new)
mblighc1cbc992008-05-27 20:01:45 +0000868
mbligh63073c92008-03-31 16:49:32 +0000869
mblighc5ddfd12008-08-04 17:15:00 +0000870def get_arch(run_function=run):
871 """
872 Get the hardware architecture of the machine.
873 run_function is used to execute the commands. It defaults to
874 utils.run() but a custom method (if provided) should be of the
875 same schema as utils.run. It should return a CmdResult object and
876 throw a CmdError exception.
877 """
878 arch = run_function('/bin/uname -m').stdout.rstrip()
879 if re.match(r'i\d86$', arch):
880 arch = 'i386'
881 return arch
882
883
showard4745ecd2009-05-26 19:34:56 +0000884def get_num_logical_cpus_per_socket(run_function=run):
mbligh9fd9afe2009-04-28 18:27:25 +0000885 """
886 Get the number of cores (including hyperthreading) per cpu.
887 run_function is used to execute the commands. It defaults to
888 utils.run() but a custom method (if provided) should be of the
889 same schema as utils.run. It should return a CmdResult object and
890 throw a CmdError exception.
891 """
showard4745ecd2009-05-26 19:34:56 +0000892 siblings = run_function('grep "^siblings" /proc/cpuinfo').stdout.rstrip()
893 num_siblings = map(int,
894 re.findall(r'^siblings\s*:\s*(\d+)\s*$',
895 siblings, re.M))
896 if len(num_siblings) == 0:
897 raise error.TestError('Unable to find siblings info in /proc/cpuinfo')
898 if min(num_siblings) != max(num_siblings):
899 raise error.TestError('Number of siblings differ %r' %
900 num_siblings)
901 return num_siblings[0]
mbligh9fd9afe2009-04-28 18:27:25 +0000902
903
jadmanski4f909252008-12-01 20:47:10 +0000904def merge_trees(src, dest):
905 """
906 Merges a source directory tree at 'src' into a destination tree at
907 'dest'. If a path is a file in both trees than the file in the source
908 tree is APPENDED to the one in the destination tree. If a path is
909 a directory in both trees then the directories are recursively merged
910 with this function. In any other case, the function will skip the
911 paths that cannot be merged (instead of failing).
912 """
913 if not os.path.exists(src):
914 return # exists only in dest
915 elif not os.path.exists(dest):
916 if os.path.isfile(src):
917 shutil.copy2(src, dest) # file only in src
918 else:
919 shutil.copytree(src, dest, symlinks=True) # dir only in src
920 return
921 elif os.path.isfile(src) and os.path.isfile(dest):
922 # src & dest are files in both trees, append src to dest
923 destfile = open(dest, "a")
924 try:
925 srcfile = open(src)
926 try:
927 destfile.write(srcfile.read())
928 finally:
929 srcfile.close()
930 finally:
931 destfile.close()
932 elif os.path.isdir(src) and os.path.isdir(dest):
933 # src & dest are directories in both trees, so recursively merge
934 for name in os.listdir(src):
935 merge_trees(os.path.join(src, name), os.path.join(dest, name))
936 else:
937 # src & dest both exist, but are incompatible
938 return
939
940
mbligh63073c92008-03-31 16:49:32 +0000941class CmdResult(object):
jadmanski0afbb632008-06-06 21:10:57 +0000942 """
943 Command execution result.
mbligh63073c92008-03-31 16:49:32 +0000944
jadmanski0afbb632008-06-06 21:10:57 +0000945 command: String containing the command line itself
946 exit_status: Integer exit code of the process
947 stdout: String containing stdout of the process
948 stderr: String containing stderr of the process
949 duration: Elapsed wall clock time running the process
950 """
mbligh63073c92008-03-31 16:49:32 +0000951
952
mblighcd63a212009-05-01 23:04:38 +0000953 def __init__(self, command="", stdout="", stderr="",
jadmanski0afbb632008-06-06 21:10:57 +0000954 exit_status=None, duration=0):
955 self.command = command
956 self.exit_status = exit_status
957 self.stdout = stdout
958 self.stderr = stderr
959 self.duration = duration
mbligh63073c92008-03-31 16:49:32 +0000960
961
jadmanski0afbb632008-06-06 21:10:57 +0000962 def __repr__(self):
963 wrapper = textwrap.TextWrapper(width = 78,
964 initial_indent="\n ",
965 subsequent_indent=" ")
966
967 stdout = self.stdout.rstrip()
968 if stdout:
969 stdout = "\nstdout:\n%s" % stdout
970
971 stderr = self.stderr.rstrip()
972 if stderr:
973 stderr = "\nstderr:\n%s" % stderr
974
975 return ("* Command: %s\n"
976 "Exit status: %s\n"
977 "Duration: %s\n"
978 "%s"
979 "%s"
980 % (wrapper.fill(self.command), self.exit_status,
981 self.duration, stdout, stderr))
mbligh63073c92008-03-31 16:49:32 +0000982
983
mbligh462c0152008-03-13 15:37:10 +0000984class run_randomly:
jadmanski0afbb632008-06-06 21:10:57 +0000985 def __init__(self, run_sequentially=False):
986 # Run sequentially is for debugging control files
987 self.test_list = []
988 self.run_sequentially = run_sequentially
mbligh462c0152008-03-13 15:37:10 +0000989
990
jadmanski0afbb632008-06-06 21:10:57 +0000991 def add(self, *args, **dargs):
992 test = (args, dargs)
993 self.test_list.append(test)
mbligh462c0152008-03-13 15:37:10 +0000994
995
jadmanski0afbb632008-06-06 21:10:57 +0000996 def run(self, fn):
997 while self.test_list:
998 test_index = random.randint(0, len(self.test_list)-1)
999 if self.run_sequentially:
1000 test_index = 0
1001 (args, dargs) = self.test_list.pop(test_index)
1002 fn(*args, **dargs)
mbligha7007722009-01-13 00:37:11 +00001003
1004
showard5deef7f2009-09-09 18:16:58 +00001005def import_site_module(path, module, dummy=None, modulefile=None):
1006 """
1007 Try to import the site specific module if it exists.
1008
1009 @param path full filename of the source file calling this (ie __file__)
1010 @param module full module name
1011 @param dummy dummy value to return in case there is no symbol to import
1012 @param modulefile module filename
1013
1014 @return site specific module or dummy
1015
1016 @raises ImportError if the site file exists but imports fails
1017 """
1018 short_module = module[module.rfind(".") + 1:]
1019
1020 if not modulefile:
1021 modulefile = short_module + ".py"
1022
1023 if os.path.exists(os.path.join(os.path.dirname(path), modulefile)):
1024 return __import__(module, {}, {}, [short_module])
1025 return dummy
1026
1027
mblighdd669372009-02-03 21:57:18 +00001028def import_site_symbol(path, module, name, dummy=None, modulefile=None):
1029 """
1030 Try to import site specific symbol from site specific file if it exists
1031
1032 @param path full filename of the source file calling this (ie __file__)
1033 @param module full module name
1034 @param name symbol name to be imported from the site file
1035 @param dummy dummy value to return in case there is no symbol to import
1036 @param modulefile module filename
1037
1038 @return site specific symbol or dummy
1039
showard5deef7f2009-09-09 18:16:58 +00001040 @raises ImportError if the site file exists but imports fails
mblighdd669372009-02-03 21:57:18 +00001041 """
showard5deef7f2009-09-09 18:16:58 +00001042 module = import_site_module(path, module, modulefile=modulefile)
1043 if not module:
1044 return dummy
mbligha7007722009-01-13 00:37:11 +00001045
showard5deef7f2009-09-09 18:16:58 +00001046 # special unique value to tell us if the symbol can't be imported
1047 cant_import = object()
mbligha7007722009-01-13 00:37:11 +00001048
showard5deef7f2009-09-09 18:16:58 +00001049 obj = getattr(module, name, cant_import)
1050 if obj is cant_import:
1051 logging.error("unable to import site symbol '%s', using non-site "
1052 "implementation", name)
1053 return dummy
mbligh062ed152009-01-13 00:57:14 +00001054
1055 return obj
1056
1057
1058def import_site_class(path, module, classname, baseclass, modulefile=None):
1059 """
1060 Try to import site specific class from site specific file if it exists
1061
1062 Args:
1063 path: full filename of the source file calling this (ie __file__)
1064 module: full module name
1065 classname: class name to be loaded from site file
mbligh0a8c3322009-04-28 18:32:19 +00001066 baseclass: base class object to return when no site file present or
1067 to mixin when site class exists but is not inherited from baseclass
mbligh062ed152009-01-13 00:57:14 +00001068 modulefile: module filename
1069
mbligh0a8c3322009-04-28 18:32:19 +00001070 Returns: baseclass if site specific class does not exist, the site specific
1071 class if it exists and is inherited from baseclass or a mixin of the
1072 site specific class and baseclass when the site specific class exists
1073 and is not inherited from baseclass
mbligh062ed152009-01-13 00:57:14 +00001074
1075 Raises: ImportError if the site file exists but imports fails
1076 """
1077
mblighdd669372009-02-03 21:57:18 +00001078 res = import_site_symbol(path, module, classname, None, modulefile)
mbligh0a8c3322009-04-28 18:32:19 +00001079 if res:
1080 if not issubclass(res, baseclass):
1081 # if not a subclass of baseclass then mix in baseclass with the
1082 # site specific class object and return the result
1083 res = type(classname, (res, baseclass), {})
1084 else:
1085 res = baseclass
mbligha7007722009-01-13 00:37:11 +00001086
mbligh062ed152009-01-13 00:57:14 +00001087 return res
1088
1089
1090def import_site_function(path, module, funcname, dummy, modulefile=None):
1091 """
1092 Try to import site specific function from site specific file if it exists
1093
1094 Args:
1095 path: full filename of the source file calling this (ie __file__)
1096 module: full module name
1097 funcname: function name to be imported from site file
1098 dummy: dummy function to return in case there is no function to import
1099 modulefile: module filename
1100
1101 Returns: site specific function object or dummy
1102
1103 Raises: ImportError if the site file exists but imports fails
1104 """
1105
mblighdd669372009-02-03 21:57:18 +00001106 return import_site_symbol(path, module, funcname, dummy, modulefile)
mblighfb676032009-04-01 18:25:38 +00001107
1108
showard549afad2009-08-20 23:33:36 +00001109def _get_pid_path(program_name):
1110 my_path = os.path.dirname(__file__)
1111 return os.path.abspath(os.path.join(my_path, "..", "..",
1112 "%s.pid" % program_name))
1113
1114
mblighfb676032009-04-01 18:25:38 +00001115def write_pid(program_name):
1116 """
1117 Try to drop <program_name>.pid in the main autotest directory.
1118
1119 Args:
1120 program_name: prefix for file name
1121 """
showard549afad2009-08-20 23:33:36 +00001122 pidfile = open(_get_pid_path(program_name), "w")
1123 try:
1124 pidfile.write("%s\n" % os.getpid())
1125 finally:
1126 pidfile.close()
mblighfb676032009-04-01 18:25:38 +00001127
showard549afad2009-08-20 23:33:36 +00001128
1129def delete_pid_file_if_exists(program_name):
1130 """
1131 Tries to remove <program_name>.pid from the main autotest directory.
1132 """
1133 pidfile_path = _get_pid_path(program_name)
1134
1135 try:
1136 os.remove(pidfile_path)
1137 except OSError:
1138 if not os.path.exists(pidfile_path):
1139 return
1140 raise
1141
1142
1143def get_pid_from_file(program_name):
1144 """
1145 Reads the pid from <program_name>.pid in the autotest directory.
1146
1147 @param program_name the name of the program
1148 @return the pid if the file exists, None otherwise.
1149 """
1150 pidfile_path = _get_pid_path(program_name)
1151 if not os.path.exists(pidfile_path):
1152 return None
1153
1154 pidfile = open(_get_pid_path(program_name), 'r')
1155
1156 try:
1157 try:
1158 pid = int(pidfile.readline())
1159 except IOError:
1160 if not os.path.exists(pidfile_path):
1161 return None
1162 raise
1163 finally:
1164 pidfile.close()
1165
1166 return pid
1167
1168
showard8de37132009-08-31 18:33:08 +00001169def program_is_alive(program_name):
showard549afad2009-08-20 23:33:36 +00001170 """
1171 Checks if the process is alive and not in Zombie state.
1172
1173 @param program_name the name of the program
1174 @return True if still alive, False otherwise
1175 """
1176 pid = get_pid_from_file(program_name)
1177 if pid is None:
1178 return False
1179 return pid_is_alive(pid)
1180
1181
showard8de37132009-08-31 18:33:08 +00001182def signal_program(program_name, sig=signal.SIGTERM):
showard549afad2009-08-20 23:33:36 +00001183 """
1184 Sends a signal to the process listed in <program_name>.pid
1185
1186 @param program_name the name of the program
1187 @param sig signal to send
1188 """
1189 pid = get_pid_from_file(program_name)
1190 if pid:
1191 signal_pid(pid, sig)
mbligh45561782009-05-11 21:14:34 +00001192
1193
1194def get_relative_path(path, reference):
1195 """Given 2 absolute paths "path" and "reference", compute the path of
1196 "path" as relative to the directory "reference".
1197
1198 @param path the absolute path to convert to a relative path
1199 @param reference an absolute directory path to which the relative
1200 path will be computed
1201 """
1202 # normalize the paths (remove double slashes, etc)
1203 assert(os.path.isabs(path))
1204 assert(os.path.isabs(reference))
1205
1206 path = os.path.normpath(path)
1207 reference = os.path.normpath(reference)
1208
1209 # we could use os.path.split() but it splits from the end
1210 path_list = path.split(os.path.sep)[1:]
1211 ref_list = reference.split(os.path.sep)[1:]
1212
1213 # find the longest leading common path
1214 for i in xrange(min(len(path_list), len(ref_list))):
1215 if path_list[i] != ref_list[i]:
1216 # decrement i so when exiting this loop either by no match or by
1217 # end of range we are one step behind
1218 i -= 1
1219 break
1220 i += 1
1221 # drop the common part of the paths, not interested in that anymore
1222 del path_list[:i]
1223
1224 # for each uncommon component in the reference prepend a ".."
1225 path_list[:0] = ['..'] * (len(ref_list) - i)
1226
1227 return os.path.join(*path_list)
mbligh277a0e42009-07-11 00:11:45 +00001228
1229
1230def sh_escape(command):
1231 """
1232 Escape special characters from a command so that it can be passed
1233 as a double quoted (" ") string in a (ba)sh command.
1234
1235 Args:
1236 command: the command string to escape.
1237
1238 Returns:
1239 The escaped command string. The required englobing double
1240 quotes are NOT added and so should be added at some point by
1241 the caller.
1242
1243 See also: http://www.tldp.org/LDP/abs/html/escapingsection.html
1244 """
1245 command = command.replace("\\", "\\\\")
1246 command = command.replace("$", r'\$')
1247 command = command.replace('"', r'\"')
1248 command = command.replace('`', r'\`')
1249 return command