blob: 12e66c66f521aa7b6d786572bfd59a3fabe48170 [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):
mbligh104a5382010-02-02 18:15:39 +0000616 """
617 Run a command
618
619 @param timeout: timeout in seconds
620 @param ignore_status: if ignore_status=False, throw an exception if the
621 command's exit code is non-zero
622 if ignore_stauts=True, return the exit code.
623
624 @return exit status of command
625 (note, this will always be zero unless ignore_status=True)
626 """
mblighf8dffb12008-10-29 16:45:26 +0000627 return run(command, timeout=timeout, ignore_status=ignore_status,
showard108d73e2009-06-22 18:14:41 +0000628 stdout_tee=TEE_TO_LOGS, stderr_tee=TEE_TO_LOGS).exit_status
mbligh63073c92008-03-31 16:49:32 +0000629
630
mbligha5630a52008-09-03 22:09:50 +0000631def system_parallel(commands, timeout=None, ignore_status=False):
632 """This function returns a list of exit statuses for the respective
633 list of commands."""
634 return [bg_jobs.exit_status for bg_jobs in
mblighf8dffb12008-10-29 16:45:26 +0000635 run_parallel(commands, timeout=timeout, ignore_status=ignore_status,
showard108d73e2009-06-22 18:14:41 +0000636 stdout_tee=TEE_TO_LOGS, stderr_tee=TEE_TO_LOGS)]
mbligh849a0f62008-08-28 20:12:19 +0000637
638
mbligh8ea61e22008-05-09 18:09:37 +0000639def system_output(command, timeout=None, ignore_status=False,
mblighc823e8d2009-10-02 00:01:35 +0000640 retain_output=False, args=()):
641 """
642 Run a command and return the stdout output.
643
644 @param command: command string to execute.
645 @param timeout: time limit in seconds before attempting to kill the
646 running process. The function will take a few seconds longer
647 than 'timeout' to complete if it has to kill the process.
648 @param ignore_status: do not raise an exception, no matter what the exit
649 code of the command is.
650 @param retain_output: set to True to make stdout/stderr of the command
651 output to be also sent to the logging system
652 @param args: sequence of strings of arguments to be given to the command
653 inside " quotes after they have been escaped for that; each
654 element in the sequence will be given as a separate command
655 argument
656
657 @return a string with the stdout output of the command.
658 """
jadmanski0afbb632008-06-06 21:10:57 +0000659 if retain_output:
mblighf8dffb12008-10-29 16:45:26 +0000660 out = run(command, timeout=timeout, ignore_status=ignore_status,
mblighc823e8d2009-10-02 00:01:35 +0000661 stdout_tee=TEE_TO_LOGS, stderr_tee=TEE_TO_LOGS,
662 args=args).stdout
jadmanski0afbb632008-06-06 21:10:57 +0000663 else:
mblighc823e8d2009-10-02 00:01:35 +0000664 out = run(command, timeout=timeout, ignore_status=ignore_status,
665 args=args).stdout
666 if out[-1:] == '\n':
667 out = out[:-1]
jadmanski0afbb632008-06-06 21:10:57 +0000668 return out
mbligh63073c92008-03-31 16:49:32 +0000669
mbligh849a0f62008-08-28 20:12:19 +0000670
mbligha5630a52008-09-03 22:09:50 +0000671def system_output_parallel(commands, timeout=None, ignore_status=False,
672 retain_output=False):
673 if retain_output:
showard108d73e2009-06-22 18:14:41 +0000674 out = [bg_job.stdout for bg_job
675 in run_parallel(commands, timeout=timeout,
676 ignore_status=ignore_status,
677 stdout_tee=TEE_TO_LOGS, stderr_tee=TEE_TO_LOGS)]
mbligha5630a52008-09-03 22:09:50 +0000678 else:
mblighf8dffb12008-10-29 16:45:26 +0000679 out = [bg_job.stdout for bg_job in run_parallel(commands,
680 timeout=timeout, ignore_status=ignore_status)]
mbligha5630a52008-09-03 22:09:50 +0000681 for x in out:
682 if out[-1:] == '\n': out = out[:-1]
683 return out
684
685
mbligh98467952008-11-19 00:25:45 +0000686def strip_unicode(input):
687 if type(input) == list:
688 return [strip_unicode(i) for i in input]
689 elif type(input) == dict:
690 output = {}
691 for key in input.keys():
692 output[str(key)] = strip_unicode(input[key])
693 return output
694 elif type(input) == unicode:
695 return str(input)
696 else:
697 return input
698
699
mbligha5630a52008-09-03 22:09:50 +0000700def get_cpu_percentage(function, *args, **dargs):
701 """Returns a tuple containing the CPU% and return value from function call.
702
703 This function calculates the usage time by taking the difference of
704 the user and system times both before and after the function call.
705 """
706 child_pre = resource.getrusage(resource.RUSAGE_CHILDREN)
707 self_pre = resource.getrusage(resource.RUSAGE_SELF)
708 start = time.time()
709 to_return = function(*args, **dargs)
710 elapsed = time.time() - start
711 self_post = resource.getrusage(resource.RUSAGE_SELF)
712 child_post = resource.getrusage(resource.RUSAGE_CHILDREN)
713
714 # Calculate CPU Percentage
715 s_user, s_system = [a - b for a, b in zip(self_post, self_pre)[:2]]
716 c_user, c_system = [a - b for a, b in zip(child_post, child_pre)[:2]]
717 cpu_percent = (s_user + c_user + s_system + c_system) / elapsed
718
719 return cpu_percent, to_return
720
721
mblighc1cbc992008-05-27 20:01:45 +0000722"""
723This function is used when there is a need to run more than one
724job simultaneously starting exactly at the same time. It basically returns
725a modified control file (containing the synchronization code prepended)
726whenever it is ready to run the control file. The synchronization
727is done using barriers to make sure that the jobs start at the same time.
728
729Here is how the synchronization is done to make sure that the tests
730start at exactly the same time on the client.
731sc_bar is a server barrier and s_bar, c_bar are the normal barriers
732
733 Job1 Job2 ...... JobN
734 Server: | sc_bar
735 Server: | s_bar ...... s_bar
736 Server: | at.run() at.run() ...... at.run()
737 ----------|------------------------------------------------------
738 Client | sc_bar
739 Client | c_bar c_bar ...... c_bar
740 Client | <run test> <run test> ...... <run test>
741
742
743PARAMS:
744 control_file : The control file which to which the above synchronization
745 code would be prepended to
746 host_name : The host name on which the job is going to run
747 host_num (non negative) : A number to identify the machine so that we have
748 different sets of s_bar_ports for each of the machines.
749 instance : The number of the job
750 num_jobs : Total number of jobs that are going to run in parallel with
751 this job starting at the same time
752 port_base : Port number that is used to derive the actual barrier ports.
753
754RETURN VALUE:
755 The modified control file.
756
757"""
758def get_sync_control_file(control, host_name, host_num,
jadmanski0afbb632008-06-06 21:10:57 +0000759 instance, num_jobs, port_base=63100):
760 sc_bar_port = port_base
761 c_bar_port = port_base
762 if host_num < 0:
763 print "Please provide a non negative number for the host"
764 return None
765 s_bar_port = port_base + 1 + host_num # The set of s_bar_ports are
766 # the same for a given machine
mblighc1cbc992008-05-27 20:01:45 +0000767
jadmanski0afbb632008-06-06 21:10:57 +0000768 sc_bar_timeout = 180
769 s_bar_timeout = c_bar_timeout = 120
mblighc1cbc992008-05-27 20:01:45 +0000770
jadmanski0afbb632008-06-06 21:10:57 +0000771 # The barrier code snippet is prepended into the conrol file
772 # dynamically before at.run() is called finally.
773 control_new = []
mblighc1cbc992008-05-27 20:01:45 +0000774
jadmanski0afbb632008-06-06 21:10:57 +0000775 # jobid is the unique name used to identify the processes
776 # trying to reach the barriers
777 jobid = "%s#%d" % (host_name, instance)
mblighc1cbc992008-05-27 20:01:45 +0000778
jadmanski0afbb632008-06-06 21:10:57 +0000779 rendv = []
780 # rendvstr is a temp holder for the rendezvous list of the processes
781 for n in range(num_jobs):
782 rendv.append("'%s#%d'" % (host_name, n))
783 rendvstr = ",".join(rendv)
mblighc1cbc992008-05-27 20:01:45 +0000784
jadmanski0afbb632008-06-06 21:10:57 +0000785 if instance == 0:
786 # Do the setup and wait at the server barrier
787 # Clean up the tmp and the control dirs for the first instance
788 control_new.append('if os.path.exists(job.tmpdir):')
789 control_new.append("\t system('umount -f %s > /dev/null"
790 "2> /dev/null' % job.tmpdir,"
791 "ignore_status=True)")
792 control_new.append("\t system('rm -rf ' + job.tmpdir)")
793 control_new.append(
794 'b0 = job.barrier("%s", "sc_bar", %d, port=%d)'
795 % (jobid, sc_bar_timeout, sc_bar_port))
796 control_new.append(
mbligh9c12f772009-06-22 19:03:55 +0000797 'b0.rendezvous_servers("PARALLEL_MASTER", "%s")'
jadmanski0afbb632008-06-06 21:10:57 +0000798 % jobid)
mblighc1cbc992008-05-27 20:01:45 +0000799
jadmanski0afbb632008-06-06 21:10:57 +0000800 elif instance == 1:
801 # Wait at the server barrier to wait for instance=0
802 # process to complete setup
803 b0 = barrier.barrier("PARALLEL_MASTER", "sc_bar", sc_bar_timeout,
804 port=sc_bar_port)
mbligh9c12f772009-06-22 19:03:55 +0000805 b0.rendezvous_servers("PARALLEL_MASTER", jobid)
mblighc1cbc992008-05-27 20:01:45 +0000806
jadmanski0afbb632008-06-06 21:10:57 +0000807 if(num_jobs > 2):
808 b1 = barrier.barrier(jobid, "s_bar", s_bar_timeout,
809 port=s_bar_port)
mbligh9c12f772009-06-22 19:03:55 +0000810 b1.rendezvous(rendvstr)
mblighc1cbc992008-05-27 20:01:45 +0000811
jadmanski0afbb632008-06-06 21:10:57 +0000812 else:
813 # For the rest of the clients
814 b2 = barrier.barrier(jobid, "s_bar", s_bar_timeout, port=s_bar_port)
mbligh9c12f772009-06-22 19:03:55 +0000815 b2.rendezvous(rendvstr)
mblighc1cbc992008-05-27 20:01:45 +0000816
jadmanski0afbb632008-06-06 21:10:57 +0000817 # Client side barrier for all the tests to start at the same time
818 control_new.append('b1 = job.barrier("%s", "c_bar", %d, port=%d)'
819 % (jobid, c_bar_timeout, c_bar_port))
mbligh9c12f772009-06-22 19:03:55 +0000820 control_new.append("b1.rendezvous(%s)" % rendvstr)
mblighc1cbc992008-05-27 20:01:45 +0000821
jadmanski0afbb632008-06-06 21:10:57 +0000822 # Stick in the rest of the control file
823 control_new.append(control)
mblighc1cbc992008-05-27 20:01:45 +0000824
jadmanski0afbb632008-06-06 21:10:57 +0000825 return "\n".join(control_new)
mblighc1cbc992008-05-27 20:01:45 +0000826
mbligh63073c92008-03-31 16:49:32 +0000827
mblighc5ddfd12008-08-04 17:15:00 +0000828def get_arch(run_function=run):
829 """
830 Get the hardware architecture of the machine.
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 """
836 arch = run_function('/bin/uname -m').stdout.rstrip()
837 if re.match(r'i\d86$', arch):
838 arch = 'i386'
839 return arch
840
841
showard4745ecd2009-05-26 19:34:56 +0000842def get_num_logical_cpus_per_socket(run_function=run):
mbligh9fd9afe2009-04-28 18:27:25 +0000843 """
844 Get the number of cores (including hyperthreading) per cpu.
845 run_function is used to execute the commands. It defaults to
846 utils.run() but a custom method (if provided) should be of the
847 same schema as utils.run. It should return a CmdResult object and
848 throw a CmdError exception.
849 """
showard4745ecd2009-05-26 19:34:56 +0000850 siblings = run_function('grep "^siblings" /proc/cpuinfo').stdout.rstrip()
851 num_siblings = map(int,
852 re.findall(r'^siblings\s*:\s*(\d+)\s*$',
853 siblings, re.M))
854 if len(num_siblings) == 0:
855 raise error.TestError('Unable to find siblings info in /proc/cpuinfo')
856 if min(num_siblings) != max(num_siblings):
857 raise error.TestError('Number of siblings differ %r' %
858 num_siblings)
859 return num_siblings[0]
mbligh9fd9afe2009-04-28 18:27:25 +0000860
861
jadmanski4f909252008-12-01 20:47:10 +0000862def merge_trees(src, dest):
863 """
864 Merges a source directory tree at 'src' into a destination tree at
865 'dest'. If a path is a file in both trees than the file in the source
866 tree is APPENDED to the one in the destination tree. If a path is
867 a directory in both trees then the directories are recursively merged
868 with this function. In any other case, the function will skip the
869 paths that cannot be merged (instead of failing).
870 """
871 if not os.path.exists(src):
872 return # exists only in dest
873 elif not os.path.exists(dest):
874 if os.path.isfile(src):
875 shutil.copy2(src, dest) # file only in src
876 else:
877 shutil.copytree(src, dest, symlinks=True) # dir only in src
878 return
879 elif os.path.isfile(src) and os.path.isfile(dest):
880 # src & dest are files in both trees, append src to dest
881 destfile = open(dest, "a")
882 try:
883 srcfile = open(src)
884 try:
885 destfile.write(srcfile.read())
886 finally:
887 srcfile.close()
888 finally:
889 destfile.close()
890 elif os.path.isdir(src) and os.path.isdir(dest):
891 # src & dest are directories in both trees, so recursively merge
892 for name in os.listdir(src):
893 merge_trees(os.path.join(src, name), os.path.join(dest, name))
894 else:
895 # src & dest both exist, but are incompatible
896 return
897
898
mbligh63073c92008-03-31 16:49:32 +0000899class CmdResult(object):
jadmanski0afbb632008-06-06 21:10:57 +0000900 """
901 Command execution result.
mbligh63073c92008-03-31 16:49:32 +0000902
jadmanski0afbb632008-06-06 21:10:57 +0000903 command: String containing the command line itself
904 exit_status: Integer exit code of the process
905 stdout: String containing stdout of the process
906 stderr: String containing stderr of the process
907 duration: Elapsed wall clock time running the process
908 """
mbligh63073c92008-03-31 16:49:32 +0000909
910
mblighcd63a212009-05-01 23:04:38 +0000911 def __init__(self, command="", stdout="", stderr="",
jadmanski0afbb632008-06-06 21:10:57 +0000912 exit_status=None, duration=0):
913 self.command = command
914 self.exit_status = exit_status
915 self.stdout = stdout
916 self.stderr = stderr
917 self.duration = duration
mbligh63073c92008-03-31 16:49:32 +0000918
919
jadmanski0afbb632008-06-06 21:10:57 +0000920 def __repr__(self):
921 wrapper = textwrap.TextWrapper(width = 78,
922 initial_indent="\n ",
923 subsequent_indent=" ")
924
925 stdout = self.stdout.rstrip()
926 if stdout:
927 stdout = "\nstdout:\n%s" % stdout
928
929 stderr = self.stderr.rstrip()
930 if stderr:
931 stderr = "\nstderr:\n%s" % stderr
932
933 return ("* Command: %s\n"
934 "Exit status: %s\n"
935 "Duration: %s\n"
936 "%s"
937 "%s"
938 % (wrapper.fill(self.command), self.exit_status,
939 self.duration, stdout, stderr))
mbligh63073c92008-03-31 16:49:32 +0000940
941
mbligh462c0152008-03-13 15:37:10 +0000942class run_randomly:
jadmanski0afbb632008-06-06 21:10:57 +0000943 def __init__(self, run_sequentially=False):
944 # Run sequentially is for debugging control files
945 self.test_list = []
946 self.run_sequentially = run_sequentially
mbligh462c0152008-03-13 15:37:10 +0000947
948
jadmanski0afbb632008-06-06 21:10:57 +0000949 def add(self, *args, **dargs):
950 test = (args, dargs)
951 self.test_list.append(test)
mbligh462c0152008-03-13 15:37:10 +0000952
953
jadmanski0afbb632008-06-06 21:10:57 +0000954 def run(self, fn):
955 while self.test_list:
956 test_index = random.randint(0, len(self.test_list)-1)
957 if self.run_sequentially:
958 test_index = 0
959 (args, dargs) = self.test_list.pop(test_index)
960 fn(*args, **dargs)
mbligha7007722009-01-13 00:37:11 +0000961
962
showard5deef7f2009-09-09 18:16:58 +0000963def import_site_module(path, module, dummy=None, modulefile=None):
964 """
965 Try to import the site specific module if it exists.
966
967 @param path full filename of the source file calling this (ie __file__)
968 @param module full module name
969 @param dummy dummy value to return in case there is no symbol to import
970 @param modulefile module filename
971
972 @return site specific module or dummy
973
974 @raises ImportError if the site file exists but imports fails
975 """
976 short_module = module[module.rfind(".") + 1:]
977
978 if not modulefile:
979 modulefile = short_module + ".py"
980
981 if os.path.exists(os.path.join(os.path.dirname(path), modulefile)):
982 return __import__(module, {}, {}, [short_module])
983 return dummy
984
985
mblighdd669372009-02-03 21:57:18 +0000986def import_site_symbol(path, module, name, dummy=None, modulefile=None):
987 """
988 Try to import site specific symbol from site specific file if it exists
989
990 @param path full filename of the source file calling this (ie __file__)
991 @param module full module name
992 @param name symbol name to be imported from the site file
993 @param dummy dummy value to return in case there is no symbol to import
994 @param modulefile module filename
995
996 @return site specific symbol or dummy
997
showard5deef7f2009-09-09 18:16:58 +0000998 @raises ImportError if the site file exists but imports fails
mblighdd669372009-02-03 21:57:18 +0000999 """
showard5deef7f2009-09-09 18:16:58 +00001000 module = import_site_module(path, module, modulefile=modulefile)
1001 if not module:
1002 return dummy
mbligha7007722009-01-13 00:37:11 +00001003
showard5deef7f2009-09-09 18:16:58 +00001004 # special unique value to tell us if the symbol can't be imported
1005 cant_import = object()
mbligha7007722009-01-13 00:37:11 +00001006
showard5deef7f2009-09-09 18:16:58 +00001007 obj = getattr(module, name, cant_import)
1008 if obj is cant_import:
1009 logging.error("unable to import site symbol '%s', using non-site "
1010 "implementation", name)
1011 return dummy
mbligh062ed152009-01-13 00:57:14 +00001012
1013 return obj
1014
1015
1016def import_site_class(path, module, classname, baseclass, modulefile=None):
1017 """
1018 Try to import site specific class from site specific file if it exists
1019
1020 Args:
1021 path: full filename of the source file calling this (ie __file__)
1022 module: full module name
1023 classname: class name to be loaded from site file
mbligh0a8c3322009-04-28 18:32:19 +00001024 baseclass: base class object to return when no site file present or
1025 to mixin when site class exists but is not inherited from baseclass
mbligh062ed152009-01-13 00:57:14 +00001026 modulefile: module filename
1027
mbligh0a8c3322009-04-28 18:32:19 +00001028 Returns: baseclass if site specific class does not exist, the site specific
1029 class if it exists and is inherited from baseclass or a mixin of the
1030 site specific class and baseclass when the site specific class exists
1031 and is not inherited from baseclass
mbligh062ed152009-01-13 00:57:14 +00001032
1033 Raises: ImportError if the site file exists but imports fails
1034 """
1035
mblighdd669372009-02-03 21:57:18 +00001036 res = import_site_symbol(path, module, classname, None, modulefile)
mbligh0a8c3322009-04-28 18:32:19 +00001037 if res:
1038 if not issubclass(res, baseclass):
1039 # if not a subclass of baseclass then mix in baseclass with the
1040 # site specific class object and return the result
1041 res = type(classname, (res, baseclass), {})
1042 else:
1043 res = baseclass
mbligha7007722009-01-13 00:37:11 +00001044
mbligh062ed152009-01-13 00:57:14 +00001045 return res
1046
1047
1048def import_site_function(path, module, funcname, dummy, modulefile=None):
1049 """
1050 Try to import site specific function from site specific file if it exists
1051
1052 Args:
1053 path: full filename of the source file calling this (ie __file__)
1054 module: full module name
1055 funcname: function name to be imported from site file
1056 dummy: dummy function to return in case there is no function to import
1057 modulefile: module filename
1058
1059 Returns: site specific function object or dummy
1060
1061 Raises: ImportError if the site file exists but imports fails
1062 """
1063
mblighdd669372009-02-03 21:57:18 +00001064 return import_site_symbol(path, module, funcname, dummy, modulefile)
mblighfb676032009-04-01 18:25:38 +00001065
1066
showard549afad2009-08-20 23:33:36 +00001067def _get_pid_path(program_name):
1068 my_path = os.path.dirname(__file__)
1069 return os.path.abspath(os.path.join(my_path, "..", "..",
1070 "%s.pid" % program_name))
1071
1072
mblighfb676032009-04-01 18:25:38 +00001073def write_pid(program_name):
1074 """
1075 Try to drop <program_name>.pid in the main autotest directory.
1076
1077 Args:
1078 program_name: prefix for file name
1079 """
showard549afad2009-08-20 23:33:36 +00001080 pidfile = open(_get_pid_path(program_name), "w")
1081 try:
1082 pidfile.write("%s\n" % os.getpid())
1083 finally:
1084 pidfile.close()
mblighfb676032009-04-01 18:25:38 +00001085
showard549afad2009-08-20 23:33:36 +00001086
1087def delete_pid_file_if_exists(program_name):
1088 """
1089 Tries to remove <program_name>.pid from the main autotest directory.
1090 """
1091 pidfile_path = _get_pid_path(program_name)
1092
1093 try:
1094 os.remove(pidfile_path)
1095 except OSError:
1096 if not os.path.exists(pidfile_path):
1097 return
1098 raise
1099
1100
1101def get_pid_from_file(program_name):
1102 """
1103 Reads the pid from <program_name>.pid in the autotest directory.
1104
1105 @param program_name the name of the program
1106 @return the pid if the file exists, None otherwise.
1107 """
1108 pidfile_path = _get_pid_path(program_name)
1109 if not os.path.exists(pidfile_path):
1110 return None
1111
1112 pidfile = open(_get_pid_path(program_name), 'r')
1113
1114 try:
1115 try:
1116 pid = int(pidfile.readline())
1117 except IOError:
1118 if not os.path.exists(pidfile_path):
1119 return None
1120 raise
1121 finally:
1122 pidfile.close()
1123
1124 return pid
1125
1126
showard8de37132009-08-31 18:33:08 +00001127def program_is_alive(program_name):
showard549afad2009-08-20 23:33:36 +00001128 """
1129 Checks if the process is alive and not in Zombie state.
1130
1131 @param program_name the name of the program
1132 @return True if still alive, False otherwise
1133 """
1134 pid = get_pid_from_file(program_name)
1135 if pid is None:
1136 return False
1137 return pid_is_alive(pid)
1138
1139
showard8de37132009-08-31 18:33:08 +00001140def signal_program(program_name, sig=signal.SIGTERM):
showard549afad2009-08-20 23:33:36 +00001141 """
1142 Sends a signal to the process listed in <program_name>.pid
1143
1144 @param program_name the name of the program
1145 @param sig signal to send
1146 """
1147 pid = get_pid_from_file(program_name)
1148 if pid:
1149 signal_pid(pid, sig)
mbligh45561782009-05-11 21:14:34 +00001150
1151
1152def get_relative_path(path, reference):
1153 """Given 2 absolute paths "path" and "reference", compute the path of
1154 "path" as relative to the directory "reference".
1155
1156 @param path the absolute path to convert to a relative path
1157 @param reference an absolute directory path to which the relative
1158 path will be computed
1159 """
1160 # normalize the paths (remove double slashes, etc)
1161 assert(os.path.isabs(path))
1162 assert(os.path.isabs(reference))
1163
1164 path = os.path.normpath(path)
1165 reference = os.path.normpath(reference)
1166
1167 # we could use os.path.split() but it splits from the end
1168 path_list = path.split(os.path.sep)[1:]
1169 ref_list = reference.split(os.path.sep)[1:]
1170
1171 # find the longest leading common path
1172 for i in xrange(min(len(path_list), len(ref_list))):
1173 if path_list[i] != ref_list[i]:
1174 # decrement i so when exiting this loop either by no match or by
1175 # end of range we are one step behind
1176 i -= 1
1177 break
1178 i += 1
1179 # drop the common part of the paths, not interested in that anymore
1180 del path_list[:i]
1181
1182 # for each uncommon component in the reference prepend a ".."
1183 path_list[:0] = ['..'] * (len(ref_list) - i)
1184
1185 return os.path.join(*path_list)
mbligh277a0e42009-07-11 00:11:45 +00001186
1187
1188def sh_escape(command):
1189 """
1190 Escape special characters from a command so that it can be passed
1191 as a double quoted (" ") string in a (ba)sh command.
1192
1193 Args:
1194 command: the command string to escape.
1195
1196 Returns:
1197 The escaped command string. The required englobing double
1198 quotes are NOT added and so should be added at some point by
1199 the caller.
1200
1201 See also: http://www.tldp.org/LDP/abs/html/escapingsection.html
1202 """
1203 command = command.replace("\\", "\\\\")
1204 command = command.replace("$", r'\$')
1205 command = command.replace('"', r'\"')
1206 command = command.replace('`', r'\`')
1207 return command