blob: bda72e24028e2143e3f8af9a7e194b04cd9b67d9 [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
showard39986a62009-12-10 21:41:53 +000037# prefixes for logging stdout/stderr of commands
38STDOUT_PREFIX = '[stdout] '
39STDERR_PREFIX = '[stderr] '
40
41
42def get_stream_tee_file(stream, level, prefix=''):
showard108d73e2009-06-22 18:14:41 +000043 if stream is None:
44 return _the_null_stream
45 if stream is TEE_TO_LOGS:
showard39986a62009-12-10 21:41:53 +000046 return logging_manager.LoggingFile(level=level, prefix=prefix)
showard108d73e2009-06-22 18:14:41 +000047 return stream
48
49
mbligh849a0f62008-08-28 20:12:19 +000050class BgJob(object):
showard170873e2009-01-07 00:22:26 +000051 def __init__(self, command, stdout_tee=None, stderr_tee=None, verbose=True,
showardb45a4662009-07-15 14:27:56 +000052 stdin=None, stderr_level=DEFAULT_STDERR_LEVEL):
mbligh849a0f62008-08-28 20:12:19 +000053 self.command = command
showard39986a62009-12-10 21:41:53 +000054 self.stdout_tee = get_stream_tee_file(stdout_tee, DEFAULT_STDOUT_LEVEL,
55 prefix=STDOUT_PREFIX)
56 self.stderr_tee = get_stream_tee_file(stderr_tee, stderr_level,
57 prefix=STDERR_PREFIX)
mbligh849a0f62008-08-28 20:12:19 +000058 self.result = CmdResult(command)
jadmanski093a0682009-10-13 14:55:43 +000059
60 # allow for easy stdin input by string, we'll let subprocess create
61 # a pipe for stdin input and we'll write to it in the wait loop
62 if isinstance(stdin, basestring):
63 self.string_stdin = stdin
64 stdin = subprocess.PIPE
65 else:
66 self.string_stdin = None
67
mblighbd96b452008-09-03 23:14:27 +000068 if verbose:
showardb18134f2009-03-20 20:52:18 +000069 logging.debug("Running '%s'" % command)
mbligh849a0f62008-08-28 20:12:19 +000070 self.sp = subprocess.Popen(command, stdout=subprocess.PIPE,
71 stderr=subprocess.PIPE,
72 preexec_fn=self._reset_sigpipe, shell=True,
showard170873e2009-01-07 00:22:26 +000073 executable="/bin/bash",
74 stdin=stdin)
mbligh849a0f62008-08-28 20:12:19 +000075
76
77 def output_prepare(self, stdout_file=None, stderr_file=None):
78 self.stdout_file = stdout_file
79 self.stderr_file = stderr_file
80
mbligh45ffc432008-12-09 23:35:17 +000081
mbligh849a0f62008-08-28 20:12:19 +000082 def process_output(self, stdout=True, final_read=False):
83 """output_prepare must be called prior to calling this"""
84 if stdout:
85 pipe, buf, tee = self.sp.stdout, self.stdout_file, self.stdout_tee
86 else:
87 pipe, buf, tee = self.sp.stderr, self.stderr_file, self.stderr_tee
88
89 if final_read:
90 # read in all the data we can from pipe and then stop
91 data = []
92 while select.select([pipe], [], [], 0)[0]:
93 data.append(os.read(pipe.fileno(), 1024))
94 if len(data[-1]) == 0:
95 break
96 data = "".join(data)
97 else:
98 # perform a single read
99 data = os.read(pipe.fileno(), 1024)
100 buf.write(data)
showard108d73e2009-06-22 18:14:41 +0000101 tee.write(data)
mbligh849a0f62008-08-28 20:12:19 +0000102
103
104 def cleanup(self):
showard108d73e2009-06-22 18:14:41 +0000105 self.stdout_tee.flush()
106 self.stderr_tee.flush()
mbligh849a0f62008-08-28 20:12:19 +0000107 self.sp.stdout.close()
108 self.sp.stderr.close()
109 self.result.stdout = self.stdout_file.getvalue()
110 self.result.stderr = self.stderr_file.getvalue()
111
112
113 def _reset_sigpipe(self):
114 signal.signal(signal.SIGPIPE, signal.SIG_DFL)
115
mbligh81edd792008-08-26 16:54:02 +0000116
117def ip_to_long(ip):
118 # !L is a long in network byte order
119 return struct.unpack('!L', socket.inet_aton(ip))[0]
120
121
122def long_to_ip(number):
123 # See above comment.
124 return socket.inet_ntoa(struct.pack('!L', number))
125
126
127def create_subnet_mask(bits):
mbligh81edd792008-08-26 16:54:02 +0000128 return (1 << 32) - (1 << 32-bits)
129
130
131def format_ip_with_mask(ip, mask_bits):
132 masked_ip = ip_to_long(ip) & create_subnet_mask(mask_bits)
133 return "%s/%s" % (long_to_ip(masked_ip), mask_bits)
mbligh6231cd62008-02-02 19:18:33 +0000134
mblighde0d47e2008-03-28 14:37:18 +0000135
jadmanskie80d4712008-10-03 16:15:59 +0000136def normalize_hostname(alias):
137 ip = socket.gethostbyname(alias)
138 return socket.gethostbyaddr(ip)[0]
139
140
mblighd6d043c2008-09-27 21:00:45 +0000141def get_ip_local_port_range():
142 match = re.match(r'\s*(\d+)\s*(\d+)\s*$',
143 read_one_line('/proc/sys/net/ipv4/ip_local_port_range'))
144 return (int(match.group(1)), int(match.group(2)))
145
146
147def set_ip_local_port_range(lower, upper):
148 write_one_line('/proc/sys/net/ipv4/ip_local_port_range',
149 '%d %d\n' % (lower, upper))
150
mbligh315b9412008-10-01 03:34:11 +0000151
mbligh45ffc432008-12-09 23:35:17 +0000152
153def send_email(mail_from, mail_to, subject, body):
154 """
155 Sends an email via smtp
156
157 mail_from: string with email address of sender
158 mail_to: string or list with email address(es) of recipients
159 subject: string with subject of email
160 body: (multi-line) string with body of email
161 """
162 if isinstance(mail_to, str):
163 mail_to = [mail_to]
164 msg = "From: %s\nTo: %s\nSubject: %s\n\n%s" % (mail_from, ','.join(mail_to),
165 subject, body)
166 try:
167 mailer = smtplib.SMTP('localhost')
168 try:
169 mailer.sendmail(mail_from, mail_to, msg)
170 finally:
171 mailer.quit()
172 except Exception, e:
173 # Emails are non-critical, not errors, but don't raise them
174 print "Sending email failed. Reason: %s" % repr(e)
175
176
jadmanski5182e162008-05-13 21:48:16 +0000177def read_one_line(filename):
mbligh6e8840c2008-07-11 18:05:49 +0000178 return open(filename, 'r').readline().rstrip('\n')
jadmanski5182e162008-05-13 21:48:16 +0000179
180
mblighb9d05512008-10-18 13:53:27 +0000181def write_one_line(filename, line):
182 open_write_close(filename, line.rstrip('\n') + '\n')
183
184
185def open_write_close(filename, data):
mbligh618ac9e2008-10-06 17:14:32 +0000186 f = open(filename, 'w')
mblighb9d05512008-10-18 13:53:27 +0000187 try:
188 f.write(data)
189 finally:
190 f.close()
jadmanski5182e162008-05-13 21:48:16 +0000191
192
mblighde0d47e2008-03-28 14:37:18 +0000193def read_keyval(path):
jadmanski0afbb632008-06-06 21:10:57 +0000194 """
195 Read a key-value pair format file into a dictionary, and return it.
196 Takes either a filename or directory name as input. If it's a
197 directory name, we assume you want the file to be called keyval.
198 """
199 if os.path.isdir(path):
200 path = os.path.join(path, 'keyval')
201 keyval = {}
jadmanski58962982009-04-21 19:54:34 +0000202 if os.path.exists(path):
203 for line in open(path):
204 line = re.sub('#.*', '', line).rstrip()
205 if not re.search(r'^[-\.\w]+=', line):
206 raise ValueError('Invalid format line: %s' % line)
207 key, value = line.split('=', 1)
208 if re.search('^\d+$', value):
209 value = int(value)
210 elif re.search('^(\d+\.)?\d+$', value):
211 value = float(value)
212 keyval[key] = value
jadmanski0afbb632008-06-06 21:10:57 +0000213 return keyval
mblighde0d47e2008-03-28 14:37:18 +0000214
215
jadmanskicc549172008-05-21 18:11:51 +0000216def write_keyval(path, dictionary, type_tag=None):
jadmanski0afbb632008-06-06 21:10:57 +0000217 """
218 Write a key-value pair format file out to a file. This uses append
219 mode to open the file, so existing text will not be overwritten or
220 reparsed.
jadmanskicc549172008-05-21 18:11:51 +0000221
jadmanski0afbb632008-06-06 21:10:57 +0000222 If type_tag is None, then the key must be composed of alphanumeric
223 characters (or dashes+underscores). However, if type-tag is not
224 null then the keys must also have "{type_tag}" as a suffix. At
225 the moment the only valid values of type_tag are "attr" and "perf".
226 """
227 if os.path.isdir(path):
228 path = os.path.join(path, 'keyval')
229 keyval = open(path, 'a')
jadmanskicc549172008-05-21 18:11:51 +0000230
jadmanski0afbb632008-06-06 21:10:57 +0000231 if type_tag is None:
mbligh97227ea2009-03-11 17:09:50 +0000232 key_regex = re.compile(r'^[-\.\w]+$')
jadmanski0afbb632008-06-06 21:10:57 +0000233 else:
234 if type_tag not in ('attr', 'perf'):
235 raise ValueError('Invalid type tag: %s' % type_tag)
236 escaped_tag = re.escape(type_tag)
mbligh97227ea2009-03-11 17:09:50 +0000237 key_regex = re.compile(r'^[-\.\w]+\{%s\}$' % escaped_tag)
jadmanski0afbb632008-06-06 21:10:57 +0000238 try:
mbligh6955e232009-07-11 00:58:47 +0000239 for key in sorted(dictionary.keys()):
jadmanski0afbb632008-06-06 21:10:57 +0000240 if not key_regex.search(key):
241 raise ValueError('Invalid key: %s' % key)
mbligh6955e232009-07-11 00:58:47 +0000242 keyval.write('%s=%s\n' % (key, dictionary[key]))
jadmanski0afbb632008-06-06 21:10:57 +0000243 finally:
244 keyval.close()
mbligh6231cd62008-02-02 19:18:33 +0000245
246
247def is_url(path):
jadmanski0afbb632008-06-06 21:10:57 +0000248 """Return true if path looks like a URL"""
249 # for now, just handle http and ftp
250 url_parts = urlparse.urlparse(path)
251 return (url_parts[0] in ('http', 'ftp'))
mbligh6231cd62008-02-02 19:18:33 +0000252
253
mblighb2896192009-07-11 00:12:37 +0000254def urlopen(url, data=None, timeout=5):
255 """Wrapper to urllib2.urlopen with timeout addition."""
mbligh02ff2d52008-06-03 15:00:21 +0000256
jadmanski0afbb632008-06-06 21:10:57 +0000257 # Save old timeout
258 old_timeout = socket.getdefaulttimeout()
259 socket.setdefaulttimeout(timeout)
260 try:
mblighb2896192009-07-11 00:12:37 +0000261 return urllib2.urlopen(url, data=data)
jadmanski0afbb632008-06-06 21:10:57 +0000262 finally:
263 socket.setdefaulttimeout(old_timeout)
mbligh02ff2d52008-06-03 15:00:21 +0000264
265
mblighb2896192009-07-11 00:12:37 +0000266def urlretrieve(url, filename, data=None, timeout=300):
267 """Retrieve a file from given url."""
268 logging.debug('Fetching %s -> %s', url, filename)
269
270 src_file = urlopen(url, data=data, timeout=timeout)
jadmanski0afbb632008-06-06 21:10:57 +0000271 try:
mblighb2896192009-07-11 00:12:37 +0000272 dest_file = open(filename, 'wb')
273 try:
274 shutil.copyfileobj(src_file, dest_file)
275 finally:
276 dest_file.close()
jadmanski0afbb632008-06-06 21:10:57 +0000277 finally:
mblighb2896192009-07-11 00:12:37 +0000278 src_file.close()
jadmanski0afbb632008-06-06 21:10:57 +0000279
mbligh02ff2d52008-06-03 15:00:21 +0000280
mbligh6231cd62008-02-02 19:18:33 +0000281def get_file(src, dest, permissions=None):
jadmanski0afbb632008-06-06 21:10:57 +0000282 """Get a file from src, which can be local or a remote URL"""
mbligh25284cd2009-06-08 16:17:24 +0000283 if src == dest:
jadmanski0afbb632008-06-06 21:10:57 +0000284 return
mbligh25284cd2009-06-08 16:17:24 +0000285
286 if is_url(src):
mblighb2896192009-07-11 00:12:37 +0000287 urlretrieve(src, dest)
jadmanski0afbb632008-06-06 21:10:57 +0000288 else:
289 shutil.copyfile(src, dest)
mbligh25284cd2009-06-08 16:17:24 +0000290
jadmanski0afbb632008-06-06 21:10:57 +0000291 if permissions:
292 os.chmod(dest, permissions)
293 return dest
mbligh6231cd62008-02-02 19:18:33 +0000294
295
296def unmap_url(srcdir, src, destdir='.'):
jadmanski0afbb632008-06-06 21:10:57 +0000297 """
298 Receives either a path to a local file or a URL.
299 returns either the path to the local file, or the fetched URL
mbligh6231cd62008-02-02 19:18:33 +0000300
jadmanski0afbb632008-06-06 21:10:57 +0000301 unmap_url('/usr/src', 'foo.tar', '/tmp')
302 = '/usr/src/foo.tar'
303 unmap_url('/usr/src', 'http://site/file', '/tmp')
304 = '/tmp/file'
305 (after retrieving it)
306 """
307 if is_url(src):
308 url_parts = urlparse.urlparse(src)
309 filename = os.path.basename(url_parts[2])
310 dest = os.path.join(destdir, filename)
311 return get_file(src, dest)
312 else:
313 return os.path.join(srcdir, src)
mbligh6231cd62008-02-02 19:18:33 +0000314
315
316def update_version(srcdir, preserve_srcdir, new_version, install,
jadmanski0afbb632008-06-06 21:10:57 +0000317 *args, **dargs):
318 """
319 Make sure srcdir is version new_version
mbligh6231cd62008-02-02 19:18:33 +0000320
jadmanski0afbb632008-06-06 21:10:57 +0000321 If not, delete it and install() the new version.
mbligh6231cd62008-02-02 19:18:33 +0000322
jadmanski0afbb632008-06-06 21:10:57 +0000323 In the preserve_srcdir case, we just check it's up to date,
324 and if not, we rerun install, without removing srcdir
325 """
326 versionfile = os.path.join(srcdir, '.version')
327 install_needed = True
mbligh6231cd62008-02-02 19:18:33 +0000328
jadmanski0afbb632008-06-06 21:10:57 +0000329 if os.path.exists(versionfile):
330 old_version = pickle.load(open(versionfile))
331 if old_version == new_version:
332 install_needed = False
mbligh6231cd62008-02-02 19:18:33 +0000333
jadmanski0afbb632008-06-06 21:10:57 +0000334 if install_needed:
335 if not preserve_srcdir and os.path.exists(srcdir):
336 shutil.rmtree(srcdir)
337 install(*args, **dargs)
338 if os.path.exists(srcdir):
339 pickle.dump(new_version, open(versionfile, 'w'))
mbligh462c0152008-03-13 15:37:10 +0000340
341
showardb45a4662009-07-15 14:27:56 +0000342def get_stderr_level(stderr_is_expected):
343 if stderr_is_expected:
344 return DEFAULT_STDOUT_LEVEL
345 return DEFAULT_STDERR_LEVEL
346
347
mbligh63073c92008-03-31 16:49:32 +0000348def run(command, timeout=None, ignore_status=False,
showardb45a4662009-07-15 14:27:56 +0000349 stdout_tee=None, stderr_tee=None, verbose=True, stdin=None,
mblighc823e8d2009-10-02 00:01:35 +0000350 stderr_is_expected=None, args=()):
jadmanski0afbb632008-06-06 21:10:57 +0000351 """
352 Run a command on the host.
mbligh63073c92008-03-31 16:49:32 +0000353
mblighc823e8d2009-10-02 00:01:35 +0000354 @param command: the command line string.
355 @param timeout: time limit in seconds before attempting to kill the
356 running process. The run() function will take a few seconds
357 longer than 'timeout' to complete if it has to kill the process.
358 @param ignore_status: do not raise an exception, no matter what the exit
359 code of the command is.
360 @param stdout_tee: optional file-like object to which stdout data
361 will be written as it is generated (data will still be stored
362 in result.stdout).
363 @param stderr_tee: likewise for stderr.
364 @param verbose: if True, log the command being run.
jadmanski093a0682009-10-13 14:55:43 +0000365 @param stdin: stdin to pass to the executed process (can be a file
366 descriptor, a file object of a real file or a string).
mblighc823e8d2009-10-02 00:01:35 +0000367 @param args: sequence of strings of arguments to be given to the command
368 inside " quotes after they have been escaped for that; each
369 element in the sequence will be given as a separate command
370 argument
mbligh63073c92008-03-31 16:49:32 +0000371
mblighc823e8d2009-10-02 00:01:35 +0000372 @return a CmdResult object
mbligh63073c92008-03-31 16:49:32 +0000373
mblighc823e8d2009-10-02 00:01:35 +0000374 @raise CmdError: the exit code of the command execution was not 0
jadmanski0afbb632008-06-06 21:10:57 +0000375 """
mbligh2c7f7d62009-12-19 05:24:04 +0000376 if isinstance(args, basestring):
377 raise TypeError('Got a string for the "args" keyword argument, '
378 'need a sequence.')
379
mblighc823e8d2009-10-02 00:01:35 +0000380 for arg in args:
381 command += ' "%s"' % sh_escape(arg)
showardb45a4662009-07-15 14:27:56 +0000382 if stderr_is_expected is None:
383 stderr_is_expected = ignore_status
mblighc823e8d2009-10-02 00:01:35 +0000384
showard170873e2009-01-07 00:22:26 +0000385 bg_job = join_bg_jobs(
showardb45a4662009-07-15 14:27:56 +0000386 (BgJob(command, stdout_tee, stderr_tee, verbose, stdin=stdin,
387 stderr_level=get_stderr_level(stderr_is_expected)),),
showard170873e2009-01-07 00:22:26 +0000388 timeout)[0]
mbligh849a0f62008-08-28 20:12:19 +0000389 if not ignore_status and bg_job.result.exit_status:
jadmanski9c1098b2008-09-02 14:18:48 +0000390 raise error.CmdError(command, bg_job.result,
mbligh849a0f62008-08-28 20:12:19 +0000391 "Command returned non-zero exit status")
mbligh63073c92008-03-31 16:49:32 +0000392
mbligh849a0f62008-08-28 20:12:19 +0000393 return bg_job.result
mbligh63073c92008-03-31 16:49:32 +0000394
mbligh45ffc432008-12-09 23:35:17 +0000395
mbligha5630a52008-09-03 22:09:50 +0000396def run_parallel(commands, timeout=None, ignore_status=False,
397 stdout_tee=None, stderr_tee=None):
jadmanski093a0682009-10-13 14:55:43 +0000398 """
399 Behaves the same as run() with the following exceptions:
mbligha5630a52008-09-03 22:09:50 +0000400
401 - commands is a list of commands to run in parallel.
402 - ignore_status toggles whether or not an exception should be raised
403 on any error.
404
jadmanski093a0682009-10-13 14:55:43 +0000405 @return: a list of CmdResult objects
mbligha5630a52008-09-03 22:09:50 +0000406 """
407 bg_jobs = []
408 for command in commands:
showardb45a4662009-07-15 14:27:56 +0000409 bg_jobs.append(BgJob(command, stdout_tee, stderr_tee,
410 stderr_level=get_stderr_level(ignore_status)))
mbligha5630a52008-09-03 22:09:50 +0000411
412 # Updates objects in bg_jobs list with their process information
413 join_bg_jobs(bg_jobs, timeout)
414
415 for bg_job in bg_jobs:
416 if not ignore_status and bg_job.result.exit_status:
417 raise error.CmdError(command, bg_job.result,
418 "Command returned non-zero exit status")
419
420 return [bg_job.result for bg_job in bg_jobs]
421
422
mbligh849a0f62008-08-28 20:12:19 +0000423@deprecated
mbligh63073c92008-03-31 16:49:32 +0000424def run_bg(command):
mbligh849a0f62008-08-28 20:12:19 +0000425 """Function deprecated. Please use BgJob class instead."""
426 bg_job = BgJob(command)
427 return bg_job.sp, bg_job.result
mbligh63073c92008-03-31 16:49:32 +0000428
429
mbligh849a0f62008-08-28 20:12:19 +0000430def join_bg_jobs(bg_jobs, timeout=None):
mbligha5630a52008-09-03 22:09:50 +0000431 """Joins the bg_jobs with the current thread.
432
433 Returns the same list of bg_jobs objects that was passed in.
434 """
mblighae69f262009-04-17 20:14:56 +0000435 ret, timeout_error = 0, False
mbligh849a0f62008-08-28 20:12:19 +0000436 for bg_job in bg_jobs:
437 bg_job.output_prepare(StringIO.StringIO(), StringIO.StringIO())
mbligh63073c92008-03-31 16:49:32 +0000438
jadmanski0afbb632008-06-06 21:10:57 +0000439 try:
440 # We are holding ends to stdin, stdout pipes
441 # hence we need to be sure to close those fds no mater what
442 start_time = time.time()
mbligh849a0f62008-08-28 20:12:19 +0000443 timeout_error = _wait_for_commands(bg_jobs, start_time, timeout)
444
445 for bg_job in bg_jobs:
446 # Process stdout and stderr
447 bg_job.process_output(stdout=True,final_read=True)
448 bg_job.process_output(stdout=False,final_read=True)
jadmanski0afbb632008-06-06 21:10:57 +0000449 finally:
450 # close our ends of the pipes to the sp no matter what
mbligh849a0f62008-08-28 20:12:19 +0000451 for bg_job in bg_jobs:
452 bg_job.cleanup()
mbligh63073c92008-03-31 16:49:32 +0000453
mbligh849a0f62008-08-28 20:12:19 +0000454 if timeout_error:
455 # TODO: This needs to be fixed to better represent what happens when
456 # running in parallel. However this is backwards compatable, so it will
457 # do for the time being.
458 raise error.CmdError(bg_jobs[0].command, bg_jobs[0].result,
459 "Command(s) did not complete within %d seconds"
460 % timeout)
mbligh63073c92008-03-31 16:49:32 +0000461
mbligh63073c92008-03-31 16:49:32 +0000462
mbligh849a0f62008-08-28 20:12:19 +0000463 return bg_jobs
mbligh63073c92008-03-31 16:49:32 +0000464
mbligh849a0f62008-08-28 20:12:19 +0000465
466def _wait_for_commands(bg_jobs, start_time, timeout):
467 # This returns True if it must return due to a timeout, otherwise False.
468
mblighf0b4a0a2008-09-03 20:46:16 +0000469 # To check for processes which terminate without producing any output
470 # a 1 second timeout is used in select.
471 SELECT_TIMEOUT = 1
472
jadmanski093a0682009-10-13 14:55:43 +0000473 read_list = []
474 write_list = []
mbligh849a0f62008-08-28 20:12:19 +0000475 reverse_dict = {}
jadmanski093a0682009-10-13 14:55:43 +0000476
mbligh849a0f62008-08-28 20:12:19 +0000477 for bg_job in bg_jobs:
jadmanski093a0682009-10-13 14:55:43 +0000478 read_list.append(bg_job.sp.stdout)
479 read_list.append(bg_job.sp.stderr)
480 reverse_dict[bg_job.sp.stdout] = (bg_job, True)
481 reverse_dict[bg_job.sp.stderr] = (bg_job, False)
482 if bg_job.string_stdin:
483 write_list.append(bg_job.sp.stdin)
484 reverse_dict[bg_job.sp.stdin] = bg_job
mbligh849a0f62008-08-28 20:12:19 +0000485
jadmanski0afbb632008-06-06 21:10:57 +0000486 if timeout:
487 stop_time = start_time + timeout
488 time_left = stop_time - time.time()
489 else:
490 time_left = None # so that select never times out
jadmanski093a0682009-10-13 14:55:43 +0000491
jadmanski0afbb632008-06-06 21:10:57 +0000492 while not timeout or time_left > 0:
jadmanski093a0682009-10-13 14:55:43 +0000493 # select will return when we may write to stdin or when there is
494 # stdout/stderr output we can read (including when it is
jadmanski0afbb632008-06-06 21:10:57 +0000495 # EOF, that is the process has terminated).
jadmanski093a0682009-10-13 14:55:43 +0000496 read_ready, write_ready, _ = select.select(read_list, write_list, [],
497 SELECT_TIMEOUT)
mbligh849a0f62008-08-28 20:12:19 +0000498
jadmanski0afbb632008-06-06 21:10:57 +0000499 # os.read() has to be used instead of
500 # subproc.stdout.read() which will otherwise block
jadmanski093a0682009-10-13 14:55:43 +0000501 for file_obj in read_ready:
502 bg_job, is_stdout = reverse_dict[file_obj]
503 bg_job.process_output(is_stdout)
mbligh63073c92008-03-31 16:49:32 +0000504
jadmanski093a0682009-10-13 14:55:43 +0000505 for file_obj in write_ready:
506 # we can write PIPE_BUF bytes without blocking
507 # POSIX requires PIPE_BUF is >= 512
508 bg_job = reverse_dict[file_obj]
509 file_obj.write(bg_job.string_stdin[:512])
510 bg_job.string_stdin = bg_job.string_stdin[512:]
511 # no more input data, close stdin, remove it from the select set
512 if not bg_job.string_stdin:
513 file_obj.close()
514 write_list.remove(file_obj)
515 del reverse_dict[file_obj]
516
517 all_jobs_finished = True
518 for bg_job in bg_jobs:
519 if bg_job.result.exit_status is not None:
520 continue
521
mbligh849a0f62008-08-28 20:12:19 +0000522 bg_job.result.exit_status = bg_job.sp.poll()
jadmanski093a0682009-10-13 14:55:43 +0000523 if bg_job.result.exit_status is not None:
524 # process exited, remove its stdout/stdin from the select set
525 read_list.remove(bg_job.sp.stdout)
526 read_list.remove(bg_job.sp.stderr)
527 del reverse_dict[bg_job.sp.stdout]
528 del reverse_dict[bg_job.sp.stderr]
529 else:
530 all_jobs_finished = False
531
532 if all_jobs_finished:
533 return False
mbligh8ea61e22008-05-09 18:09:37 +0000534
jadmanski0afbb632008-06-06 21:10:57 +0000535 if timeout:
536 time_left = stop_time - time.time()
mbligh63073c92008-03-31 16:49:32 +0000537
mbligh849a0f62008-08-28 20:12:19 +0000538 # Kill all processes which did not complete prior to timeout
jadmanski093a0682009-10-13 14:55:43 +0000539 for bg_job in bg_jobs:
540 if bg_job.result.exit_status is not None:
541 continue
542
543 logging.warn('run process timeout (%s) fired on: %s', timeout,
544 bg_job.command)
mbligh849a0f62008-08-28 20:12:19 +0000545 nuke_subprocess(bg_job.sp)
mbligh095dc642008-10-01 03:41:35 +0000546 bg_job.result.exit_status = bg_job.sp.poll()
mbligh8ea61e22008-05-09 18:09:37 +0000547
mbligh849a0f62008-08-28 20:12:19 +0000548 return True
mbligh63073c92008-03-31 16:49:32 +0000549
550
showard549afad2009-08-20 23:33:36 +0000551def pid_is_alive(pid):
552 """
553 True if process pid exists and is not yet stuck in Zombie state.
554 Zombies are impossible to move between cgroups, etc.
555 pid can be integer, or text of integer.
556 """
557 path = '/proc/%s/stat' % pid
558
559 try:
560 stat = read_one_line(path)
561 except IOError:
562 if not os.path.exists(path):
563 # file went away
564 return False
565 raise
566
567 return stat.split()[2] != 'Z'
568
569
570def signal_pid(pid, sig):
571 """
572 Sends a signal to a process id. Returns True if the process terminated
573 successfully, False otherwise.
574 """
575 try:
576 os.kill(pid, sig)
577 except OSError:
578 # The process may have died before we could kill it.
579 pass
580
581 for i in range(5):
582 if not pid_is_alive(pid):
583 return True
584 time.sleep(1)
585
586 # The process is still alive
587 return False
588
589
mbligh63073c92008-03-31 16:49:32 +0000590def nuke_subprocess(subproc):
jadmanski09f92032008-09-17 14:05:27 +0000591 # check if the subprocess is still alive, first
592 if subproc.poll() is not None:
593 return subproc.poll()
594
jadmanski0afbb632008-06-06 21:10:57 +0000595 # the process has not terminated within timeout,
596 # kill it via an escalating series of signals.
597 signal_queue = [signal.SIGTERM, signal.SIGKILL]
598 for sig in signal_queue:
showard549afad2009-08-20 23:33:36 +0000599 signal_pid(subproc.pid, sig)
600 if subproc.poll() is not None:
601 return subproc.poll()
mbligh63073c92008-03-31 16:49:32 +0000602
603
showard786da9a2009-10-12 20:31:20 +0000604def nuke_pid(pid, signal_queue=(signal.SIGTERM, signal.SIGKILL)):
jadmanski0afbb632008-06-06 21:10:57 +0000605 # the process has not terminated within timeout,
606 # kill it via an escalating series of signals.
jadmanski0afbb632008-06-06 21:10:57 +0000607 for sig in signal_queue:
showard549afad2009-08-20 23:33:36 +0000608 if signal_pid(pid, sig):
609 return
mbligh63073c92008-03-31 16:49:32 +0000610
showard549afad2009-08-20 23:33:36 +0000611 # no signal successfully terminated the process
612 raise error.AutoservRunError('Could not kill %d' % pid, None)
mbligh298ed592009-06-15 21:52:57 +0000613
614
mbligh63073c92008-03-31 16:49:32 +0000615def system(command, timeout=None, ignore_status=False):
mbligha5630a52008-09-03 22:09:50 +0000616 """This function returns the exit status of command."""
mblighf8dffb12008-10-29 16:45:26 +0000617 return run(command, timeout=timeout, ignore_status=ignore_status,
showard108d73e2009-06-22 18:14:41 +0000618 stdout_tee=TEE_TO_LOGS, stderr_tee=TEE_TO_LOGS).exit_status
mbligh63073c92008-03-31 16:49:32 +0000619
620
mbligha5630a52008-09-03 22:09:50 +0000621def system_parallel(commands, timeout=None, ignore_status=False):
622 """This function returns a list of exit statuses for the respective
623 list of commands."""
624 return [bg_jobs.exit_status for bg_jobs in
mblighf8dffb12008-10-29 16:45:26 +0000625 run_parallel(commands, timeout=timeout, ignore_status=ignore_status,
showard108d73e2009-06-22 18:14:41 +0000626 stdout_tee=TEE_TO_LOGS, stderr_tee=TEE_TO_LOGS)]
mbligh849a0f62008-08-28 20:12:19 +0000627
628
mbligh8ea61e22008-05-09 18:09:37 +0000629def system_output(command, timeout=None, ignore_status=False,
mblighc823e8d2009-10-02 00:01:35 +0000630 retain_output=False, args=()):
631 """
632 Run a command and return the stdout output.
633
634 @param command: command string to execute.
635 @param timeout: time limit in seconds before attempting to kill the
636 running process. The function will take a few seconds longer
637 than 'timeout' to complete if it has to kill the process.
638 @param ignore_status: do not raise an exception, no matter what the exit
639 code of the command is.
640 @param retain_output: set to True to make stdout/stderr of the command
641 output to be also sent to the logging system
642 @param args: sequence of strings of arguments to be given to the command
643 inside " quotes after they have been escaped for that; each
644 element in the sequence will be given as a separate command
645 argument
646
647 @return a string with the stdout output of the command.
648 """
jadmanski0afbb632008-06-06 21:10:57 +0000649 if retain_output:
mblighf8dffb12008-10-29 16:45:26 +0000650 out = run(command, timeout=timeout, ignore_status=ignore_status,
mblighc823e8d2009-10-02 00:01:35 +0000651 stdout_tee=TEE_TO_LOGS, stderr_tee=TEE_TO_LOGS,
652 args=args).stdout
jadmanski0afbb632008-06-06 21:10:57 +0000653 else:
mblighc823e8d2009-10-02 00:01:35 +0000654 out = run(command, timeout=timeout, ignore_status=ignore_status,
655 args=args).stdout
656 if out[-1:] == '\n':
657 out = out[:-1]
jadmanski0afbb632008-06-06 21:10:57 +0000658 return out
mbligh63073c92008-03-31 16:49:32 +0000659
mbligh849a0f62008-08-28 20:12:19 +0000660
mbligha5630a52008-09-03 22:09:50 +0000661def system_output_parallel(commands, timeout=None, ignore_status=False,
662 retain_output=False):
663 if retain_output:
showard108d73e2009-06-22 18:14:41 +0000664 out = [bg_job.stdout for bg_job
665 in run_parallel(commands, timeout=timeout,
666 ignore_status=ignore_status,
667 stdout_tee=TEE_TO_LOGS, stderr_tee=TEE_TO_LOGS)]
mbligha5630a52008-09-03 22:09:50 +0000668 else:
mblighf8dffb12008-10-29 16:45:26 +0000669 out = [bg_job.stdout for bg_job in run_parallel(commands,
670 timeout=timeout, ignore_status=ignore_status)]
mbligha5630a52008-09-03 22:09:50 +0000671 for x in out:
672 if out[-1:] == '\n': out = out[:-1]
673 return out
674
675
mbligh98467952008-11-19 00:25:45 +0000676def strip_unicode(input):
677 if type(input) == list:
678 return [strip_unicode(i) for i in input]
679 elif type(input) == dict:
680 output = {}
681 for key in input.keys():
682 output[str(key)] = strip_unicode(input[key])
683 return output
684 elif type(input) == unicode:
685 return str(input)
686 else:
687 return input
688
689
mbligha5630a52008-09-03 22:09:50 +0000690def get_cpu_percentage(function, *args, **dargs):
691 """Returns a tuple containing the CPU% and return value from function call.
692
693 This function calculates the usage time by taking the difference of
694 the user and system times both before and after the function call.
695 """
696 child_pre = resource.getrusage(resource.RUSAGE_CHILDREN)
697 self_pre = resource.getrusage(resource.RUSAGE_SELF)
698 start = time.time()
699 to_return = function(*args, **dargs)
700 elapsed = time.time() - start
701 self_post = resource.getrusage(resource.RUSAGE_SELF)
702 child_post = resource.getrusage(resource.RUSAGE_CHILDREN)
703
704 # Calculate CPU Percentage
705 s_user, s_system = [a - b for a, b in zip(self_post, self_pre)[:2]]
706 c_user, c_system = [a - b for a, b in zip(child_post, child_pre)[:2]]
707 cpu_percent = (s_user + c_user + s_system + c_system) / elapsed
708
709 return cpu_percent, to_return
710
711
mblighc1cbc992008-05-27 20:01:45 +0000712"""
713This function is used when there is a need to run more than one
714job simultaneously starting exactly at the same time. It basically returns
715a modified control file (containing the synchronization code prepended)
716whenever it is ready to run the control file. The synchronization
717is done using barriers to make sure that the jobs start at the same time.
718
719Here is how the synchronization is done to make sure that the tests
720start at exactly the same time on the client.
721sc_bar is a server barrier and s_bar, c_bar are the normal barriers
722
723 Job1 Job2 ...... JobN
724 Server: | sc_bar
725 Server: | s_bar ...... s_bar
726 Server: | at.run() at.run() ...... at.run()
727 ----------|------------------------------------------------------
728 Client | sc_bar
729 Client | c_bar c_bar ...... c_bar
730 Client | <run test> <run test> ...... <run test>
731
732
733PARAMS:
734 control_file : The control file which to which the above synchronization
735 code would be prepended to
736 host_name : The host name on which the job is going to run
737 host_num (non negative) : A number to identify the machine so that we have
738 different sets of s_bar_ports for each of the machines.
739 instance : The number of the job
740 num_jobs : Total number of jobs that are going to run in parallel with
741 this job starting at the same time
742 port_base : Port number that is used to derive the actual barrier ports.
743
744RETURN VALUE:
745 The modified control file.
746
747"""
748def get_sync_control_file(control, host_name, host_num,
jadmanski0afbb632008-06-06 21:10:57 +0000749 instance, num_jobs, port_base=63100):
750 sc_bar_port = port_base
751 c_bar_port = port_base
752 if host_num < 0:
753 print "Please provide a non negative number for the host"
754 return None
755 s_bar_port = port_base + 1 + host_num # The set of s_bar_ports are
756 # the same for a given machine
mblighc1cbc992008-05-27 20:01:45 +0000757
jadmanski0afbb632008-06-06 21:10:57 +0000758 sc_bar_timeout = 180
759 s_bar_timeout = c_bar_timeout = 120
mblighc1cbc992008-05-27 20:01:45 +0000760
jadmanski0afbb632008-06-06 21:10:57 +0000761 # The barrier code snippet is prepended into the conrol file
762 # dynamically before at.run() is called finally.
763 control_new = []
mblighc1cbc992008-05-27 20:01:45 +0000764
jadmanski0afbb632008-06-06 21:10:57 +0000765 # jobid is the unique name used to identify the processes
766 # trying to reach the barriers
767 jobid = "%s#%d" % (host_name, instance)
mblighc1cbc992008-05-27 20:01:45 +0000768
jadmanski0afbb632008-06-06 21:10:57 +0000769 rendv = []
770 # rendvstr is a temp holder for the rendezvous list of the processes
771 for n in range(num_jobs):
772 rendv.append("'%s#%d'" % (host_name, n))
773 rendvstr = ",".join(rendv)
mblighc1cbc992008-05-27 20:01:45 +0000774
jadmanski0afbb632008-06-06 21:10:57 +0000775 if instance == 0:
776 # Do the setup and wait at the server barrier
777 # Clean up the tmp and the control dirs for the first instance
778 control_new.append('if os.path.exists(job.tmpdir):')
779 control_new.append("\t system('umount -f %s > /dev/null"
780 "2> /dev/null' % job.tmpdir,"
781 "ignore_status=True)")
782 control_new.append("\t system('rm -rf ' + job.tmpdir)")
783 control_new.append(
784 'b0 = job.barrier("%s", "sc_bar", %d, port=%d)'
785 % (jobid, sc_bar_timeout, sc_bar_port))
786 control_new.append(
mbligh9c12f772009-06-22 19:03:55 +0000787 'b0.rendezvous_servers("PARALLEL_MASTER", "%s")'
jadmanski0afbb632008-06-06 21:10:57 +0000788 % jobid)
mblighc1cbc992008-05-27 20:01:45 +0000789
jadmanski0afbb632008-06-06 21:10:57 +0000790 elif instance == 1:
791 # Wait at the server barrier to wait for instance=0
792 # process to complete setup
793 b0 = barrier.barrier("PARALLEL_MASTER", "sc_bar", sc_bar_timeout,
794 port=sc_bar_port)
mbligh9c12f772009-06-22 19:03:55 +0000795 b0.rendezvous_servers("PARALLEL_MASTER", jobid)
mblighc1cbc992008-05-27 20:01:45 +0000796
jadmanski0afbb632008-06-06 21:10:57 +0000797 if(num_jobs > 2):
798 b1 = barrier.barrier(jobid, "s_bar", s_bar_timeout,
799 port=s_bar_port)
mbligh9c12f772009-06-22 19:03:55 +0000800 b1.rendezvous(rendvstr)
mblighc1cbc992008-05-27 20:01:45 +0000801
jadmanski0afbb632008-06-06 21:10:57 +0000802 else:
803 # For the rest of the clients
804 b2 = barrier.barrier(jobid, "s_bar", s_bar_timeout, port=s_bar_port)
mbligh9c12f772009-06-22 19:03:55 +0000805 b2.rendezvous(rendvstr)
mblighc1cbc992008-05-27 20:01:45 +0000806
jadmanski0afbb632008-06-06 21:10:57 +0000807 # Client side barrier for all the tests to start at the same time
808 control_new.append('b1 = job.barrier("%s", "c_bar", %d, port=%d)'
809 % (jobid, c_bar_timeout, c_bar_port))
mbligh9c12f772009-06-22 19:03:55 +0000810 control_new.append("b1.rendezvous(%s)" % rendvstr)
mblighc1cbc992008-05-27 20:01:45 +0000811
jadmanski0afbb632008-06-06 21:10:57 +0000812 # Stick in the rest of the control file
813 control_new.append(control)
mblighc1cbc992008-05-27 20:01:45 +0000814
jadmanski0afbb632008-06-06 21:10:57 +0000815 return "\n".join(control_new)
mblighc1cbc992008-05-27 20:01:45 +0000816
mbligh63073c92008-03-31 16:49:32 +0000817
mblighc5ddfd12008-08-04 17:15:00 +0000818def get_arch(run_function=run):
819 """
820 Get the hardware architecture of the machine.
821 run_function is used to execute the commands. It defaults to
822 utils.run() but a custom method (if provided) should be of the
823 same schema as utils.run. It should return a CmdResult object and
824 throw a CmdError exception.
825 """
826 arch = run_function('/bin/uname -m').stdout.rstrip()
827 if re.match(r'i\d86$', arch):
828 arch = 'i386'
829 return arch
830
831
showard4745ecd2009-05-26 19:34:56 +0000832def get_num_logical_cpus_per_socket(run_function=run):
mbligh9fd9afe2009-04-28 18:27:25 +0000833 """
834 Get the number of cores (including hyperthreading) per cpu.
835 run_function is used to execute the commands. It defaults to
836 utils.run() but a custom method (if provided) should be of the
837 same schema as utils.run. It should return a CmdResult object and
838 throw a CmdError exception.
839 """
showard4745ecd2009-05-26 19:34:56 +0000840 siblings = run_function('grep "^siblings" /proc/cpuinfo').stdout.rstrip()
841 num_siblings = map(int,
842 re.findall(r'^siblings\s*:\s*(\d+)\s*$',
843 siblings, re.M))
844 if len(num_siblings) == 0:
845 raise error.TestError('Unable to find siblings info in /proc/cpuinfo')
846 if min(num_siblings) != max(num_siblings):
847 raise error.TestError('Number of siblings differ %r' %
848 num_siblings)
849 return num_siblings[0]
mbligh9fd9afe2009-04-28 18:27:25 +0000850
851
jadmanski4f909252008-12-01 20:47:10 +0000852def merge_trees(src, dest):
853 """
854 Merges a source directory tree at 'src' into a destination tree at
855 'dest'. If a path is a file in both trees than the file in the source
856 tree is APPENDED to the one in the destination tree. If a path is
857 a directory in both trees then the directories are recursively merged
858 with this function. In any other case, the function will skip the
859 paths that cannot be merged (instead of failing).
860 """
861 if not os.path.exists(src):
862 return # exists only in dest
863 elif not os.path.exists(dest):
864 if os.path.isfile(src):
865 shutil.copy2(src, dest) # file only in src
866 else:
867 shutil.copytree(src, dest, symlinks=True) # dir only in src
868 return
869 elif os.path.isfile(src) and os.path.isfile(dest):
870 # src & dest are files in both trees, append src to dest
871 destfile = open(dest, "a")
872 try:
873 srcfile = open(src)
874 try:
875 destfile.write(srcfile.read())
876 finally:
877 srcfile.close()
878 finally:
879 destfile.close()
880 elif os.path.isdir(src) and os.path.isdir(dest):
881 # src & dest are directories in both trees, so recursively merge
882 for name in os.listdir(src):
883 merge_trees(os.path.join(src, name), os.path.join(dest, name))
884 else:
885 # src & dest both exist, but are incompatible
886 return
887
888
mbligh63073c92008-03-31 16:49:32 +0000889class CmdResult(object):
jadmanski0afbb632008-06-06 21:10:57 +0000890 """
891 Command execution result.
mbligh63073c92008-03-31 16:49:32 +0000892
jadmanski0afbb632008-06-06 21:10:57 +0000893 command: String containing the command line itself
894 exit_status: Integer exit code of the process
895 stdout: String containing stdout of the process
896 stderr: String containing stderr of the process
897 duration: Elapsed wall clock time running the process
898 """
mbligh63073c92008-03-31 16:49:32 +0000899
900
mblighcd63a212009-05-01 23:04:38 +0000901 def __init__(self, command="", stdout="", stderr="",
jadmanski0afbb632008-06-06 21:10:57 +0000902 exit_status=None, duration=0):
903 self.command = command
904 self.exit_status = exit_status
905 self.stdout = stdout
906 self.stderr = stderr
907 self.duration = duration
mbligh63073c92008-03-31 16:49:32 +0000908
909
jadmanski0afbb632008-06-06 21:10:57 +0000910 def __repr__(self):
911 wrapper = textwrap.TextWrapper(width = 78,
912 initial_indent="\n ",
913 subsequent_indent=" ")
914
915 stdout = self.stdout.rstrip()
916 if stdout:
917 stdout = "\nstdout:\n%s" % stdout
918
919 stderr = self.stderr.rstrip()
920 if stderr:
921 stderr = "\nstderr:\n%s" % stderr
922
923 return ("* Command: %s\n"
924 "Exit status: %s\n"
925 "Duration: %s\n"
926 "%s"
927 "%s"
928 % (wrapper.fill(self.command), self.exit_status,
929 self.duration, stdout, stderr))
mbligh63073c92008-03-31 16:49:32 +0000930
931
mbligh462c0152008-03-13 15:37:10 +0000932class run_randomly:
jadmanski0afbb632008-06-06 21:10:57 +0000933 def __init__(self, run_sequentially=False):
934 # Run sequentially is for debugging control files
935 self.test_list = []
936 self.run_sequentially = run_sequentially
mbligh462c0152008-03-13 15:37:10 +0000937
938
jadmanski0afbb632008-06-06 21:10:57 +0000939 def add(self, *args, **dargs):
940 test = (args, dargs)
941 self.test_list.append(test)
mbligh462c0152008-03-13 15:37:10 +0000942
943
jadmanski0afbb632008-06-06 21:10:57 +0000944 def run(self, fn):
945 while self.test_list:
946 test_index = random.randint(0, len(self.test_list)-1)
947 if self.run_sequentially:
948 test_index = 0
949 (args, dargs) = self.test_list.pop(test_index)
950 fn(*args, **dargs)
mbligha7007722009-01-13 00:37:11 +0000951
952
showard5deef7f2009-09-09 18:16:58 +0000953def import_site_module(path, module, dummy=None, modulefile=None):
954 """
955 Try to import the site specific module if it exists.
956
957 @param path full filename of the source file calling this (ie __file__)
958 @param module full module name
959 @param dummy dummy value to return in case there is no symbol to import
960 @param modulefile module filename
961
962 @return site specific module or dummy
963
964 @raises ImportError if the site file exists but imports fails
965 """
966 short_module = module[module.rfind(".") + 1:]
967
968 if not modulefile:
969 modulefile = short_module + ".py"
970
971 if os.path.exists(os.path.join(os.path.dirname(path), modulefile)):
972 return __import__(module, {}, {}, [short_module])
973 return dummy
974
975
mblighdd669372009-02-03 21:57:18 +0000976def import_site_symbol(path, module, name, dummy=None, modulefile=None):
977 """
978 Try to import site specific symbol from site specific file if it exists
979
980 @param path full filename of the source file calling this (ie __file__)
981 @param module full module name
982 @param name symbol name to be imported from the site file
983 @param dummy dummy value to return in case there is no symbol to import
984 @param modulefile module filename
985
986 @return site specific symbol or dummy
987
showard5deef7f2009-09-09 18:16:58 +0000988 @raises ImportError if the site file exists but imports fails
mblighdd669372009-02-03 21:57:18 +0000989 """
showard5deef7f2009-09-09 18:16:58 +0000990 module = import_site_module(path, module, modulefile=modulefile)
991 if not module:
992 return dummy
mbligha7007722009-01-13 00:37:11 +0000993
showard5deef7f2009-09-09 18:16:58 +0000994 # special unique value to tell us if the symbol can't be imported
995 cant_import = object()
mbligha7007722009-01-13 00:37:11 +0000996
showard5deef7f2009-09-09 18:16:58 +0000997 obj = getattr(module, name, cant_import)
998 if obj is cant_import:
999 logging.error("unable to import site symbol '%s', using non-site "
1000 "implementation", name)
1001 return dummy
mbligh062ed152009-01-13 00:57:14 +00001002
1003 return obj
1004
1005
1006def import_site_class(path, module, classname, baseclass, modulefile=None):
1007 """
1008 Try to import site specific class from site specific file if it exists
1009
1010 Args:
1011 path: full filename of the source file calling this (ie __file__)
1012 module: full module name
1013 classname: class name to be loaded from site file
mbligh0a8c3322009-04-28 18:32:19 +00001014 baseclass: base class object to return when no site file present or
1015 to mixin when site class exists but is not inherited from baseclass
mbligh062ed152009-01-13 00:57:14 +00001016 modulefile: module filename
1017
mbligh0a8c3322009-04-28 18:32:19 +00001018 Returns: baseclass if site specific class does not exist, the site specific
1019 class if it exists and is inherited from baseclass or a mixin of the
1020 site specific class and baseclass when the site specific class exists
1021 and is not inherited from baseclass
mbligh062ed152009-01-13 00:57:14 +00001022
1023 Raises: ImportError if the site file exists but imports fails
1024 """
1025
mblighdd669372009-02-03 21:57:18 +00001026 res = import_site_symbol(path, module, classname, None, modulefile)
mbligh0a8c3322009-04-28 18:32:19 +00001027 if res:
1028 if not issubclass(res, baseclass):
1029 # if not a subclass of baseclass then mix in baseclass with the
1030 # site specific class object and return the result
1031 res = type(classname, (res, baseclass), {})
1032 else:
1033 res = baseclass
mbligha7007722009-01-13 00:37:11 +00001034
mbligh062ed152009-01-13 00:57:14 +00001035 return res
1036
1037
1038def import_site_function(path, module, funcname, dummy, modulefile=None):
1039 """
1040 Try to import site specific function from site specific file if it exists
1041
1042 Args:
1043 path: full filename of the source file calling this (ie __file__)
1044 module: full module name
1045 funcname: function name to be imported from site file
1046 dummy: dummy function to return in case there is no function to import
1047 modulefile: module filename
1048
1049 Returns: site specific function object or dummy
1050
1051 Raises: ImportError if the site file exists but imports fails
1052 """
1053
mblighdd669372009-02-03 21:57:18 +00001054 return import_site_symbol(path, module, funcname, dummy, modulefile)
mblighfb676032009-04-01 18:25:38 +00001055
1056
showard549afad2009-08-20 23:33:36 +00001057def _get_pid_path(program_name):
1058 my_path = os.path.dirname(__file__)
1059 return os.path.abspath(os.path.join(my_path, "..", "..",
1060 "%s.pid" % program_name))
1061
1062
mblighfb676032009-04-01 18:25:38 +00001063def write_pid(program_name):
1064 """
1065 Try to drop <program_name>.pid in the main autotest directory.
1066
1067 Args:
1068 program_name: prefix for file name
1069 """
showard549afad2009-08-20 23:33:36 +00001070 pidfile = open(_get_pid_path(program_name), "w")
1071 try:
1072 pidfile.write("%s\n" % os.getpid())
1073 finally:
1074 pidfile.close()
mblighfb676032009-04-01 18:25:38 +00001075
showard549afad2009-08-20 23:33:36 +00001076
1077def delete_pid_file_if_exists(program_name):
1078 """
1079 Tries to remove <program_name>.pid from the main autotest directory.
1080 """
1081 pidfile_path = _get_pid_path(program_name)
1082
1083 try:
1084 os.remove(pidfile_path)
1085 except OSError:
1086 if not os.path.exists(pidfile_path):
1087 return
1088 raise
1089
1090
1091def get_pid_from_file(program_name):
1092 """
1093 Reads the pid from <program_name>.pid in the autotest directory.
1094
1095 @param program_name the name of the program
1096 @return the pid if the file exists, None otherwise.
1097 """
1098 pidfile_path = _get_pid_path(program_name)
1099 if not os.path.exists(pidfile_path):
1100 return None
1101
1102 pidfile = open(_get_pid_path(program_name), 'r')
1103
1104 try:
1105 try:
1106 pid = int(pidfile.readline())
1107 except IOError:
1108 if not os.path.exists(pidfile_path):
1109 return None
1110 raise
1111 finally:
1112 pidfile.close()
1113
1114 return pid
1115
1116
showard8de37132009-08-31 18:33:08 +00001117def program_is_alive(program_name):
showard549afad2009-08-20 23:33:36 +00001118 """
1119 Checks if the process is alive and not in Zombie state.
1120
1121 @param program_name the name of the program
1122 @return True if still alive, False otherwise
1123 """
1124 pid = get_pid_from_file(program_name)
1125 if pid is None:
1126 return False
1127 return pid_is_alive(pid)
1128
1129
showard8de37132009-08-31 18:33:08 +00001130def signal_program(program_name, sig=signal.SIGTERM):
showard549afad2009-08-20 23:33:36 +00001131 """
1132 Sends a signal to the process listed in <program_name>.pid
1133
1134 @param program_name the name of the program
1135 @param sig signal to send
1136 """
1137 pid = get_pid_from_file(program_name)
1138 if pid:
1139 signal_pid(pid, sig)
mbligh45561782009-05-11 21:14:34 +00001140
1141
1142def get_relative_path(path, reference):
1143 """Given 2 absolute paths "path" and "reference", compute the path of
1144 "path" as relative to the directory "reference".
1145
1146 @param path the absolute path to convert to a relative path
1147 @param reference an absolute directory path to which the relative
1148 path will be computed
1149 """
1150 # normalize the paths (remove double slashes, etc)
1151 assert(os.path.isabs(path))
1152 assert(os.path.isabs(reference))
1153
1154 path = os.path.normpath(path)
1155 reference = os.path.normpath(reference)
1156
1157 # we could use os.path.split() but it splits from the end
1158 path_list = path.split(os.path.sep)[1:]
1159 ref_list = reference.split(os.path.sep)[1:]
1160
1161 # find the longest leading common path
1162 for i in xrange(min(len(path_list), len(ref_list))):
1163 if path_list[i] != ref_list[i]:
1164 # decrement i so when exiting this loop either by no match or by
1165 # end of range we are one step behind
1166 i -= 1
1167 break
1168 i += 1
1169 # drop the common part of the paths, not interested in that anymore
1170 del path_list[:i]
1171
1172 # for each uncommon component in the reference prepend a ".."
1173 path_list[:0] = ['..'] * (len(ref_list) - i)
1174
1175 return os.path.join(*path_list)
mbligh277a0e42009-07-11 00:11:45 +00001176
1177
1178def sh_escape(command):
1179 """
1180 Escape special characters from a command so that it can be passed
1181 as a double quoted (" ") string in a (ba)sh command.
1182
1183 Args:
1184 command: the command string to escape.
1185
1186 Returns:
1187 The escaped command string. The required englobing double
1188 quotes are NOT added and so should be added at some point by
1189 the caller.
1190
1191 See also: http://www.tldp.org/LDP/abs/html/escapingsection.html
1192 """
1193 command = command.replace("\\", "\\\\")
1194 command = command.replace("$", r'\$')
1195 command = command.replace('"', r'\"')
1196 command = command.replace('`', r'\`')
1197 return command