blob: 647166ccea81f84ee178b24672264134ab422612 [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
showard108d73e2009-06-22 18:14:41 +00007from autotest_lib.client.common_lib import error, barrier, logging_manager
mbligh81edd792008-08-26 16:54:02 +00008
mbligh849a0f62008-08-28 20:12:19 +00009def deprecated(func):
10 """This is a decorator which can be used to mark functions as deprecated.
11 It will result in a warning being emmitted when the function is used."""
12 def new_func(*args, **dargs):
13 warnings.warn("Call to deprecated function %s." % func.__name__,
14 category=DeprecationWarning)
15 return func(*args, **dargs)
16 new_func.__name__ = func.__name__
17 new_func.__doc__ = func.__doc__
18 new_func.__dict__.update(func.__dict__)
19 return new_func
20
21
showard108d73e2009-06-22 18:14:41 +000022class _NullStream(object):
23 def write(self, data):
24 pass
25
26
27 def flush(self):
28 pass
29
30
31TEE_TO_LOGS = object()
32_the_null_stream = _NullStream()
33
showardb45a4662009-07-15 14:27:56 +000034DEFAULT_STDOUT_LEVEL = logging.DEBUG
35DEFAULT_STDERR_LEVEL = logging.ERROR
36
showard108d73e2009-06-22 18:14:41 +000037def get_stream_tee_file(stream, level):
38 if stream is None:
39 return _the_null_stream
40 if stream is TEE_TO_LOGS:
41 return logging_manager.LoggingFile(level=level)
42 return stream
43
44
mbligh849a0f62008-08-28 20:12:19 +000045class BgJob(object):
showard170873e2009-01-07 00:22:26 +000046 def __init__(self, command, stdout_tee=None, stderr_tee=None, verbose=True,
showardb45a4662009-07-15 14:27:56 +000047 stdin=None, stderr_level=DEFAULT_STDERR_LEVEL):
mbligh849a0f62008-08-28 20:12:19 +000048 self.command = command
showardb45a4662009-07-15 14:27:56 +000049 self.stdout_tee = get_stream_tee_file(stdout_tee, DEFAULT_STDOUT_LEVEL)
50 self.stderr_tee = get_stream_tee_file(stderr_tee, stderr_level)
mbligh849a0f62008-08-28 20:12:19 +000051 self.result = CmdResult(command)
jadmanski093a0682009-10-13 14:55:43 +000052
53 # allow for easy stdin input by string, we'll let subprocess create
54 # a pipe for stdin input and we'll write to it in the wait loop
55 if isinstance(stdin, basestring):
56 self.string_stdin = stdin
57 stdin = subprocess.PIPE
58 else:
59 self.string_stdin = None
60
mblighbd96b452008-09-03 23:14:27 +000061 if verbose:
showardb18134f2009-03-20 20:52:18 +000062 logging.debug("Running '%s'" % command)
mbligh849a0f62008-08-28 20:12:19 +000063 self.sp = subprocess.Popen(command, stdout=subprocess.PIPE,
64 stderr=subprocess.PIPE,
65 preexec_fn=self._reset_sigpipe, shell=True,
showard170873e2009-01-07 00:22:26 +000066 executable="/bin/bash",
67 stdin=stdin)
mbligh849a0f62008-08-28 20:12:19 +000068
69
70 def output_prepare(self, stdout_file=None, stderr_file=None):
71 self.stdout_file = stdout_file
72 self.stderr_file = stderr_file
73
mbligh45ffc432008-12-09 23:35:17 +000074
mbligh849a0f62008-08-28 20:12:19 +000075 def process_output(self, stdout=True, final_read=False):
76 """output_prepare must be called prior to calling this"""
77 if stdout:
78 pipe, buf, tee = self.sp.stdout, self.stdout_file, self.stdout_tee
79 else:
80 pipe, buf, tee = self.sp.stderr, self.stderr_file, self.stderr_tee
81
82 if final_read:
83 # read in all the data we can from pipe and then stop
84 data = []
85 while select.select([pipe], [], [], 0)[0]:
86 data.append(os.read(pipe.fileno(), 1024))
87 if len(data[-1]) == 0:
88 break
89 data = "".join(data)
90 else:
91 # perform a single read
92 data = os.read(pipe.fileno(), 1024)
93 buf.write(data)
showard108d73e2009-06-22 18:14:41 +000094 tee.write(data)
mbligh849a0f62008-08-28 20:12:19 +000095
96
97 def cleanup(self):
showard108d73e2009-06-22 18:14:41 +000098 self.stdout_tee.flush()
99 self.stderr_tee.flush()
mbligh849a0f62008-08-28 20:12:19 +0000100 self.sp.stdout.close()
101 self.sp.stderr.close()
102 self.result.stdout = self.stdout_file.getvalue()
103 self.result.stderr = self.stderr_file.getvalue()
104
105
106 def _reset_sigpipe(self):
107 signal.signal(signal.SIGPIPE, signal.SIG_DFL)
108
mbligh81edd792008-08-26 16:54:02 +0000109
110def ip_to_long(ip):
111 # !L is a long in network byte order
112 return struct.unpack('!L', socket.inet_aton(ip))[0]
113
114
115def long_to_ip(number):
116 # See above comment.
117 return socket.inet_ntoa(struct.pack('!L', number))
118
119
120def create_subnet_mask(bits):
mbligh81edd792008-08-26 16:54:02 +0000121 return (1 << 32) - (1 << 32-bits)
122
123
124def format_ip_with_mask(ip, mask_bits):
125 masked_ip = ip_to_long(ip) & create_subnet_mask(mask_bits)
126 return "%s/%s" % (long_to_ip(masked_ip), mask_bits)
mbligh6231cd62008-02-02 19:18:33 +0000127
mblighde0d47e2008-03-28 14:37:18 +0000128
jadmanskie80d4712008-10-03 16:15:59 +0000129def normalize_hostname(alias):
130 ip = socket.gethostbyname(alias)
131 return socket.gethostbyaddr(ip)[0]
132
133
mblighd6d043c2008-09-27 21:00:45 +0000134def get_ip_local_port_range():
135 match = re.match(r'\s*(\d+)\s*(\d+)\s*$',
136 read_one_line('/proc/sys/net/ipv4/ip_local_port_range'))
137 return (int(match.group(1)), int(match.group(2)))
138
139
140def set_ip_local_port_range(lower, upper):
141 write_one_line('/proc/sys/net/ipv4/ip_local_port_range',
142 '%d %d\n' % (lower, upper))
143
mbligh315b9412008-10-01 03:34:11 +0000144
mbligh45ffc432008-12-09 23:35:17 +0000145
146def send_email(mail_from, mail_to, subject, body):
147 """
148 Sends an email via smtp
149
150 mail_from: string with email address of sender
151 mail_to: string or list with email address(es) of recipients
152 subject: string with subject of email
153 body: (multi-line) string with body of email
154 """
155 if isinstance(mail_to, str):
156 mail_to = [mail_to]
157 msg = "From: %s\nTo: %s\nSubject: %s\n\n%s" % (mail_from, ','.join(mail_to),
158 subject, body)
159 try:
160 mailer = smtplib.SMTP('localhost')
161 try:
162 mailer.sendmail(mail_from, mail_to, msg)
163 finally:
164 mailer.quit()
165 except Exception, e:
166 # Emails are non-critical, not errors, but don't raise them
167 print "Sending email failed. Reason: %s" % repr(e)
168
169
jadmanski5182e162008-05-13 21:48:16 +0000170def read_one_line(filename):
mbligh6e8840c2008-07-11 18:05:49 +0000171 return open(filename, 'r').readline().rstrip('\n')
jadmanski5182e162008-05-13 21:48:16 +0000172
173
mblighb9d05512008-10-18 13:53:27 +0000174def write_one_line(filename, line):
175 open_write_close(filename, line.rstrip('\n') + '\n')
176
177
178def open_write_close(filename, data):
mbligh618ac9e2008-10-06 17:14:32 +0000179 f = open(filename, 'w')
mblighb9d05512008-10-18 13:53:27 +0000180 try:
181 f.write(data)
182 finally:
183 f.close()
jadmanski5182e162008-05-13 21:48:16 +0000184
185
mblighde0d47e2008-03-28 14:37:18 +0000186def read_keyval(path):
jadmanski0afbb632008-06-06 21:10:57 +0000187 """
188 Read a key-value pair format file into a dictionary, and return it.
189 Takes either a filename or directory name as input. If it's a
190 directory name, we assume you want the file to be called keyval.
191 """
192 if os.path.isdir(path):
193 path = os.path.join(path, 'keyval')
194 keyval = {}
jadmanski58962982009-04-21 19:54:34 +0000195 if os.path.exists(path):
196 for line in open(path):
197 line = re.sub('#.*', '', line).rstrip()
198 if not re.search(r'^[-\.\w]+=', line):
199 raise ValueError('Invalid format line: %s' % line)
200 key, value = line.split('=', 1)
201 if re.search('^\d+$', value):
202 value = int(value)
203 elif re.search('^(\d+\.)?\d+$', value):
204 value = float(value)
205 keyval[key] = value
jadmanski0afbb632008-06-06 21:10:57 +0000206 return keyval
mblighde0d47e2008-03-28 14:37:18 +0000207
208
jadmanskicc549172008-05-21 18:11:51 +0000209def write_keyval(path, dictionary, type_tag=None):
jadmanski0afbb632008-06-06 21:10:57 +0000210 """
211 Write a key-value pair format file out to a file. This uses append
212 mode to open the file, so existing text will not be overwritten or
213 reparsed.
jadmanskicc549172008-05-21 18:11:51 +0000214
jadmanski0afbb632008-06-06 21:10:57 +0000215 If type_tag is None, then the key must be composed of alphanumeric
216 characters (or dashes+underscores). However, if type-tag is not
217 null then the keys must also have "{type_tag}" as a suffix. At
218 the moment the only valid values of type_tag are "attr" and "perf".
219 """
220 if os.path.isdir(path):
221 path = os.path.join(path, 'keyval')
222 keyval = open(path, 'a')
jadmanskicc549172008-05-21 18:11:51 +0000223
jadmanski0afbb632008-06-06 21:10:57 +0000224 if type_tag is None:
mbligh97227ea2009-03-11 17:09:50 +0000225 key_regex = re.compile(r'^[-\.\w]+$')
jadmanski0afbb632008-06-06 21:10:57 +0000226 else:
227 if type_tag not in ('attr', 'perf'):
228 raise ValueError('Invalid type tag: %s' % type_tag)
229 escaped_tag = re.escape(type_tag)
mbligh97227ea2009-03-11 17:09:50 +0000230 key_regex = re.compile(r'^[-\.\w]+\{%s\}$' % escaped_tag)
jadmanski0afbb632008-06-06 21:10:57 +0000231 try:
mbligh6955e232009-07-11 00:58:47 +0000232 for key in sorted(dictionary.keys()):
jadmanski0afbb632008-06-06 21:10:57 +0000233 if not key_regex.search(key):
234 raise ValueError('Invalid key: %s' % key)
mbligh6955e232009-07-11 00:58:47 +0000235 keyval.write('%s=%s\n' % (key, dictionary[key]))
jadmanski0afbb632008-06-06 21:10:57 +0000236 finally:
237 keyval.close()
mbligh6231cd62008-02-02 19:18:33 +0000238
239
240def is_url(path):
jadmanski0afbb632008-06-06 21:10:57 +0000241 """Return true if path looks like a URL"""
242 # for now, just handle http and ftp
243 url_parts = urlparse.urlparse(path)
244 return (url_parts[0] in ('http', 'ftp'))
mbligh6231cd62008-02-02 19:18:33 +0000245
246
mblighb2896192009-07-11 00:12:37 +0000247def urlopen(url, data=None, timeout=5):
248 """Wrapper to urllib2.urlopen with timeout addition."""
mbligh02ff2d52008-06-03 15:00:21 +0000249
jadmanski0afbb632008-06-06 21:10:57 +0000250 # Save old timeout
251 old_timeout = socket.getdefaulttimeout()
252 socket.setdefaulttimeout(timeout)
253 try:
mblighb2896192009-07-11 00:12:37 +0000254 return urllib2.urlopen(url, data=data)
jadmanski0afbb632008-06-06 21:10:57 +0000255 finally:
256 socket.setdefaulttimeout(old_timeout)
mbligh02ff2d52008-06-03 15:00:21 +0000257
258
mblighb2896192009-07-11 00:12:37 +0000259def urlretrieve(url, filename, data=None, timeout=300):
260 """Retrieve a file from given url."""
261 logging.debug('Fetching %s -> %s', url, filename)
262
263 src_file = urlopen(url, data=data, timeout=timeout)
jadmanski0afbb632008-06-06 21:10:57 +0000264 try:
mblighb2896192009-07-11 00:12:37 +0000265 dest_file = open(filename, 'wb')
266 try:
267 shutil.copyfileobj(src_file, dest_file)
268 finally:
269 dest_file.close()
jadmanski0afbb632008-06-06 21:10:57 +0000270 finally:
mblighb2896192009-07-11 00:12:37 +0000271 src_file.close()
jadmanski0afbb632008-06-06 21:10:57 +0000272
mbligh02ff2d52008-06-03 15:00:21 +0000273
mbligh6231cd62008-02-02 19:18:33 +0000274def get_file(src, dest, permissions=None):
jadmanski0afbb632008-06-06 21:10:57 +0000275 """Get a file from src, which can be local or a remote URL"""
mbligh25284cd2009-06-08 16:17:24 +0000276 if src == dest:
jadmanski0afbb632008-06-06 21:10:57 +0000277 return
mbligh25284cd2009-06-08 16:17:24 +0000278
279 if is_url(src):
mblighb2896192009-07-11 00:12:37 +0000280 urlretrieve(src, dest)
jadmanski0afbb632008-06-06 21:10:57 +0000281 else:
282 shutil.copyfile(src, dest)
mbligh25284cd2009-06-08 16:17:24 +0000283
jadmanski0afbb632008-06-06 21:10:57 +0000284 if permissions:
285 os.chmod(dest, permissions)
286 return dest
mbligh6231cd62008-02-02 19:18:33 +0000287
288
289def unmap_url(srcdir, src, destdir='.'):
jadmanski0afbb632008-06-06 21:10:57 +0000290 """
291 Receives either a path to a local file or a URL.
292 returns either the path to the local file, or the fetched URL
mbligh6231cd62008-02-02 19:18:33 +0000293
jadmanski0afbb632008-06-06 21:10:57 +0000294 unmap_url('/usr/src', 'foo.tar', '/tmp')
295 = '/usr/src/foo.tar'
296 unmap_url('/usr/src', 'http://site/file', '/tmp')
297 = '/tmp/file'
298 (after retrieving it)
299 """
300 if is_url(src):
301 url_parts = urlparse.urlparse(src)
302 filename = os.path.basename(url_parts[2])
303 dest = os.path.join(destdir, filename)
304 return get_file(src, dest)
305 else:
306 return os.path.join(srcdir, src)
mbligh6231cd62008-02-02 19:18:33 +0000307
308
309def update_version(srcdir, preserve_srcdir, new_version, install,
jadmanski0afbb632008-06-06 21:10:57 +0000310 *args, **dargs):
311 """
312 Make sure srcdir is version new_version
mbligh6231cd62008-02-02 19:18:33 +0000313
jadmanski0afbb632008-06-06 21:10:57 +0000314 If not, delete it and install() the new version.
mbligh6231cd62008-02-02 19:18:33 +0000315
jadmanski0afbb632008-06-06 21:10:57 +0000316 In the preserve_srcdir case, we just check it's up to date,
317 and if not, we rerun install, without removing srcdir
318 """
319 versionfile = os.path.join(srcdir, '.version')
320 install_needed = True
mbligh6231cd62008-02-02 19:18:33 +0000321
jadmanski0afbb632008-06-06 21:10:57 +0000322 if os.path.exists(versionfile):
323 old_version = pickle.load(open(versionfile))
324 if old_version == new_version:
325 install_needed = False
mbligh6231cd62008-02-02 19:18:33 +0000326
jadmanski0afbb632008-06-06 21:10:57 +0000327 if install_needed:
328 if not preserve_srcdir and os.path.exists(srcdir):
329 shutil.rmtree(srcdir)
330 install(*args, **dargs)
331 if os.path.exists(srcdir):
332 pickle.dump(new_version, open(versionfile, 'w'))
mbligh462c0152008-03-13 15:37:10 +0000333
334
showardb45a4662009-07-15 14:27:56 +0000335def get_stderr_level(stderr_is_expected):
336 if stderr_is_expected:
337 return DEFAULT_STDOUT_LEVEL
338 return DEFAULT_STDERR_LEVEL
339
340
mbligh63073c92008-03-31 16:49:32 +0000341def run(command, timeout=None, ignore_status=False,
showardb45a4662009-07-15 14:27:56 +0000342 stdout_tee=None, stderr_tee=None, verbose=True, stdin=None,
mblighc823e8d2009-10-02 00:01:35 +0000343 stderr_is_expected=None, args=()):
jadmanski0afbb632008-06-06 21:10:57 +0000344 """
345 Run a command on the host.
mbligh63073c92008-03-31 16:49:32 +0000346
mblighc823e8d2009-10-02 00:01:35 +0000347 @param command: the command line string.
348 @param timeout: time limit in seconds before attempting to kill the
349 running process. The run() function will take a few seconds
350 longer than 'timeout' to complete if it has to kill the process.
351 @param ignore_status: do not raise an exception, no matter what the exit
352 code of the command is.
353 @param stdout_tee: optional file-like object to which stdout data
354 will be written as it is generated (data will still be stored
355 in result.stdout).
356 @param stderr_tee: likewise for stderr.
357 @param verbose: if True, log the command being run.
jadmanski093a0682009-10-13 14:55:43 +0000358 @param stdin: stdin to pass to the executed process (can be a file
359 descriptor, a file object of a real file or a string).
mblighc823e8d2009-10-02 00:01:35 +0000360 @param args: sequence of strings of arguments to be given to the command
361 inside " quotes after they have been escaped for that; each
362 element in the sequence will be given as a separate command
363 argument
mbligh63073c92008-03-31 16:49:32 +0000364
mblighc823e8d2009-10-02 00:01:35 +0000365 @return a CmdResult object
mbligh63073c92008-03-31 16:49:32 +0000366
mblighc823e8d2009-10-02 00:01:35 +0000367 @raise CmdError: the exit code of the command execution was not 0
jadmanski0afbb632008-06-06 21:10:57 +0000368 """
mblighc823e8d2009-10-02 00:01:35 +0000369 for arg in args:
370 command += ' "%s"' % sh_escape(arg)
showardb45a4662009-07-15 14:27:56 +0000371 if stderr_is_expected is None:
372 stderr_is_expected = ignore_status
mblighc823e8d2009-10-02 00:01:35 +0000373
showard170873e2009-01-07 00:22:26 +0000374 bg_job = join_bg_jobs(
showardb45a4662009-07-15 14:27:56 +0000375 (BgJob(command, stdout_tee, stderr_tee, verbose, stdin=stdin,
376 stderr_level=get_stderr_level(stderr_is_expected)),),
showard170873e2009-01-07 00:22:26 +0000377 timeout)[0]
mbligh849a0f62008-08-28 20:12:19 +0000378 if not ignore_status and bg_job.result.exit_status:
jadmanski9c1098b2008-09-02 14:18:48 +0000379 raise error.CmdError(command, bg_job.result,
mbligh849a0f62008-08-28 20:12:19 +0000380 "Command returned non-zero exit status")
mbligh63073c92008-03-31 16:49:32 +0000381
mbligh849a0f62008-08-28 20:12:19 +0000382 return bg_job.result
mbligh63073c92008-03-31 16:49:32 +0000383
mbligh45ffc432008-12-09 23:35:17 +0000384
mbligha5630a52008-09-03 22:09:50 +0000385def run_parallel(commands, timeout=None, ignore_status=False,
386 stdout_tee=None, stderr_tee=None):
jadmanski093a0682009-10-13 14:55:43 +0000387 """
388 Behaves the same as run() with the following exceptions:
mbligha5630a52008-09-03 22:09:50 +0000389
390 - commands is a list of commands to run in parallel.
391 - ignore_status toggles whether or not an exception should be raised
392 on any error.
393
jadmanski093a0682009-10-13 14:55:43 +0000394 @return: a list of CmdResult objects
mbligha5630a52008-09-03 22:09:50 +0000395 """
396 bg_jobs = []
397 for command in commands:
showardb45a4662009-07-15 14:27:56 +0000398 bg_jobs.append(BgJob(command, stdout_tee, stderr_tee,
399 stderr_level=get_stderr_level(ignore_status)))
mbligha5630a52008-09-03 22:09:50 +0000400
401 # Updates objects in bg_jobs list with their process information
402 join_bg_jobs(bg_jobs, timeout)
403
404 for bg_job in bg_jobs:
405 if not ignore_status and bg_job.result.exit_status:
406 raise error.CmdError(command, bg_job.result,
407 "Command returned non-zero exit status")
408
409 return [bg_job.result for bg_job in bg_jobs]
410
411
mbligh849a0f62008-08-28 20:12:19 +0000412@deprecated
mbligh63073c92008-03-31 16:49:32 +0000413def run_bg(command):
mbligh849a0f62008-08-28 20:12:19 +0000414 """Function deprecated. Please use BgJob class instead."""
415 bg_job = BgJob(command)
416 return bg_job.sp, bg_job.result
mbligh63073c92008-03-31 16:49:32 +0000417
418
mbligh849a0f62008-08-28 20:12:19 +0000419def join_bg_jobs(bg_jobs, timeout=None):
mbligha5630a52008-09-03 22:09:50 +0000420 """Joins the bg_jobs with the current thread.
421
422 Returns the same list of bg_jobs objects that was passed in.
423 """
mblighae69f262009-04-17 20:14:56 +0000424 ret, timeout_error = 0, False
mbligh849a0f62008-08-28 20:12:19 +0000425 for bg_job in bg_jobs:
426 bg_job.output_prepare(StringIO.StringIO(), StringIO.StringIO())
mbligh63073c92008-03-31 16:49:32 +0000427
jadmanski0afbb632008-06-06 21:10:57 +0000428 try:
429 # We are holding ends to stdin, stdout pipes
430 # hence we need to be sure to close those fds no mater what
431 start_time = time.time()
mbligh849a0f62008-08-28 20:12:19 +0000432 timeout_error = _wait_for_commands(bg_jobs, start_time, timeout)
433
434 for bg_job in bg_jobs:
435 # Process stdout and stderr
436 bg_job.process_output(stdout=True,final_read=True)
437 bg_job.process_output(stdout=False,final_read=True)
jadmanski0afbb632008-06-06 21:10:57 +0000438 finally:
439 # close our ends of the pipes to the sp no matter what
mbligh849a0f62008-08-28 20:12:19 +0000440 for bg_job in bg_jobs:
441 bg_job.cleanup()
mbligh63073c92008-03-31 16:49:32 +0000442
mbligh849a0f62008-08-28 20:12:19 +0000443 if timeout_error:
444 # TODO: This needs to be fixed to better represent what happens when
445 # running in parallel. However this is backwards compatable, so it will
446 # do for the time being.
447 raise error.CmdError(bg_jobs[0].command, bg_jobs[0].result,
448 "Command(s) did not complete within %d seconds"
449 % timeout)
mbligh63073c92008-03-31 16:49:32 +0000450
mbligh63073c92008-03-31 16:49:32 +0000451
mbligh849a0f62008-08-28 20:12:19 +0000452 return bg_jobs
mbligh63073c92008-03-31 16:49:32 +0000453
mbligh849a0f62008-08-28 20:12:19 +0000454
455def _wait_for_commands(bg_jobs, start_time, timeout):
456 # This returns True if it must return due to a timeout, otherwise False.
457
mblighf0b4a0a2008-09-03 20:46:16 +0000458 # To check for processes which terminate without producing any output
459 # a 1 second timeout is used in select.
460 SELECT_TIMEOUT = 1
461
jadmanski093a0682009-10-13 14:55:43 +0000462 read_list = []
463 write_list = []
mbligh849a0f62008-08-28 20:12:19 +0000464 reverse_dict = {}
jadmanski093a0682009-10-13 14:55:43 +0000465
mbligh849a0f62008-08-28 20:12:19 +0000466 for bg_job in bg_jobs:
jadmanski093a0682009-10-13 14:55:43 +0000467 read_list.append(bg_job.sp.stdout)
468 read_list.append(bg_job.sp.stderr)
469 reverse_dict[bg_job.sp.stdout] = (bg_job, True)
470 reverse_dict[bg_job.sp.stderr] = (bg_job, False)
471 if bg_job.string_stdin:
472 write_list.append(bg_job.sp.stdin)
473 reverse_dict[bg_job.sp.stdin] = bg_job
mbligh849a0f62008-08-28 20:12:19 +0000474
jadmanski0afbb632008-06-06 21:10:57 +0000475 if timeout:
476 stop_time = start_time + timeout
477 time_left = stop_time - time.time()
478 else:
479 time_left = None # so that select never times out
jadmanski093a0682009-10-13 14:55:43 +0000480
jadmanski0afbb632008-06-06 21:10:57 +0000481 while not timeout or time_left > 0:
jadmanski093a0682009-10-13 14:55:43 +0000482 # select will return when we may write to stdin or when there is
483 # stdout/stderr output we can read (including when it is
jadmanski0afbb632008-06-06 21:10:57 +0000484 # EOF, that is the process has terminated).
jadmanski093a0682009-10-13 14:55:43 +0000485 read_ready, write_ready, _ = select.select(read_list, write_list, [],
486 SELECT_TIMEOUT)
mbligh849a0f62008-08-28 20:12:19 +0000487
jadmanski0afbb632008-06-06 21:10:57 +0000488 # os.read() has to be used instead of
489 # subproc.stdout.read() which will otherwise block
jadmanski093a0682009-10-13 14:55:43 +0000490 for file_obj in read_ready:
491 bg_job, is_stdout = reverse_dict[file_obj]
492 bg_job.process_output(is_stdout)
mbligh63073c92008-03-31 16:49:32 +0000493
jadmanski093a0682009-10-13 14:55:43 +0000494 for file_obj in write_ready:
495 # we can write PIPE_BUF bytes without blocking
496 # POSIX requires PIPE_BUF is >= 512
497 bg_job = reverse_dict[file_obj]
498 file_obj.write(bg_job.string_stdin[:512])
499 bg_job.string_stdin = bg_job.string_stdin[512:]
500 # no more input data, close stdin, remove it from the select set
501 if not bg_job.string_stdin:
502 file_obj.close()
503 write_list.remove(file_obj)
504 del reverse_dict[file_obj]
505
506 all_jobs_finished = True
507 for bg_job in bg_jobs:
508 if bg_job.result.exit_status is not None:
509 continue
510
mbligh849a0f62008-08-28 20:12:19 +0000511 bg_job.result.exit_status = bg_job.sp.poll()
jadmanski093a0682009-10-13 14:55:43 +0000512 if bg_job.result.exit_status is not None:
513 # process exited, remove its stdout/stdin from the select set
514 read_list.remove(bg_job.sp.stdout)
515 read_list.remove(bg_job.sp.stderr)
516 del reverse_dict[bg_job.sp.stdout]
517 del reverse_dict[bg_job.sp.stderr]
518 else:
519 all_jobs_finished = False
520
521 if all_jobs_finished:
522 return False
mbligh8ea61e22008-05-09 18:09:37 +0000523
jadmanski0afbb632008-06-06 21:10:57 +0000524 if timeout:
525 time_left = stop_time - time.time()
mbligh63073c92008-03-31 16:49:32 +0000526
mbligh849a0f62008-08-28 20:12:19 +0000527 # Kill all processes which did not complete prior to timeout
jadmanski093a0682009-10-13 14:55:43 +0000528 for bg_job in bg_jobs:
529 if bg_job.result.exit_status is not None:
530 continue
531
532 logging.warn('run process timeout (%s) fired on: %s', timeout,
533 bg_job.command)
mbligh849a0f62008-08-28 20:12:19 +0000534 nuke_subprocess(bg_job.sp)
mbligh095dc642008-10-01 03:41:35 +0000535 bg_job.result.exit_status = bg_job.sp.poll()
mbligh8ea61e22008-05-09 18:09:37 +0000536
mbligh849a0f62008-08-28 20:12:19 +0000537 return True
mbligh63073c92008-03-31 16:49:32 +0000538
539
showard549afad2009-08-20 23:33:36 +0000540def pid_is_alive(pid):
541 """
542 True if process pid exists and is not yet stuck in Zombie state.
543 Zombies are impossible to move between cgroups, etc.
544 pid can be integer, or text of integer.
545 """
546 path = '/proc/%s/stat' % pid
547
548 try:
549 stat = read_one_line(path)
550 except IOError:
551 if not os.path.exists(path):
552 # file went away
553 return False
554 raise
555
556 return stat.split()[2] != 'Z'
557
558
559def signal_pid(pid, sig):
560 """
561 Sends a signal to a process id. Returns True if the process terminated
562 successfully, False otherwise.
563 """
564 try:
565 os.kill(pid, sig)
566 except OSError:
567 # The process may have died before we could kill it.
568 pass
569
570 for i in range(5):
571 if not pid_is_alive(pid):
572 return True
573 time.sleep(1)
574
575 # The process is still alive
576 return False
577
578
mbligh63073c92008-03-31 16:49:32 +0000579def nuke_subprocess(subproc):
jadmanski09f92032008-09-17 14:05:27 +0000580 # check if the subprocess is still alive, first
581 if subproc.poll() is not None:
582 return subproc.poll()
583
jadmanski0afbb632008-06-06 21:10:57 +0000584 # the process has not terminated within timeout,
585 # kill it via an escalating series of signals.
586 signal_queue = [signal.SIGTERM, signal.SIGKILL]
587 for sig in signal_queue:
showard549afad2009-08-20 23:33:36 +0000588 signal_pid(subproc.pid, sig)
589 if subproc.poll() is not None:
590 return subproc.poll()
mbligh63073c92008-03-31 16:49:32 +0000591
592
showard786da9a2009-10-12 20:31:20 +0000593def nuke_pid(pid, signal_queue=(signal.SIGTERM, signal.SIGKILL)):
jadmanski0afbb632008-06-06 21:10:57 +0000594 # the process has not terminated within timeout,
595 # kill it via an escalating series of signals.
jadmanski0afbb632008-06-06 21:10:57 +0000596 for sig in signal_queue:
showard549afad2009-08-20 23:33:36 +0000597 if signal_pid(pid, sig):
598 return
mbligh63073c92008-03-31 16:49:32 +0000599
showard549afad2009-08-20 23:33:36 +0000600 # no signal successfully terminated the process
601 raise error.AutoservRunError('Could not kill %d' % pid, None)
mbligh298ed592009-06-15 21:52:57 +0000602
603
mbligh63073c92008-03-31 16:49:32 +0000604def system(command, timeout=None, ignore_status=False):
mbligha5630a52008-09-03 22:09:50 +0000605 """This function returns the exit status of command."""
mblighf8dffb12008-10-29 16:45:26 +0000606 return run(command, timeout=timeout, ignore_status=ignore_status,
showard108d73e2009-06-22 18:14:41 +0000607 stdout_tee=TEE_TO_LOGS, stderr_tee=TEE_TO_LOGS).exit_status
mbligh63073c92008-03-31 16:49:32 +0000608
609
mbligha5630a52008-09-03 22:09:50 +0000610def system_parallel(commands, timeout=None, ignore_status=False):
611 """This function returns a list of exit statuses for the respective
612 list of commands."""
613 return [bg_jobs.exit_status for bg_jobs in
mblighf8dffb12008-10-29 16:45:26 +0000614 run_parallel(commands, timeout=timeout, ignore_status=ignore_status,
showard108d73e2009-06-22 18:14:41 +0000615 stdout_tee=TEE_TO_LOGS, stderr_tee=TEE_TO_LOGS)]
mbligh849a0f62008-08-28 20:12:19 +0000616
617
mbligh8ea61e22008-05-09 18:09:37 +0000618def system_output(command, timeout=None, ignore_status=False,
mblighc823e8d2009-10-02 00:01:35 +0000619 retain_output=False, args=()):
620 """
621 Run a command and return the stdout output.
622
623 @param command: command string to execute.
624 @param timeout: time limit in seconds before attempting to kill the
625 running process. The function will take a few seconds longer
626 than 'timeout' to complete if it has to kill the process.
627 @param ignore_status: do not raise an exception, no matter what the exit
628 code of the command is.
629 @param retain_output: set to True to make stdout/stderr of the command
630 output to be also sent to the logging system
631 @param args: sequence of strings of arguments to be given to the command
632 inside " quotes after they have been escaped for that; each
633 element in the sequence will be given as a separate command
634 argument
635
636 @return a string with the stdout output of the command.
637 """
jadmanski0afbb632008-06-06 21:10:57 +0000638 if retain_output:
mblighf8dffb12008-10-29 16:45:26 +0000639 out = run(command, timeout=timeout, ignore_status=ignore_status,
mblighc823e8d2009-10-02 00:01:35 +0000640 stdout_tee=TEE_TO_LOGS, stderr_tee=TEE_TO_LOGS,
641 args=args).stdout
jadmanski0afbb632008-06-06 21:10:57 +0000642 else:
mblighc823e8d2009-10-02 00:01:35 +0000643 out = run(command, timeout=timeout, ignore_status=ignore_status,
644 args=args).stdout
645 if out[-1:] == '\n':
646 out = out[:-1]
jadmanski0afbb632008-06-06 21:10:57 +0000647 return out
mbligh63073c92008-03-31 16:49:32 +0000648
mbligh849a0f62008-08-28 20:12:19 +0000649
mbligha5630a52008-09-03 22:09:50 +0000650def system_output_parallel(commands, timeout=None, ignore_status=False,
651 retain_output=False):
652 if retain_output:
showard108d73e2009-06-22 18:14:41 +0000653 out = [bg_job.stdout for bg_job
654 in run_parallel(commands, timeout=timeout,
655 ignore_status=ignore_status,
656 stdout_tee=TEE_TO_LOGS, stderr_tee=TEE_TO_LOGS)]
mbligha5630a52008-09-03 22:09:50 +0000657 else:
mblighf8dffb12008-10-29 16:45:26 +0000658 out = [bg_job.stdout for bg_job in run_parallel(commands,
659 timeout=timeout, ignore_status=ignore_status)]
mbligha5630a52008-09-03 22:09:50 +0000660 for x in out:
661 if out[-1:] == '\n': out = out[:-1]
662 return out
663
664
mbligh98467952008-11-19 00:25:45 +0000665def strip_unicode(input):
666 if type(input) == list:
667 return [strip_unicode(i) for i in input]
668 elif type(input) == dict:
669 output = {}
670 for key in input.keys():
671 output[str(key)] = strip_unicode(input[key])
672 return output
673 elif type(input) == unicode:
674 return str(input)
675 else:
676 return input
677
678
mbligha5630a52008-09-03 22:09:50 +0000679def get_cpu_percentage(function, *args, **dargs):
680 """Returns a tuple containing the CPU% and return value from function call.
681
682 This function calculates the usage time by taking the difference of
683 the user and system times both before and after the function call.
684 """
685 child_pre = resource.getrusage(resource.RUSAGE_CHILDREN)
686 self_pre = resource.getrusage(resource.RUSAGE_SELF)
687 start = time.time()
688 to_return = function(*args, **dargs)
689 elapsed = time.time() - start
690 self_post = resource.getrusage(resource.RUSAGE_SELF)
691 child_post = resource.getrusage(resource.RUSAGE_CHILDREN)
692
693 # Calculate CPU Percentage
694 s_user, s_system = [a - b for a, b in zip(self_post, self_pre)[:2]]
695 c_user, c_system = [a - b for a, b in zip(child_post, child_pre)[:2]]
696 cpu_percent = (s_user + c_user + s_system + c_system) / elapsed
697
698 return cpu_percent, to_return
699
700
mblighc1cbc992008-05-27 20:01:45 +0000701"""
702This function is used when there is a need to run more than one
703job simultaneously starting exactly at the same time. It basically returns
704a modified control file (containing the synchronization code prepended)
705whenever it is ready to run the control file. The synchronization
706is done using barriers to make sure that the jobs start at the same time.
707
708Here is how the synchronization is done to make sure that the tests
709start at exactly the same time on the client.
710sc_bar is a server barrier and s_bar, c_bar are the normal barriers
711
712 Job1 Job2 ...... JobN
713 Server: | sc_bar
714 Server: | s_bar ...... s_bar
715 Server: | at.run() at.run() ...... at.run()
716 ----------|------------------------------------------------------
717 Client | sc_bar
718 Client | c_bar c_bar ...... c_bar
719 Client | <run test> <run test> ...... <run test>
720
721
722PARAMS:
723 control_file : The control file which to which the above synchronization
724 code would be prepended to
725 host_name : The host name on which the job is going to run
726 host_num (non negative) : A number to identify the machine so that we have
727 different sets of s_bar_ports for each of the machines.
728 instance : The number of the job
729 num_jobs : Total number of jobs that are going to run in parallel with
730 this job starting at the same time
731 port_base : Port number that is used to derive the actual barrier ports.
732
733RETURN VALUE:
734 The modified control file.
735
736"""
737def get_sync_control_file(control, host_name, host_num,
jadmanski0afbb632008-06-06 21:10:57 +0000738 instance, num_jobs, port_base=63100):
739 sc_bar_port = port_base
740 c_bar_port = port_base
741 if host_num < 0:
742 print "Please provide a non negative number for the host"
743 return None
744 s_bar_port = port_base + 1 + host_num # The set of s_bar_ports are
745 # the same for a given machine
mblighc1cbc992008-05-27 20:01:45 +0000746
jadmanski0afbb632008-06-06 21:10:57 +0000747 sc_bar_timeout = 180
748 s_bar_timeout = c_bar_timeout = 120
mblighc1cbc992008-05-27 20:01:45 +0000749
jadmanski0afbb632008-06-06 21:10:57 +0000750 # The barrier code snippet is prepended into the conrol file
751 # dynamically before at.run() is called finally.
752 control_new = []
mblighc1cbc992008-05-27 20:01:45 +0000753
jadmanski0afbb632008-06-06 21:10:57 +0000754 # jobid is the unique name used to identify the processes
755 # trying to reach the barriers
756 jobid = "%s#%d" % (host_name, instance)
mblighc1cbc992008-05-27 20:01:45 +0000757
jadmanski0afbb632008-06-06 21:10:57 +0000758 rendv = []
759 # rendvstr is a temp holder for the rendezvous list of the processes
760 for n in range(num_jobs):
761 rendv.append("'%s#%d'" % (host_name, n))
762 rendvstr = ",".join(rendv)
mblighc1cbc992008-05-27 20:01:45 +0000763
jadmanski0afbb632008-06-06 21:10:57 +0000764 if instance == 0:
765 # Do the setup and wait at the server barrier
766 # Clean up the tmp and the control dirs for the first instance
767 control_new.append('if os.path.exists(job.tmpdir):')
768 control_new.append("\t system('umount -f %s > /dev/null"
769 "2> /dev/null' % job.tmpdir,"
770 "ignore_status=True)")
771 control_new.append("\t system('rm -rf ' + job.tmpdir)")
772 control_new.append(
773 'b0 = job.barrier("%s", "sc_bar", %d, port=%d)'
774 % (jobid, sc_bar_timeout, sc_bar_port))
775 control_new.append(
mbligh9c12f772009-06-22 19:03:55 +0000776 'b0.rendezvous_servers("PARALLEL_MASTER", "%s")'
jadmanski0afbb632008-06-06 21:10:57 +0000777 % jobid)
mblighc1cbc992008-05-27 20:01:45 +0000778
jadmanski0afbb632008-06-06 21:10:57 +0000779 elif instance == 1:
780 # Wait at the server barrier to wait for instance=0
781 # process to complete setup
782 b0 = barrier.barrier("PARALLEL_MASTER", "sc_bar", sc_bar_timeout,
783 port=sc_bar_port)
mbligh9c12f772009-06-22 19:03:55 +0000784 b0.rendezvous_servers("PARALLEL_MASTER", jobid)
mblighc1cbc992008-05-27 20:01:45 +0000785
jadmanski0afbb632008-06-06 21:10:57 +0000786 if(num_jobs > 2):
787 b1 = barrier.barrier(jobid, "s_bar", s_bar_timeout,
788 port=s_bar_port)
mbligh9c12f772009-06-22 19:03:55 +0000789 b1.rendezvous(rendvstr)
mblighc1cbc992008-05-27 20:01:45 +0000790
jadmanski0afbb632008-06-06 21:10:57 +0000791 else:
792 # For the rest of the clients
793 b2 = barrier.barrier(jobid, "s_bar", s_bar_timeout, port=s_bar_port)
mbligh9c12f772009-06-22 19:03:55 +0000794 b2.rendezvous(rendvstr)
mblighc1cbc992008-05-27 20:01:45 +0000795
jadmanski0afbb632008-06-06 21:10:57 +0000796 # Client side barrier for all the tests to start at the same time
797 control_new.append('b1 = job.barrier("%s", "c_bar", %d, port=%d)'
798 % (jobid, c_bar_timeout, c_bar_port))
mbligh9c12f772009-06-22 19:03:55 +0000799 control_new.append("b1.rendezvous(%s)" % rendvstr)
mblighc1cbc992008-05-27 20:01:45 +0000800
jadmanski0afbb632008-06-06 21:10:57 +0000801 # Stick in the rest of the control file
802 control_new.append(control)
mblighc1cbc992008-05-27 20:01:45 +0000803
jadmanski0afbb632008-06-06 21:10:57 +0000804 return "\n".join(control_new)
mblighc1cbc992008-05-27 20:01:45 +0000805
mbligh63073c92008-03-31 16:49:32 +0000806
mblighc5ddfd12008-08-04 17:15:00 +0000807def get_arch(run_function=run):
808 """
809 Get the hardware architecture of the machine.
810 run_function is used to execute the commands. It defaults to
811 utils.run() but a custom method (if provided) should be of the
812 same schema as utils.run. It should return a CmdResult object and
813 throw a CmdError exception.
814 """
815 arch = run_function('/bin/uname -m').stdout.rstrip()
816 if re.match(r'i\d86$', arch):
817 arch = 'i386'
818 return arch
819
820
showard4745ecd2009-05-26 19:34:56 +0000821def get_num_logical_cpus_per_socket(run_function=run):
mbligh9fd9afe2009-04-28 18:27:25 +0000822 """
823 Get the number of cores (including hyperthreading) per cpu.
824 run_function is used to execute the commands. It defaults to
825 utils.run() but a custom method (if provided) should be of the
826 same schema as utils.run. It should return a CmdResult object and
827 throw a CmdError exception.
828 """
showard4745ecd2009-05-26 19:34:56 +0000829 siblings = run_function('grep "^siblings" /proc/cpuinfo').stdout.rstrip()
830 num_siblings = map(int,
831 re.findall(r'^siblings\s*:\s*(\d+)\s*$',
832 siblings, re.M))
833 if len(num_siblings) == 0:
834 raise error.TestError('Unable to find siblings info in /proc/cpuinfo')
835 if min(num_siblings) != max(num_siblings):
836 raise error.TestError('Number of siblings differ %r' %
837 num_siblings)
838 return num_siblings[0]
mbligh9fd9afe2009-04-28 18:27:25 +0000839
840
jadmanski4f909252008-12-01 20:47:10 +0000841def merge_trees(src, dest):
842 """
843 Merges a source directory tree at 'src' into a destination tree at
844 'dest'. If a path is a file in both trees than the file in the source
845 tree is APPENDED to the one in the destination tree. If a path is
846 a directory in both trees then the directories are recursively merged
847 with this function. In any other case, the function will skip the
848 paths that cannot be merged (instead of failing).
849 """
850 if not os.path.exists(src):
851 return # exists only in dest
852 elif not os.path.exists(dest):
853 if os.path.isfile(src):
854 shutil.copy2(src, dest) # file only in src
855 else:
856 shutil.copytree(src, dest, symlinks=True) # dir only in src
857 return
858 elif os.path.isfile(src) and os.path.isfile(dest):
859 # src & dest are files in both trees, append src to dest
860 destfile = open(dest, "a")
861 try:
862 srcfile = open(src)
863 try:
864 destfile.write(srcfile.read())
865 finally:
866 srcfile.close()
867 finally:
868 destfile.close()
869 elif os.path.isdir(src) and os.path.isdir(dest):
870 # src & dest are directories in both trees, so recursively merge
871 for name in os.listdir(src):
872 merge_trees(os.path.join(src, name), os.path.join(dest, name))
873 else:
874 # src & dest both exist, but are incompatible
875 return
876
877
mbligh63073c92008-03-31 16:49:32 +0000878class CmdResult(object):
jadmanski0afbb632008-06-06 21:10:57 +0000879 """
880 Command execution result.
mbligh63073c92008-03-31 16:49:32 +0000881
jadmanski0afbb632008-06-06 21:10:57 +0000882 command: String containing the command line itself
883 exit_status: Integer exit code of the process
884 stdout: String containing stdout of the process
885 stderr: String containing stderr of the process
886 duration: Elapsed wall clock time running the process
887 """
mbligh63073c92008-03-31 16:49:32 +0000888
889
mblighcd63a212009-05-01 23:04:38 +0000890 def __init__(self, command="", stdout="", stderr="",
jadmanski0afbb632008-06-06 21:10:57 +0000891 exit_status=None, duration=0):
892 self.command = command
893 self.exit_status = exit_status
894 self.stdout = stdout
895 self.stderr = stderr
896 self.duration = duration
mbligh63073c92008-03-31 16:49:32 +0000897
898
jadmanski0afbb632008-06-06 21:10:57 +0000899 def __repr__(self):
900 wrapper = textwrap.TextWrapper(width = 78,
901 initial_indent="\n ",
902 subsequent_indent=" ")
903
904 stdout = self.stdout.rstrip()
905 if stdout:
906 stdout = "\nstdout:\n%s" % stdout
907
908 stderr = self.stderr.rstrip()
909 if stderr:
910 stderr = "\nstderr:\n%s" % stderr
911
912 return ("* Command: %s\n"
913 "Exit status: %s\n"
914 "Duration: %s\n"
915 "%s"
916 "%s"
917 % (wrapper.fill(self.command), self.exit_status,
918 self.duration, stdout, stderr))
mbligh63073c92008-03-31 16:49:32 +0000919
920
mbligh462c0152008-03-13 15:37:10 +0000921class run_randomly:
jadmanski0afbb632008-06-06 21:10:57 +0000922 def __init__(self, run_sequentially=False):
923 # Run sequentially is for debugging control files
924 self.test_list = []
925 self.run_sequentially = run_sequentially
mbligh462c0152008-03-13 15:37:10 +0000926
927
jadmanski0afbb632008-06-06 21:10:57 +0000928 def add(self, *args, **dargs):
929 test = (args, dargs)
930 self.test_list.append(test)
mbligh462c0152008-03-13 15:37:10 +0000931
932
jadmanski0afbb632008-06-06 21:10:57 +0000933 def run(self, fn):
934 while self.test_list:
935 test_index = random.randint(0, len(self.test_list)-1)
936 if self.run_sequentially:
937 test_index = 0
938 (args, dargs) = self.test_list.pop(test_index)
939 fn(*args, **dargs)
mbligha7007722009-01-13 00:37:11 +0000940
941
showard5deef7f2009-09-09 18:16:58 +0000942def import_site_module(path, module, dummy=None, modulefile=None):
943 """
944 Try to import the site specific module if it exists.
945
946 @param path full filename of the source file calling this (ie __file__)
947 @param module full module name
948 @param dummy dummy value to return in case there is no symbol to import
949 @param modulefile module filename
950
951 @return site specific module or dummy
952
953 @raises ImportError if the site file exists but imports fails
954 """
955 short_module = module[module.rfind(".") + 1:]
956
957 if not modulefile:
958 modulefile = short_module + ".py"
959
960 if os.path.exists(os.path.join(os.path.dirname(path), modulefile)):
961 return __import__(module, {}, {}, [short_module])
962 return dummy
963
964
mblighdd669372009-02-03 21:57:18 +0000965def import_site_symbol(path, module, name, dummy=None, modulefile=None):
966 """
967 Try to import site specific symbol from site specific file if it exists
968
969 @param path full filename of the source file calling this (ie __file__)
970 @param module full module name
971 @param name symbol name to be imported from the site file
972 @param dummy dummy value to return in case there is no symbol to import
973 @param modulefile module filename
974
975 @return site specific symbol or dummy
976
showard5deef7f2009-09-09 18:16:58 +0000977 @raises ImportError if the site file exists but imports fails
mblighdd669372009-02-03 21:57:18 +0000978 """
showard5deef7f2009-09-09 18:16:58 +0000979 module = import_site_module(path, module, modulefile=modulefile)
980 if not module:
981 return dummy
mbligha7007722009-01-13 00:37:11 +0000982
showard5deef7f2009-09-09 18:16:58 +0000983 # special unique value to tell us if the symbol can't be imported
984 cant_import = object()
mbligha7007722009-01-13 00:37:11 +0000985
showard5deef7f2009-09-09 18:16:58 +0000986 obj = getattr(module, name, cant_import)
987 if obj is cant_import:
988 logging.error("unable to import site symbol '%s', using non-site "
989 "implementation", name)
990 return dummy
mbligh062ed152009-01-13 00:57:14 +0000991
992 return obj
993
994
995def import_site_class(path, module, classname, baseclass, modulefile=None):
996 """
997 Try to import site specific class from site specific file if it exists
998
999 Args:
1000 path: full filename of the source file calling this (ie __file__)
1001 module: full module name
1002 classname: class name to be loaded from site file
mbligh0a8c3322009-04-28 18:32:19 +00001003 baseclass: base class object to return when no site file present or
1004 to mixin when site class exists but is not inherited from baseclass
mbligh062ed152009-01-13 00:57:14 +00001005 modulefile: module filename
1006
mbligh0a8c3322009-04-28 18:32:19 +00001007 Returns: baseclass if site specific class does not exist, the site specific
1008 class if it exists and is inherited from baseclass or a mixin of the
1009 site specific class and baseclass when the site specific class exists
1010 and is not inherited from baseclass
mbligh062ed152009-01-13 00:57:14 +00001011
1012 Raises: ImportError if the site file exists but imports fails
1013 """
1014
mblighdd669372009-02-03 21:57:18 +00001015 res = import_site_symbol(path, module, classname, None, modulefile)
mbligh0a8c3322009-04-28 18:32:19 +00001016 if res:
1017 if not issubclass(res, baseclass):
1018 # if not a subclass of baseclass then mix in baseclass with the
1019 # site specific class object and return the result
1020 res = type(classname, (res, baseclass), {})
1021 else:
1022 res = baseclass
mbligha7007722009-01-13 00:37:11 +00001023
mbligh062ed152009-01-13 00:57:14 +00001024 return res
1025
1026
1027def import_site_function(path, module, funcname, dummy, modulefile=None):
1028 """
1029 Try to import site specific function from site specific file if it exists
1030
1031 Args:
1032 path: full filename of the source file calling this (ie __file__)
1033 module: full module name
1034 funcname: function name to be imported from site file
1035 dummy: dummy function to return in case there is no function to import
1036 modulefile: module filename
1037
1038 Returns: site specific function object or dummy
1039
1040 Raises: ImportError if the site file exists but imports fails
1041 """
1042
mblighdd669372009-02-03 21:57:18 +00001043 return import_site_symbol(path, module, funcname, dummy, modulefile)
mblighfb676032009-04-01 18:25:38 +00001044
1045
showard549afad2009-08-20 23:33:36 +00001046def _get_pid_path(program_name):
1047 my_path = os.path.dirname(__file__)
1048 return os.path.abspath(os.path.join(my_path, "..", "..",
1049 "%s.pid" % program_name))
1050
1051
mblighfb676032009-04-01 18:25:38 +00001052def write_pid(program_name):
1053 """
1054 Try to drop <program_name>.pid in the main autotest directory.
1055
1056 Args:
1057 program_name: prefix for file name
1058 """
showard549afad2009-08-20 23:33:36 +00001059 pidfile = open(_get_pid_path(program_name), "w")
1060 try:
1061 pidfile.write("%s\n" % os.getpid())
1062 finally:
1063 pidfile.close()
mblighfb676032009-04-01 18:25:38 +00001064
showard549afad2009-08-20 23:33:36 +00001065
1066def delete_pid_file_if_exists(program_name):
1067 """
1068 Tries to remove <program_name>.pid from the main autotest directory.
1069 """
1070 pidfile_path = _get_pid_path(program_name)
1071
1072 try:
1073 os.remove(pidfile_path)
1074 except OSError:
1075 if not os.path.exists(pidfile_path):
1076 return
1077 raise
1078
1079
1080def get_pid_from_file(program_name):
1081 """
1082 Reads the pid from <program_name>.pid in the autotest directory.
1083
1084 @param program_name the name of the program
1085 @return the pid if the file exists, None otherwise.
1086 """
1087 pidfile_path = _get_pid_path(program_name)
1088 if not os.path.exists(pidfile_path):
1089 return None
1090
1091 pidfile = open(_get_pid_path(program_name), 'r')
1092
1093 try:
1094 try:
1095 pid = int(pidfile.readline())
1096 except IOError:
1097 if not os.path.exists(pidfile_path):
1098 return None
1099 raise
1100 finally:
1101 pidfile.close()
1102
1103 return pid
1104
1105
showard8de37132009-08-31 18:33:08 +00001106def program_is_alive(program_name):
showard549afad2009-08-20 23:33:36 +00001107 """
1108 Checks if the process is alive and not in Zombie state.
1109
1110 @param program_name the name of the program
1111 @return True if still alive, False otherwise
1112 """
1113 pid = get_pid_from_file(program_name)
1114 if pid is None:
1115 return False
1116 return pid_is_alive(pid)
1117
1118
showard8de37132009-08-31 18:33:08 +00001119def signal_program(program_name, sig=signal.SIGTERM):
showard549afad2009-08-20 23:33:36 +00001120 """
1121 Sends a signal to the process listed in <program_name>.pid
1122
1123 @param program_name the name of the program
1124 @param sig signal to send
1125 """
1126 pid = get_pid_from_file(program_name)
1127 if pid:
1128 signal_pid(pid, sig)
mbligh45561782009-05-11 21:14:34 +00001129
1130
1131def get_relative_path(path, reference):
1132 """Given 2 absolute paths "path" and "reference", compute the path of
1133 "path" as relative to the directory "reference".
1134
1135 @param path the absolute path to convert to a relative path
1136 @param reference an absolute directory path to which the relative
1137 path will be computed
1138 """
1139 # normalize the paths (remove double slashes, etc)
1140 assert(os.path.isabs(path))
1141 assert(os.path.isabs(reference))
1142
1143 path = os.path.normpath(path)
1144 reference = os.path.normpath(reference)
1145
1146 # we could use os.path.split() but it splits from the end
1147 path_list = path.split(os.path.sep)[1:]
1148 ref_list = reference.split(os.path.sep)[1:]
1149
1150 # find the longest leading common path
1151 for i in xrange(min(len(path_list), len(ref_list))):
1152 if path_list[i] != ref_list[i]:
1153 # decrement i so when exiting this loop either by no match or by
1154 # end of range we are one step behind
1155 i -= 1
1156 break
1157 i += 1
1158 # drop the common part of the paths, not interested in that anymore
1159 del path_list[:i]
1160
1161 # for each uncommon component in the reference prepend a ".."
1162 path_list[:0] = ['..'] * (len(ref_list) - i)
1163
1164 return os.path.join(*path_list)
mbligh277a0e42009-07-11 00:11:45 +00001165
1166
1167def sh_escape(command):
1168 """
1169 Escape special characters from a command so that it can be passed
1170 as a double quoted (" ") string in a (ba)sh command.
1171
1172 Args:
1173 command: the command string to escape.
1174
1175 Returns:
1176 The escaped command string. The required englobing double
1177 quotes are NOT added and so should be added at some point by
1178 the caller.
1179
1180 See also: http://www.tldp.org/LDP/abs/html/escapingsection.html
1181 """
1182 command = command.replace("\\", "\\\\")
1183 command = command.replace("$", r'\$')
1184 command = command.replace('"', r'\"')
1185 command = command.replace('`', r'\`')
1186 return command