blob: 8f3683ab083052fc422d89f04a031b7aeba61faa [file] [log] [blame]
mblighc86b0b42006-07-28 17:35:28 +00001"""The main job wrapper
mbligha2508052006-05-28 21:29:53 +00002
mblighc86b0b42006-07-28 17:35:28 +00003This is the core infrastructure.
4"""
5
6__author__ = """Copyright Andy Whitcroft, Martin J. Bligh 2006"""
mbligha2508052006-05-28 21:29:53 +00007
mbligh8f243ec2006-10-10 05:55:49 +00008# standard stuff
mbligh366ff1b2008-04-25 16:07:56 +00009import os, sys, re, pickle, shutil, time, traceback, types, copy
mbligh302482e2008-05-01 20:06:16 +000010
mbligh8f243ec2006-10-10 05:55:49 +000011# autotest stuff
mblighc61fb362008-06-05 16:22:15 +000012from autotest_lib.client.bin import autotest_utils, parallel, kernel, xen
13from autotest_lib.client.bin import profilers, fd_stack, boottool, harness
14from autotest_lib.client.bin import config, sysinfo, cpuset, test, filesystem
mblighe829ba52008-06-03 15:04:08 +000015from autotest_lib.client.common_lib import error, barrier, logging, utils
mbligh302482e2008-05-01 20:06:16 +000016
mbligh366ff1b2008-04-25 16:07:56 +000017JOB_PREAMBLE = """
18from common.error import *
19from autotest_utils import *
20"""
21
mbligh302482e2008-05-01 20:06:16 +000022class StepError(error.AutotestError):
jadmanski0afbb632008-06-06 21:10:57 +000023 pass
mbligh12a04cb2008-04-25 16:07:20 +000024
25
mblighcaa62c22008-04-07 21:51:17 +000026class base_job:
jadmanski0afbb632008-06-06 21:10:57 +000027 """The actual job against which we do everything.
mblighc86b0b42006-07-28 17:35:28 +000028
jadmanski0afbb632008-06-06 21:10:57 +000029 Properties:
30 autodir
31 The top level autotest directory (/usr/local/autotest).
32 Comes from os.environ['AUTODIR'].
33 bindir
34 <autodir>/bin/
35 libdir
36 <autodir>/lib/
37 testdir
38 <autodir>/tests/
39 site_testdir
40 <autodir>/site_tests/
41 profdir
42 <autodir>/profilers/
43 tmpdir
44 <autodir>/tmp/
45 resultdir
46 <autodir>/results/<jobtag>
47 stdout
48 fd_stack object for stdout
49 stderr
50 fd_stack object for stderr
51 profilers
52 the profilers object for this job
53 harness
54 the server harness object for this job
55 config
56 the job configuration for this job
57 """
mblighc86b0b42006-07-28 17:35:28 +000058
jadmanski0afbb632008-06-06 21:10:57 +000059 DEFAULT_LOG_FILENAME = "status"
mblighd528d302007-12-19 16:19:05 +000060
jadmanski0afbb632008-06-06 21:10:57 +000061 def __init__(self, control, jobtag, cont, harness_type=None,
62 use_external_logging = False):
63 """
64 control
65 The control file (pathname of)
66 jobtag
67 The job tag string (eg "default")
68 cont
69 If this is the continuation of this job
70 harness_type
71 An alternative server harness
72 """
73 self.autodir = os.environ['AUTODIR']
74 self.bindir = os.path.join(self.autodir, 'bin')
75 self.libdir = os.path.join(self.autodir, 'lib')
76 self.testdir = os.path.join(self.autodir, 'tests')
77 self.site_testdir = os.path.join(self.autodir, 'site_tests')
78 self.profdir = os.path.join(self.autodir, 'profilers')
79 self.tmpdir = os.path.join(self.autodir, 'tmp')
80 self.resultdir = os.path.join(self.autodir, 'results', jobtag)
81 self.sysinfodir = os.path.join(self.resultdir, 'sysinfo')
82 self.control = os.path.abspath(control)
83 self.state_file = self.control + '.state'
84 self.current_step_ancestry = []
85 self.next_step_index = 0
86 self.__load_state()
mbligha2508052006-05-28 21:29:53 +000087
jadmanski0afbb632008-06-06 21:10:57 +000088 if not cont:
89 """
90 Don't cleanup the tmp dir (which contains the lockfile)
91 in the constructor, this would be a problem for multiple
92 jobs starting at the same time on the same client. Instead
93 do the delete at the server side. We simply create the tmp
94 directory here if it does not already exist.
95 """
96 if not os.path.exists(self.tmpdir):
97 os.mkdir(self.tmpdir)
apw96da1a42006-11-02 00:23:18 +000098
jadmanski0afbb632008-06-06 21:10:57 +000099 results = os.path.join(self.autodir, 'results')
100 if not os.path.exists(results):
101 os.mkdir(results)
mblighc1cbc992008-05-27 20:01:45 +0000102
jadmanski0afbb632008-06-06 21:10:57 +0000103 download = os.path.join(self.testdir, 'download')
104 if not os.path.exists(download):
105 os.mkdir(download)
apw96da1a42006-11-02 00:23:18 +0000106
jadmanski0afbb632008-06-06 21:10:57 +0000107 if os.path.exists(self.resultdir):
mbligh15440802008-06-06 22:48:19 +0000108 utils.system('rm -rf ' + self.resultdir)
jadmanski0afbb632008-06-06 21:10:57 +0000109 os.mkdir(self.resultdir)
110 os.mkdir(self.sysinfodir)
apw870988b2007-09-25 16:50:53 +0000111
jadmanski0afbb632008-06-06 21:10:57 +0000112 os.mkdir(os.path.join(self.resultdir, 'debug'))
113 os.mkdir(os.path.join(self.resultdir, 'analysis'))
mblighf4ca14f2008-03-03 16:03:05 +0000114
jadmanski0afbb632008-06-06 21:10:57 +0000115 shutil.copyfile(self.control,
116 os.path.join(self.resultdir, 'control'))
mbligh4b089662006-06-14 22:34:58 +0000117
mblighf4c35322006-03-13 01:01:10 +0000118
jadmanski0afbb632008-06-06 21:10:57 +0000119 self.control = control
120 self.jobtag = jobtag
121 self.log_filename = self.DEFAULT_LOG_FILENAME
122 self.container = None
jadmanskia9c75c42008-05-01 22:05:31 +0000123
jadmanski0afbb632008-06-06 21:10:57 +0000124 self.stdout = fd_stack.fd_stack(1, sys.stdout)
125 self.stderr = fd_stack.fd_stack(2, sys.stderr)
mblighf4c35322006-03-13 01:01:10 +0000126
jadmanski0afbb632008-06-06 21:10:57 +0000127 self._init_group_level()
apw059e1b12006-10-12 17:18:26 +0000128
jadmanski0afbb632008-06-06 21:10:57 +0000129 self.config = config.config(self)
apwd27e55f2006-12-01 11:22:08 +0000130
jadmanski0afbb632008-06-06 21:10:57 +0000131 self.harness = harness.select(harness_type, self)
mbligh72905562006-05-25 01:30:49 +0000132
jadmanski0afbb632008-06-06 21:10:57 +0000133 self.profilers = profilers.profilers(self)
mblighcaa605c2006-10-02 00:37:35 +0000134
jadmanski0afbb632008-06-06 21:10:57 +0000135 try:
136 tool = self.config_get('boottool.executable')
137 self.bootloader = boottool.boottool(tool)
138 except:
139 pass
mbligh3a6d6ca2006-04-23 15:50:24 +0000140
jadmanski0afbb632008-06-06 21:10:57 +0000141 sysinfo.log_per_reboot_data(self.sysinfodir)
apw357f50f2006-12-01 11:22:39 +0000142
jadmanski0afbb632008-06-06 21:10:57 +0000143 if not cont:
144 self.record('START', None, None)
145 self._increment_group_level()
apwf91efaf2007-11-24 17:32:13 +0000146
jadmanski0afbb632008-06-06 21:10:57 +0000147 self.harness.run_start()
jadmanski8415f962008-05-06 20:38:53 +0000148
jadmanski0afbb632008-06-06 21:10:57 +0000149 if use_external_logging:
150 self.enable_external_logging()
jadmanski8415f962008-05-06 20:38:53 +0000151
jadmanski0afbb632008-06-06 21:10:57 +0000152 # load the max disk usage rate - default to no monitoring
mbligh15440802008-06-06 22:48:19 +0000153 self.max_disk_usage_rate = self.get_state('__monitor_disk', default=0.0)
jadmanski8415f962008-05-06 20:38:53 +0000154
jadmanski8415f962008-05-06 20:38:53 +0000155
jadmanski0afbb632008-06-06 21:10:57 +0000156 def monitor_disk_usage(self, max_rate):
157 """\
158 Signal that the job should monitor disk space usage on /
159 and generate a warning if a test uses up disk space at a
160 rate exceeding 'max_rate'.
mbligh0692e472007-08-30 16:07:53 +0000161
jadmanski0afbb632008-06-06 21:10:57 +0000162 Parameters:
163 max_rate - the maximium allowed rate of disk consumption
164 during a test, in MB/hour, or 0 to indicate
165 no limit.
166 """
167 self.set_state('__monitor_disk', max_rate)
168 self.max_disk_usage_rate = max_rate
mbligh0692e472007-08-30 16:07:53 +0000169
170
jadmanski0afbb632008-06-06 21:10:57 +0000171 def relative_path(self, path):
172 """\
173 Return a patch relative to the job results directory
174 """
175 head = len(self.resultdir) + 1 # remove the / inbetween
176 return path[head:]
mbligh362ab3d2007-08-30 11:24:04 +0000177
mblighcaa605c2006-10-02 00:37:35 +0000178
jadmanski0afbb632008-06-06 21:10:57 +0000179 def control_get(self):
180 return self.control
mbligh8d83cdc2007-12-03 18:09:18 +0000181
182
jadmanski0afbb632008-06-06 21:10:57 +0000183 def control_set(self, control):
184 self.control = os.path.abspath(control)
apwde1503a2006-10-10 08:34:21 +0000185
186
jadmanski0afbb632008-06-06 21:10:57 +0000187 def harness_select(self, which):
188 self.harness = harness.select(which, self)
apw059e1b12006-10-12 17:18:26 +0000189
190
jadmanski0afbb632008-06-06 21:10:57 +0000191 def config_set(self, name, value):
192 self.config.set(name, value)
apw059e1b12006-10-12 17:18:26 +0000193
mbligh1e8858e2006-11-24 22:18:35 +0000194
jadmanski0afbb632008-06-06 21:10:57 +0000195 def config_get(self, name):
196 return self.config.get(name)
mbligh72b88fc2006-12-16 18:41:35 +0000197
jadmanski0afbb632008-06-06 21:10:57 +0000198 def setup_dirs(self, results_dir, tmp_dir):
199 if not tmp_dir:
200 tmp_dir = os.path.join(self.tmpdir, 'build')
201 if not os.path.exists(tmp_dir):
202 os.mkdir(tmp_dir)
203 if not os.path.isdir(tmp_dir):
204 e_msg = "Temp dir (%s) is not a dir - args backwards?" % self.tmpdir
205 raise ValueError(e_msg)
mbligh8baa2ea2006-12-17 23:01:24 +0000206
jadmanski0afbb632008-06-06 21:10:57 +0000207 # We label the first build "build" and then subsequent ones
208 # as "build.2", "build.3", etc. Whilst this is a little bit
209 # inconsistent, 99.9% of jobs will only have one build
210 # (that's not done as kernbench, sparse, or buildtest),
211 # so it works out much cleaner. One of life's comprimises.
212 if not results_dir:
213 results_dir = os.path.join(self.resultdir, 'build')
214 i = 2
215 while os.path.exists(results_dir):
216 results_dir = os.path.join(self.resultdir, 'build.%d' % i)
217 i += 1
218 if not os.path.exists(results_dir):
219 os.mkdir(results_dir)
mbligh8baa2ea2006-12-17 23:01:24 +0000220
jadmanski0afbb632008-06-06 21:10:57 +0000221 return (results_dir, tmp_dir)
mbligh8baa2ea2006-12-17 23:01:24 +0000222
223
jadmanski0afbb632008-06-06 21:10:57 +0000224 def xen(self, base_tree, results_dir = '', tmp_dir = '', leave = False, \
225 kjob = None ):
226 """Summon a xen object"""
227 (results_dir, tmp_dir) = self.setup_dirs(results_dir, tmp_dir)
228 build_dir = 'xen'
229 return xen.xen(self, base_tree, results_dir, tmp_dir, build_dir, leave, kjob)
mblighf4c35322006-03-13 01:01:10 +0000230
mblighcaa605c2006-10-02 00:37:35 +0000231
jadmanski0afbb632008-06-06 21:10:57 +0000232 def kernel(self, base_tree, results_dir = '', tmp_dir = '', leave = False):
233 """Summon a kernel object"""
234 (results_dir, tmp_dir) = self.setup_dirs(results_dir, tmp_dir)
235 build_dir = 'linux'
mbligh15440802008-06-06 22:48:19 +0000236 return kernel.auto_kernel(self, base_tree, results_dir, tmp_dir,
237 build_dir, leave)
mblighfadca202006-09-23 04:40:01 +0000238
mblighcaa605c2006-10-02 00:37:35 +0000239
jadmanski0afbb632008-06-06 21:10:57 +0000240 def barrier(self, *args, **kwds):
241 """Create a barrier object"""
242 return barrier.barrier(*args, **kwds)
mbligh4b089662006-06-14 22:34:58 +0000243
244
jadmanski0afbb632008-06-06 21:10:57 +0000245 def setup_dep(self, deps):
246 """Set up the dependencies for this test.
apwf1a81162006-04-25 10:10:29 +0000247
jadmanski0afbb632008-06-06 21:10:57 +0000248 deps is a list of libraries required for this test.
249 """
250 for dep in deps:
251 try:
252 os.chdir(os.path.join(self.autodir, 'deps', dep))
253 utils.system('./' + dep + '.py')
254 except:
255 err = "setting up dependency " + dep + "\n"
256 raise error.UnhandledError(err)
mblighcaa605c2006-10-02 00:37:35 +0000257
mbligh12a7df72006-10-06 03:54:33 +0000258
jadmanski0afbb632008-06-06 21:10:57 +0000259 def __runtest(self, url, tag, args, dargs):
260 try:
261 l = lambda : test.runtest(self, url, tag, args, dargs)
262 pid = parallel.fork_start(self.resultdir, l)
263 parallel.fork_waitfor(self.resultdir, pid)
264 except error.AutotestError:
265 raise
266 except Exception, e:
267 msg = "Unhandled %s error occured during test\n"
268 msg %= str(e.__class__.__name__)
269 raise error.UnhandledError(msg)
mbligh7dd510c2007-11-13 17:11:22 +0000270
mbligh65938a22007-12-10 16:58:52 +0000271
jadmanski0afbb632008-06-06 21:10:57 +0000272 def run_test(self, url, *args, **dargs):
273 """Summon a test object and run it.
jadmanski8415f962008-05-06 20:38:53 +0000274
jadmanski0afbb632008-06-06 21:10:57 +0000275 tag
276 tag to add to testname
277 url
278 url of the test to run
279 """
mbligh7dd510c2007-11-13 17:11:22 +0000280
jadmanski0afbb632008-06-06 21:10:57 +0000281 if not url:
282 raise TypeError("Test name is invalid. "
283 "Switched arguments?")
284 (group, testname) = test.testname(url)
285 namelen = len(testname)
286 dargs = dargs.copy()
287 tntag = dargs.pop('tag', None)
288 if tntag: # testname tag is included in reported test name
289 testname += '.' + tntag
290 subdir = testname
291 sdtag = dargs.pop('subdir_tag', None)
292 if sdtag: # subdir-only tag is not included in reports
293 subdir = subdir + '.' + sdtag
294 tag = subdir[namelen+1:] # '' if none
mbligh7dd510c2007-11-13 17:11:22 +0000295
jadmanski0afbb632008-06-06 21:10:57 +0000296 outputdir = os.path.join(self.resultdir, subdir)
297 if os.path.exists(outputdir):
298 msg = ("%s already exists, test <%s> may have"
299 " already run with tag <%s>"
300 % (outputdir, testname, tag) )
301 raise error.TestError(msg)
302 os.mkdir(outputdir)
mbligh7dd510c2007-11-13 17:11:22 +0000303
jadmanski0afbb632008-06-06 21:10:57 +0000304 container = dargs.pop('container', None)
305 if container:
306 cname = container.get('name', None)
307 if not cname: # get old name
308 cname = container.get('container_name', None)
309 mbytes = container.get('mbytes', None)
310 if not mbytes: # get old name
311 mbytes = container.get('mem', None)
312 cpus = container.get('cpus', None)
313 if not cpus: # get old name
314 cpus = container.get('cpu', None)
315 root = container.get('root', None)
316 self.new_container(mbytes=mbytes, cpus=cpus,
317 root=root, name=cname)
318 # We are running in a container now...
mbligh7dd510c2007-11-13 17:11:22 +0000319
jadmanski0afbb632008-06-06 21:10:57 +0000320 def log_warning(reason):
321 self.record("WARN", subdir, testname, reason)
mbligh15440802008-06-06 22:48:19 +0000322 @disk_usage_monitor.watch(log_warning, "/", self.max_disk_usage_rate)
jadmanski0afbb632008-06-06 21:10:57 +0000323 def group_func():
324 try:
325 self.__runtest(url, tag, args, dargs)
326 except error.TestNAError, detail:
327 self.record('TEST_NA', subdir, testname,
328 str(detail))
329 raise
330 except Exception, detail:
331 self.record('FAIL', subdir, testname,
332 str(detail))
333 raise
334 else:
335 self.record('GOOD', subdir, testname,
336 'completed successfully')
mbligh7dd510c2007-11-13 17:11:22 +0000337
jadmanski0afbb632008-06-06 21:10:57 +0000338 result, exc_info = self.__rungroup(subdir, testname, group_func)
339 if container:
340 self.release_container()
341 if exc_info and isinstance(exc_info[1], error.TestError):
342 return False
343 elif exc_info:
344 raise exc_info[0], exc_info[1], exc_info[2]
345 else:
346 return True
apw0865f482006-03-30 18:50:19 +0000347
mblighd7fb4a62006-10-01 00:57:53 +0000348
jadmanski0afbb632008-06-06 21:10:57 +0000349 def __rungroup(self, subdir, testname, function, *args, **dargs):
350 """\
351 subdir:
352 name of the group
353 testname:
354 name of the test to run, or support step
355 function:
356 subroutine to run
357 *args:
358 arguments for the function
mbligh88ab90f2007-08-29 15:52:49 +0000359
jadmanski0afbb632008-06-06 21:10:57 +0000360 Returns a 2-tuple (result, exc_info) where result
361 is the return value of function, and exc_info is
362 the sys.exc_info() of the exception thrown by the
363 function (which may be None).
364 """
apw1da244b2007-09-27 17:18:01 +0000365
jadmanski0afbb632008-06-06 21:10:57 +0000366 result, exc_info = None, None
367 try:
368 self.record('START', subdir, testname)
369 self._increment_group_level()
370 result = function(*args, **dargs)
371 self._decrement_group_level()
372 self.record('END GOOD', subdir, testname)
373 except error.TestNAError, e:
374 self._decrement_group_level()
375 self.record('END TEST_NA', subdir, testname, str(e))
376 except Exception, e:
377 exc_info = sys.exc_info()
378 self._decrement_group_level()
379 err_msg = str(e) + '\n' + traceback.format_exc()
380 self.record('END FAIL', subdir, testname, err_msg)
mblighd660afe2008-06-05 22:17:53 +0000381
jadmanski0afbb632008-06-06 21:10:57 +0000382 return result, exc_info
apw1da244b2007-09-27 17:18:01 +0000383
mbligh88ab90f2007-08-29 15:52:49 +0000384
jadmanski0afbb632008-06-06 21:10:57 +0000385 def run_group(self, function, *args, **dargs):
386 """\
387 function:
388 subroutine to run
389 *args:
390 arguments for the function
391 """
apw08403ca2007-09-27 17:17:22 +0000392
jadmanski0afbb632008-06-06 21:10:57 +0000393 # Allow the tag for the group to be specified
394 name = function.__name__
395 tag = dargs.pop('tag', None)
396 if tag:
397 name = tag
mbligh88ab90f2007-08-29 15:52:49 +0000398
jadmanski0afbb632008-06-06 21:10:57 +0000399 outputdir = os.path.join(self.resultdir, name)
400 if os.path.exists(outputdir):
401 msg = ("%s already exists, test <%s> may have"
402 " already run with tag <%s>"
403 % (outputdir, name, name) )
404 raise error.TestError(msg)
405 os.mkdir(outputdir)
406
mbligh15440802008-06-06 22:48:19 +0000407 result, exc_info = self.__rungroup(name, name, function, *args, **dargs)
jadmanski0afbb632008-06-06 21:10:57 +0000408
409 # if there was a non-TestError exception, raise it
410 if exc_info and not isinstance(exc_info[1], error.TestError):
411 err = ''.join(traceback.format_exception(*exc_info))
412 raise error.TestError(name + ' failed\n' + err)
413
414 # pass back the actual return value from the function
415 return result
416
417
418 def new_container(self, mbytes=None, cpus=None, root=None, name=None):
419 if not autotest_utils.grep('cpuset', '/proc/filesystems'):
420 print "Containers not enabled by latest reboot"
421 return # containers weren't enabled in this kernel boot
422 pid = os.getpid()
423 if not name:
424 name = 'test%d' % pid # make arbitrary unique name
mbligh15440802008-06-06 22:48:19 +0000425 self.container = cpuset.cpuset(name, job_size=mbytes, job_pid=pid,
426 cpus=cpus, root=root)
jadmanski0afbb632008-06-06 21:10:57 +0000427 # This job's python shell is now running in the new container
428 # and all forked test processes will inherit that container
429
430
431 def release_container(self):
432 if self.container:
433 self.container.release()
434 self.container = None
435
mbligh68119582008-01-25 18:16:41 +0000436
jadmanski0afbb632008-06-06 21:10:57 +0000437 def cpu_count(self):
438 if self.container:
439 return len(self.container.cpus)
440 return autotest_utils.count_cpus() # use total system count
mbligh68119582008-01-25 18:16:41 +0000441
mbligh68119582008-01-25 18:16:41 +0000442
jadmanski0afbb632008-06-06 21:10:57 +0000443 # Check the passed kernel identifier against the command line
444 # and the running kernel, abort the job on missmatch.
445 def kernel_check_ident(self, expected_when, expected_id, subdir,
446 type = 'src', patches=[]):
447 print (("POST BOOT: checking booted kernel " +
448 "mark=%d identity='%s' type='%s'") %
449 (expected_when, expected_id, type))
mbligh68119582008-01-25 18:16:41 +0000450
jadmanski0afbb632008-06-06 21:10:57 +0000451 running_id = autotest_utils.running_os_ident()
mbligh68119582008-01-25 18:16:41 +0000452
jadmanski0afbb632008-06-06 21:10:57 +0000453 cmdline = utils.read_one_line("/proc/cmdline")
mbligh68119582008-01-25 18:16:41 +0000454
jadmanski0afbb632008-06-06 21:10:57 +0000455 find_sum = re.compile(r'.*IDENT=(\d+)')
456 m = find_sum.match(cmdline)
457 cmdline_when = -1
458 if m:
459 cmdline_when = int(m.groups()[0])
apwce73d892007-09-25 16:53:05 +0000460
jadmanski0afbb632008-06-06 21:10:57 +0000461 # We have all the facts, see if they indicate we
462 # booted the requested kernel or not.
463 bad = False
464 if (type == 'src' and expected_id != running_id or
465 type == 'rpm' and
466 not running_id.startswith(expected_id + '::')):
467 print "check_kernel_ident: kernel identifier mismatch"
468 bad = True
469 if expected_when != cmdline_when:
470 print "check_kernel_ident: kernel command line mismatch"
471 bad = True
apwce73d892007-09-25 16:53:05 +0000472
jadmanski0afbb632008-06-06 21:10:57 +0000473 if bad:
474 print " Expected Ident: " + expected_id
475 print " Running Ident: " + running_id
476 print " Expected Mark: %d" % (expected_when)
477 print "Command Line Mark: %d" % (cmdline_when)
478 print " Command Line: " + cmdline
apwce73d892007-09-25 16:53:05 +0000479
jadmanski0afbb632008-06-06 21:10:57 +0000480 raise error.JobError("boot failure", "reboot.verify")
apwce73d892007-09-25 16:53:05 +0000481
jadmanski0afbb632008-06-06 21:10:57 +0000482 kernel_info = {'kernel': expected_id}
483 for i, patch in enumerate(patches):
484 kernel_info["patch%d" % i] = patch
485 self.record('GOOD', subdir, 'reboot.verify', expected_id)
486 self._decrement_group_level()
487 self.record('END GOOD', subdir, 'reboot',
488 optional_fields=kernel_info)
apwce73d892007-09-25 16:53:05 +0000489
apwce73d892007-09-25 16:53:05 +0000490
jadmanski0afbb632008-06-06 21:10:57 +0000491 def filesystem(self, device, mountpoint = None, loop_size = 0):
492 if not mountpoint:
493 mountpoint = self.tmpdir
494 return filesystem.filesystem(self, device, mountpoint,loop_size)
apwce73d892007-09-25 16:53:05 +0000495
apwce73d892007-09-25 16:53:05 +0000496
jadmanski0afbb632008-06-06 21:10:57 +0000497 def enable_external_logging(self):
498 pass
apwce73d892007-09-25 16:53:05 +0000499
mblighd7fb4a62006-10-01 00:57:53 +0000500
jadmanski0afbb632008-06-06 21:10:57 +0000501 def disable_external_logging(self):
502 pass
mblighcaa62c22008-04-07 21:51:17 +0000503
504
jadmanski0afbb632008-06-06 21:10:57 +0000505 def reboot_setup(self):
506 pass
mblighcaa62c22008-04-07 21:51:17 +0000507
mblighcaa62c22008-04-07 21:51:17 +0000508
jadmanski0afbb632008-06-06 21:10:57 +0000509 def reboot(self, tag='autotest'):
510 self.reboot_setup()
511 self.record('START', None, 'reboot')
512 self._increment_group_level()
513 self.record('GOOD', None, 'reboot.start')
514 self.harness.run_reboot()
515 default = self.config_get('boot.set_default')
516 if default:
517 self.bootloader.set_default(tag)
518 else:
519 self.bootloader.boot_once(tag)
520 cmd = "(sleep 5; reboot) </dev/null >/dev/null 2>&1 &"
521 utils.system(cmd)
522 self.quit()
mblighcaa605c2006-10-02 00:37:35 +0000523
mblighcaa605c2006-10-02 00:37:35 +0000524
jadmanski0afbb632008-06-06 21:10:57 +0000525 def noop(self, text):
526 print "job: noop: " + text
mblighcaa605c2006-10-02 00:37:35 +0000527
apw0865f482006-03-30 18:50:19 +0000528
jadmanski0afbb632008-06-06 21:10:57 +0000529 def parallel(self, *tasklist):
530 """Run tasks in parallel"""
mblighcaa605c2006-10-02 00:37:35 +0000531
jadmanski0afbb632008-06-06 21:10:57 +0000532 pids = []
533 old_log_filename = self.log_filename
534 for i, task in enumerate(tasklist):
535 self.log_filename = old_log_filename + (".%d" % i)
536 task_func = lambda: task[0](*task[1:])
mbligh15440802008-06-06 22:48:19 +0000537 pids.append(parallel.fork_start(self.resultdir, task_func))
apw8fef4ac2006-10-10 22:53:37 +0000538
jadmanski0afbb632008-06-06 21:10:57 +0000539 old_log_path = os.path.join(self.resultdir, old_log_filename)
540 old_log = open(old_log_path, "a")
541 exceptions = []
542 for i, pid in enumerate(pids):
543 # wait for the task to finish
544 try:
545 parallel.fork_waitfor(self.resultdir, pid)
546 except Exception, e:
547 exceptions.append(e)
548 # copy the logs from the subtask into the main log
549 new_log_path = old_log_path + (".%d" % i)
550 if os.path.exists(new_log_path):
551 new_log = open(new_log_path)
552 old_log.write(new_log.read())
553 new_log.close()
554 old_log.flush()
555 os.remove(new_log_path)
556 old_log.close()
mblighd528d302007-12-19 16:19:05 +0000557
jadmanski0afbb632008-06-06 21:10:57 +0000558 self.log_filename = old_log_filename
mblighd528d302007-12-19 16:19:05 +0000559
jadmanski0afbb632008-06-06 21:10:57 +0000560 # handle any exceptions raised by the parallel tasks
561 if exceptions:
562 msg = "%d task(s) failed" % len(exceptions)
563 raise error.JobError(msg, str(exceptions), exceptions)
apw0865f482006-03-30 18:50:19 +0000564
mblighd509b712008-01-14 17:41:25 +0000565
jadmanski0afbb632008-06-06 21:10:57 +0000566 def quit(self):
567 # XXX: should have a better name.
568 self.harness.run_pause()
569 raise error.JobContinue("more to come")
mblighcaa605c2006-10-02 00:37:35 +0000570
apw0865f482006-03-30 18:50:19 +0000571
jadmanski0afbb632008-06-06 21:10:57 +0000572 def complete(self, status):
573 """Clean up and exit"""
574 # We are about to exit 'complete' so clean up the control file.
575 try:
576 os.unlink(self.state_file)
577 except:
578 pass
mblighcaa605c2006-10-02 00:37:35 +0000579
jadmanski0afbb632008-06-06 21:10:57 +0000580 self.harness.run_complete()
581 self.disable_external_logging()
582 sys.exit(status)
mblighc0b10d32008-03-03 16:03:28 +0000583
apw0865f482006-03-30 18:50:19 +0000584
jadmanski0afbb632008-06-06 21:10:57 +0000585 def set_state(self, var, val):
586 # Deep copies make sure that the state can't be altered
587 # without it being re-written. Perf wise, deep copies
588 # are overshadowed by pickling/loading.
589 self.state[var] = copy.deepcopy(val)
590 pickle.dump(self.state, open(self.state_file, 'w'))
mblighcaa605c2006-10-02 00:37:35 +0000591
mbligh366ff1b2008-04-25 16:07:56 +0000592
jadmanski0afbb632008-06-06 21:10:57 +0000593 def __load_state(self):
594 assert not hasattr(self, "state")
595 try:
596 self.state = pickle.load(open(self.state_file, 'r'))
597 self.state_existed = True
598 except Exception:
599 print "Initializing the state engine."
600 self.state = {}
601 self.set_state('__steps', []) # writes pickle file
602 self.state_existed = False
mbligh366ff1b2008-04-25 16:07:56 +0000603
mbligh366ff1b2008-04-25 16:07:56 +0000604
jadmanski0afbb632008-06-06 21:10:57 +0000605 def get_state(self, var, default=None):
606 if var in self.state or default == None:
607 val = self.state[var]
608 else:
609 val = default
610 return copy.deepcopy(val)
mbligh366ff1b2008-04-25 16:07:56 +0000611
mbligh366ff1b2008-04-25 16:07:56 +0000612
jadmanski0afbb632008-06-06 21:10:57 +0000613 def __create_step_tuple(self, fn, args, dargs):
614 # Legacy code passes in an array where the first arg is
615 # the function or its name.
616 if isinstance(fn, list):
617 assert(len(args) == 0)
618 assert(len(dargs) == 0)
619 args = fn[1:]
620 fn = fn[0]
621 # Pickling actual functions is harry, thus we have to call
622 # them by name. Unfortunately, this means only functions
623 # defined globally can be used as a next step.
624 if callable(fn):
625 fn = fn.__name__
626 if not isinstance(fn, types.StringTypes):
627 raise StepError("Next steps must be functions or "
628 "strings containing the function name")
629 ancestry = copy.copy(self.current_step_ancestry)
630 return (ancestry, fn, args, dargs)
mbligh366ff1b2008-04-25 16:07:56 +0000631
mbligh12a04cb2008-04-25 16:07:20 +0000632
jadmanski0afbb632008-06-06 21:10:57 +0000633 def next_step_append(self, fn, *args, **dargs):
634 """Define the next step and place it at the end"""
635 steps = self.get_state('__steps')
636 steps.append(self.__create_step_tuple(fn, args, dargs))
637 self.set_state('__steps', steps)
mbligh12a04cb2008-04-25 16:07:20 +0000638
apw0865f482006-03-30 18:50:19 +0000639
jadmanski0afbb632008-06-06 21:10:57 +0000640 def next_step(self, fn, *args, **dargs):
641 """Create a new step and place it after any steps added
642 while running the current step but before any steps added in
643 previous steps"""
644 steps = self.get_state('__steps')
645 steps.insert(self.next_step_index,
646 self.__create_step_tuple(fn, args, dargs))
647 self.next_step_index += 1
648 self.set_state('__steps', steps)
mblighcaa605c2006-10-02 00:37:35 +0000649
mbligh8f4d0432008-06-02 19:42:50 +0000650
jadmanski0afbb632008-06-06 21:10:57 +0000651 def next_step_prepend(self, fn, *args, **dargs):
652 """Insert a new step, executing first"""
653 steps = self.get_state('__steps')
654 steps.insert(0, self.__create_step_tuple(fn, args, dargs))
655 self.next_step_index += 1
656 self.set_state('__steps', steps)
mbligh8f4d0432008-06-02 19:42:50 +0000657
mbligh237bed32007-09-05 13:05:57 +0000658
jadmanski0afbb632008-06-06 21:10:57 +0000659 def _run_step_fn(self, local_vars, fn, args, dargs):
660 """Run a (step) function within the given context"""
mbligh237bed32007-09-05 13:05:57 +0000661
jadmanski0afbb632008-06-06 21:10:57 +0000662 local_vars['__args'] = args
663 local_vars['__dargs'] = dargs
mbligh15440802008-06-06 22:48:19 +0000664 exec('__ret = %s(*__args, **__dargs)' % fn, local_vars, local_vars)
jadmanski0afbb632008-06-06 21:10:57 +0000665 return local_vars['__ret']
mblighb274ef52008-06-02 19:40:01 +0000666
mblighb274ef52008-06-02 19:40:01 +0000667
jadmanski0afbb632008-06-06 21:10:57 +0000668 def _create_frame(self, global_vars, ancestry, fn_name):
669 """Set up the environment like it would have been when this
670 function was first defined.
mblighb274ef52008-06-02 19:40:01 +0000671
jadmanski0afbb632008-06-06 21:10:57 +0000672 Child step engine 'implementations' must have 'return locals()'
673 at end end of their steps. Because of this, we can call the
674 parent function and get back all child functions (i.e. those
675 defined within it).
mblighb274ef52008-06-02 19:40:01 +0000676
jadmanski0afbb632008-06-06 21:10:57 +0000677 Unfortunately, the call stack of the function calling
678 job.next_step might have been deeper than the function it
679 added. In order to make sure that the environment is what it
680 should be, we need to then pop off the frames we built until
681 we find the frame where the function was first defined."""
mblighb274ef52008-06-02 19:40:01 +0000682
jadmanski0afbb632008-06-06 21:10:57 +0000683 # The copies ensure that the parent frames are not modified
684 # while building child frames. This matters if we then
685 # pop some frames in the next part of this function.
686 current_frame = copy.copy(global_vars)
687 frames = [current_frame]
688 for steps_fn_name in ancestry:
mbligh15440802008-06-06 22:48:19 +0000689 ret = self._run_step_fn(current_frame, steps_fn_name, [], {})
jadmanski0afbb632008-06-06 21:10:57 +0000690 current_frame = copy.copy(ret)
691 frames.append(current_frame)
mblighb274ef52008-06-02 19:40:01 +0000692
jadmanski0afbb632008-06-06 21:10:57 +0000693 while len(frames) > 2:
694 if fn_name not in frames[-2]:
695 break
696 if frames[-2][fn_name] != frames[-1][fn_name]:
697 break
698 frames.pop()
699 ancestry.pop()
mblighb274ef52008-06-02 19:40:01 +0000700
jadmanski0afbb632008-06-06 21:10:57 +0000701 return (frames[-1], ancestry)
mblighb274ef52008-06-02 19:40:01 +0000702
mblighb274ef52008-06-02 19:40:01 +0000703
jadmanski0afbb632008-06-06 21:10:57 +0000704 def _add_step_init(self, local_vars, current_function):
705 """If the function returned a dictionary that includes a
706 function named 'step_init', prepend it to our list of steps.
707 This will only get run the first time a function with a nested
708 use of the step engine is run."""
mblighb274ef52008-06-02 19:40:01 +0000709
jadmanski0afbb632008-06-06 21:10:57 +0000710 if (isinstance(local_vars, dict) and
711 'step_init' in local_vars and
712 callable(local_vars['step_init'])):
713 # The init step is a child of the function
714 # we were just running.
715 self.current_step_ancestry.append(current_function)
716 self.next_step_prepend('step_init')
mblighb274ef52008-06-02 19:40:01 +0000717
mblighb274ef52008-06-02 19:40:01 +0000718
jadmanski0afbb632008-06-06 21:10:57 +0000719 def step_engine(self):
720 """the stepping engine -- if the control file defines
721 step_init we will be using this engine to drive multiple runs.
722 """
723 """Do the next step"""
mblighb274ef52008-06-02 19:40:01 +0000724
jadmanski0afbb632008-06-06 21:10:57 +0000725 # Set up the environment and then interpret the control file.
726 # Some control files will have code outside of functions,
727 # which means we need to have our state engine initialized
728 # before reading in the file.
729 global_control_vars = {'job': self}
730 exec(JOB_PREAMBLE, global_control_vars, global_control_vars)
731 execfile(self.control, global_control_vars, global_control_vars)
apw83f8d772006-04-27 14:12:56 +0000732
jadmanski0afbb632008-06-06 21:10:57 +0000733 # If we loaded in a mid-job state file, then we presumably
734 # know what steps we have yet to run.
735 if not self.state_existed:
736 if global_control_vars.has_key('step_init'):
737 self.next_step(global_control_vars['step_init'])
apw83f8d772006-04-27 14:12:56 +0000738
jadmanski0afbb632008-06-06 21:10:57 +0000739 # Iterate through the steps. If we reboot, we'll simply
740 # continue iterating on the next step.
741 while len(self.get_state('__steps')) > 0:
742 steps = self.get_state('__steps')
743 (ancestry, fn_name, args, dargs) = steps.pop(0)
744 self.set_state('__steps', steps)
apw0865f482006-03-30 18:50:19 +0000745
jadmanski0afbb632008-06-06 21:10:57 +0000746 self.next_step_index = 0
mbligh15440802008-06-06 22:48:19 +0000747 ret = self._create_frame(global_control_vars, ancestry, fn_name)
jadmanski0afbb632008-06-06 21:10:57 +0000748 local_vars, self.current_step_ancestry = ret
mbligh15440802008-06-06 22:48:19 +0000749 local_vars = self._run_step_fn(local_vars, fn_name, args, dargs)
jadmanski0afbb632008-06-06 21:10:57 +0000750 self._add_step_init(local_vars, fn_name)
apw0865f482006-03-30 18:50:19 +0000751
apw0865f482006-03-30 18:50:19 +0000752
jadmanski0afbb632008-06-06 21:10:57 +0000753 def _init_group_level(self):
754 self.group_level = self.get_state("__group_level", default=0)
mblighcaa605c2006-10-02 00:37:35 +0000755
jadmanskia9c75c42008-05-01 22:05:31 +0000756
jadmanski0afbb632008-06-06 21:10:57 +0000757 def _increment_group_level(self):
758 self.group_level += 1
759 self.set_state("__group_level", self.group_level)
jadmanskia9c75c42008-05-01 22:05:31 +0000760
jadmanskia9c75c42008-05-01 22:05:31 +0000761
jadmanski0afbb632008-06-06 21:10:57 +0000762 def _decrement_group_level(self):
763 self.group_level -= 1
764 self.set_state("__group_level", self.group_level)
jadmanskia9c75c42008-05-01 22:05:31 +0000765
jadmanskia9c75c42008-05-01 22:05:31 +0000766
jadmanski0afbb632008-06-06 21:10:57 +0000767 def record(self, status_code, subdir, operation, status = '',
768 optional_fields=None):
769 """
770 Record job-level status
jadmanskia9c75c42008-05-01 22:05:31 +0000771
jadmanski0afbb632008-06-06 21:10:57 +0000772 The intent is to make this file both machine parseable and
773 human readable. That involves a little more complexity, but
774 really isn't all that bad ;-)
apw7db8d0b2006-10-09 08:10:25 +0000775
jadmanski0afbb632008-06-06 21:10:57 +0000776 Format is <status code>\t<subdir>\t<operation>\t<status>
mbligh09f288a2007-09-18 21:34:57 +0000777
jadmanski0afbb632008-06-06 21:10:57 +0000778 status code: (GOOD|WARN|FAIL|ABORT)
779 or START
780 or END (GOOD|WARN|FAIL|ABORT)
mbligh09f288a2007-09-18 21:34:57 +0000781
jadmanski0afbb632008-06-06 21:10:57 +0000782 subdir: MUST be a relevant subdirectory in the results,
783 or None, which will be represented as '----'
mbligh09f288a2007-09-18 21:34:57 +0000784
jadmanski0afbb632008-06-06 21:10:57 +0000785 operation: description of what you ran (e.g. "dbench", or
786 "mkfs -t foobar /dev/sda9")
mbligh09f288a2007-09-18 21:34:57 +0000787
jadmanski0afbb632008-06-06 21:10:57 +0000788 status: error message or "completed sucessfully"
mbligh09f288a2007-09-18 21:34:57 +0000789
jadmanski0afbb632008-06-06 21:10:57 +0000790 ------------------------------------------------------------
mbligh09f288a2007-09-18 21:34:57 +0000791
jadmanski0afbb632008-06-06 21:10:57 +0000792 Initial tabs indicate indent levels for grouping, and is
793 governed by self.group_level
mbligh09f288a2007-09-18 21:34:57 +0000794
jadmanski0afbb632008-06-06 21:10:57 +0000795 multiline messages have secondary lines prefaced by a double
796 space (' ')
797 """
mbligh09f288a2007-09-18 21:34:57 +0000798
jadmanski0afbb632008-06-06 21:10:57 +0000799 if subdir:
800 if re.match(r'[\n\t]', subdir):
mbligh15440802008-06-06 22:48:19 +0000801 raise ValueError("Invalid character in subdir string")
jadmanski0afbb632008-06-06 21:10:57 +0000802 substr = subdir
803 else:
804 substr = '----'
mbligh09f288a2007-09-18 21:34:57 +0000805
jadmanski0afbb632008-06-06 21:10:57 +0000806 if not logging.is_valid_status(status_code):
mbligh15440802008-06-06 22:48:19 +0000807 raise ValueError("Invalid status code supplied: %s" % status_code)
jadmanski0afbb632008-06-06 21:10:57 +0000808 if not operation:
809 operation = '----'
jadmanskia9c75c42008-05-01 22:05:31 +0000810
jadmanski0afbb632008-06-06 21:10:57 +0000811 if re.match(r'[\n\t]', operation):
mbligh15440802008-06-06 22:48:19 +0000812 raise ValueError("Invalid character in operation string")
jadmanski0afbb632008-06-06 21:10:57 +0000813 operation = operation.rstrip()
jadmanskia9c75c42008-05-01 22:05:31 +0000814
jadmanski0afbb632008-06-06 21:10:57 +0000815 if not optional_fields:
816 optional_fields = {}
jadmanskia9c75c42008-05-01 22:05:31 +0000817
jadmanski0afbb632008-06-06 21:10:57 +0000818 status = status.rstrip()
819 status = re.sub(r"\t", " ", status)
820 # Ensure any continuation lines are marked so we can
821 # detect them in the status file to ensure it is parsable.
mbligh15440802008-06-06 22:48:19 +0000822 status = re.sub(r"\n", "\n" + "\t" * self.group_level + " ", status)
mbligh09f288a2007-09-18 21:34:57 +0000823
jadmanski0afbb632008-06-06 21:10:57 +0000824 # Generate timestamps for inclusion in the logs
825 epoch_time = int(time.time()) # seconds since epoch, in UTC
826 local_time = time.localtime(epoch_time)
827 optional_fields["timestamp"] = str(epoch_time)
828 optional_fields["localtime"] = time.strftime("%b %d %H:%M:%S",
829 local_time)
mbligh30270302007-11-05 20:33:52 +0000830
jadmanski0afbb632008-06-06 21:10:57 +0000831 fields = [status_code, substr, operation]
832 fields += ["%s=%s" % x for x in optional_fields.iteritems()]
833 fields.append(status)
jadmanskia9c75c42008-05-01 22:05:31 +0000834
jadmanski0afbb632008-06-06 21:10:57 +0000835 msg = '\t'.join(str(x) for x in fields)
836 msg = '\t' * self.group_level + msg
apw7db8d0b2006-10-09 08:10:25 +0000837
jadmanski0afbb632008-06-06 21:10:57 +0000838 msg_tag = ""
839 if "." in self.log_filename:
840 msg_tag = self.log_filename.split(".", 1)[1]
mblighd528d302007-12-19 16:19:05 +0000841
mbligh15440802008-06-06 22:48:19 +0000842 self.harness.test_status_detail(status_code, substr, operation, status,
843 msg_tag)
jadmanski0afbb632008-06-06 21:10:57 +0000844 self.harness.test_status(msg, msg_tag)
mblighd528d302007-12-19 16:19:05 +0000845
jadmanski0afbb632008-06-06 21:10:57 +0000846 # log to stdout (if enabled)
847 #if self.log_filename == self.DEFAULT_LOG_FILENAME:
848 print msg
mblighd528d302007-12-19 16:19:05 +0000849
jadmanski0afbb632008-06-06 21:10:57 +0000850 # log to the "root" status log
851 status_file = os.path.join(self.resultdir, self.log_filename)
852 open(status_file, "a").write(msg + "\n")
mblighd528d302007-12-19 16:19:05 +0000853
jadmanski0afbb632008-06-06 21:10:57 +0000854 # log to the subdir status log (if subdir is set)
855 if subdir:
856 dir = os.path.join(self.resultdir, subdir)
mbligh15440802008-06-06 22:48:19 +0000857 status_file = os.path.join(dir, self.DEFAULT_LOG_FILENAME)
jadmanski0afbb632008-06-06 21:10:57 +0000858 open(status_file, "a").write(msg + "\n")
apwce9abe92006-04-27 14:14:04 +0000859
860
jadmanski8415f962008-05-06 20:38:53 +0000861class disk_usage_monitor:
jadmanski0afbb632008-06-06 21:10:57 +0000862 def __init__(self, logging_func, device, max_mb_per_hour):
863 self.func = logging_func
864 self.device = device
865 self.max_mb_per_hour = max_mb_per_hour
jadmanski8415f962008-05-06 20:38:53 +0000866
867
jadmanski0afbb632008-06-06 21:10:57 +0000868 def start(self):
869 self.initial_space = autotest_utils.freespace(self.device)
870 self.start_time = time.time()
jadmanski8415f962008-05-06 20:38:53 +0000871
872
jadmanski0afbb632008-06-06 21:10:57 +0000873 def stop(self):
874 # if no maximum usage rate was set, we don't need to
875 # generate any warnings
876 if not self.max_mb_per_hour:
877 return
jadmanski8415f962008-05-06 20:38:53 +0000878
jadmanski0afbb632008-06-06 21:10:57 +0000879 final_space = autotest_utils.freespace(self.device)
880 used_space = self.initial_space - final_space
881 stop_time = time.time()
882 total_time = stop_time - self.start_time
883 # round up the time to one minute, to keep extremely short
884 # tests from generating false positives due to short, badly
885 # timed bursts of activity
886 total_time = max(total_time, 60.0)
jadmanski8415f962008-05-06 20:38:53 +0000887
jadmanski0afbb632008-06-06 21:10:57 +0000888 # determine the usage rate
889 bytes_per_sec = used_space / total_time
890 mb_per_sec = bytes_per_sec / 1024**2
891 mb_per_hour = mb_per_sec * 60 * 60
jadmanski8415f962008-05-06 20:38:53 +0000892
jadmanski0afbb632008-06-06 21:10:57 +0000893 if mb_per_hour > self.max_mb_per_hour:
mbligh15440802008-06-06 22:48:19 +0000894 msg = ("disk space on %s was consumed at a rate of %.2f MB/hour")
jadmanski0afbb632008-06-06 21:10:57 +0000895 msg %= (self.device, mb_per_hour)
896 self.func(msg)
jadmanski8415f962008-05-06 20:38:53 +0000897
898
jadmanski0afbb632008-06-06 21:10:57 +0000899 @classmethod
900 def watch(cls, *monitor_args, **monitor_dargs):
901 """ Generic decorator to wrap a function call with the
902 standard create-monitor -> start -> call -> stop idiom."""
903 def decorator(func):
904 def watched_func(*args, **dargs):
905 monitor = cls(*monitor_args, **monitor_dargs)
906 monitor.start()
907 try:
908 func(*args, **dargs)
909 finally:
910 monitor.stop()
911 return watched_func
912 return decorator
jadmanski8415f962008-05-06 20:38:53 +0000913
914
mblighcaa62c22008-04-07 21:51:17 +0000915def runjob(control, cont = False, tag = "default", harness_type = '',
jadmanski0afbb632008-06-06 21:10:57 +0000916 use_external_logging = False):
917 """The main interface to this module
mblighc86b0b42006-07-28 17:35:28 +0000918
jadmanski0afbb632008-06-06 21:10:57 +0000919 control
920 The control file to use for this job.
921 cont
922 Whether this is the continuation of a previously started job
923 """
924 control = os.path.abspath(control)
925 state = control + '.state'
apwce9abe92006-04-27 14:14:04 +0000926
jadmanski0afbb632008-06-06 21:10:57 +0000927 # instantiate the job object ready for the control file.
928 myjob = None
929 try:
930 # Check that the control file is valid
931 if not os.path.exists(control):
mbligh15440802008-06-06 22:48:19 +0000932 raise error.JobError(control + ": control file not found")
apwce9abe92006-04-27 14:14:04 +0000933
jadmanski0afbb632008-06-06 21:10:57 +0000934 # When continuing, the job is complete when there is no
935 # state file, ensure we don't try and continue.
936 if cont and not os.path.exists(state):
937 raise error.JobComplete("all done")
938 if cont == False and os.path.exists(state):
939 os.unlink(state)
apwce9abe92006-04-27 14:14:04 +0000940
mbligh15440802008-06-06 22:48:19 +0000941 myjob = job(control, tag, cont, harness_type, use_external_logging)
apwce9abe92006-04-27 14:14:04 +0000942
jadmanski0afbb632008-06-06 21:10:57 +0000943 # Load in the users control file, may do any one of:
944 # 1) execute in toto
945 # 2) define steps, and select the first via next_step()
946 myjob.step_engine()
apwce9abe92006-04-27 14:14:04 +0000947
jadmanski0afbb632008-06-06 21:10:57 +0000948 except error.JobContinue:
949 sys.exit(5)
apwce9abe92006-04-27 14:14:04 +0000950
jadmanski0afbb632008-06-06 21:10:57 +0000951 except error.JobComplete:
952 sys.exit(1)
apwb832e1b2007-11-24 20:24:38 +0000953
jadmanski0afbb632008-06-06 21:10:57 +0000954 except error.JobError, instance:
955 print "JOB ERROR: " + instance.args[0]
956 if myjob:
957 command = None
958 if len(instance.args) > 1:
959 command = instance.args[1]
960 myjob.record('ABORT', None, command, instance.args[0])
961 myjob._decrement_group_level()
962 myjob.record('END ABORT', None, None)
963 assert(myjob.group_level == 0)
964 myjob.complete(1)
965 else:
966 sys.exit(1)
apwce9abe92006-04-27 14:14:04 +0000967
jadmanski0afbb632008-06-06 21:10:57 +0000968 except Exception, e:
969 msg = str(e) + '\n' + traceback.format_exc()
970 print "JOB ERROR: " + msg
971 if myjob:
972 myjob.record('ABORT', None, None, msg)
973 myjob._decrement_group_level()
974 myjob.record('END ABORT', None, None)
975 assert(myjob.group_level == 0)
976 myjob.complete(1)
977 else:
978 sys.exit(1)
mbligh892d37f2007-03-01 17:03:25 +0000979
jadmanski0afbb632008-06-06 21:10:57 +0000980 # If we get here, then we assume the job is complete and good.
981 myjob._decrement_group_level()
982 myjob.record('END GOOD', None, None)
983 assert(myjob.group_level == 0)
mbligh0144e5a2008-03-07 18:17:53 +0000984
jadmanski0afbb632008-06-06 21:10:57 +0000985 myjob.complete(0)
mblighcaa62c22008-04-07 21:51:17 +0000986
987
988# site_job.py may be non-existant or empty, make sure that an appropriate
989# site_job class is created nevertheless
990try:
jadmanski0afbb632008-06-06 21:10:57 +0000991 from site_job import site_job
mblighcaa62c22008-04-07 21:51:17 +0000992except ImportError:
jadmanski0afbb632008-06-06 21:10:57 +0000993 class site_job(base_job):
994 pass
mblighcaa62c22008-04-07 21:51:17 +0000995
996class job(site_job):
jadmanski0afbb632008-06-06 21:10:57 +0000997 pass