blob: 2001a4d03185ffb179158edd4937f8910b9d177b [file] [log] [blame]
mbligh63073c92008-03-31 16:49:32 +00001#!/usr/bin/python
2#
3# Copyright 2008 Google Inc. Released under the GPL v2
4
mbligh849a0f62008-08-28 20:12:19 +00005import os, pickle, random, re, resource, select, shutil, signal, StringIO
6import socket, struct, subprocess, sys, time, textwrap, urllib, urlparse
7import warnings
mbligh81edd792008-08-26 16:54:02 +00008from autotest_lib.client.common_lib import error, barrier
9
mbligh849a0f62008-08-28 20:12:19 +000010def deprecated(func):
11 """This is a decorator which can be used to mark functions as deprecated.
12 It will result in a warning being emmitted when the function is used."""
13 def new_func(*args, **dargs):
14 warnings.warn("Call to deprecated function %s." % func.__name__,
15 category=DeprecationWarning)
16 return func(*args, **dargs)
17 new_func.__name__ = func.__name__
18 new_func.__doc__ = func.__doc__
19 new_func.__dict__.update(func.__dict__)
20 return new_func
21
22
23class BgJob(object):
24 def __init__(self, command, stdout_tee=None, stderr_tee=None):
25 self.command = command
26 self.stdout_tee = stdout_tee
27 self.stderr_tee = stderr_tee
28 self.result = CmdResult(command)
29 print "running: %s" % command
30 self.sp = subprocess.Popen(command, stdout=subprocess.PIPE,
31 stderr=subprocess.PIPE,
32 preexec_fn=self._reset_sigpipe, shell=True,
33 executable="/bin/bash")
34
35
36 def output_prepare(self, stdout_file=None, stderr_file=None):
37 self.stdout_file = stdout_file
38 self.stderr_file = stderr_file
39
40 def process_output(self, stdout=True, final_read=False):
41 """output_prepare must be called prior to calling this"""
42 if stdout:
43 pipe, buf, tee = self.sp.stdout, self.stdout_file, self.stdout_tee
44 else:
45 pipe, buf, tee = self.sp.stderr, self.stderr_file, self.stderr_tee
46
47 if final_read:
48 # read in all the data we can from pipe and then stop
49 data = []
50 while select.select([pipe], [], [], 0)[0]:
51 data.append(os.read(pipe.fileno(), 1024))
52 if len(data[-1]) == 0:
53 break
54 data = "".join(data)
55 else:
56 # perform a single read
57 data = os.read(pipe.fileno(), 1024)
58 buf.write(data)
59 if tee:
60 tee.write(data)
61 tee.flush()
62
63
64 def cleanup(self):
65 self.sp.stdout.close()
66 self.sp.stderr.close()
67 self.result.stdout = self.stdout_file.getvalue()
68 self.result.stderr = self.stderr_file.getvalue()
69
70
71 def _reset_sigpipe(self):
72 signal.signal(signal.SIGPIPE, signal.SIG_DFL)
73
mbligh81edd792008-08-26 16:54:02 +000074
75def ip_to_long(ip):
76 # !L is a long in network byte order
77 return struct.unpack('!L', socket.inet_aton(ip))[0]
78
79
80def long_to_ip(number):
81 # See above comment.
82 return socket.inet_ntoa(struct.pack('!L', number))
83
84
85def create_subnet_mask(bits):
86 # ~ does weird things in python...but this does work
87 return (1 << 32) - (1 << 32-bits)
88
89
90def format_ip_with_mask(ip, mask_bits):
91 masked_ip = ip_to_long(ip) & create_subnet_mask(mask_bits)
92 return "%s/%s" % (long_to_ip(masked_ip), mask_bits)
mbligh6231cd62008-02-02 19:18:33 +000093
mblighde0d47e2008-03-28 14:37:18 +000094
jadmanski5182e162008-05-13 21:48:16 +000095def read_one_line(filename):
mbligh6e8840c2008-07-11 18:05:49 +000096 return open(filename, 'r').readline().rstrip('\n')
jadmanski5182e162008-05-13 21:48:16 +000097
98
99def write_one_line(filename, str):
mbligh6e8840c2008-07-11 18:05:49 +0000100 open(filename, 'w').write(str.rstrip('\n') + '\n')
jadmanski5182e162008-05-13 21:48:16 +0000101
102
mblighde0d47e2008-03-28 14:37:18 +0000103def read_keyval(path):
jadmanski0afbb632008-06-06 21:10:57 +0000104 """
105 Read a key-value pair format file into a dictionary, and return it.
106 Takes either a filename or directory name as input. If it's a
107 directory name, we assume you want the file to be called keyval.
108 """
109 if os.path.isdir(path):
110 path = os.path.join(path, 'keyval')
111 keyval = {}
112 for line in open(path):
jadmanskia6014a02008-07-14 19:41:54 +0000113 line = re.sub('#.*', '', line).rstrip()
jadmanski0afbb632008-06-06 21:10:57 +0000114 if not re.search(r'^[-\w]+=', line):
115 raise ValueError('Invalid format line: %s' % line)
116 key, value = line.split('=', 1)
117 if re.search('^\d+$', value):
118 value = int(value)
119 elif re.search('^(\d+\.)?\d+$', value):
120 value = float(value)
121 keyval[key] = value
122 return keyval
mblighde0d47e2008-03-28 14:37:18 +0000123
124
jadmanskicc549172008-05-21 18:11:51 +0000125def write_keyval(path, dictionary, type_tag=None):
jadmanski0afbb632008-06-06 21:10:57 +0000126 """
127 Write a key-value pair format file out to a file. This uses append
128 mode to open the file, so existing text will not be overwritten or
129 reparsed.
jadmanskicc549172008-05-21 18:11:51 +0000130
jadmanski0afbb632008-06-06 21:10:57 +0000131 If type_tag is None, then the key must be composed of alphanumeric
132 characters (or dashes+underscores). However, if type-tag is not
133 null then the keys must also have "{type_tag}" as a suffix. At
134 the moment the only valid values of type_tag are "attr" and "perf".
135 """
136 if os.path.isdir(path):
137 path = os.path.join(path, 'keyval')
138 keyval = open(path, 'a')
jadmanskicc549172008-05-21 18:11:51 +0000139
jadmanski0afbb632008-06-06 21:10:57 +0000140 if type_tag is None:
141 key_regex = re.compile(r'^[-\w]+$')
142 else:
143 if type_tag not in ('attr', 'perf'):
144 raise ValueError('Invalid type tag: %s' % type_tag)
145 escaped_tag = re.escape(type_tag)
146 key_regex = re.compile(r'^[-\w]+\{%s\}$' % escaped_tag)
147 try:
148 for key, value in dictionary.iteritems():
149 if not key_regex.search(key):
150 raise ValueError('Invalid key: %s' % key)
151 keyval.write('%s=%s\n' % (key, value))
152 finally:
153 keyval.close()
mbligh6231cd62008-02-02 19:18:33 +0000154
155
156def is_url(path):
jadmanski0afbb632008-06-06 21:10:57 +0000157 """Return true if path looks like a URL"""
158 # for now, just handle http and ftp
159 url_parts = urlparse.urlparse(path)
160 return (url_parts[0] in ('http', 'ftp'))
mbligh6231cd62008-02-02 19:18:33 +0000161
162
mbligh02ff2d52008-06-03 15:00:21 +0000163def urlopen(url, data=None, proxies=None, timeout=300):
jadmanski0afbb632008-06-06 21:10:57 +0000164 """Wrapper to urllib.urlopen with timeout addition."""
mbligh02ff2d52008-06-03 15:00:21 +0000165
jadmanski0afbb632008-06-06 21:10:57 +0000166 # Save old timeout
167 old_timeout = socket.getdefaulttimeout()
168 socket.setdefaulttimeout(timeout)
169 try:
170 return urllib.urlopen(url, data=data, proxies=proxies)
171 finally:
172 socket.setdefaulttimeout(old_timeout)
mbligh02ff2d52008-06-03 15:00:21 +0000173
174
175def urlretrieve(url, filename=None, reporthook=None, data=None, timeout=300):
jadmanski0afbb632008-06-06 21:10:57 +0000176 """Wrapper to urllib.urlretrieve with timeout addition."""
177 old_timeout = socket.getdefaulttimeout()
178 socket.setdefaulttimeout(timeout)
179 try:
180 return urllib.urlretrieve(url, filename=filename,
181 reporthook=reporthook, data=data)
182 finally:
183 socket.setdefaulttimeout(old_timeout)
184
mbligh02ff2d52008-06-03 15:00:21 +0000185
mbligh6231cd62008-02-02 19:18:33 +0000186def get_file(src, dest, permissions=None):
jadmanski0afbb632008-06-06 21:10:57 +0000187 """Get a file from src, which can be local or a remote URL"""
188 if (src == dest):
189 return
190 if (is_url(src)):
191 print 'PWD: ' + os.getcwd()
192 print 'Fetching \n\t', src, '\n\t->', dest
193 try:
194 urllib.urlretrieve(src, dest)
195 except IOError, e:
196 raise error.AutotestError('Unable to retrieve %s (to %s)'
197 % (src, dest), e)
198 else:
199 shutil.copyfile(src, dest)
200 if permissions:
201 os.chmod(dest, permissions)
202 return dest
mbligh6231cd62008-02-02 19:18:33 +0000203
204
205def unmap_url(srcdir, src, destdir='.'):
jadmanski0afbb632008-06-06 21:10:57 +0000206 """
207 Receives either a path to a local file or a URL.
208 returns either the path to the local file, or the fetched URL
mbligh6231cd62008-02-02 19:18:33 +0000209
jadmanski0afbb632008-06-06 21:10:57 +0000210 unmap_url('/usr/src', 'foo.tar', '/tmp')
211 = '/usr/src/foo.tar'
212 unmap_url('/usr/src', 'http://site/file', '/tmp')
213 = '/tmp/file'
214 (after retrieving it)
215 """
216 if is_url(src):
217 url_parts = urlparse.urlparse(src)
218 filename = os.path.basename(url_parts[2])
219 dest = os.path.join(destdir, filename)
220 return get_file(src, dest)
221 else:
222 return os.path.join(srcdir, src)
mbligh6231cd62008-02-02 19:18:33 +0000223
224
225def update_version(srcdir, preserve_srcdir, new_version, install,
jadmanski0afbb632008-06-06 21:10:57 +0000226 *args, **dargs):
227 """
228 Make sure srcdir is version new_version
mbligh6231cd62008-02-02 19:18:33 +0000229
jadmanski0afbb632008-06-06 21:10:57 +0000230 If not, delete it and install() the new version.
mbligh6231cd62008-02-02 19:18:33 +0000231
jadmanski0afbb632008-06-06 21:10:57 +0000232 In the preserve_srcdir case, we just check it's up to date,
233 and if not, we rerun install, without removing srcdir
234 """
235 versionfile = os.path.join(srcdir, '.version')
236 install_needed = True
mbligh6231cd62008-02-02 19:18:33 +0000237
jadmanski0afbb632008-06-06 21:10:57 +0000238 if os.path.exists(versionfile):
239 old_version = pickle.load(open(versionfile))
240 if old_version == new_version:
241 install_needed = False
mbligh6231cd62008-02-02 19:18:33 +0000242
jadmanski0afbb632008-06-06 21:10:57 +0000243 if install_needed:
244 if not preserve_srcdir and os.path.exists(srcdir):
245 shutil.rmtree(srcdir)
246 install(*args, **dargs)
247 if os.path.exists(srcdir):
248 pickle.dump(new_version, open(versionfile, 'w'))
mbligh462c0152008-03-13 15:37:10 +0000249
250
mbligh63073c92008-03-31 16:49:32 +0000251def run(command, timeout=None, ignore_status=False,
jadmanski0afbb632008-06-06 21:10:57 +0000252 stdout_tee=None, stderr_tee=None):
253 """
254 Run a command on the host.
mbligh63073c92008-03-31 16:49:32 +0000255
jadmanski0afbb632008-06-06 21:10:57 +0000256 Args:
257 command: the command line string
258 timeout: time limit in seconds before attempting to
259 kill the running process. The run() function
260 will take a few seconds longer than 'timeout'
261 to complete if it has to kill the process.
262 ignore_status: do not raise an exception, no matter what
263 the exit code of the command is.
264 stdout_tee: optional file-like object to which stdout data
265 will be written as it is generated (data will still
266 be stored in result.stdout)
267 stderr_tee: likewise for stderr
mbligh63073c92008-03-31 16:49:32 +0000268
jadmanski0afbb632008-06-06 21:10:57 +0000269 Returns:
270 a CmdResult object
mbligh63073c92008-03-31 16:49:32 +0000271
jadmanski0afbb632008-06-06 21:10:57 +0000272 Raises:
273 CmdError: the exit code of the command
274 execution was not 0
275 """
mbligh849a0f62008-08-28 20:12:19 +0000276 bg_job = join_bg_jobs((BgJob(command, stdout_tee, stderr_tee),),
277 timeout)[0]
278 if not ignore_status and bg_job.result.exit_status:
jadmanski9c1098b2008-09-02 14:18:48 +0000279 raise error.CmdError(command, bg_job.result,
mbligh849a0f62008-08-28 20:12:19 +0000280 "Command returned non-zero exit status")
mbligh63073c92008-03-31 16:49:32 +0000281
mbligh849a0f62008-08-28 20:12:19 +0000282 return bg_job.result
mbligh63073c92008-03-31 16:49:32 +0000283
mbligh849a0f62008-08-28 20:12:19 +0000284@deprecated
mbligh63073c92008-03-31 16:49:32 +0000285def run_bg(command):
mbligh849a0f62008-08-28 20:12:19 +0000286 """Function deprecated. Please use BgJob class instead."""
287 bg_job = BgJob(command)
288 return bg_job.sp, bg_job.result
mbligh63073c92008-03-31 16:49:32 +0000289
290
mbligh849a0f62008-08-28 20:12:19 +0000291def join_bg_jobs(bg_jobs, timeout=None):
jadmanski0afbb632008-06-06 21:10:57 +0000292 """Join the subprocess with the current thread. See run description."""
mbligh849a0f62008-08-28 20:12:19 +0000293 ret, timeouterr = 0, False
294 for bg_job in bg_jobs:
295 bg_job.output_prepare(StringIO.StringIO(), StringIO.StringIO())
mbligh63073c92008-03-31 16:49:32 +0000296
jadmanski0afbb632008-06-06 21:10:57 +0000297 try:
298 # We are holding ends to stdin, stdout pipes
299 # hence we need to be sure to close those fds no mater what
300 start_time = time.time()
mbligh849a0f62008-08-28 20:12:19 +0000301 timeout_error = _wait_for_commands(bg_jobs, start_time, timeout)
302
303 for bg_job in bg_jobs:
304 # Process stdout and stderr
305 bg_job.process_output(stdout=True,final_read=True)
306 bg_job.process_output(stdout=False,final_read=True)
jadmanski0afbb632008-06-06 21:10:57 +0000307 finally:
308 # close our ends of the pipes to the sp no matter what
mbligh849a0f62008-08-28 20:12:19 +0000309 for bg_job in bg_jobs:
310 bg_job.cleanup()
mbligh63073c92008-03-31 16:49:32 +0000311
mbligh849a0f62008-08-28 20:12:19 +0000312 if timeout_error:
313 # TODO: This needs to be fixed to better represent what happens when
314 # running in parallel. However this is backwards compatable, so it will
315 # do for the time being.
316 raise error.CmdError(bg_jobs[0].command, bg_jobs[0].result,
317 "Command(s) did not complete within %d seconds"
318 % timeout)
mbligh63073c92008-03-31 16:49:32 +0000319
mbligh63073c92008-03-31 16:49:32 +0000320
mbligh849a0f62008-08-28 20:12:19 +0000321 return bg_jobs
mbligh63073c92008-03-31 16:49:32 +0000322
mbligh849a0f62008-08-28 20:12:19 +0000323
324def _wait_for_commands(bg_jobs, start_time, timeout):
325 # This returns True if it must return due to a timeout, otherwise False.
326
327 select_list = []
328 reverse_dict = {}
329 for bg_job in bg_jobs:
330 select_list.append(bg_job.sp.stdout)
331 select_list.append(bg_job.sp.stderr)
332 reverse_dict[bg_job.sp.stdout] = (bg_job,True)
333 reverse_dict[bg_job.sp.stderr] = (bg_job,False)
334
jadmanski0afbb632008-06-06 21:10:57 +0000335 if timeout:
336 stop_time = start_time + timeout
337 time_left = stop_time - time.time()
338 else:
339 time_left = None # so that select never times out
340 while not timeout or time_left > 0:
341 # select will return when stdout is ready (including when it is
342 # EOF, that is the process has terminated).
mbligh849a0f62008-08-28 20:12:19 +0000343 ready, _, _ = select.select(select_list, [], [], time_left)
344
jadmanski0afbb632008-06-06 21:10:57 +0000345 # os.read() has to be used instead of
346 # subproc.stdout.read() which will otherwise block
mbligh849a0f62008-08-28 20:12:19 +0000347 for fileno in ready:
348 bg_job,stdout = reverse_dict[fileno]
349 bg_job.process_output(stdout)
mbligh63073c92008-03-31 16:49:32 +0000350
mbligh849a0f62008-08-28 20:12:19 +0000351 remaining_jobs = [x for x in bg_jobs if x.result.exit_status is None]
352 if len(remaining_jobs) == 0:
353 return False
354 for bg_job in remaining_jobs:
355 bg_job.result.exit_status = bg_job.sp.poll()
mbligh8ea61e22008-05-09 18:09:37 +0000356
jadmanski0afbb632008-06-06 21:10:57 +0000357 if timeout:
358 time_left = stop_time - time.time()
mbligh63073c92008-03-31 16:49:32 +0000359
mbligh849a0f62008-08-28 20:12:19 +0000360 # Kill all processes which did not complete prior to timeout
361 for bg_job in [x for x in bg_jobs if x.result.exit_status is None]:
362 nuke_subprocess(bg_job.sp)
mbligh8ea61e22008-05-09 18:09:37 +0000363
mbligh849a0f62008-08-28 20:12:19 +0000364 return True
mbligh63073c92008-03-31 16:49:32 +0000365
366
mbligh63073c92008-03-31 16:49:32 +0000367def nuke_subprocess(subproc):
jadmanski0afbb632008-06-06 21:10:57 +0000368 # the process has not terminated within timeout,
369 # kill it via an escalating series of signals.
370 signal_queue = [signal.SIGTERM, signal.SIGKILL]
371 for sig in signal_queue:
372 try:
373 os.kill(subproc.pid, sig)
374 # The process may have died before we could kill it.
375 except OSError:
376 pass
mbligh63073c92008-03-31 16:49:32 +0000377
jadmanski0afbb632008-06-06 21:10:57 +0000378 for i in range(5):
379 rc = subproc.poll()
380 if rc != None:
381 return rc
382 time.sleep(1)
mbligh63073c92008-03-31 16:49:32 +0000383
384
385def nuke_pid(pid):
jadmanski0afbb632008-06-06 21:10:57 +0000386 # the process has not terminated within timeout,
387 # kill it via an escalating series of signals.
388 signal_queue = [signal.SIGTERM, signal.SIGKILL]
389 for sig in signal_queue:
390 try:
391 os.kill(pid, sig)
mbligh63073c92008-03-31 16:49:32 +0000392
jadmanski0afbb632008-06-06 21:10:57 +0000393 # The process may have died before we could kill it.
394 except OSError:
395 pass
mbligh63073c92008-03-31 16:49:32 +0000396
jadmanski0afbb632008-06-06 21:10:57 +0000397 try:
398 for i in range(5):
399 status = os.waitpid(pid, os.WNOHANG)[0]
400 if status == pid:
401 return
402 time.sleep(1)
mbligh63073c92008-03-31 16:49:32 +0000403
jadmanski0afbb632008-06-06 21:10:57 +0000404 if status != pid:
405 raise error.AutoservRunError('Could not kill %d'
406 % pid, None)
mbligh63073c92008-03-31 16:49:32 +0000407
jadmanski0afbb632008-06-06 21:10:57 +0000408 # the process died before we join it.
409 except OSError:
410 pass
mbligh63073c92008-03-31 16:49:32 +0000411
412
mbligh63073c92008-03-31 16:49:32 +0000413
414def system(command, timeout=None, ignore_status=False):
jadmanski0afbb632008-06-06 21:10:57 +0000415 return run(command, timeout, ignore_status,
416 stdout_tee=sys.stdout, stderr_tee=sys.stderr).exit_status
mbligh63073c92008-03-31 16:49:32 +0000417
418
mbligh849a0f62008-08-28 20:12:19 +0000419def system_parallel(commands, timeout=None):
420 bg_jobs = []
421 for command in commands:
422 bg_jobs.append(BgJob(command, sys.stdout, sys.stderr))
423
424 return [bg_job.result.stdout for bg_job in join_bg_jobs(bg_jobs,
425 timeout)]
426
427
mbligh8ea61e22008-05-09 18:09:37 +0000428def system_output(command, timeout=None, ignore_status=False,
jadmanski0afbb632008-06-06 21:10:57 +0000429 retain_output=False):
430 if retain_output:
431 out = run(command, timeout, ignore_status,
432 stdout_tee=sys.stdout, stderr_tee=sys.stderr).stdout
433 else:
434 out = run(command, timeout, ignore_status).stdout
435 if out[-1:] == '\n': out = out[:-1]
436 return out
mbligh63073c92008-03-31 16:49:32 +0000437
mbligh849a0f62008-08-28 20:12:19 +0000438
mblighc1cbc992008-05-27 20:01:45 +0000439"""
440This function is used when there is a need to run more than one
441job simultaneously starting exactly at the same time. It basically returns
442a modified control file (containing the synchronization code prepended)
443whenever it is ready to run the control file. The synchronization
444is done using barriers to make sure that the jobs start at the same time.
445
446Here is how the synchronization is done to make sure that the tests
447start at exactly the same time on the client.
448sc_bar is a server barrier and s_bar, c_bar are the normal barriers
449
450 Job1 Job2 ...... JobN
451 Server: | sc_bar
452 Server: | s_bar ...... s_bar
453 Server: | at.run() at.run() ...... at.run()
454 ----------|------------------------------------------------------
455 Client | sc_bar
456 Client | c_bar c_bar ...... c_bar
457 Client | <run test> <run test> ...... <run test>
458
459
460PARAMS:
461 control_file : The control file which to which the above synchronization
462 code would be prepended to
463 host_name : The host name on which the job is going to run
464 host_num (non negative) : A number to identify the machine so that we have
465 different sets of s_bar_ports for each of the machines.
466 instance : The number of the job
467 num_jobs : Total number of jobs that are going to run in parallel with
468 this job starting at the same time
469 port_base : Port number that is used to derive the actual barrier ports.
470
471RETURN VALUE:
472 The modified control file.
473
474"""
475def get_sync_control_file(control, host_name, host_num,
jadmanski0afbb632008-06-06 21:10:57 +0000476 instance, num_jobs, port_base=63100):
477 sc_bar_port = port_base
478 c_bar_port = port_base
479 if host_num < 0:
480 print "Please provide a non negative number for the host"
481 return None
482 s_bar_port = port_base + 1 + host_num # The set of s_bar_ports are
483 # the same for a given machine
mblighc1cbc992008-05-27 20:01:45 +0000484
jadmanski0afbb632008-06-06 21:10:57 +0000485 sc_bar_timeout = 180
486 s_bar_timeout = c_bar_timeout = 120
mblighc1cbc992008-05-27 20:01:45 +0000487
jadmanski0afbb632008-06-06 21:10:57 +0000488 # The barrier code snippet is prepended into the conrol file
489 # dynamically before at.run() is called finally.
490 control_new = []
mblighc1cbc992008-05-27 20:01:45 +0000491
jadmanski0afbb632008-06-06 21:10:57 +0000492 # jobid is the unique name used to identify the processes
493 # trying to reach the barriers
494 jobid = "%s#%d" % (host_name, instance)
mblighc1cbc992008-05-27 20:01:45 +0000495
jadmanski0afbb632008-06-06 21:10:57 +0000496 rendv = []
497 # rendvstr is a temp holder for the rendezvous list of the processes
498 for n in range(num_jobs):
499 rendv.append("'%s#%d'" % (host_name, n))
500 rendvstr = ",".join(rendv)
mblighc1cbc992008-05-27 20:01:45 +0000501
jadmanski0afbb632008-06-06 21:10:57 +0000502 if instance == 0:
503 # Do the setup and wait at the server barrier
504 # Clean up the tmp and the control dirs for the first instance
505 control_new.append('if os.path.exists(job.tmpdir):')
506 control_new.append("\t system('umount -f %s > /dev/null"
507 "2> /dev/null' % job.tmpdir,"
508 "ignore_status=True)")
509 control_new.append("\t system('rm -rf ' + job.tmpdir)")
510 control_new.append(
511 'b0 = job.barrier("%s", "sc_bar", %d, port=%d)'
512 % (jobid, sc_bar_timeout, sc_bar_port))
513 control_new.append(
514 'b0.rendevous_servers("PARALLEL_MASTER", "%s")'
515 % jobid)
mblighc1cbc992008-05-27 20:01:45 +0000516
jadmanski0afbb632008-06-06 21:10:57 +0000517 elif instance == 1:
518 # Wait at the server barrier to wait for instance=0
519 # process to complete setup
520 b0 = barrier.barrier("PARALLEL_MASTER", "sc_bar", sc_bar_timeout,
521 port=sc_bar_port)
522 b0.rendevous_servers("PARALLEL_MASTER", jobid)
mblighc1cbc992008-05-27 20:01:45 +0000523
jadmanski0afbb632008-06-06 21:10:57 +0000524 if(num_jobs > 2):
525 b1 = barrier.barrier(jobid, "s_bar", s_bar_timeout,
526 port=s_bar_port)
527 b1.rendevous(rendvstr)
mblighc1cbc992008-05-27 20:01:45 +0000528
jadmanski0afbb632008-06-06 21:10:57 +0000529 else:
530 # For the rest of the clients
531 b2 = barrier.barrier(jobid, "s_bar", s_bar_timeout, port=s_bar_port)
532 b2.rendevous(rendvstr)
mblighc1cbc992008-05-27 20:01:45 +0000533
jadmanski0afbb632008-06-06 21:10:57 +0000534 # Client side barrier for all the tests to start at the same time
535 control_new.append('b1 = job.barrier("%s", "c_bar", %d, port=%d)'
536 % (jobid, c_bar_timeout, c_bar_port))
537 control_new.append("b1.rendevous(%s)" % rendvstr)
mblighc1cbc992008-05-27 20:01:45 +0000538
jadmanski0afbb632008-06-06 21:10:57 +0000539 # Stick in the rest of the control file
540 control_new.append(control)
mblighc1cbc992008-05-27 20:01:45 +0000541
jadmanski0afbb632008-06-06 21:10:57 +0000542 return "\n".join(control_new)
mblighc1cbc992008-05-27 20:01:45 +0000543
mbligh63073c92008-03-31 16:49:32 +0000544
mblighc5ddfd12008-08-04 17:15:00 +0000545def get_arch(run_function=run):
546 """
547 Get the hardware architecture of the machine.
548 run_function is used to execute the commands. It defaults to
549 utils.run() but a custom method (if provided) should be of the
550 same schema as utils.run. It should return a CmdResult object and
551 throw a CmdError exception.
552 """
553 arch = run_function('/bin/uname -m').stdout.rstrip()
554 if re.match(r'i\d86$', arch):
555 arch = 'i386'
556 return arch
557
558
mbligh63073c92008-03-31 16:49:32 +0000559class CmdResult(object):
jadmanski0afbb632008-06-06 21:10:57 +0000560 """
561 Command execution result.
mbligh63073c92008-03-31 16:49:32 +0000562
jadmanski0afbb632008-06-06 21:10:57 +0000563 command: String containing the command line itself
564 exit_status: Integer exit code of the process
565 stdout: String containing stdout of the process
566 stderr: String containing stderr of the process
567 duration: Elapsed wall clock time running the process
568 """
mbligh63073c92008-03-31 16:49:32 +0000569
570
jadmanski0afbb632008-06-06 21:10:57 +0000571 def __init__(self, command=None, stdout="", stderr="",
572 exit_status=None, duration=0):
573 self.command = command
574 self.exit_status = exit_status
575 self.stdout = stdout
576 self.stderr = stderr
577 self.duration = duration
mbligh63073c92008-03-31 16:49:32 +0000578
579
jadmanski0afbb632008-06-06 21:10:57 +0000580 def __repr__(self):
581 wrapper = textwrap.TextWrapper(width = 78,
582 initial_indent="\n ",
583 subsequent_indent=" ")
584
585 stdout = self.stdout.rstrip()
586 if stdout:
587 stdout = "\nstdout:\n%s" % stdout
588
589 stderr = self.stderr.rstrip()
590 if stderr:
591 stderr = "\nstderr:\n%s" % stderr
592
593 return ("* Command: %s\n"
594 "Exit status: %s\n"
595 "Duration: %s\n"
596 "%s"
597 "%s"
598 % (wrapper.fill(self.command), self.exit_status,
599 self.duration, stdout, stderr))
mbligh63073c92008-03-31 16:49:32 +0000600
601
mbligh462c0152008-03-13 15:37:10 +0000602class run_randomly:
jadmanski0afbb632008-06-06 21:10:57 +0000603 def __init__(self, run_sequentially=False):
604 # Run sequentially is for debugging control files
605 self.test_list = []
606 self.run_sequentially = run_sequentially
mbligh462c0152008-03-13 15:37:10 +0000607
608
jadmanski0afbb632008-06-06 21:10:57 +0000609 def add(self, *args, **dargs):
610 test = (args, dargs)
611 self.test_list.append(test)
mbligh462c0152008-03-13 15:37:10 +0000612
613
jadmanski0afbb632008-06-06 21:10:57 +0000614 def run(self, fn):
615 while self.test_list:
616 test_index = random.randint(0, len(self.test_list)-1)
617 if self.run_sequentially:
618 test_index = 0
619 (args, dargs) = self.test_list.pop(test_index)
620 fn(*args, **dargs)