blob: cd5a752d33804c4c1b2c33ee40ccf2a3462d7e72 [file] [log] [blame]
mbligh63073c92008-03-31 16:49:32 +00001#
2# Copyright 2008 Google Inc. Released under the GPL v2
3
mbligh849a0f62008-08-28 20:12:19 +00004import os, pickle, random, re, resource, select, shutil, signal, StringIO
mblighb2896192009-07-11 00:12:37 +00005import socket, struct, subprocess, sys, time, textwrap, urlparse
mbligh25284cd2009-06-08 16:17:24 +00006import warnings, smtplib, logging, urllib2
lmrfed221f2010-02-04 03:08:30 +00007try:
8 import hashlib
9except ImportError:
10 import md5, sha
mbligh999fb132010-04-23 17:22:03 +000011from autotest_lib.client.common_lib import error, logging_manager
mbligh81edd792008-08-26 16:54:02 +000012
mbligh849a0f62008-08-28 20:12:19 +000013def deprecated(func):
14 """This is a decorator which can be used to mark functions as deprecated.
15 It will result in a warning being emmitted when the function is used."""
16 def new_func(*args, **dargs):
17 warnings.warn("Call to deprecated function %s." % func.__name__,
18 category=DeprecationWarning)
19 return func(*args, **dargs)
20 new_func.__name__ = func.__name__
21 new_func.__doc__ = func.__doc__
22 new_func.__dict__.update(func.__dict__)
23 return new_func
24
25
showard108d73e2009-06-22 18:14:41 +000026class _NullStream(object):
27 def write(self, data):
28 pass
29
30
31 def flush(self):
32 pass
33
34
35TEE_TO_LOGS = object()
36_the_null_stream = _NullStream()
37
showardb45a4662009-07-15 14:27:56 +000038DEFAULT_STDOUT_LEVEL = logging.DEBUG
39DEFAULT_STDERR_LEVEL = logging.ERROR
40
showard39986a62009-12-10 21:41:53 +000041# prefixes for logging stdout/stderr of commands
42STDOUT_PREFIX = '[stdout] '
43STDERR_PREFIX = '[stderr] '
44
45
46def get_stream_tee_file(stream, level, prefix=''):
showard108d73e2009-06-22 18:14:41 +000047 if stream is None:
48 return _the_null_stream
49 if stream is TEE_TO_LOGS:
showard39986a62009-12-10 21:41:53 +000050 return logging_manager.LoggingFile(level=level, prefix=prefix)
showard108d73e2009-06-22 18:14:41 +000051 return stream
52
53
mbligh849a0f62008-08-28 20:12:19 +000054class BgJob(object):
showard170873e2009-01-07 00:22:26 +000055 def __init__(self, command, stdout_tee=None, stderr_tee=None, verbose=True,
showardb45a4662009-07-15 14:27:56 +000056 stdin=None, stderr_level=DEFAULT_STDERR_LEVEL):
mbligh849a0f62008-08-28 20:12:19 +000057 self.command = command
showard39986a62009-12-10 21:41:53 +000058 self.stdout_tee = get_stream_tee_file(stdout_tee, DEFAULT_STDOUT_LEVEL,
59 prefix=STDOUT_PREFIX)
60 self.stderr_tee = get_stream_tee_file(stderr_tee, stderr_level,
61 prefix=STDERR_PREFIX)
mbligh849a0f62008-08-28 20:12:19 +000062 self.result = CmdResult(command)
jadmanski093a0682009-10-13 14:55:43 +000063
64 # allow for easy stdin input by string, we'll let subprocess create
65 # a pipe for stdin input and we'll write to it in the wait loop
66 if isinstance(stdin, basestring):
67 self.string_stdin = stdin
68 stdin = subprocess.PIPE
69 else:
70 self.string_stdin = None
71
mblighbd96b452008-09-03 23:14:27 +000072 if verbose:
showardb18134f2009-03-20 20:52:18 +000073 logging.debug("Running '%s'" % command)
mbligh849a0f62008-08-28 20:12:19 +000074 self.sp = subprocess.Popen(command, stdout=subprocess.PIPE,
75 stderr=subprocess.PIPE,
76 preexec_fn=self._reset_sigpipe, shell=True,
showard170873e2009-01-07 00:22:26 +000077 executable="/bin/bash",
78 stdin=stdin)
mbligh849a0f62008-08-28 20:12:19 +000079
80
81 def output_prepare(self, stdout_file=None, stderr_file=None):
82 self.stdout_file = stdout_file
83 self.stderr_file = stderr_file
84
mbligh45ffc432008-12-09 23:35:17 +000085
mbligh849a0f62008-08-28 20:12:19 +000086 def process_output(self, stdout=True, final_read=False):
87 """output_prepare must be called prior to calling this"""
88 if stdout:
89 pipe, buf, tee = self.sp.stdout, self.stdout_file, self.stdout_tee
90 else:
91 pipe, buf, tee = self.sp.stderr, self.stderr_file, self.stderr_tee
92
93 if final_read:
94 # read in all the data we can from pipe and then stop
95 data = []
96 while select.select([pipe], [], [], 0)[0]:
97 data.append(os.read(pipe.fileno(), 1024))
98 if len(data[-1]) == 0:
99 break
100 data = "".join(data)
101 else:
102 # perform a single read
103 data = os.read(pipe.fileno(), 1024)
104 buf.write(data)
showard108d73e2009-06-22 18:14:41 +0000105 tee.write(data)
mbligh849a0f62008-08-28 20:12:19 +0000106
107
108 def cleanup(self):
showard108d73e2009-06-22 18:14:41 +0000109 self.stdout_tee.flush()
110 self.stderr_tee.flush()
mbligh849a0f62008-08-28 20:12:19 +0000111 self.sp.stdout.close()
112 self.sp.stderr.close()
113 self.result.stdout = self.stdout_file.getvalue()
114 self.result.stderr = self.stderr_file.getvalue()
115
116
117 def _reset_sigpipe(self):
118 signal.signal(signal.SIGPIPE, signal.SIG_DFL)
119
mbligh81edd792008-08-26 16:54:02 +0000120
121def ip_to_long(ip):
122 # !L is a long in network byte order
123 return struct.unpack('!L', socket.inet_aton(ip))[0]
124
125
126def long_to_ip(number):
127 # See above comment.
128 return socket.inet_ntoa(struct.pack('!L', number))
129
130
131def create_subnet_mask(bits):
mbligh81edd792008-08-26 16:54:02 +0000132 return (1 << 32) - (1 << 32-bits)
133
134
135def format_ip_with_mask(ip, mask_bits):
136 masked_ip = ip_to_long(ip) & create_subnet_mask(mask_bits)
137 return "%s/%s" % (long_to_ip(masked_ip), mask_bits)
mbligh6231cd62008-02-02 19:18:33 +0000138
mblighde0d47e2008-03-28 14:37:18 +0000139
jadmanskie80d4712008-10-03 16:15:59 +0000140def normalize_hostname(alias):
141 ip = socket.gethostbyname(alias)
142 return socket.gethostbyaddr(ip)[0]
143
144
mblighd6d043c2008-09-27 21:00:45 +0000145def get_ip_local_port_range():
146 match = re.match(r'\s*(\d+)\s*(\d+)\s*$',
147 read_one_line('/proc/sys/net/ipv4/ip_local_port_range'))
148 return (int(match.group(1)), int(match.group(2)))
149
150
151def set_ip_local_port_range(lower, upper):
152 write_one_line('/proc/sys/net/ipv4/ip_local_port_range',
153 '%d %d\n' % (lower, upper))
154
mbligh315b9412008-10-01 03:34:11 +0000155
mbligh45ffc432008-12-09 23:35:17 +0000156
157def send_email(mail_from, mail_to, subject, body):
158 """
159 Sends an email via smtp
160
161 mail_from: string with email address of sender
162 mail_to: string or list with email address(es) of recipients
163 subject: string with subject of email
164 body: (multi-line) string with body of email
165 """
166 if isinstance(mail_to, str):
167 mail_to = [mail_to]
168 msg = "From: %s\nTo: %s\nSubject: %s\n\n%s" % (mail_from, ','.join(mail_to),
169 subject, body)
170 try:
171 mailer = smtplib.SMTP('localhost')
172 try:
173 mailer.sendmail(mail_from, mail_to, msg)
174 finally:
175 mailer.quit()
176 except Exception, e:
177 # Emails are non-critical, not errors, but don't raise them
178 print "Sending email failed. Reason: %s" % repr(e)
179
180
jadmanski5182e162008-05-13 21:48:16 +0000181def read_one_line(filename):
mbligh6e8840c2008-07-11 18:05:49 +0000182 return open(filename, 'r').readline().rstrip('\n')
jadmanski5182e162008-05-13 21:48:16 +0000183
184
jamesrenc3940222010-02-19 21:57:37 +0000185def read_file(filename):
186 f = open(filename)
187 try:
188 return f.read()
189 finally:
190 f.close()
191
192
mblighb9d05512008-10-18 13:53:27 +0000193def write_one_line(filename, line):
194 open_write_close(filename, line.rstrip('\n') + '\n')
195
196
197def open_write_close(filename, data):
mbligh618ac9e2008-10-06 17:14:32 +0000198 f = open(filename, 'w')
mblighb9d05512008-10-18 13:53:27 +0000199 try:
200 f.write(data)
201 finally:
202 f.close()
jadmanski5182e162008-05-13 21:48:16 +0000203
204
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)
jamesrenf0f9dae2010-02-26 02:21:14 +0000524 if bg_job.string_stdin is not None:
jadmanski093a0682009-10-13 14:55:43 +0000525 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
mblighc5ddfd12008-08-04 17:15:00 +0000764def get_arch(run_function=run):
765 """
766 Get the hardware architecture of the machine.
767 run_function is used to execute the commands. It defaults to
768 utils.run() but a custom method (if provided) should be of the
769 same schema as utils.run. It should return a CmdResult object and
770 throw a CmdError exception.
771 """
772 arch = run_function('/bin/uname -m').stdout.rstrip()
773 if re.match(r'i\d86$', arch):
774 arch = 'i386'
775 return arch
776
777
showard4745ecd2009-05-26 19:34:56 +0000778def get_num_logical_cpus_per_socket(run_function=run):
mbligh9fd9afe2009-04-28 18:27:25 +0000779 """
780 Get the number of cores (including hyperthreading) per cpu.
781 run_function is used to execute the commands. It defaults to
782 utils.run() but a custom method (if provided) should be of the
783 same schema as utils.run. It should return a CmdResult object and
784 throw a CmdError exception.
785 """
showard4745ecd2009-05-26 19:34:56 +0000786 siblings = run_function('grep "^siblings" /proc/cpuinfo').stdout.rstrip()
787 num_siblings = map(int,
788 re.findall(r'^siblings\s*:\s*(\d+)\s*$',
789 siblings, re.M))
790 if len(num_siblings) == 0:
791 raise error.TestError('Unable to find siblings info in /proc/cpuinfo')
792 if min(num_siblings) != max(num_siblings):
793 raise error.TestError('Number of siblings differ %r' %
794 num_siblings)
795 return num_siblings[0]
mbligh9fd9afe2009-04-28 18:27:25 +0000796
797
jadmanski4f909252008-12-01 20:47:10 +0000798def merge_trees(src, dest):
799 """
800 Merges a source directory tree at 'src' into a destination tree at
801 'dest'. If a path is a file in both trees than the file in the source
802 tree is APPENDED to the one in the destination tree. If a path is
803 a directory in both trees then the directories are recursively merged
804 with this function. In any other case, the function will skip the
805 paths that cannot be merged (instead of failing).
806 """
807 if not os.path.exists(src):
808 return # exists only in dest
809 elif not os.path.exists(dest):
810 if os.path.isfile(src):
811 shutil.copy2(src, dest) # file only in src
812 else:
813 shutil.copytree(src, dest, symlinks=True) # dir only in src
814 return
815 elif os.path.isfile(src) and os.path.isfile(dest):
816 # src & dest are files in both trees, append src to dest
817 destfile = open(dest, "a")
818 try:
819 srcfile = open(src)
820 try:
821 destfile.write(srcfile.read())
822 finally:
823 srcfile.close()
824 finally:
825 destfile.close()
826 elif os.path.isdir(src) and os.path.isdir(dest):
827 # src & dest are directories in both trees, so recursively merge
828 for name in os.listdir(src):
829 merge_trees(os.path.join(src, name), os.path.join(dest, name))
830 else:
831 # src & dest both exist, but are incompatible
832 return
833
834
mbligh63073c92008-03-31 16:49:32 +0000835class CmdResult(object):
jadmanski0afbb632008-06-06 21:10:57 +0000836 """
837 Command execution result.
mbligh63073c92008-03-31 16:49:32 +0000838
jadmanski0afbb632008-06-06 21:10:57 +0000839 command: String containing the command line itself
840 exit_status: Integer exit code of the process
841 stdout: String containing stdout of the process
842 stderr: String containing stderr of the process
843 duration: Elapsed wall clock time running the process
844 """
mbligh63073c92008-03-31 16:49:32 +0000845
846
mblighcd63a212009-05-01 23:04:38 +0000847 def __init__(self, command="", stdout="", stderr="",
jadmanski0afbb632008-06-06 21:10:57 +0000848 exit_status=None, duration=0):
849 self.command = command
850 self.exit_status = exit_status
851 self.stdout = stdout
852 self.stderr = stderr
853 self.duration = duration
mbligh63073c92008-03-31 16:49:32 +0000854
855
jadmanski0afbb632008-06-06 21:10:57 +0000856 def __repr__(self):
857 wrapper = textwrap.TextWrapper(width = 78,
858 initial_indent="\n ",
859 subsequent_indent=" ")
860
861 stdout = self.stdout.rstrip()
862 if stdout:
863 stdout = "\nstdout:\n%s" % stdout
864
865 stderr = self.stderr.rstrip()
866 if stderr:
867 stderr = "\nstderr:\n%s" % stderr
868
869 return ("* Command: %s\n"
870 "Exit status: %s\n"
871 "Duration: %s\n"
872 "%s"
873 "%s"
874 % (wrapper.fill(self.command), self.exit_status,
875 self.duration, stdout, stderr))
mbligh63073c92008-03-31 16:49:32 +0000876
877
mbligh462c0152008-03-13 15:37:10 +0000878class run_randomly:
jadmanski0afbb632008-06-06 21:10:57 +0000879 def __init__(self, run_sequentially=False):
880 # Run sequentially is for debugging control files
881 self.test_list = []
882 self.run_sequentially = run_sequentially
mbligh462c0152008-03-13 15:37:10 +0000883
884
jadmanski0afbb632008-06-06 21:10:57 +0000885 def add(self, *args, **dargs):
886 test = (args, dargs)
887 self.test_list.append(test)
mbligh462c0152008-03-13 15:37:10 +0000888
889
jadmanski0afbb632008-06-06 21:10:57 +0000890 def run(self, fn):
891 while self.test_list:
892 test_index = random.randint(0, len(self.test_list)-1)
893 if self.run_sequentially:
894 test_index = 0
895 (args, dargs) = self.test_list.pop(test_index)
896 fn(*args, **dargs)
mbligha7007722009-01-13 00:37:11 +0000897
898
showard5deef7f2009-09-09 18:16:58 +0000899def import_site_module(path, module, dummy=None, modulefile=None):
900 """
901 Try to import the site specific module if it exists.
902
903 @param path full filename of the source file calling this (ie __file__)
904 @param module full module name
905 @param dummy dummy value to return in case there is no symbol to import
906 @param modulefile module filename
907
908 @return site specific module or dummy
909
910 @raises ImportError if the site file exists but imports fails
911 """
912 short_module = module[module.rfind(".") + 1:]
913
914 if not modulefile:
915 modulefile = short_module + ".py"
916
917 if os.path.exists(os.path.join(os.path.dirname(path), modulefile)):
918 return __import__(module, {}, {}, [short_module])
919 return dummy
920
921
mblighdd669372009-02-03 21:57:18 +0000922def import_site_symbol(path, module, name, dummy=None, modulefile=None):
923 """
924 Try to import site specific symbol from site specific file if it exists
925
926 @param path full filename of the source file calling this (ie __file__)
927 @param module full module name
928 @param name symbol name to be imported from the site file
929 @param dummy dummy value to return in case there is no symbol to import
930 @param modulefile module filename
931
932 @return site specific symbol or dummy
933
showard5deef7f2009-09-09 18:16:58 +0000934 @raises ImportError if the site file exists but imports fails
mblighdd669372009-02-03 21:57:18 +0000935 """
showard5deef7f2009-09-09 18:16:58 +0000936 module = import_site_module(path, module, modulefile=modulefile)
937 if not module:
938 return dummy
mbligha7007722009-01-13 00:37:11 +0000939
showard5deef7f2009-09-09 18:16:58 +0000940 # special unique value to tell us if the symbol can't be imported
941 cant_import = object()
mbligha7007722009-01-13 00:37:11 +0000942
showard5deef7f2009-09-09 18:16:58 +0000943 obj = getattr(module, name, cant_import)
944 if obj is cant_import:
mblighf989e582010-04-01 17:10:35 +0000945 logging.debug("unable to import site symbol '%s', using non-site "
showard5deef7f2009-09-09 18:16:58 +0000946 "implementation", name)
947 return dummy
mbligh062ed152009-01-13 00:57:14 +0000948
949 return obj
950
951
952def import_site_class(path, module, classname, baseclass, modulefile=None):
953 """
954 Try to import site specific class from site specific file if it exists
955
956 Args:
957 path: full filename of the source file calling this (ie __file__)
958 module: full module name
959 classname: class name to be loaded from site file
mbligh0a8c3322009-04-28 18:32:19 +0000960 baseclass: base class object to return when no site file present or
961 to mixin when site class exists but is not inherited from baseclass
mbligh062ed152009-01-13 00:57:14 +0000962 modulefile: module filename
963
mbligh0a8c3322009-04-28 18:32:19 +0000964 Returns: baseclass if site specific class does not exist, the site specific
965 class if it exists and is inherited from baseclass or a mixin of the
966 site specific class and baseclass when the site specific class exists
967 and is not inherited from baseclass
mbligh062ed152009-01-13 00:57:14 +0000968
969 Raises: ImportError if the site file exists but imports fails
970 """
971
mblighdd669372009-02-03 21:57:18 +0000972 res = import_site_symbol(path, module, classname, None, modulefile)
mbligh0a8c3322009-04-28 18:32:19 +0000973 if res:
974 if not issubclass(res, baseclass):
975 # if not a subclass of baseclass then mix in baseclass with the
976 # site specific class object and return the result
977 res = type(classname, (res, baseclass), {})
978 else:
979 res = baseclass
mbligha7007722009-01-13 00:37:11 +0000980
mbligh062ed152009-01-13 00:57:14 +0000981 return res
982
983
984def import_site_function(path, module, funcname, dummy, modulefile=None):
985 """
986 Try to import site specific function from site specific file if it exists
987
988 Args:
989 path: full filename of the source file calling this (ie __file__)
990 module: full module name
991 funcname: function name to be imported from site file
992 dummy: dummy function to return in case there is no function to import
993 modulefile: module filename
994
995 Returns: site specific function object or dummy
996
997 Raises: ImportError if the site file exists but imports fails
998 """
999
mblighdd669372009-02-03 21:57:18 +00001000 return import_site_symbol(path, module, funcname, dummy, modulefile)
mblighfb676032009-04-01 18:25:38 +00001001
1002
showard549afad2009-08-20 23:33:36 +00001003def _get_pid_path(program_name):
1004 my_path = os.path.dirname(__file__)
1005 return os.path.abspath(os.path.join(my_path, "..", "..",
1006 "%s.pid" % program_name))
1007
1008
mblighfb676032009-04-01 18:25:38 +00001009def write_pid(program_name):
1010 """
1011 Try to drop <program_name>.pid in the main autotest directory.
1012
1013 Args:
1014 program_name: prefix for file name
1015 """
showard549afad2009-08-20 23:33:36 +00001016 pidfile = open(_get_pid_path(program_name), "w")
1017 try:
1018 pidfile.write("%s\n" % os.getpid())
1019 finally:
1020 pidfile.close()
mblighfb676032009-04-01 18:25:38 +00001021
showard549afad2009-08-20 23:33:36 +00001022
1023def delete_pid_file_if_exists(program_name):
1024 """
1025 Tries to remove <program_name>.pid from the main autotest directory.
1026 """
1027 pidfile_path = _get_pid_path(program_name)
1028
1029 try:
1030 os.remove(pidfile_path)
1031 except OSError:
1032 if not os.path.exists(pidfile_path):
1033 return
1034 raise
1035
1036
1037def get_pid_from_file(program_name):
1038 """
1039 Reads the pid from <program_name>.pid in the autotest directory.
1040
1041 @param program_name the name of the program
1042 @return the pid if the file exists, None otherwise.
1043 """
1044 pidfile_path = _get_pid_path(program_name)
1045 if not os.path.exists(pidfile_path):
1046 return None
1047
1048 pidfile = open(_get_pid_path(program_name), 'r')
1049
1050 try:
1051 try:
1052 pid = int(pidfile.readline())
1053 except IOError:
1054 if not os.path.exists(pidfile_path):
1055 return None
1056 raise
1057 finally:
1058 pidfile.close()
1059
1060 return pid
1061
1062
showard8de37132009-08-31 18:33:08 +00001063def program_is_alive(program_name):
showard549afad2009-08-20 23:33:36 +00001064 """
1065 Checks if the process is alive and not in Zombie state.
1066
1067 @param program_name the name of the program
1068 @return True if still alive, False otherwise
1069 """
1070 pid = get_pid_from_file(program_name)
1071 if pid is None:
1072 return False
1073 return pid_is_alive(pid)
1074
1075
showard8de37132009-08-31 18:33:08 +00001076def signal_program(program_name, sig=signal.SIGTERM):
showard549afad2009-08-20 23:33:36 +00001077 """
1078 Sends a signal to the process listed in <program_name>.pid
1079
1080 @param program_name the name of the program
1081 @param sig signal to send
1082 """
1083 pid = get_pid_from_file(program_name)
1084 if pid:
1085 signal_pid(pid, sig)
mbligh45561782009-05-11 21:14:34 +00001086
1087
1088def get_relative_path(path, reference):
1089 """Given 2 absolute paths "path" and "reference", compute the path of
1090 "path" as relative to the directory "reference".
1091
1092 @param path the absolute path to convert to a relative path
1093 @param reference an absolute directory path to which the relative
1094 path will be computed
1095 """
1096 # normalize the paths (remove double slashes, etc)
1097 assert(os.path.isabs(path))
1098 assert(os.path.isabs(reference))
1099
1100 path = os.path.normpath(path)
1101 reference = os.path.normpath(reference)
1102
1103 # we could use os.path.split() but it splits from the end
1104 path_list = path.split(os.path.sep)[1:]
1105 ref_list = reference.split(os.path.sep)[1:]
1106
1107 # find the longest leading common path
1108 for i in xrange(min(len(path_list), len(ref_list))):
1109 if path_list[i] != ref_list[i]:
1110 # decrement i so when exiting this loop either by no match or by
1111 # end of range we are one step behind
1112 i -= 1
1113 break
1114 i += 1
1115 # drop the common part of the paths, not interested in that anymore
1116 del path_list[:i]
1117
1118 # for each uncommon component in the reference prepend a ".."
1119 path_list[:0] = ['..'] * (len(ref_list) - i)
1120
1121 return os.path.join(*path_list)
mbligh277a0e42009-07-11 00:11:45 +00001122
1123
1124def sh_escape(command):
1125 """
1126 Escape special characters from a command so that it can be passed
1127 as a double quoted (" ") string in a (ba)sh command.
1128
1129 Args:
1130 command: the command string to escape.
1131
1132 Returns:
1133 The escaped command string. The required englobing double
1134 quotes are NOT added and so should be added at some point by
1135 the caller.
1136
1137 See also: http://www.tldp.org/LDP/abs/html/escapingsection.html
1138 """
1139 command = command.replace("\\", "\\\\")
1140 command = command.replace("$", r'\$')
1141 command = command.replace('"', r'\"')
1142 command = command.replace('`', r'\`')
1143 return command
mblighab88fbb2010-04-08 18:31:13 +00001144
1145
1146def configure(extra=None, configure='./configure'):
1147 """
1148 Run configure passing in the correct host, build, and target options.
1149
1150 @param extra: extra command line arguments to pass to configure
1151 @param configure: which configure script to use
1152 """
1153 args = []
1154 if 'CHOST' in os.environ:
1155 args.append('--host=' + os.environ['CHOST'])
1156 if 'CBUILD' in os.environ:
1157 args.append('--build=' + os.environ['CBUILD'])
1158 if 'CTARGET' in os.environ:
1159 args.append('--target=' + os.environ['CTARGET'])
1160 if extra:
1161 args.append(extra)
1162
1163 system('%s %s' % (configure, ' '.join(args)))
1164
mbligh777db852010-04-22 21:59:57 +00001165
1166def compare_versions(ver1, ver2):
mbligh58609322010-05-03 17:58:20 +00001167 """Version number comparison between ver1 and ver2 strings.
mbligh777db852010-04-22 21:59:57 +00001168
mbligh58609322010-05-03 17:58:20 +00001169 >>> compare_tuple("1", "2")
1170 -1
1171 >>> compare_tuple("foo-1.1", "foo-1.2")
1172 -1
1173 >>> compare_tuple("1.2", "1.2a")
1174 -1
1175 >>> compare_tuple("1.2b", "1.2a")
1176 1
1177 >>> compare_tuple("1.3.5.3a", "1.3.5.3b")
1178 -1
mbligh777db852010-04-22 21:59:57 +00001179
1180 Args:
mbligh58609322010-05-03 17:58:20 +00001181 ver1: version string
1182 ver2: version string
mbligh777db852010-04-22 21:59:57 +00001183
1184 Returns:
1185 int: 1 if ver1 > ver2
1186 0 if ver1 == ver2
1187 -1 if ver1 < ver2
1188 """
mbligh58609322010-05-03 17:58:20 +00001189 ax = re.split('[.-]', ver1)
1190 ay = re.split('[.-]', ver2)
1191 while len(ax) > 0 and len(ay) > 0:
1192 cx = ax.pop(0)
1193 cy = ay.pop(0)
1194 maxlen = max(len(cx), len(cy))
1195 c = cmp(cx.zfill(maxlen), cy.zfill(maxlen))
1196 if c != 0:
1197 return c
1198 return cmp(len(ax), len(ay))