blob: 26d116de548e40cb5a0598cb2679454d1488577c [file] [log] [blame]
mbligh63073c92008-03-31 16:49:32 +00001#
2# Copyright 2008 Google Inc. Released under the GPL v2
3
mbligh849a0f62008-08-28 20:12:19 +00004import os, pickle, random, re, resource, select, shutil, signal, StringIO
mblighb2896192009-07-11 00:12:37 +00005import socket, struct, subprocess, sys, time, textwrap, urlparse
mbligh25284cd2009-06-08 16:17:24 +00006import warnings, smtplib, logging, urllib2
showard108d73e2009-06-22 18:14:41 +00007from autotest_lib.client.common_lib import error, barrier, logging_manager
mbligh81edd792008-08-26 16:54:02 +00008
mbligh849a0f62008-08-28 20:12:19 +00009def deprecated(func):
10 """This is a decorator which can be used to mark functions as deprecated.
11 It will result in a warning being emmitted when the function is used."""
12 def new_func(*args, **dargs):
13 warnings.warn("Call to deprecated function %s." % func.__name__,
14 category=DeprecationWarning)
15 return func(*args, **dargs)
16 new_func.__name__ = func.__name__
17 new_func.__doc__ = func.__doc__
18 new_func.__dict__.update(func.__dict__)
19 return new_func
20
21
showard108d73e2009-06-22 18:14:41 +000022class _NullStream(object):
23 def write(self, data):
24 pass
25
26
27 def flush(self):
28 pass
29
30
31TEE_TO_LOGS = object()
32_the_null_stream = _NullStream()
33
showardb45a4662009-07-15 14:27:56 +000034DEFAULT_STDOUT_LEVEL = logging.DEBUG
35DEFAULT_STDERR_LEVEL = logging.ERROR
36
showard108d73e2009-06-22 18:14:41 +000037def get_stream_tee_file(stream, level):
38 if stream is None:
39 return _the_null_stream
40 if stream is TEE_TO_LOGS:
41 return logging_manager.LoggingFile(level=level)
42 return stream
43
44
mbligh849a0f62008-08-28 20:12:19 +000045class BgJob(object):
showard170873e2009-01-07 00:22:26 +000046 def __init__(self, command, stdout_tee=None, stderr_tee=None, verbose=True,
showardb45a4662009-07-15 14:27:56 +000047 stdin=None, stderr_level=DEFAULT_STDERR_LEVEL):
mbligh849a0f62008-08-28 20:12:19 +000048 self.command = command
showardb45a4662009-07-15 14:27:56 +000049 self.stdout_tee = get_stream_tee_file(stdout_tee, DEFAULT_STDOUT_LEVEL)
50 self.stderr_tee = get_stream_tee_file(stderr_tee, stderr_level)
mbligh849a0f62008-08-28 20:12:19 +000051 self.result = CmdResult(command)
mblighbd96b452008-09-03 23:14:27 +000052 if verbose:
showardb18134f2009-03-20 20:52:18 +000053 logging.debug("Running '%s'" % command)
mbligh849a0f62008-08-28 20:12:19 +000054 self.sp = subprocess.Popen(command, stdout=subprocess.PIPE,
55 stderr=subprocess.PIPE,
56 preexec_fn=self._reset_sigpipe, shell=True,
showard170873e2009-01-07 00:22:26 +000057 executable="/bin/bash",
58 stdin=stdin)
mbligh849a0f62008-08-28 20:12:19 +000059
60
61 def output_prepare(self, stdout_file=None, stderr_file=None):
62 self.stdout_file = stdout_file
63 self.stderr_file = stderr_file
64
mbligh45ffc432008-12-09 23:35:17 +000065
mbligh849a0f62008-08-28 20:12:19 +000066 def process_output(self, stdout=True, final_read=False):
67 """output_prepare must be called prior to calling this"""
68 if stdout:
69 pipe, buf, tee = self.sp.stdout, self.stdout_file, self.stdout_tee
70 else:
71 pipe, buf, tee = self.sp.stderr, self.stderr_file, self.stderr_tee
72
73 if final_read:
74 # read in all the data we can from pipe and then stop
75 data = []
76 while select.select([pipe], [], [], 0)[0]:
77 data.append(os.read(pipe.fileno(), 1024))
78 if len(data[-1]) == 0:
79 break
80 data = "".join(data)
81 else:
82 # perform a single read
83 data = os.read(pipe.fileno(), 1024)
84 buf.write(data)
showard108d73e2009-06-22 18:14:41 +000085 tee.write(data)
mbligh849a0f62008-08-28 20:12:19 +000086
87
88 def cleanup(self):
showard108d73e2009-06-22 18:14:41 +000089 self.stdout_tee.flush()
90 self.stderr_tee.flush()
mbligh849a0f62008-08-28 20:12:19 +000091 self.sp.stdout.close()
92 self.sp.stderr.close()
93 self.result.stdout = self.stdout_file.getvalue()
94 self.result.stderr = self.stderr_file.getvalue()
95
96
97 def _reset_sigpipe(self):
98 signal.signal(signal.SIGPIPE, signal.SIG_DFL)
99
mbligh81edd792008-08-26 16:54:02 +0000100
101def ip_to_long(ip):
102 # !L is a long in network byte order
103 return struct.unpack('!L', socket.inet_aton(ip))[0]
104
105
106def long_to_ip(number):
107 # See above comment.
108 return socket.inet_ntoa(struct.pack('!L', number))
109
110
111def create_subnet_mask(bits):
mbligh81edd792008-08-26 16:54:02 +0000112 return (1 << 32) - (1 << 32-bits)
113
114
115def format_ip_with_mask(ip, mask_bits):
116 masked_ip = ip_to_long(ip) & create_subnet_mask(mask_bits)
117 return "%s/%s" % (long_to_ip(masked_ip), mask_bits)
mbligh6231cd62008-02-02 19:18:33 +0000118
mblighde0d47e2008-03-28 14:37:18 +0000119
jadmanskie80d4712008-10-03 16:15:59 +0000120def normalize_hostname(alias):
121 ip = socket.gethostbyname(alias)
122 return socket.gethostbyaddr(ip)[0]
123
124
mblighd6d043c2008-09-27 21:00:45 +0000125def get_ip_local_port_range():
126 match = re.match(r'\s*(\d+)\s*(\d+)\s*$',
127 read_one_line('/proc/sys/net/ipv4/ip_local_port_range'))
128 return (int(match.group(1)), int(match.group(2)))
129
130
131def set_ip_local_port_range(lower, upper):
132 write_one_line('/proc/sys/net/ipv4/ip_local_port_range',
133 '%d %d\n' % (lower, upper))
134
mbligh315b9412008-10-01 03:34:11 +0000135
mbligh45ffc432008-12-09 23:35:17 +0000136
137def send_email(mail_from, mail_to, subject, body):
138 """
139 Sends an email via smtp
140
141 mail_from: string with email address of sender
142 mail_to: string or list with email address(es) of recipients
143 subject: string with subject of email
144 body: (multi-line) string with body of email
145 """
146 if isinstance(mail_to, str):
147 mail_to = [mail_to]
148 msg = "From: %s\nTo: %s\nSubject: %s\n\n%s" % (mail_from, ','.join(mail_to),
149 subject, body)
150 try:
151 mailer = smtplib.SMTP('localhost')
152 try:
153 mailer.sendmail(mail_from, mail_to, msg)
154 finally:
155 mailer.quit()
156 except Exception, e:
157 # Emails are non-critical, not errors, but don't raise them
158 print "Sending email failed. Reason: %s" % repr(e)
159
160
jadmanski5182e162008-05-13 21:48:16 +0000161def read_one_line(filename):
mbligh6e8840c2008-07-11 18:05:49 +0000162 return open(filename, 'r').readline().rstrip('\n')
jadmanski5182e162008-05-13 21:48:16 +0000163
164
mblighb9d05512008-10-18 13:53:27 +0000165def write_one_line(filename, line):
166 open_write_close(filename, line.rstrip('\n') + '\n')
167
168
169def open_write_close(filename, data):
mbligh618ac9e2008-10-06 17:14:32 +0000170 f = open(filename, 'w')
mblighb9d05512008-10-18 13:53:27 +0000171 try:
172 f.write(data)
173 finally:
174 f.close()
jadmanski5182e162008-05-13 21:48:16 +0000175
176
mblighde0d47e2008-03-28 14:37:18 +0000177def read_keyval(path):
jadmanski0afbb632008-06-06 21:10:57 +0000178 """
179 Read a key-value pair format file into a dictionary, and return it.
180 Takes either a filename or directory name as input. If it's a
181 directory name, we assume you want the file to be called keyval.
182 """
183 if os.path.isdir(path):
184 path = os.path.join(path, 'keyval')
185 keyval = {}
jadmanski58962982009-04-21 19:54:34 +0000186 if os.path.exists(path):
187 for line in open(path):
188 line = re.sub('#.*', '', line).rstrip()
189 if not re.search(r'^[-\.\w]+=', line):
190 raise ValueError('Invalid format line: %s' % line)
191 key, value = line.split('=', 1)
192 if re.search('^\d+$', value):
193 value = int(value)
194 elif re.search('^(\d+\.)?\d+$', value):
195 value = float(value)
196 keyval[key] = value
jadmanski0afbb632008-06-06 21:10:57 +0000197 return keyval
mblighde0d47e2008-03-28 14:37:18 +0000198
199
jadmanskicc549172008-05-21 18:11:51 +0000200def write_keyval(path, dictionary, type_tag=None):
jadmanski0afbb632008-06-06 21:10:57 +0000201 """
202 Write a key-value pair format file out to a file. This uses append
203 mode to open the file, so existing text will not be overwritten or
204 reparsed.
jadmanskicc549172008-05-21 18:11:51 +0000205
jadmanski0afbb632008-06-06 21:10:57 +0000206 If type_tag is None, then the key must be composed of alphanumeric
207 characters (or dashes+underscores). However, if type-tag is not
208 null then the keys must also have "{type_tag}" as a suffix. At
209 the moment the only valid values of type_tag are "attr" and "perf".
210 """
211 if os.path.isdir(path):
212 path = os.path.join(path, 'keyval')
213 keyval = open(path, 'a')
jadmanskicc549172008-05-21 18:11:51 +0000214
jadmanski0afbb632008-06-06 21:10:57 +0000215 if type_tag is None:
mbligh97227ea2009-03-11 17:09:50 +0000216 key_regex = re.compile(r'^[-\.\w]+$')
jadmanski0afbb632008-06-06 21:10:57 +0000217 else:
218 if type_tag not in ('attr', 'perf'):
219 raise ValueError('Invalid type tag: %s' % type_tag)
220 escaped_tag = re.escape(type_tag)
mbligh97227ea2009-03-11 17:09:50 +0000221 key_regex = re.compile(r'^[-\.\w]+\{%s\}$' % escaped_tag)
jadmanski0afbb632008-06-06 21:10:57 +0000222 try:
mbligh6955e232009-07-11 00:58:47 +0000223 for key in sorted(dictionary.keys()):
jadmanski0afbb632008-06-06 21:10:57 +0000224 if not key_regex.search(key):
225 raise ValueError('Invalid key: %s' % key)
mbligh6955e232009-07-11 00:58:47 +0000226 keyval.write('%s=%s\n' % (key, dictionary[key]))
jadmanski0afbb632008-06-06 21:10:57 +0000227 finally:
228 keyval.close()
mbligh6231cd62008-02-02 19:18:33 +0000229
230
231def is_url(path):
jadmanski0afbb632008-06-06 21:10:57 +0000232 """Return true if path looks like a URL"""
233 # for now, just handle http and ftp
234 url_parts = urlparse.urlparse(path)
235 return (url_parts[0] in ('http', 'ftp'))
mbligh6231cd62008-02-02 19:18:33 +0000236
237
mblighb2896192009-07-11 00:12:37 +0000238def urlopen(url, data=None, timeout=5):
239 """Wrapper to urllib2.urlopen with timeout addition."""
mbligh02ff2d52008-06-03 15:00:21 +0000240
jadmanski0afbb632008-06-06 21:10:57 +0000241 # Save old timeout
242 old_timeout = socket.getdefaulttimeout()
243 socket.setdefaulttimeout(timeout)
244 try:
mblighb2896192009-07-11 00:12:37 +0000245 return urllib2.urlopen(url, data=data)
jadmanski0afbb632008-06-06 21:10:57 +0000246 finally:
247 socket.setdefaulttimeout(old_timeout)
mbligh02ff2d52008-06-03 15:00:21 +0000248
249
mblighb2896192009-07-11 00:12:37 +0000250def urlretrieve(url, filename, data=None, timeout=300):
251 """Retrieve a file from given url."""
252 logging.debug('Fetching %s -> %s', url, filename)
253
254 src_file = urlopen(url, data=data, timeout=timeout)
jadmanski0afbb632008-06-06 21:10:57 +0000255 try:
mblighb2896192009-07-11 00:12:37 +0000256 dest_file = open(filename, 'wb')
257 try:
258 shutil.copyfileobj(src_file, dest_file)
259 finally:
260 dest_file.close()
jadmanski0afbb632008-06-06 21:10:57 +0000261 finally:
mblighb2896192009-07-11 00:12:37 +0000262 src_file.close()
jadmanski0afbb632008-06-06 21:10:57 +0000263
mbligh02ff2d52008-06-03 15:00:21 +0000264
mbligh6231cd62008-02-02 19:18:33 +0000265def get_file(src, dest, permissions=None):
jadmanski0afbb632008-06-06 21:10:57 +0000266 """Get a file from src, which can be local or a remote URL"""
mbligh25284cd2009-06-08 16:17:24 +0000267 if src == dest:
jadmanski0afbb632008-06-06 21:10:57 +0000268 return
mbligh25284cd2009-06-08 16:17:24 +0000269
270 if is_url(src):
mblighb2896192009-07-11 00:12:37 +0000271 urlretrieve(src, dest)
jadmanski0afbb632008-06-06 21:10:57 +0000272 else:
273 shutil.copyfile(src, dest)
mbligh25284cd2009-06-08 16:17:24 +0000274
jadmanski0afbb632008-06-06 21:10:57 +0000275 if permissions:
276 os.chmod(dest, permissions)
277 return dest
mbligh6231cd62008-02-02 19:18:33 +0000278
279
280def unmap_url(srcdir, src, destdir='.'):
jadmanski0afbb632008-06-06 21:10:57 +0000281 """
282 Receives either a path to a local file or a URL.
283 returns either the path to the local file, or the fetched URL
mbligh6231cd62008-02-02 19:18:33 +0000284
jadmanski0afbb632008-06-06 21:10:57 +0000285 unmap_url('/usr/src', 'foo.tar', '/tmp')
286 = '/usr/src/foo.tar'
287 unmap_url('/usr/src', 'http://site/file', '/tmp')
288 = '/tmp/file'
289 (after retrieving it)
290 """
291 if is_url(src):
292 url_parts = urlparse.urlparse(src)
293 filename = os.path.basename(url_parts[2])
294 dest = os.path.join(destdir, filename)
295 return get_file(src, dest)
296 else:
297 return os.path.join(srcdir, src)
mbligh6231cd62008-02-02 19:18:33 +0000298
299
300def update_version(srcdir, preserve_srcdir, new_version, install,
jadmanski0afbb632008-06-06 21:10:57 +0000301 *args, **dargs):
302 """
303 Make sure srcdir is version new_version
mbligh6231cd62008-02-02 19:18:33 +0000304
jadmanski0afbb632008-06-06 21:10:57 +0000305 If not, delete it and install() the new version.
mbligh6231cd62008-02-02 19:18:33 +0000306
jadmanski0afbb632008-06-06 21:10:57 +0000307 In the preserve_srcdir case, we just check it's up to date,
308 and if not, we rerun install, without removing srcdir
309 """
310 versionfile = os.path.join(srcdir, '.version')
311 install_needed = True
mbligh6231cd62008-02-02 19:18:33 +0000312
jadmanski0afbb632008-06-06 21:10:57 +0000313 if os.path.exists(versionfile):
314 old_version = pickle.load(open(versionfile))
315 if old_version == new_version:
316 install_needed = False
mbligh6231cd62008-02-02 19:18:33 +0000317
jadmanski0afbb632008-06-06 21:10:57 +0000318 if install_needed:
319 if not preserve_srcdir and os.path.exists(srcdir):
320 shutil.rmtree(srcdir)
321 install(*args, **dargs)
322 if os.path.exists(srcdir):
323 pickle.dump(new_version, open(versionfile, 'w'))
mbligh462c0152008-03-13 15:37:10 +0000324
325
showardb45a4662009-07-15 14:27:56 +0000326def get_stderr_level(stderr_is_expected):
327 if stderr_is_expected:
328 return DEFAULT_STDOUT_LEVEL
329 return DEFAULT_STDERR_LEVEL
330
331
mbligh63073c92008-03-31 16:49:32 +0000332def run(command, timeout=None, ignore_status=False,
showardb45a4662009-07-15 14:27:56 +0000333 stdout_tee=None, stderr_tee=None, verbose=True, stdin=None,
mblighc823e8d2009-10-02 00:01:35 +0000334 stderr_is_expected=None, args=()):
jadmanski0afbb632008-06-06 21:10:57 +0000335 """
336 Run a command on the host.
mbligh63073c92008-03-31 16:49:32 +0000337
mblighc823e8d2009-10-02 00:01:35 +0000338 @param command: the command line string.
339 @param timeout: time limit in seconds before attempting to kill the
340 running process. The run() function will take a few seconds
341 longer than 'timeout' to complete if it has to kill the process.
342 @param ignore_status: do not raise an exception, no matter what the exit
343 code of the command is.
344 @param stdout_tee: optional file-like object to which stdout data
345 will be written as it is generated (data will still be stored
346 in result.stdout).
347 @param stderr_tee: likewise for stderr.
348 @param verbose: if True, log the command being run.
349 @param stdin: stdin to pass to the executed process.
350 @param args: sequence of strings of arguments to be given to the command
351 inside " quotes after they have been escaped for that; each
352 element in the sequence will be given as a separate command
353 argument
mbligh63073c92008-03-31 16:49:32 +0000354
mblighc823e8d2009-10-02 00:01:35 +0000355 @return a CmdResult object
mbligh63073c92008-03-31 16:49:32 +0000356
mblighc823e8d2009-10-02 00:01:35 +0000357 @raise CmdError: the exit code of the command execution was not 0
jadmanski0afbb632008-06-06 21:10:57 +0000358 """
mblighc823e8d2009-10-02 00:01:35 +0000359 for arg in args:
360 command += ' "%s"' % sh_escape(arg)
showardb45a4662009-07-15 14:27:56 +0000361 if stderr_is_expected is None:
362 stderr_is_expected = ignore_status
mblighc823e8d2009-10-02 00:01:35 +0000363
showard170873e2009-01-07 00:22:26 +0000364 bg_job = join_bg_jobs(
showardb45a4662009-07-15 14:27:56 +0000365 (BgJob(command, stdout_tee, stderr_tee, verbose, stdin=stdin,
366 stderr_level=get_stderr_level(stderr_is_expected)),),
showard170873e2009-01-07 00:22:26 +0000367 timeout)[0]
mbligh849a0f62008-08-28 20:12:19 +0000368 if not ignore_status and bg_job.result.exit_status:
jadmanski9c1098b2008-09-02 14:18:48 +0000369 raise error.CmdError(command, bg_job.result,
mbligh849a0f62008-08-28 20:12:19 +0000370 "Command returned non-zero exit status")
mbligh63073c92008-03-31 16:49:32 +0000371
mbligh849a0f62008-08-28 20:12:19 +0000372 return bg_job.result
mbligh63073c92008-03-31 16:49:32 +0000373
mbligh45ffc432008-12-09 23:35:17 +0000374
mbligha5630a52008-09-03 22:09:50 +0000375def run_parallel(commands, timeout=None, ignore_status=False,
376 stdout_tee=None, stderr_tee=None):
377 """Beahves the same as run with the following exceptions:
378
379 - commands is a list of commands to run in parallel.
380 - ignore_status toggles whether or not an exception should be raised
381 on any error.
382
383 returns a list of CmdResult objects
384 """
385 bg_jobs = []
386 for command in commands:
showardb45a4662009-07-15 14:27:56 +0000387 bg_jobs.append(BgJob(command, stdout_tee, stderr_tee,
388 stderr_level=get_stderr_level(ignore_status)))
mbligha5630a52008-09-03 22:09:50 +0000389
390 # Updates objects in bg_jobs list with their process information
391 join_bg_jobs(bg_jobs, timeout)
392
393 for bg_job in bg_jobs:
394 if not ignore_status and bg_job.result.exit_status:
395 raise error.CmdError(command, bg_job.result,
396 "Command returned non-zero exit status")
397
398 return [bg_job.result for bg_job in bg_jobs]
399
400
mbligh849a0f62008-08-28 20:12:19 +0000401@deprecated
mbligh63073c92008-03-31 16:49:32 +0000402def run_bg(command):
mbligh849a0f62008-08-28 20:12:19 +0000403 """Function deprecated. Please use BgJob class instead."""
404 bg_job = BgJob(command)
405 return bg_job.sp, bg_job.result
mbligh63073c92008-03-31 16:49:32 +0000406
407
mbligh849a0f62008-08-28 20:12:19 +0000408def join_bg_jobs(bg_jobs, timeout=None):
mbligha5630a52008-09-03 22:09:50 +0000409 """Joins the bg_jobs with the current thread.
410
411 Returns the same list of bg_jobs objects that was passed in.
412 """
mblighae69f262009-04-17 20:14:56 +0000413 ret, timeout_error = 0, False
mbligh849a0f62008-08-28 20:12:19 +0000414 for bg_job in bg_jobs:
415 bg_job.output_prepare(StringIO.StringIO(), StringIO.StringIO())
mbligh63073c92008-03-31 16:49:32 +0000416
jadmanski0afbb632008-06-06 21:10:57 +0000417 try:
418 # We are holding ends to stdin, stdout pipes
419 # hence we need to be sure to close those fds no mater what
420 start_time = time.time()
mbligh849a0f62008-08-28 20:12:19 +0000421 timeout_error = _wait_for_commands(bg_jobs, start_time, timeout)
422
423 for bg_job in bg_jobs:
424 # Process stdout and stderr
425 bg_job.process_output(stdout=True,final_read=True)
426 bg_job.process_output(stdout=False,final_read=True)
jadmanski0afbb632008-06-06 21:10:57 +0000427 finally:
428 # close our ends of the pipes to the sp no matter what
mbligh849a0f62008-08-28 20:12:19 +0000429 for bg_job in bg_jobs:
430 bg_job.cleanup()
mbligh63073c92008-03-31 16:49:32 +0000431
mbligh849a0f62008-08-28 20:12:19 +0000432 if timeout_error:
433 # TODO: This needs to be fixed to better represent what happens when
434 # running in parallel. However this is backwards compatable, so it will
435 # do for the time being.
436 raise error.CmdError(bg_jobs[0].command, bg_jobs[0].result,
437 "Command(s) did not complete within %d seconds"
438 % timeout)
mbligh63073c92008-03-31 16:49:32 +0000439
mbligh63073c92008-03-31 16:49:32 +0000440
mbligh849a0f62008-08-28 20:12:19 +0000441 return bg_jobs
mbligh63073c92008-03-31 16:49:32 +0000442
mbligh849a0f62008-08-28 20:12:19 +0000443
444def _wait_for_commands(bg_jobs, start_time, timeout):
445 # This returns True if it must return due to a timeout, otherwise False.
446
mblighf0b4a0a2008-09-03 20:46:16 +0000447 # To check for processes which terminate without producing any output
448 # a 1 second timeout is used in select.
449 SELECT_TIMEOUT = 1
450
mbligh849a0f62008-08-28 20:12:19 +0000451 select_list = []
452 reverse_dict = {}
453 for bg_job in bg_jobs:
454 select_list.append(bg_job.sp.stdout)
455 select_list.append(bg_job.sp.stderr)
456 reverse_dict[bg_job.sp.stdout] = (bg_job,True)
457 reverse_dict[bg_job.sp.stderr] = (bg_job,False)
458
jadmanski0afbb632008-06-06 21:10:57 +0000459 if timeout:
460 stop_time = start_time + timeout
461 time_left = stop_time - time.time()
462 else:
463 time_left = None # so that select never times out
464 while not timeout or time_left > 0:
465 # select will return when stdout is ready (including when it is
466 # EOF, that is the process has terminated).
mblighf0b4a0a2008-09-03 20:46:16 +0000467 ready, _, _ = select.select(select_list, [], [], SELECT_TIMEOUT)
mbligh849a0f62008-08-28 20:12:19 +0000468
jadmanski0afbb632008-06-06 21:10:57 +0000469 # os.read() has to be used instead of
470 # subproc.stdout.read() which will otherwise block
mbligh849a0f62008-08-28 20:12:19 +0000471 for fileno in ready:
472 bg_job,stdout = reverse_dict[fileno]
473 bg_job.process_output(stdout)
mbligh63073c92008-03-31 16:49:32 +0000474
mbligh849a0f62008-08-28 20:12:19 +0000475 remaining_jobs = [x for x in bg_jobs if x.result.exit_status is None]
476 if len(remaining_jobs) == 0:
477 return False
478 for bg_job in remaining_jobs:
479 bg_job.result.exit_status = bg_job.sp.poll()
mbligh8ea61e22008-05-09 18:09:37 +0000480
jadmanski0afbb632008-06-06 21:10:57 +0000481 if timeout:
482 time_left = stop_time - time.time()
mbligh63073c92008-03-31 16:49:32 +0000483
mbligh849a0f62008-08-28 20:12:19 +0000484 # Kill all processes which did not complete prior to timeout
485 for bg_job in [x for x in bg_jobs if x.result.exit_status is None]:
mbligh7afc3a62008-11-27 00:35:44 +0000486 print '* Warning: run process timeout (%s) fired' % timeout
mbligh849a0f62008-08-28 20:12:19 +0000487 nuke_subprocess(bg_job.sp)
mbligh095dc642008-10-01 03:41:35 +0000488 bg_job.result.exit_status = bg_job.sp.poll()
mbligh8ea61e22008-05-09 18:09:37 +0000489
mbligh849a0f62008-08-28 20:12:19 +0000490 return True
mbligh63073c92008-03-31 16:49:32 +0000491
492
showard549afad2009-08-20 23:33:36 +0000493def pid_is_alive(pid):
494 """
495 True if process pid exists and is not yet stuck in Zombie state.
496 Zombies are impossible to move between cgroups, etc.
497 pid can be integer, or text of integer.
498 """
499 path = '/proc/%s/stat' % pid
500
501 try:
502 stat = read_one_line(path)
503 except IOError:
504 if not os.path.exists(path):
505 # file went away
506 return False
507 raise
508
509 return stat.split()[2] != 'Z'
510
511
512def signal_pid(pid, sig):
513 """
514 Sends a signal to a process id. Returns True if the process terminated
515 successfully, False otherwise.
516 """
517 try:
518 os.kill(pid, sig)
519 except OSError:
520 # The process may have died before we could kill it.
521 pass
522
523 for i in range(5):
524 if not pid_is_alive(pid):
525 return True
526 time.sleep(1)
527
528 # The process is still alive
529 return False
530
531
mbligh63073c92008-03-31 16:49:32 +0000532def nuke_subprocess(subproc):
jadmanski09f92032008-09-17 14:05:27 +0000533 # check if the subprocess is still alive, first
534 if subproc.poll() is not None:
535 return subproc.poll()
536
jadmanski0afbb632008-06-06 21:10:57 +0000537 # the process has not terminated within timeout,
538 # kill it via an escalating series of signals.
539 signal_queue = [signal.SIGTERM, signal.SIGKILL]
540 for sig in signal_queue:
showard549afad2009-08-20 23:33:36 +0000541 signal_pid(subproc.pid, sig)
542 if subproc.poll() is not None:
543 return subproc.poll()
mbligh63073c92008-03-31 16:49:32 +0000544
545
546def nuke_pid(pid):
jadmanski0afbb632008-06-06 21:10:57 +0000547 # the process has not terminated within timeout,
548 # kill it via an escalating series of signals.
549 signal_queue = [signal.SIGTERM, signal.SIGKILL]
550 for sig in signal_queue:
showard549afad2009-08-20 23:33:36 +0000551 if signal_pid(pid, sig):
552 return
mbligh63073c92008-03-31 16:49:32 +0000553
showard549afad2009-08-20 23:33:36 +0000554 # no signal successfully terminated the process
555 raise error.AutoservRunError('Could not kill %d' % pid, None)
mbligh298ed592009-06-15 21:52:57 +0000556
557
mbligh63073c92008-03-31 16:49:32 +0000558def system(command, timeout=None, ignore_status=False):
mbligha5630a52008-09-03 22:09:50 +0000559 """This function returns the exit status of command."""
mblighf8dffb12008-10-29 16:45:26 +0000560 return run(command, timeout=timeout, ignore_status=ignore_status,
showard108d73e2009-06-22 18:14:41 +0000561 stdout_tee=TEE_TO_LOGS, stderr_tee=TEE_TO_LOGS).exit_status
mbligh63073c92008-03-31 16:49:32 +0000562
563
mbligha5630a52008-09-03 22:09:50 +0000564def system_parallel(commands, timeout=None, ignore_status=False):
565 """This function returns a list of exit statuses for the respective
566 list of commands."""
567 return [bg_jobs.exit_status for bg_jobs in
mblighf8dffb12008-10-29 16:45:26 +0000568 run_parallel(commands, timeout=timeout, ignore_status=ignore_status,
showard108d73e2009-06-22 18:14:41 +0000569 stdout_tee=TEE_TO_LOGS, stderr_tee=TEE_TO_LOGS)]
mbligh849a0f62008-08-28 20:12:19 +0000570
571
mbligh8ea61e22008-05-09 18:09:37 +0000572def system_output(command, timeout=None, ignore_status=False,
mblighc823e8d2009-10-02 00:01:35 +0000573 retain_output=False, args=()):
574 """
575 Run a command and return the stdout output.
576
577 @param command: command string to execute.
578 @param timeout: time limit in seconds before attempting to kill the
579 running process. The function will take a few seconds longer
580 than 'timeout' to complete if it has to kill the process.
581 @param ignore_status: do not raise an exception, no matter what the exit
582 code of the command is.
583 @param retain_output: set to True to make stdout/stderr of the command
584 output to be also sent to the logging system
585 @param args: sequence of strings of arguments to be given to the command
586 inside " quotes after they have been escaped for that; each
587 element in the sequence will be given as a separate command
588 argument
589
590 @return a string with the stdout output of the command.
591 """
jadmanski0afbb632008-06-06 21:10:57 +0000592 if retain_output:
mblighf8dffb12008-10-29 16:45:26 +0000593 out = run(command, timeout=timeout, ignore_status=ignore_status,
mblighc823e8d2009-10-02 00:01:35 +0000594 stdout_tee=TEE_TO_LOGS, stderr_tee=TEE_TO_LOGS,
595 args=args).stdout
jadmanski0afbb632008-06-06 21:10:57 +0000596 else:
mblighc823e8d2009-10-02 00:01:35 +0000597 out = run(command, timeout=timeout, ignore_status=ignore_status,
598 args=args).stdout
599 if out[-1:] == '\n':
600 out = out[:-1]
jadmanski0afbb632008-06-06 21:10:57 +0000601 return out
mbligh63073c92008-03-31 16:49:32 +0000602
mbligh849a0f62008-08-28 20:12:19 +0000603
mbligha5630a52008-09-03 22:09:50 +0000604def system_output_parallel(commands, timeout=None, ignore_status=False,
605 retain_output=False):
606 if retain_output:
showard108d73e2009-06-22 18:14:41 +0000607 out = [bg_job.stdout for bg_job
608 in run_parallel(commands, timeout=timeout,
609 ignore_status=ignore_status,
610 stdout_tee=TEE_TO_LOGS, stderr_tee=TEE_TO_LOGS)]
mbligha5630a52008-09-03 22:09:50 +0000611 else:
mblighf8dffb12008-10-29 16:45:26 +0000612 out = [bg_job.stdout for bg_job in run_parallel(commands,
613 timeout=timeout, ignore_status=ignore_status)]
mbligha5630a52008-09-03 22:09:50 +0000614 for x in out:
615 if out[-1:] == '\n': out = out[:-1]
616 return out
617
618
mbligh98467952008-11-19 00:25:45 +0000619def strip_unicode(input):
620 if type(input) == list:
621 return [strip_unicode(i) for i in input]
622 elif type(input) == dict:
623 output = {}
624 for key in input.keys():
625 output[str(key)] = strip_unicode(input[key])
626 return output
627 elif type(input) == unicode:
628 return str(input)
629 else:
630 return input
631
632
mbligha5630a52008-09-03 22:09:50 +0000633def get_cpu_percentage(function, *args, **dargs):
634 """Returns a tuple containing the CPU% and return value from function call.
635
636 This function calculates the usage time by taking the difference of
637 the user and system times both before and after the function call.
638 """
639 child_pre = resource.getrusage(resource.RUSAGE_CHILDREN)
640 self_pre = resource.getrusage(resource.RUSAGE_SELF)
641 start = time.time()
642 to_return = function(*args, **dargs)
643 elapsed = time.time() - start
644 self_post = resource.getrusage(resource.RUSAGE_SELF)
645 child_post = resource.getrusage(resource.RUSAGE_CHILDREN)
646
647 # Calculate CPU Percentage
648 s_user, s_system = [a - b for a, b in zip(self_post, self_pre)[:2]]
649 c_user, c_system = [a - b for a, b in zip(child_post, child_pre)[:2]]
650 cpu_percent = (s_user + c_user + s_system + c_system) / elapsed
651
652 return cpu_percent, to_return
653
654
mblighc1cbc992008-05-27 20:01:45 +0000655"""
656This function is used when there is a need to run more than one
657job simultaneously starting exactly at the same time. It basically returns
658a modified control file (containing the synchronization code prepended)
659whenever it is ready to run the control file. The synchronization
660is done using barriers to make sure that the jobs start at the same time.
661
662Here is how the synchronization is done to make sure that the tests
663start at exactly the same time on the client.
664sc_bar is a server barrier and s_bar, c_bar are the normal barriers
665
666 Job1 Job2 ...... JobN
667 Server: | sc_bar
668 Server: | s_bar ...... s_bar
669 Server: | at.run() at.run() ...... at.run()
670 ----------|------------------------------------------------------
671 Client | sc_bar
672 Client | c_bar c_bar ...... c_bar
673 Client | <run test> <run test> ...... <run test>
674
675
676PARAMS:
677 control_file : The control file which to which the above synchronization
678 code would be prepended to
679 host_name : The host name on which the job is going to run
680 host_num (non negative) : A number to identify the machine so that we have
681 different sets of s_bar_ports for each of the machines.
682 instance : The number of the job
683 num_jobs : Total number of jobs that are going to run in parallel with
684 this job starting at the same time
685 port_base : Port number that is used to derive the actual barrier ports.
686
687RETURN VALUE:
688 The modified control file.
689
690"""
691def get_sync_control_file(control, host_name, host_num,
jadmanski0afbb632008-06-06 21:10:57 +0000692 instance, num_jobs, port_base=63100):
693 sc_bar_port = port_base
694 c_bar_port = port_base
695 if host_num < 0:
696 print "Please provide a non negative number for the host"
697 return None
698 s_bar_port = port_base + 1 + host_num # The set of s_bar_ports are
699 # the same for a given machine
mblighc1cbc992008-05-27 20:01:45 +0000700
jadmanski0afbb632008-06-06 21:10:57 +0000701 sc_bar_timeout = 180
702 s_bar_timeout = c_bar_timeout = 120
mblighc1cbc992008-05-27 20:01:45 +0000703
jadmanski0afbb632008-06-06 21:10:57 +0000704 # The barrier code snippet is prepended into the conrol file
705 # dynamically before at.run() is called finally.
706 control_new = []
mblighc1cbc992008-05-27 20:01:45 +0000707
jadmanski0afbb632008-06-06 21:10:57 +0000708 # jobid is the unique name used to identify the processes
709 # trying to reach the barriers
710 jobid = "%s#%d" % (host_name, instance)
mblighc1cbc992008-05-27 20:01:45 +0000711
jadmanski0afbb632008-06-06 21:10:57 +0000712 rendv = []
713 # rendvstr is a temp holder for the rendezvous list of the processes
714 for n in range(num_jobs):
715 rendv.append("'%s#%d'" % (host_name, n))
716 rendvstr = ",".join(rendv)
mblighc1cbc992008-05-27 20:01:45 +0000717
jadmanski0afbb632008-06-06 21:10:57 +0000718 if instance == 0:
719 # Do the setup and wait at the server barrier
720 # Clean up the tmp and the control dirs for the first instance
721 control_new.append('if os.path.exists(job.tmpdir):')
722 control_new.append("\t system('umount -f %s > /dev/null"
723 "2> /dev/null' % job.tmpdir,"
724 "ignore_status=True)")
725 control_new.append("\t system('rm -rf ' + job.tmpdir)")
726 control_new.append(
727 'b0 = job.barrier("%s", "sc_bar", %d, port=%d)'
728 % (jobid, sc_bar_timeout, sc_bar_port))
729 control_new.append(
mbligh9c12f772009-06-22 19:03:55 +0000730 'b0.rendezvous_servers("PARALLEL_MASTER", "%s")'
jadmanski0afbb632008-06-06 21:10:57 +0000731 % jobid)
mblighc1cbc992008-05-27 20:01:45 +0000732
jadmanski0afbb632008-06-06 21:10:57 +0000733 elif instance == 1:
734 # Wait at the server barrier to wait for instance=0
735 # process to complete setup
736 b0 = barrier.barrier("PARALLEL_MASTER", "sc_bar", sc_bar_timeout,
737 port=sc_bar_port)
mbligh9c12f772009-06-22 19:03:55 +0000738 b0.rendezvous_servers("PARALLEL_MASTER", jobid)
mblighc1cbc992008-05-27 20:01:45 +0000739
jadmanski0afbb632008-06-06 21:10:57 +0000740 if(num_jobs > 2):
741 b1 = barrier.barrier(jobid, "s_bar", s_bar_timeout,
742 port=s_bar_port)
mbligh9c12f772009-06-22 19:03:55 +0000743 b1.rendezvous(rendvstr)
mblighc1cbc992008-05-27 20:01:45 +0000744
jadmanski0afbb632008-06-06 21:10:57 +0000745 else:
746 # For the rest of the clients
747 b2 = barrier.barrier(jobid, "s_bar", s_bar_timeout, port=s_bar_port)
mbligh9c12f772009-06-22 19:03:55 +0000748 b2.rendezvous(rendvstr)
mblighc1cbc992008-05-27 20:01:45 +0000749
jadmanski0afbb632008-06-06 21:10:57 +0000750 # Client side barrier for all the tests to start at the same time
751 control_new.append('b1 = job.barrier("%s", "c_bar", %d, port=%d)'
752 % (jobid, c_bar_timeout, c_bar_port))
mbligh9c12f772009-06-22 19:03:55 +0000753 control_new.append("b1.rendezvous(%s)" % rendvstr)
mblighc1cbc992008-05-27 20:01:45 +0000754
jadmanski0afbb632008-06-06 21:10:57 +0000755 # Stick in the rest of the control file
756 control_new.append(control)
mblighc1cbc992008-05-27 20:01:45 +0000757
jadmanski0afbb632008-06-06 21:10:57 +0000758 return "\n".join(control_new)
mblighc1cbc992008-05-27 20:01:45 +0000759
mbligh63073c92008-03-31 16:49:32 +0000760
mblighc5ddfd12008-08-04 17:15:00 +0000761def get_arch(run_function=run):
762 """
763 Get the hardware architecture of the machine.
764 run_function is used to execute the commands. It defaults to
765 utils.run() but a custom method (if provided) should be of the
766 same schema as utils.run. It should return a CmdResult object and
767 throw a CmdError exception.
768 """
769 arch = run_function('/bin/uname -m').stdout.rstrip()
770 if re.match(r'i\d86$', arch):
771 arch = 'i386'
772 return arch
773
774
showard4745ecd2009-05-26 19:34:56 +0000775def get_num_logical_cpus_per_socket(run_function=run):
mbligh9fd9afe2009-04-28 18:27:25 +0000776 """
777 Get the number of cores (including hyperthreading) per cpu.
778 run_function is used to execute the commands. It defaults to
779 utils.run() but a custom method (if provided) should be of the
780 same schema as utils.run. It should return a CmdResult object and
781 throw a CmdError exception.
782 """
showard4745ecd2009-05-26 19:34:56 +0000783 siblings = run_function('grep "^siblings" /proc/cpuinfo').stdout.rstrip()
784 num_siblings = map(int,
785 re.findall(r'^siblings\s*:\s*(\d+)\s*$',
786 siblings, re.M))
787 if len(num_siblings) == 0:
788 raise error.TestError('Unable to find siblings info in /proc/cpuinfo')
789 if min(num_siblings) != max(num_siblings):
790 raise error.TestError('Number of siblings differ %r' %
791 num_siblings)
792 return num_siblings[0]
mbligh9fd9afe2009-04-28 18:27:25 +0000793
794
jadmanski4f909252008-12-01 20:47:10 +0000795def merge_trees(src, dest):
796 """
797 Merges a source directory tree at 'src' into a destination tree at
798 'dest'. If a path is a file in both trees than the file in the source
799 tree is APPENDED to the one in the destination tree. If a path is
800 a directory in both trees then the directories are recursively merged
801 with this function. In any other case, the function will skip the
802 paths that cannot be merged (instead of failing).
803 """
804 if not os.path.exists(src):
805 return # exists only in dest
806 elif not os.path.exists(dest):
807 if os.path.isfile(src):
808 shutil.copy2(src, dest) # file only in src
809 else:
810 shutil.copytree(src, dest, symlinks=True) # dir only in src
811 return
812 elif os.path.isfile(src) and os.path.isfile(dest):
813 # src & dest are files in both trees, append src to dest
814 destfile = open(dest, "a")
815 try:
816 srcfile = open(src)
817 try:
818 destfile.write(srcfile.read())
819 finally:
820 srcfile.close()
821 finally:
822 destfile.close()
823 elif os.path.isdir(src) and os.path.isdir(dest):
824 # src & dest are directories in both trees, so recursively merge
825 for name in os.listdir(src):
826 merge_trees(os.path.join(src, name), os.path.join(dest, name))
827 else:
828 # src & dest both exist, but are incompatible
829 return
830
831
mbligh63073c92008-03-31 16:49:32 +0000832class CmdResult(object):
jadmanski0afbb632008-06-06 21:10:57 +0000833 """
834 Command execution result.
mbligh63073c92008-03-31 16:49:32 +0000835
jadmanski0afbb632008-06-06 21:10:57 +0000836 command: String containing the command line itself
837 exit_status: Integer exit code of the process
838 stdout: String containing stdout of the process
839 stderr: String containing stderr of the process
840 duration: Elapsed wall clock time running the process
841 """
mbligh63073c92008-03-31 16:49:32 +0000842
843
mblighcd63a212009-05-01 23:04:38 +0000844 def __init__(self, command="", stdout="", stderr="",
jadmanski0afbb632008-06-06 21:10:57 +0000845 exit_status=None, duration=0):
846 self.command = command
847 self.exit_status = exit_status
848 self.stdout = stdout
849 self.stderr = stderr
850 self.duration = duration
mbligh63073c92008-03-31 16:49:32 +0000851
852
jadmanski0afbb632008-06-06 21:10:57 +0000853 def __repr__(self):
854 wrapper = textwrap.TextWrapper(width = 78,
855 initial_indent="\n ",
856 subsequent_indent=" ")
857
858 stdout = self.stdout.rstrip()
859 if stdout:
860 stdout = "\nstdout:\n%s" % stdout
861
862 stderr = self.stderr.rstrip()
863 if stderr:
864 stderr = "\nstderr:\n%s" % stderr
865
866 return ("* Command: %s\n"
867 "Exit status: %s\n"
868 "Duration: %s\n"
869 "%s"
870 "%s"
871 % (wrapper.fill(self.command), self.exit_status,
872 self.duration, stdout, stderr))
mbligh63073c92008-03-31 16:49:32 +0000873
874
mbligh462c0152008-03-13 15:37:10 +0000875class run_randomly:
jadmanski0afbb632008-06-06 21:10:57 +0000876 def __init__(self, run_sequentially=False):
877 # Run sequentially is for debugging control files
878 self.test_list = []
879 self.run_sequentially = run_sequentially
mbligh462c0152008-03-13 15:37:10 +0000880
881
jadmanski0afbb632008-06-06 21:10:57 +0000882 def add(self, *args, **dargs):
883 test = (args, dargs)
884 self.test_list.append(test)
mbligh462c0152008-03-13 15:37:10 +0000885
886
jadmanski0afbb632008-06-06 21:10:57 +0000887 def run(self, fn):
888 while self.test_list:
889 test_index = random.randint(0, len(self.test_list)-1)
890 if self.run_sequentially:
891 test_index = 0
892 (args, dargs) = self.test_list.pop(test_index)
893 fn(*args, **dargs)
mbligha7007722009-01-13 00:37:11 +0000894
895
showard5deef7f2009-09-09 18:16:58 +0000896def import_site_module(path, module, dummy=None, modulefile=None):
897 """
898 Try to import the site specific module if it exists.
899
900 @param path full filename of the source file calling this (ie __file__)
901 @param module full module name
902 @param dummy dummy value to return in case there is no symbol to import
903 @param modulefile module filename
904
905 @return site specific module or dummy
906
907 @raises ImportError if the site file exists but imports fails
908 """
909 short_module = module[module.rfind(".") + 1:]
910
911 if not modulefile:
912 modulefile = short_module + ".py"
913
914 if os.path.exists(os.path.join(os.path.dirname(path), modulefile)):
915 return __import__(module, {}, {}, [short_module])
916 return dummy
917
918
mblighdd669372009-02-03 21:57:18 +0000919def import_site_symbol(path, module, name, dummy=None, modulefile=None):
920 """
921 Try to import site specific symbol from site specific file if it exists
922
923 @param path full filename of the source file calling this (ie __file__)
924 @param module full module name
925 @param name symbol name to be imported from the site file
926 @param dummy dummy value to return in case there is no symbol to import
927 @param modulefile module filename
928
929 @return site specific symbol or dummy
930
showard5deef7f2009-09-09 18:16:58 +0000931 @raises ImportError if the site file exists but imports fails
mblighdd669372009-02-03 21:57:18 +0000932 """
showard5deef7f2009-09-09 18:16:58 +0000933 module = import_site_module(path, module, modulefile=modulefile)
934 if not module:
935 return dummy
mbligha7007722009-01-13 00:37:11 +0000936
showard5deef7f2009-09-09 18:16:58 +0000937 # special unique value to tell us if the symbol can't be imported
938 cant_import = object()
mbligha7007722009-01-13 00:37:11 +0000939
showard5deef7f2009-09-09 18:16:58 +0000940 obj = getattr(module, name, cant_import)
941 if obj is cant_import:
942 logging.error("unable to import site symbol '%s', using non-site "
943 "implementation", name)
944 return dummy
mbligh062ed152009-01-13 00:57:14 +0000945
946 return obj
947
948
949def import_site_class(path, module, classname, baseclass, modulefile=None):
950 """
951 Try to import site specific class from site specific file if it exists
952
953 Args:
954 path: full filename of the source file calling this (ie __file__)
955 module: full module name
956 classname: class name to be loaded from site file
mbligh0a8c3322009-04-28 18:32:19 +0000957 baseclass: base class object to return when no site file present or
958 to mixin when site class exists but is not inherited from baseclass
mbligh062ed152009-01-13 00:57:14 +0000959 modulefile: module filename
960
mbligh0a8c3322009-04-28 18:32:19 +0000961 Returns: baseclass if site specific class does not exist, the site specific
962 class if it exists and is inherited from baseclass or a mixin of the
963 site specific class and baseclass when the site specific class exists
964 and is not inherited from baseclass
mbligh062ed152009-01-13 00:57:14 +0000965
966 Raises: ImportError if the site file exists but imports fails
967 """
968
mblighdd669372009-02-03 21:57:18 +0000969 res = import_site_symbol(path, module, classname, None, modulefile)
mbligh0a8c3322009-04-28 18:32:19 +0000970 if res:
971 if not issubclass(res, baseclass):
972 # if not a subclass of baseclass then mix in baseclass with the
973 # site specific class object and return the result
974 res = type(classname, (res, baseclass), {})
975 else:
976 res = baseclass
mbligha7007722009-01-13 00:37:11 +0000977
mbligh062ed152009-01-13 00:57:14 +0000978 return res
979
980
981def import_site_function(path, module, funcname, dummy, modulefile=None):
982 """
983 Try to import site specific function from site specific file if it exists
984
985 Args:
986 path: full filename of the source file calling this (ie __file__)
987 module: full module name
988 funcname: function name to be imported from site file
989 dummy: dummy function to return in case there is no function to import
990 modulefile: module filename
991
992 Returns: site specific function object or dummy
993
994 Raises: ImportError if the site file exists but imports fails
995 """
996
mblighdd669372009-02-03 21:57:18 +0000997 return import_site_symbol(path, module, funcname, dummy, modulefile)
mblighfb676032009-04-01 18:25:38 +0000998
999
showard549afad2009-08-20 23:33:36 +00001000def _get_pid_path(program_name):
1001 my_path = os.path.dirname(__file__)
1002 return os.path.abspath(os.path.join(my_path, "..", "..",
1003 "%s.pid" % program_name))
1004
1005
mblighfb676032009-04-01 18:25:38 +00001006def write_pid(program_name):
1007 """
1008 Try to drop <program_name>.pid in the main autotest directory.
1009
1010 Args:
1011 program_name: prefix for file name
1012 """
showard549afad2009-08-20 23:33:36 +00001013 pidfile = open(_get_pid_path(program_name), "w")
1014 try:
1015 pidfile.write("%s\n" % os.getpid())
1016 finally:
1017 pidfile.close()
mblighfb676032009-04-01 18:25:38 +00001018
showard549afad2009-08-20 23:33:36 +00001019
1020def delete_pid_file_if_exists(program_name):
1021 """
1022 Tries to remove <program_name>.pid from the main autotest directory.
1023 """
1024 pidfile_path = _get_pid_path(program_name)
1025
1026 try:
1027 os.remove(pidfile_path)
1028 except OSError:
1029 if not os.path.exists(pidfile_path):
1030 return
1031 raise
1032
1033
1034def get_pid_from_file(program_name):
1035 """
1036 Reads the pid from <program_name>.pid in the autotest directory.
1037
1038 @param program_name the name of the program
1039 @return the pid if the file exists, None otherwise.
1040 """
1041 pidfile_path = _get_pid_path(program_name)
1042 if not os.path.exists(pidfile_path):
1043 return None
1044
1045 pidfile = open(_get_pid_path(program_name), 'r')
1046
1047 try:
1048 try:
1049 pid = int(pidfile.readline())
1050 except IOError:
1051 if not os.path.exists(pidfile_path):
1052 return None
1053 raise
1054 finally:
1055 pidfile.close()
1056
1057 return pid
1058
1059
showard8de37132009-08-31 18:33:08 +00001060def program_is_alive(program_name):
showard549afad2009-08-20 23:33:36 +00001061 """
1062 Checks if the process is alive and not in Zombie state.
1063
1064 @param program_name the name of the program
1065 @return True if still alive, False otherwise
1066 """
1067 pid = get_pid_from_file(program_name)
1068 if pid is None:
1069 return False
1070 return pid_is_alive(pid)
1071
1072
showard8de37132009-08-31 18:33:08 +00001073def signal_program(program_name, sig=signal.SIGTERM):
showard549afad2009-08-20 23:33:36 +00001074 """
1075 Sends a signal to the process listed in <program_name>.pid
1076
1077 @param program_name the name of the program
1078 @param sig signal to send
1079 """
1080 pid = get_pid_from_file(program_name)
1081 if pid:
1082 signal_pid(pid, sig)
mbligh45561782009-05-11 21:14:34 +00001083
1084
1085def get_relative_path(path, reference):
1086 """Given 2 absolute paths "path" and "reference", compute the path of
1087 "path" as relative to the directory "reference".
1088
1089 @param path the absolute path to convert to a relative path
1090 @param reference an absolute directory path to which the relative
1091 path will be computed
1092 """
1093 # normalize the paths (remove double slashes, etc)
1094 assert(os.path.isabs(path))
1095 assert(os.path.isabs(reference))
1096
1097 path = os.path.normpath(path)
1098 reference = os.path.normpath(reference)
1099
1100 # we could use os.path.split() but it splits from the end
1101 path_list = path.split(os.path.sep)[1:]
1102 ref_list = reference.split(os.path.sep)[1:]
1103
1104 # find the longest leading common path
1105 for i in xrange(min(len(path_list), len(ref_list))):
1106 if path_list[i] != ref_list[i]:
1107 # decrement i so when exiting this loop either by no match or by
1108 # end of range we are one step behind
1109 i -= 1
1110 break
1111 i += 1
1112 # drop the common part of the paths, not interested in that anymore
1113 del path_list[:i]
1114
1115 # for each uncommon component in the reference prepend a ".."
1116 path_list[:0] = ['..'] * (len(ref_list) - i)
1117
1118 return os.path.join(*path_list)
mbligh277a0e42009-07-11 00:11:45 +00001119
1120
1121def sh_escape(command):
1122 """
1123 Escape special characters from a command so that it can be passed
1124 as a double quoted (" ") string in a (ba)sh command.
1125
1126 Args:
1127 command: the command string to escape.
1128
1129 Returns:
1130 The escaped command string. The required englobing double
1131 quotes are NOT added and so should be added at some point by
1132 the caller.
1133
1134 See also: http://www.tldp.org/LDP/abs/html/escapingsection.html
1135 """
1136 command = command.replace("\\", "\\\\")
1137 command = command.replace("$", r'\$')
1138 command = command.replace('"', r'\"')
1139 command = command.replace('`', r'\`')
1140 return command