blob: ddbb34b8af4f350749579fdcc088964665508e87 [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 """
mblighc823e8d2009-10-02 00:01:35 +0000376 for arg in args:
377 command += ' "%s"' % sh_escape(arg)
showardb45a4662009-07-15 14:27:56 +0000378 if stderr_is_expected is None:
379 stderr_is_expected = ignore_status
mblighc823e8d2009-10-02 00:01:35 +0000380
showard170873e2009-01-07 00:22:26 +0000381 bg_job = join_bg_jobs(
showardb45a4662009-07-15 14:27:56 +0000382 (BgJob(command, stdout_tee, stderr_tee, verbose, stdin=stdin,
383 stderr_level=get_stderr_level(stderr_is_expected)),),
showard170873e2009-01-07 00:22:26 +0000384 timeout)[0]
mbligh849a0f62008-08-28 20:12:19 +0000385 if not ignore_status and bg_job.result.exit_status:
jadmanski9c1098b2008-09-02 14:18:48 +0000386 raise error.CmdError(command, bg_job.result,
mbligh849a0f62008-08-28 20:12:19 +0000387 "Command returned non-zero exit status")
mbligh63073c92008-03-31 16:49:32 +0000388
mbligh849a0f62008-08-28 20:12:19 +0000389 return bg_job.result
mbligh63073c92008-03-31 16:49:32 +0000390
mbligh45ffc432008-12-09 23:35:17 +0000391
mbligha5630a52008-09-03 22:09:50 +0000392def run_parallel(commands, timeout=None, ignore_status=False,
393 stdout_tee=None, stderr_tee=None):
jadmanski093a0682009-10-13 14:55:43 +0000394 """
395 Behaves the same as run() with the following exceptions:
mbligha5630a52008-09-03 22:09:50 +0000396
397 - commands is a list of commands to run in parallel.
398 - ignore_status toggles whether or not an exception should be raised
399 on any error.
400
jadmanski093a0682009-10-13 14:55:43 +0000401 @return: a list of CmdResult objects
mbligha5630a52008-09-03 22:09:50 +0000402 """
403 bg_jobs = []
404 for command in commands:
showardb45a4662009-07-15 14:27:56 +0000405 bg_jobs.append(BgJob(command, stdout_tee, stderr_tee,
406 stderr_level=get_stderr_level(ignore_status)))
mbligha5630a52008-09-03 22:09:50 +0000407
408 # Updates objects in bg_jobs list with their process information
409 join_bg_jobs(bg_jobs, timeout)
410
411 for bg_job in bg_jobs:
412 if not ignore_status and bg_job.result.exit_status:
413 raise error.CmdError(command, bg_job.result,
414 "Command returned non-zero exit status")
415
416 return [bg_job.result for bg_job in bg_jobs]
417
418
mbligh849a0f62008-08-28 20:12:19 +0000419@deprecated
mbligh63073c92008-03-31 16:49:32 +0000420def run_bg(command):
mbligh849a0f62008-08-28 20:12:19 +0000421 """Function deprecated. Please use BgJob class instead."""
422 bg_job = BgJob(command)
423 return bg_job.sp, bg_job.result
mbligh63073c92008-03-31 16:49:32 +0000424
425
mbligh849a0f62008-08-28 20:12:19 +0000426def join_bg_jobs(bg_jobs, timeout=None):
mbligha5630a52008-09-03 22:09:50 +0000427 """Joins the bg_jobs with the current thread.
428
429 Returns the same list of bg_jobs objects that was passed in.
430 """
mblighae69f262009-04-17 20:14:56 +0000431 ret, timeout_error = 0, False
mbligh849a0f62008-08-28 20:12:19 +0000432 for bg_job in bg_jobs:
433 bg_job.output_prepare(StringIO.StringIO(), StringIO.StringIO())
mbligh63073c92008-03-31 16:49:32 +0000434
jadmanski0afbb632008-06-06 21:10:57 +0000435 try:
436 # We are holding ends to stdin, stdout pipes
437 # hence we need to be sure to close those fds no mater what
438 start_time = time.time()
mbligh849a0f62008-08-28 20:12:19 +0000439 timeout_error = _wait_for_commands(bg_jobs, start_time, timeout)
440
441 for bg_job in bg_jobs:
442 # Process stdout and stderr
443 bg_job.process_output(stdout=True,final_read=True)
444 bg_job.process_output(stdout=False,final_read=True)
jadmanski0afbb632008-06-06 21:10:57 +0000445 finally:
446 # close our ends of the pipes to the sp no matter what
mbligh849a0f62008-08-28 20:12:19 +0000447 for bg_job in bg_jobs:
448 bg_job.cleanup()
mbligh63073c92008-03-31 16:49:32 +0000449
mbligh849a0f62008-08-28 20:12:19 +0000450 if timeout_error:
451 # TODO: This needs to be fixed to better represent what happens when
452 # running in parallel. However this is backwards compatable, so it will
453 # do for the time being.
454 raise error.CmdError(bg_jobs[0].command, bg_jobs[0].result,
455 "Command(s) did not complete within %d seconds"
456 % timeout)
mbligh63073c92008-03-31 16:49:32 +0000457
mbligh63073c92008-03-31 16:49:32 +0000458
mbligh849a0f62008-08-28 20:12:19 +0000459 return bg_jobs
mbligh63073c92008-03-31 16:49:32 +0000460
mbligh849a0f62008-08-28 20:12:19 +0000461
462def _wait_for_commands(bg_jobs, start_time, timeout):
463 # This returns True if it must return due to a timeout, otherwise False.
464
mblighf0b4a0a2008-09-03 20:46:16 +0000465 # To check for processes which terminate without producing any output
466 # a 1 second timeout is used in select.
467 SELECT_TIMEOUT = 1
468
jadmanski093a0682009-10-13 14:55:43 +0000469 read_list = []
470 write_list = []
mbligh849a0f62008-08-28 20:12:19 +0000471 reverse_dict = {}
jadmanski093a0682009-10-13 14:55:43 +0000472
mbligh849a0f62008-08-28 20:12:19 +0000473 for bg_job in bg_jobs:
jadmanski093a0682009-10-13 14:55:43 +0000474 read_list.append(bg_job.sp.stdout)
475 read_list.append(bg_job.sp.stderr)
476 reverse_dict[bg_job.sp.stdout] = (bg_job, True)
477 reverse_dict[bg_job.sp.stderr] = (bg_job, False)
478 if bg_job.string_stdin:
479 write_list.append(bg_job.sp.stdin)
480 reverse_dict[bg_job.sp.stdin] = bg_job
mbligh849a0f62008-08-28 20:12:19 +0000481
jadmanski0afbb632008-06-06 21:10:57 +0000482 if timeout:
483 stop_time = start_time + timeout
484 time_left = stop_time - time.time()
485 else:
486 time_left = None # so that select never times out
jadmanski093a0682009-10-13 14:55:43 +0000487
jadmanski0afbb632008-06-06 21:10:57 +0000488 while not timeout or time_left > 0:
jadmanski093a0682009-10-13 14:55:43 +0000489 # select will return when we may write to stdin or when there is
490 # stdout/stderr output we can read (including when it is
jadmanski0afbb632008-06-06 21:10:57 +0000491 # EOF, that is the process has terminated).
jadmanski093a0682009-10-13 14:55:43 +0000492 read_ready, write_ready, _ = select.select(read_list, write_list, [],
493 SELECT_TIMEOUT)
mbligh849a0f62008-08-28 20:12:19 +0000494
jadmanski0afbb632008-06-06 21:10:57 +0000495 # os.read() has to be used instead of
496 # subproc.stdout.read() which will otherwise block
jadmanski093a0682009-10-13 14:55:43 +0000497 for file_obj in read_ready:
498 bg_job, is_stdout = reverse_dict[file_obj]
499 bg_job.process_output(is_stdout)
mbligh63073c92008-03-31 16:49:32 +0000500
jadmanski093a0682009-10-13 14:55:43 +0000501 for file_obj in write_ready:
502 # we can write PIPE_BUF bytes without blocking
503 # POSIX requires PIPE_BUF is >= 512
504 bg_job = reverse_dict[file_obj]
505 file_obj.write(bg_job.string_stdin[:512])
506 bg_job.string_stdin = bg_job.string_stdin[512:]
507 # no more input data, close stdin, remove it from the select set
508 if not bg_job.string_stdin:
509 file_obj.close()
510 write_list.remove(file_obj)
511 del reverse_dict[file_obj]
512
513 all_jobs_finished = True
514 for bg_job in bg_jobs:
515 if bg_job.result.exit_status is not None:
516 continue
517
mbligh849a0f62008-08-28 20:12:19 +0000518 bg_job.result.exit_status = bg_job.sp.poll()
jadmanski093a0682009-10-13 14:55:43 +0000519 if bg_job.result.exit_status is not None:
520 # process exited, remove its stdout/stdin from the select set
521 read_list.remove(bg_job.sp.stdout)
522 read_list.remove(bg_job.sp.stderr)
523 del reverse_dict[bg_job.sp.stdout]
524 del reverse_dict[bg_job.sp.stderr]
525 else:
526 all_jobs_finished = False
527
528 if all_jobs_finished:
529 return False
mbligh8ea61e22008-05-09 18:09:37 +0000530
jadmanski0afbb632008-06-06 21:10:57 +0000531 if timeout:
532 time_left = stop_time - time.time()
mbligh63073c92008-03-31 16:49:32 +0000533
mbligh849a0f62008-08-28 20:12:19 +0000534 # Kill all processes which did not complete prior to timeout
jadmanski093a0682009-10-13 14:55:43 +0000535 for bg_job in bg_jobs:
536 if bg_job.result.exit_status is not None:
537 continue
538
539 logging.warn('run process timeout (%s) fired on: %s', timeout,
540 bg_job.command)
mbligh849a0f62008-08-28 20:12:19 +0000541 nuke_subprocess(bg_job.sp)
mbligh095dc642008-10-01 03:41:35 +0000542 bg_job.result.exit_status = bg_job.sp.poll()
mbligh8ea61e22008-05-09 18:09:37 +0000543
mbligh849a0f62008-08-28 20:12:19 +0000544 return True
mbligh63073c92008-03-31 16:49:32 +0000545
546
showard549afad2009-08-20 23:33:36 +0000547def pid_is_alive(pid):
548 """
549 True if process pid exists and is not yet stuck in Zombie state.
550 Zombies are impossible to move between cgroups, etc.
551 pid can be integer, or text of integer.
552 """
553 path = '/proc/%s/stat' % pid
554
555 try:
556 stat = read_one_line(path)
557 except IOError:
558 if not os.path.exists(path):
559 # file went away
560 return False
561 raise
562
563 return stat.split()[2] != 'Z'
564
565
566def signal_pid(pid, sig):
567 """
568 Sends a signal to a process id. Returns True if the process terminated
569 successfully, False otherwise.
570 """
571 try:
572 os.kill(pid, sig)
573 except OSError:
574 # The process may have died before we could kill it.
575 pass
576
577 for i in range(5):
578 if not pid_is_alive(pid):
579 return True
580 time.sleep(1)
581
582 # The process is still alive
583 return False
584
585
mbligh63073c92008-03-31 16:49:32 +0000586def nuke_subprocess(subproc):
jadmanski09f92032008-09-17 14:05:27 +0000587 # check if the subprocess is still alive, first
588 if subproc.poll() is not None:
589 return subproc.poll()
590
jadmanski0afbb632008-06-06 21:10:57 +0000591 # the process has not terminated within timeout,
592 # kill it via an escalating series of signals.
593 signal_queue = [signal.SIGTERM, signal.SIGKILL]
594 for sig in signal_queue:
showard549afad2009-08-20 23:33:36 +0000595 signal_pid(subproc.pid, sig)
596 if subproc.poll() is not None:
597 return subproc.poll()
mbligh63073c92008-03-31 16:49:32 +0000598
599
showard786da9a2009-10-12 20:31:20 +0000600def nuke_pid(pid, signal_queue=(signal.SIGTERM, signal.SIGKILL)):
jadmanski0afbb632008-06-06 21:10:57 +0000601 # the process has not terminated within timeout,
602 # kill it via an escalating series of signals.
jadmanski0afbb632008-06-06 21:10:57 +0000603 for sig in signal_queue:
showard549afad2009-08-20 23:33:36 +0000604 if signal_pid(pid, sig):
605 return
mbligh63073c92008-03-31 16:49:32 +0000606
showard549afad2009-08-20 23:33:36 +0000607 # no signal successfully terminated the process
608 raise error.AutoservRunError('Could not kill %d' % pid, None)
mbligh298ed592009-06-15 21:52:57 +0000609
610
mbligh63073c92008-03-31 16:49:32 +0000611def system(command, timeout=None, ignore_status=False):
mbligha5630a52008-09-03 22:09:50 +0000612 """This function returns the exit status of command."""
mblighf8dffb12008-10-29 16:45:26 +0000613 return run(command, timeout=timeout, ignore_status=ignore_status,
showard108d73e2009-06-22 18:14:41 +0000614 stdout_tee=TEE_TO_LOGS, stderr_tee=TEE_TO_LOGS).exit_status
mbligh63073c92008-03-31 16:49:32 +0000615
616
mbligha5630a52008-09-03 22:09:50 +0000617def system_parallel(commands, timeout=None, ignore_status=False):
618 """This function returns a list of exit statuses for the respective
619 list of commands."""
620 return [bg_jobs.exit_status for bg_jobs in
mblighf8dffb12008-10-29 16:45:26 +0000621 run_parallel(commands, timeout=timeout, ignore_status=ignore_status,
showard108d73e2009-06-22 18:14:41 +0000622 stdout_tee=TEE_TO_LOGS, stderr_tee=TEE_TO_LOGS)]
mbligh849a0f62008-08-28 20:12:19 +0000623
624
mbligh8ea61e22008-05-09 18:09:37 +0000625def system_output(command, timeout=None, ignore_status=False,
mblighc823e8d2009-10-02 00:01:35 +0000626 retain_output=False, args=()):
627 """
628 Run a command and return the stdout output.
629
630 @param command: command string to execute.
631 @param timeout: time limit in seconds before attempting to kill the
632 running process. The function will take a few seconds longer
633 than 'timeout' to complete if it has to kill the process.
634 @param ignore_status: do not raise an exception, no matter what the exit
635 code of the command is.
636 @param retain_output: set to True to make stdout/stderr of the command
637 output to be also sent to the logging system
638 @param args: sequence of strings of arguments to be given to the command
639 inside " quotes after they have been escaped for that; each
640 element in the sequence will be given as a separate command
641 argument
642
643 @return a string with the stdout output of the command.
644 """
jadmanski0afbb632008-06-06 21:10:57 +0000645 if retain_output:
mblighf8dffb12008-10-29 16:45:26 +0000646 out = run(command, timeout=timeout, ignore_status=ignore_status,
mblighc823e8d2009-10-02 00:01:35 +0000647 stdout_tee=TEE_TO_LOGS, stderr_tee=TEE_TO_LOGS,
648 args=args).stdout
jadmanski0afbb632008-06-06 21:10:57 +0000649 else:
mblighc823e8d2009-10-02 00:01:35 +0000650 out = run(command, timeout=timeout, ignore_status=ignore_status,
651 args=args).stdout
652 if out[-1:] == '\n':
653 out = out[:-1]
jadmanski0afbb632008-06-06 21:10:57 +0000654 return out
mbligh63073c92008-03-31 16:49:32 +0000655
mbligh849a0f62008-08-28 20:12:19 +0000656
mbligha5630a52008-09-03 22:09:50 +0000657def system_output_parallel(commands, timeout=None, ignore_status=False,
658 retain_output=False):
659 if retain_output:
showard108d73e2009-06-22 18:14:41 +0000660 out = [bg_job.stdout for bg_job
661 in run_parallel(commands, timeout=timeout,
662 ignore_status=ignore_status,
663 stdout_tee=TEE_TO_LOGS, stderr_tee=TEE_TO_LOGS)]
mbligha5630a52008-09-03 22:09:50 +0000664 else:
mblighf8dffb12008-10-29 16:45:26 +0000665 out = [bg_job.stdout for bg_job in run_parallel(commands,
666 timeout=timeout, ignore_status=ignore_status)]
mbligha5630a52008-09-03 22:09:50 +0000667 for x in out:
668 if out[-1:] == '\n': out = out[:-1]
669 return out
670
671
mbligh98467952008-11-19 00:25:45 +0000672def strip_unicode(input):
673 if type(input) == list:
674 return [strip_unicode(i) for i in input]
675 elif type(input) == dict:
676 output = {}
677 for key in input.keys():
678 output[str(key)] = strip_unicode(input[key])
679 return output
680 elif type(input) == unicode:
681 return str(input)
682 else:
683 return input
684
685
mbligha5630a52008-09-03 22:09:50 +0000686def get_cpu_percentage(function, *args, **dargs):
687 """Returns a tuple containing the CPU% and return value from function call.
688
689 This function calculates the usage time by taking the difference of
690 the user and system times both before and after the function call.
691 """
692 child_pre = resource.getrusage(resource.RUSAGE_CHILDREN)
693 self_pre = resource.getrusage(resource.RUSAGE_SELF)
694 start = time.time()
695 to_return = function(*args, **dargs)
696 elapsed = time.time() - start
697 self_post = resource.getrusage(resource.RUSAGE_SELF)
698 child_post = resource.getrusage(resource.RUSAGE_CHILDREN)
699
700 # Calculate CPU Percentage
701 s_user, s_system = [a - b for a, b in zip(self_post, self_pre)[:2]]
702 c_user, c_system = [a - b for a, b in zip(child_post, child_pre)[:2]]
703 cpu_percent = (s_user + c_user + s_system + c_system) / elapsed
704
705 return cpu_percent, to_return
706
707
mblighc1cbc992008-05-27 20:01:45 +0000708"""
709This function is used when there is a need to run more than one
710job simultaneously starting exactly at the same time. It basically returns
711a modified control file (containing the synchronization code prepended)
712whenever it is ready to run the control file. The synchronization
713is done using barriers to make sure that the jobs start at the same time.
714
715Here is how the synchronization is done to make sure that the tests
716start at exactly the same time on the client.
717sc_bar is a server barrier and s_bar, c_bar are the normal barriers
718
719 Job1 Job2 ...... JobN
720 Server: | sc_bar
721 Server: | s_bar ...... s_bar
722 Server: | at.run() at.run() ...... at.run()
723 ----------|------------------------------------------------------
724 Client | sc_bar
725 Client | c_bar c_bar ...... c_bar
726 Client | <run test> <run test> ...... <run test>
727
728
729PARAMS:
730 control_file : The control file which to which the above synchronization
731 code would be prepended to
732 host_name : The host name on which the job is going to run
733 host_num (non negative) : A number to identify the machine so that we have
734 different sets of s_bar_ports for each of the machines.
735 instance : The number of the job
736 num_jobs : Total number of jobs that are going to run in parallel with
737 this job starting at the same time
738 port_base : Port number that is used to derive the actual barrier ports.
739
740RETURN VALUE:
741 The modified control file.
742
743"""
744def get_sync_control_file(control, host_name, host_num,
jadmanski0afbb632008-06-06 21:10:57 +0000745 instance, num_jobs, port_base=63100):
746 sc_bar_port = port_base
747 c_bar_port = port_base
748 if host_num < 0:
749 print "Please provide a non negative number for the host"
750 return None
751 s_bar_port = port_base + 1 + host_num # The set of s_bar_ports are
752 # the same for a given machine
mblighc1cbc992008-05-27 20:01:45 +0000753
jadmanski0afbb632008-06-06 21:10:57 +0000754 sc_bar_timeout = 180
755 s_bar_timeout = c_bar_timeout = 120
mblighc1cbc992008-05-27 20:01:45 +0000756
jadmanski0afbb632008-06-06 21:10:57 +0000757 # The barrier code snippet is prepended into the conrol file
758 # dynamically before at.run() is called finally.
759 control_new = []
mblighc1cbc992008-05-27 20:01:45 +0000760
jadmanski0afbb632008-06-06 21:10:57 +0000761 # jobid is the unique name used to identify the processes
762 # trying to reach the barriers
763 jobid = "%s#%d" % (host_name, instance)
mblighc1cbc992008-05-27 20:01:45 +0000764
jadmanski0afbb632008-06-06 21:10:57 +0000765 rendv = []
766 # rendvstr is a temp holder for the rendezvous list of the processes
767 for n in range(num_jobs):
768 rendv.append("'%s#%d'" % (host_name, n))
769 rendvstr = ",".join(rendv)
mblighc1cbc992008-05-27 20:01:45 +0000770
jadmanski0afbb632008-06-06 21:10:57 +0000771 if instance == 0:
772 # Do the setup and wait at the server barrier
773 # Clean up the tmp and the control dirs for the first instance
774 control_new.append('if os.path.exists(job.tmpdir):')
775 control_new.append("\t system('umount -f %s > /dev/null"
776 "2> /dev/null' % job.tmpdir,"
777 "ignore_status=True)")
778 control_new.append("\t system('rm -rf ' + job.tmpdir)")
779 control_new.append(
780 'b0 = job.barrier("%s", "sc_bar", %d, port=%d)'
781 % (jobid, sc_bar_timeout, sc_bar_port))
782 control_new.append(
mbligh9c12f772009-06-22 19:03:55 +0000783 'b0.rendezvous_servers("PARALLEL_MASTER", "%s")'
jadmanski0afbb632008-06-06 21:10:57 +0000784 % jobid)
mblighc1cbc992008-05-27 20:01:45 +0000785
jadmanski0afbb632008-06-06 21:10:57 +0000786 elif instance == 1:
787 # Wait at the server barrier to wait for instance=0
788 # process to complete setup
789 b0 = barrier.barrier("PARALLEL_MASTER", "sc_bar", sc_bar_timeout,
790 port=sc_bar_port)
mbligh9c12f772009-06-22 19:03:55 +0000791 b0.rendezvous_servers("PARALLEL_MASTER", jobid)
mblighc1cbc992008-05-27 20:01:45 +0000792
jadmanski0afbb632008-06-06 21:10:57 +0000793 if(num_jobs > 2):
794 b1 = barrier.barrier(jobid, "s_bar", s_bar_timeout,
795 port=s_bar_port)
mbligh9c12f772009-06-22 19:03:55 +0000796 b1.rendezvous(rendvstr)
mblighc1cbc992008-05-27 20:01:45 +0000797
jadmanski0afbb632008-06-06 21:10:57 +0000798 else:
799 # For the rest of the clients
800 b2 = barrier.barrier(jobid, "s_bar", s_bar_timeout, port=s_bar_port)
mbligh9c12f772009-06-22 19:03:55 +0000801 b2.rendezvous(rendvstr)
mblighc1cbc992008-05-27 20:01:45 +0000802
jadmanski0afbb632008-06-06 21:10:57 +0000803 # Client side barrier for all the tests to start at the same time
804 control_new.append('b1 = job.barrier("%s", "c_bar", %d, port=%d)'
805 % (jobid, c_bar_timeout, c_bar_port))
mbligh9c12f772009-06-22 19:03:55 +0000806 control_new.append("b1.rendezvous(%s)" % rendvstr)
mblighc1cbc992008-05-27 20:01:45 +0000807
jadmanski0afbb632008-06-06 21:10:57 +0000808 # Stick in the rest of the control file
809 control_new.append(control)
mblighc1cbc992008-05-27 20:01:45 +0000810
jadmanski0afbb632008-06-06 21:10:57 +0000811 return "\n".join(control_new)
mblighc1cbc992008-05-27 20:01:45 +0000812
mbligh63073c92008-03-31 16:49:32 +0000813
mblighc5ddfd12008-08-04 17:15:00 +0000814def get_arch(run_function=run):
815 """
816 Get the hardware architecture of the machine.
817 run_function is used to execute the commands. It defaults to
818 utils.run() but a custom method (if provided) should be of the
819 same schema as utils.run. It should return a CmdResult object and
820 throw a CmdError exception.
821 """
822 arch = run_function('/bin/uname -m').stdout.rstrip()
823 if re.match(r'i\d86$', arch):
824 arch = 'i386'
825 return arch
826
827
showard4745ecd2009-05-26 19:34:56 +0000828def get_num_logical_cpus_per_socket(run_function=run):
mbligh9fd9afe2009-04-28 18:27:25 +0000829 """
830 Get the number of cores (including hyperthreading) per cpu.
831 run_function is used to execute the commands. It defaults to
832 utils.run() but a custom method (if provided) should be of the
833 same schema as utils.run. It should return a CmdResult object and
834 throw a CmdError exception.
835 """
showard4745ecd2009-05-26 19:34:56 +0000836 siblings = run_function('grep "^siblings" /proc/cpuinfo').stdout.rstrip()
837 num_siblings = map(int,
838 re.findall(r'^siblings\s*:\s*(\d+)\s*$',
839 siblings, re.M))
840 if len(num_siblings) == 0:
841 raise error.TestError('Unable to find siblings info in /proc/cpuinfo')
842 if min(num_siblings) != max(num_siblings):
843 raise error.TestError('Number of siblings differ %r' %
844 num_siblings)
845 return num_siblings[0]
mbligh9fd9afe2009-04-28 18:27:25 +0000846
847
jadmanski4f909252008-12-01 20:47:10 +0000848def merge_trees(src, dest):
849 """
850 Merges a source directory tree at 'src' into a destination tree at
851 'dest'. If a path is a file in both trees than the file in the source
852 tree is APPENDED to the one in the destination tree. If a path is
853 a directory in both trees then the directories are recursively merged
854 with this function. In any other case, the function will skip the
855 paths that cannot be merged (instead of failing).
856 """
857 if not os.path.exists(src):
858 return # exists only in dest
859 elif not os.path.exists(dest):
860 if os.path.isfile(src):
861 shutil.copy2(src, dest) # file only in src
862 else:
863 shutil.copytree(src, dest, symlinks=True) # dir only in src
864 return
865 elif os.path.isfile(src) and os.path.isfile(dest):
866 # src & dest are files in both trees, append src to dest
867 destfile = open(dest, "a")
868 try:
869 srcfile = open(src)
870 try:
871 destfile.write(srcfile.read())
872 finally:
873 srcfile.close()
874 finally:
875 destfile.close()
876 elif os.path.isdir(src) and os.path.isdir(dest):
877 # src & dest are directories in both trees, so recursively merge
878 for name in os.listdir(src):
879 merge_trees(os.path.join(src, name), os.path.join(dest, name))
880 else:
881 # src & dest both exist, but are incompatible
882 return
883
884
mbligh63073c92008-03-31 16:49:32 +0000885class CmdResult(object):
jadmanski0afbb632008-06-06 21:10:57 +0000886 """
887 Command execution result.
mbligh63073c92008-03-31 16:49:32 +0000888
jadmanski0afbb632008-06-06 21:10:57 +0000889 command: String containing the command line itself
890 exit_status: Integer exit code of the process
891 stdout: String containing stdout of the process
892 stderr: String containing stderr of the process
893 duration: Elapsed wall clock time running the process
894 """
mbligh63073c92008-03-31 16:49:32 +0000895
896
mblighcd63a212009-05-01 23:04:38 +0000897 def __init__(self, command="", stdout="", stderr="",
jadmanski0afbb632008-06-06 21:10:57 +0000898 exit_status=None, duration=0):
899 self.command = command
900 self.exit_status = exit_status
901 self.stdout = stdout
902 self.stderr = stderr
903 self.duration = duration
mbligh63073c92008-03-31 16:49:32 +0000904
905
jadmanski0afbb632008-06-06 21:10:57 +0000906 def __repr__(self):
907 wrapper = textwrap.TextWrapper(width = 78,
908 initial_indent="\n ",
909 subsequent_indent=" ")
910
911 stdout = self.stdout.rstrip()
912 if stdout:
913 stdout = "\nstdout:\n%s" % stdout
914
915 stderr = self.stderr.rstrip()
916 if stderr:
917 stderr = "\nstderr:\n%s" % stderr
918
919 return ("* Command: %s\n"
920 "Exit status: %s\n"
921 "Duration: %s\n"
922 "%s"
923 "%s"
924 % (wrapper.fill(self.command), self.exit_status,
925 self.duration, stdout, stderr))
mbligh63073c92008-03-31 16:49:32 +0000926
927
mbligh462c0152008-03-13 15:37:10 +0000928class run_randomly:
jadmanski0afbb632008-06-06 21:10:57 +0000929 def __init__(self, run_sequentially=False):
930 # Run sequentially is for debugging control files
931 self.test_list = []
932 self.run_sequentially = run_sequentially
mbligh462c0152008-03-13 15:37:10 +0000933
934
jadmanski0afbb632008-06-06 21:10:57 +0000935 def add(self, *args, **dargs):
936 test = (args, dargs)
937 self.test_list.append(test)
mbligh462c0152008-03-13 15:37:10 +0000938
939
jadmanski0afbb632008-06-06 21:10:57 +0000940 def run(self, fn):
941 while self.test_list:
942 test_index = random.randint(0, len(self.test_list)-1)
943 if self.run_sequentially:
944 test_index = 0
945 (args, dargs) = self.test_list.pop(test_index)
946 fn(*args, **dargs)
mbligha7007722009-01-13 00:37:11 +0000947
948
showard5deef7f2009-09-09 18:16:58 +0000949def import_site_module(path, module, dummy=None, modulefile=None):
950 """
951 Try to import the site specific module if it exists.
952
953 @param path full filename of the source file calling this (ie __file__)
954 @param module full module name
955 @param dummy dummy value to return in case there is no symbol to import
956 @param modulefile module filename
957
958 @return site specific module or dummy
959
960 @raises ImportError if the site file exists but imports fails
961 """
962 short_module = module[module.rfind(".") + 1:]
963
964 if not modulefile:
965 modulefile = short_module + ".py"
966
967 if os.path.exists(os.path.join(os.path.dirname(path), modulefile)):
968 return __import__(module, {}, {}, [short_module])
969 return dummy
970
971
mblighdd669372009-02-03 21:57:18 +0000972def import_site_symbol(path, module, name, dummy=None, modulefile=None):
973 """
974 Try to import site specific symbol from site specific file if it exists
975
976 @param path full filename of the source file calling this (ie __file__)
977 @param module full module name
978 @param name symbol name to be imported from the site file
979 @param dummy dummy value to return in case there is no symbol to import
980 @param modulefile module filename
981
982 @return site specific symbol or dummy
983
showard5deef7f2009-09-09 18:16:58 +0000984 @raises ImportError if the site file exists but imports fails
mblighdd669372009-02-03 21:57:18 +0000985 """
showard5deef7f2009-09-09 18:16:58 +0000986 module = import_site_module(path, module, modulefile=modulefile)
987 if not module:
988 return dummy
mbligha7007722009-01-13 00:37:11 +0000989
showard5deef7f2009-09-09 18:16:58 +0000990 # special unique value to tell us if the symbol can't be imported
991 cant_import = object()
mbligha7007722009-01-13 00:37:11 +0000992
showard5deef7f2009-09-09 18:16:58 +0000993 obj = getattr(module, name, cant_import)
994 if obj is cant_import:
995 logging.error("unable to import site symbol '%s', using non-site "
996 "implementation", name)
997 return dummy
mbligh062ed152009-01-13 00:57:14 +0000998
999 return obj
1000
1001
1002def import_site_class(path, module, classname, baseclass, modulefile=None):
1003 """
1004 Try to import site specific class from site specific file if it exists
1005
1006 Args:
1007 path: full filename of the source file calling this (ie __file__)
1008 module: full module name
1009 classname: class name to be loaded from site file
mbligh0a8c3322009-04-28 18:32:19 +00001010 baseclass: base class object to return when no site file present or
1011 to mixin when site class exists but is not inherited from baseclass
mbligh062ed152009-01-13 00:57:14 +00001012 modulefile: module filename
1013
mbligh0a8c3322009-04-28 18:32:19 +00001014 Returns: baseclass if site specific class does not exist, the site specific
1015 class if it exists and is inherited from baseclass or a mixin of the
1016 site specific class and baseclass when the site specific class exists
1017 and is not inherited from baseclass
mbligh062ed152009-01-13 00:57:14 +00001018
1019 Raises: ImportError if the site file exists but imports fails
1020 """
1021
mblighdd669372009-02-03 21:57:18 +00001022 res = import_site_symbol(path, module, classname, None, modulefile)
mbligh0a8c3322009-04-28 18:32:19 +00001023 if res:
1024 if not issubclass(res, baseclass):
1025 # if not a subclass of baseclass then mix in baseclass with the
1026 # site specific class object and return the result
1027 res = type(classname, (res, baseclass), {})
1028 else:
1029 res = baseclass
mbligha7007722009-01-13 00:37:11 +00001030
mbligh062ed152009-01-13 00:57:14 +00001031 return res
1032
1033
1034def import_site_function(path, module, funcname, dummy, modulefile=None):
1035 """
1036 Try to import site specific function from site specific file if it exists
1037
1038 Args:
1039 path: full filename of the source file calling this (ie __file__)
1040 module: full module name
1041 funcname: function name to be imported from site file
1042 dummy: dummy function to return in case there is no function to import
1043 modulefile: module filename
1044
1045 Returns: site specific function object or dummy
1046
1047 Raises: ImportError if the site file exists but imports fails
1048 """
1049
mblighdd669372009-02-03 21:57:18 +00001050 return import_site_symbol(path, module, funcname, dummy, modulefile)
mblighfb676032009-04-01 18:25:38 +00001051
1052
showard549afad2009-08-20 23:33:36 +00001053def _get_pid_path(program_name):
1054 my_path = os.path.dirname(__file__)
1055 return os.path.abspath(os.path.join(my_path, "..", "..",
1056 "%s.pid" % program_name))
1057
1058
mblighfb676032009-04-01 18:25:38 +00001059def write_pid(program_name):
1060 """
1061 Try to drop <program_name>.pid in the main autotest directory.
1062
1063 Args:
1064 program_name: prefix for file name
1065 """
showard549afad2009-08-20 23:33:36 +00001066 pidfile = open(_get_pid_path(program_name), "w")
1067 try:
1068 pidfile.write("%s\n" % os.getpid())
1069 finally:
1070 pidfile.close()
mblighfb676032009-04-01 18:25:38 +00001071
showard549afad2009-08-20 23:33:36 +00001072
1073def delete_pid_file_if_exists(program_name):
1074 """
1075 Tries to remove <program_name>.pid from the main autotest directory.
1076 """
1077 pidfile_path = _get_pid_path(program_name)
1078
1079 try:
1080 os.remove(pidfile_path)
1081 except OSError:
1082 if not os.path.exists(pidfile_path):
1083 return
1084 raise
1085
1086
1087def get_pid_from_file(program_name):
1088 """
1089 Reads the pid from <program_name>.pid in the autotest directory.
1090
1091 @param program_name the name of the program
1092 @return the pid if the file exists, None otherwise.
1093 """
1094 pidfile_path = _get_pid_path(program_name)
1095 if not os.path.exists(pidfile_path):
1096 return None
1097
1098 pidfile = open(_get_pid_path(program_name), 'r')
1099
1100 try:
1101 try:
1102 pid = int(pidfile.readline())
1103 except IOError:
1104 if not os.path.exists(pidfile_path):
1105 return None
1106 raise
1107 finally:
1108 pidfile.close()
1109
1110 return pid
1111
1112
showard8de37132009-08-31 18:33:08 +00001113def program_is_alive(program_name):
showard549afad2009-08-20 23:33:36 +00001114 """
1115 Checks if the process is alive and not in Zombie state.
1116
1117 @param program_name the name of the program
1118 @return True if still alive, False otherwise
1119 """
1120 pid = get_pid_from_file(program_name)
1121 if pid is None:
1122 return False
1123 return pid_is_alive(pid)
1124
1125
showard8de37132009-08-31 18:33:08 +00001126def signal_program(program_name, sig=signal.SIGTERM):
showard549afad2009-08-20 23:33:36 +00001127 """
1128 Sends a signal to the process listed in <program_name>.pid
1129
1130 @param program_name the name of the program
1131 @param sig signal to send
1132 """
1133 pid = get_pid_from_file(program_name)
1134 if pid:
1135 signal_pid(pid, sig)
mbligh45561782009-05-11 21:14:34 +00001136
1137
1138def get_relative_path(path, reference):
1139 """Given 2 absolute paths "path" and "reference", compute the path of
1140 "path" as relative to the directory "reference".
1141
1142 @param path the absolute path to convert to a relative path
1143 @param reference an absolute directory path to which the relative
1144 path will be computed
1145 """
1146 # normalize the paths (remove double slashes, etc)
1147 assert(os.path.isabs(path))
1148 assert(os.path.isabs(reference))
1149
1150 path = os.path.normpath(path)
1151 reference = os.path.normpath(reference)
1152
1153 # we could use os.path.split() but it splits from the end
1154 path_list = path.split(os.path.sep)[1:]
1155 ref_list = reference.split(os.path.sep)[1:]
1156
1157 # find the longest leading common path
1158 for i in xrange(min(len(path_list), len(ref_list))):
1159 if path_list[i] != ref_list[i]:
1160 # decrement i so when exiting this loop either by no match or by
1161 # end of range we are one step behind
1162 i -= 1
1163 break
1164 i += 1
1165 # drop the common part of the paths, not interested in that anymore
1166 del path_list[:i]
1167
1168 # for each uncommon component in the reference prepend a ".."
1169 path_list[:0] = ['..'] * (len(ref_list) - i)
1170
1171 return os.path.join(*path_list)
mbligh277a0e42009-07-11 00:11:45 +00001172
1173
1174def sh_escape(command):
1175 """
1176 Escape special characters from a command so that it can be passed
1177 as a double quoted (" ") string in a (ba)sh command.
1178
1179 Args:
1180 command: the command string to escape.
1181
1182 Returns:
1183 The escaped command string. The required englobing double
1184 quotes are NOT added and so should be added at some point by
1185 the caller.
1186
1187 See also: http://www.tldp.org/LDP/abs/html/escapingsection.html
1188 """
1189 command = command.replace("\\", "\\\\")
1190 command = command.replace("$", r'\$')
1191 command = command.replace('"', r'\"')
1192 command = command.replace('`', r'\`')
1193 return command