blob: 19521e09ba987bf83cd5f82d472729be0f780a33 [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):
mbligh12a04cb2008-04-25 16:07:20 +000023 pass
24
25
mblighcaa62c22008-04-07 21:51:17 +000026class base_job:
mblighc86b0b42006-07-28 17:35:28 +000027 """The actual job against which we do everything.
28
29 Properties:
mbligh72b88fc2006-12-16 18:41:35 +000030 autodir
mblighc86b0b42006-07-28 17:35:28 +000031 The top level autotest directory (/usr/local/autotest).
32 Comes from os.environ['AUTODIR'].
mbligh72b88fc2006-12-16 18:41:35 +000033 bindir
mblighc86b0b42006-07-28 17:35:28 +000034 <autodir>/bin/
mblighd5a38832008-01-25 18:15:39 +000035 libdir
36 <autodir>/lib/
mbligh72b88fc2006-12-16 18:41:35 +000037 testdir
mblighc86b0b42006-07-28 17:35:28 +000038 <autodir>/tests/
mbligh84bafdb2008-01-26 19:43:34 +000039 site_testdir
40 <autodir>/site_tests/
mblighc86b0b42006-07-28 17:35:28 +000041 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
apw504a7dd2006-10-12 17:18:37 +000053 harness
54 the server harness object for this job
apw059e1b12006-10-12 17:18:26 +000055 config
56 the job configuration for this job
mblighc86b0b42006-07-28 17:35:28 +000057 """
58
mblighd528d302007-12-19 16:19:05 +000059 DEFAULT_LOG_FILENAME = "status"
60
mblighcaa62c22008-04-07 21:51:17 +000061 def __init__(self, control, jobtag, cont, harness_type=None,
62 use_external_logging = False):
mblighc86b0b42006-07-28 17:35:28 +000063 """
64 control
65 The control file (pathname of)
66 jobtag
67 The job tag string (eg "default")
apw96da1a42006-11-02 00:23:18 +000068 cont
69 If this is the continuation of this job
apwe68a7132006-12-01 11:21:37 +000070 harness_type
71 An alternative server harness
mblighc86b0b42006-07-28 17:35:28 +000072 """
mblighf4c35322006-03-13 01:01:10 +000073 self.autodir = os.environ['AUTODIR']
apw870988b2007-09-25 16:50:53 +000074 self.bindir = os.path.join(self.autodir, 'bin')
mblighd5a38832008-01-25 18:15:39 +000075 self.libdir = os.path.join(self.autodir, 'lib')
apw870988b2007-09-25 16:50:53 +000076 self.testdir = os.path.join(self.autodir, 'tests')
mbligh84bafdb2008-01-26 19:43:34 +000077 self.site_testdir = os.path.join(self.autodir, 'site_tests')
apw870988b2007-09-25 16:50:53 +000078 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)
mbligh0fb83972008-01-10 16:30:02 +000081 self.sysinfodir = os.path.join(self.resultdir, 'sysinfo')
mbligh8d83cdc2007-12-03 18:09:18 +000082 self.control = os.path.abspath(control)
mbligh366ff1b2008-04-25 16:07:56 +000083 self.state_file = self.control + '.state'
mblighb274ef52008-06-02 19:40:01 +000084 self.current_step_ancestry = []
mbligh8f4d0432008-06-02 19:42:50 +000085 self.next_step_index = 0
jadmanskia9c75c42008-05-01 22:05:31 +000086 self.__load_state()
mbligha2508052006-05-28 21:29:53 +000087
apw96da1a42006-11-02 00:23:18 +000088 if not cont:
mblighc1cbc992008-05-27 20:01:45 +000089 """
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
apw870988b2007-09-25 16:50:53 +000099 results = os.path.join(self.autodir, 'results')
100 if not os.path.exists(results):
101 os.mkdir(results)
mblighfbfb77d2007-02-15 18:54:03 +0000102
apwf3d28622007-09-25 16:49:17 +0000103 download = os.path.join(self.testdir, 'download')
mblighc1cbc992008-05-27 20:01:45 +0000104 if not os.path.exists(download):
105 os.mkdir(download)
106
apw96da1a42006-11-02 00:23:18 +0000107 if os.path.exists(self.resultdir):
mblighe829ba52008-06-03 15:04:08 +0000108 utils.system('rm -rf '
mbligh302482e2008-05-01 20:06:16 +0000109 + self.resultdir)
apw96da1a42006-11-02 00:23:18 +0000110 os.mkdir(self.resultdir)
mbligh0fb83972008-01-10 16:30:02 +0000111 os.mkdir(self.sysinfodir)
apw96da1a42006-11-02 00:23:18 +0000112
apw870988b2007-09-25 16:50:53 +0000113 os.mkdir(os.path.join(self.resultdir, 'debug'))
114 os.mkdir(os.path.join(self.resultdir, 'analysis'))
apw870988b2007-09-25 16:50:53 +0000115
mbligh8d83cdc2007-12-03 18:09:18 +0000116 shutil.copyfile(self.control,
117 os.path.join(self.resultdir, 'control'))
mblighf4ca14f2008-03-03 16:03:05 +0000118
mbligh4b089662006-06-14 22:34:58 +0000119
apwecf41b72006-03-31 14:00:55 +0000120 self.control = control
mbligh27113602007-10-31 21:07:51 +0000121 self.jobtag = jobtag
mblighd528d302007-12-19 16:19:05 +0000122 self.log_filename = self.DEFAULT_LOG_FILENAME
mbligh68119582008-01-25 18:16:41 +0000123 self.container = None
mblighf4c35322006-03-13 01:01:10 +0000124
mbligh56f1fbb2006-10-01 15:10:56 +0000125 self.stdout = fd_stack.fd_stack(1, sys.stdout)
126 self.stderr = fd_stack.fd_stack(2, sys.stderr)
jadmanskia9c75c42008-05-01 22:05:31 +0000127
128 self._init_group_level()
mblighf4c35322006-03-13 01:01:10 +0000129
apw059e1b12006-10-12 17:18:26 +0000130 self.config = config.config(self)
131
apwd27e55f2006-12-01 11:22:08 +0000132 self.harness = harness.select(harness_type, self)
133
mbligha35553b2006-04-23 15:52:25 +0000134 self.profilers = profilers.profilers(self)
mbligh72905562006-05-25 01:30:49 +0000135
mblighcaa605c2006-10-02 00:37:35 +0000136 try:
apw90154af2006-12-01 11:23:36 +0000137 tool = self.config_get('boottool.executable')
138 self.bootloader = boottool.boottool(tool)
mblighcaa605c2006-10-02 00:37:35 +0000139 except:
140 pass
141
mbligh0fb83972008-01-10 16:30:02 +0000142 sysinfo.log_per_reboot_data(self.sysinfodir)
mbligh3a6d6ca2006-04-23 15:50:24 +0000143
mbligh30270302007-11-05 20:33:52 +0000144 if not cont:
mblighc3430162007-11-14 23:57:19 +0000145 self.record('START', None, None)
jadmanskia9c75c42008-05-01 22:05:31 +0000146 self._increment_group_level()
apw357f50f2006-12-01 11:22:39 +0000147
apwf91efaf2007-11-24 17:32:13 +0000148 self.harness.run_start()
mblighcaa62c22008-04-07 21:51:17 +0000149
150 if use_external_logging:
151 self.enable_external_logging()
apwf91efaf2007-11-24 17:32:13 +0000152
jadmanski8415f962008-05-06 20:38:53 +0000153 # load the max disk usage rate - default to no monitoring
154 self.max_disk_usage_rate = self.get_state('__monitor_disk',
155 default=0.0)
156
157
158 def monitor_disk_usage(self, max_rate):
159 """\
160 Signal that the job should monitor disk space usage on /
161 and generate a warning if a test uses up disk space at a
162 rate exceeding 'max_rate'.
163
164 Parameters:
165 max_rate - the maximium allowed rate of disk consumption
166 during a test, in MB/hour, or 0 to indicate
167 no limit.
168 """
169 self.set_state('__monitor_disk', max_rate)
170 self.max_disk_usage_rate = max_rate
171
mbligh0692e472007-08-30 16:07:53 +0000172
173 def relative_path(self, path):
174 """\
175 Return a patch relative to the job results directory
176 """
mbligh1c250ca2007-08-30 16:31:38 +0000177 head = len(self.resultdir) + 1 # remove the / inbetween
178 return path[head:]
mbligh0692e472007-08-30 16:07:53 +0000179
180
mbligh362ab3d2007-08-30 11:24:04 +0000181 def control_get(self):
182 return self.control
183
mblighcaa605c2006-10-02 00:37:35 +0000184
mbligh8d83cdc2007-12-03 18:09:18 +0000185 def control_set(self, control):
186 self.control = os.path.abspath(control)
187
188
apwde1503a2006-10-10 08:34:21 +0000189 def harness_select(self, which):
190 self.harness = harness.select(which, self)
191
192
apw059e1b12006-10-12 17:18:26 +0000193 def config_set(self, name, value):
194 self.config.set(name, value)
195
196
197 def config_get(self, name):
198 return self.config.get(name)
199
mbligh8baa2ea2006-12-17 23:01:24 +0000200 def setup_dirs(self, results_dir, tmp_dir):
mbligh1e8858e2006-11-24 22:18:35 +0000201 if not tmp_dir:
apw870988b2007-09-25 16:50:53 +0000202 tmp_dir = os.path.join(self.tmpdir, 'build')
mbligh1e8858e2006-11-24 22:18:35 +0000203 if not os.path.exists(tmp_dir):
204 os.mkdir(tmp_dir)
205 if not os.path.isdir(tmp_dir):
mbligh642b03e2008-01-14 16:53:15 +0000206 e_msg = "Temp dir (%s) is not a dir - args backwards?" % self.tmpdir
207 raise ValueError(e_msg)
mbligh1e8858e2006-11-24 22:18:35 +0000208
209 # We label the first build "build" and then subsequent ones
210 # as "build.2", "build.3", etc. Whilst this is a little bit
211 # inconsistent, 99.9% of jobs will only have one build
212 # (that's not done as kernbench, sparse, or buildtest),
213 # so it works out much cleaner. One of life's comprimises.
214 if not results_dir:
215 results_dir = os.path.join(self.resultdir, 'build')
216 i = 2
217 while os.path.exists(results_dir):
218 results_dir = os.path.join(self.resultdir, 'build.%d' % i)
mblighd9223fc2006-11-26 17:19:54 +0000219 i += 1
mbligh1e8858e2006-11-24 22:18:35 +0000220 if not os.path.exists(results_dir):
221 os.mkdir(results_dir)
mbligh72b88fc2006-12-16 18:41:35 +0000222
mbligh8baa2ea2006-12-17 23:01:24 +0000223 return (results_dir, tmp_dir)
224
225
226 def xen(self, base_tree, results_dir = '', tmp_dir = '', leave = False, \
227 kjob = None ):
228 """Summon a xen object"""
229 (results_dir, tmp_dir) = self.setup_dirs(results_dir, tmp_dir)
230 build_dir = 'xen'
231 return xen.xen(self, base_tree, results_dir, tmp_dir, build_dir, leave, kjob)
232
233
234 def kernel(self, base_tree, results_dir = '', tmp_dir = '', leave = False):
235 """Summon a kernel object"""
mbligh669caa12007-11-05 18:32:13 +0000236 (results_dir, tmp_dir) = self.setup_dirs(results_dir, tmp_dir)
mbligh8baa2ea2006-12-17 23:01:24 +0000237 build_dir = 'linux'
mbligh6ee7ee02007-11-13 23:49:05 +0000238 return kernel.auto_kernel(self, base_tree, results_dir,
239 tmp_dir, build_dir, leave)
mblighf4c35322006-03-13 01:01:10 +0000240
mblighcaa605c2006-10-02 00:37:35 +0000241
mbligh6b504ff2007-12-12 21:03:49 +0000242 def barrier(self, *args, **kwds):
mblighfadca202006-09-23 04:40:01 +0000243 """Create a barrier object"""
mbligh6b504ff2007-12-12 21:03:49 +0000244 return barrier.barrier(*args, **kwds)
mblighfadca202006-09-23 04:40:01 +0000245
mblighcaa605c2006-10-02 00:37:35 +0000246
mbligh4b089662006-06-14 22:34:58 +0000247 def setup_dep(self, deps):
mblighc86b0b42006-07-28 17:35:28 +0000248 """Set up the dependencies for this test.
249
250 deps is a list of libraries required for this test.
251 """
mbligh4b089662006-06-14 22:34:58 +0000252 for dep in deps:
253 try:
apw870988b2007-09-25 16:50:53 +0000254 os.chdir(os.path.join(self.autodir, 'deps', dep))
mblighe829ba52008-06-03 15:04:08 +0000255 utils.system('./' + dep + '.py')
mbligh4b089662006-06-14 22:34:58 +0000256 except:
mbligh302482e2008-05-01 20:06:16 +0000257 err = "setting up dependency " + dep + "\n"
258 raise error.UnhandledError(err)
mbligh4b089662006-06-14 22:34:58 +0000259
260
mbligh72b88fc2006-12-16 18:41:35 +0000261 def __runtest(self, url, tag, args, dargs):
262 try:
mbligh53c41502007-10-23 20:45:04 +0000263 l = lambda : test.runtest(self, url, tag, args, dargs)
mbligh302482e2008-05-01 20:06:16 +0000264 pid = parallel.fork_start(self.resultdir, l)
265 parallel.fork_waitfor(self.resultdir, pid)
266 except error.AutotestError:
mbligh72b88fc2006-12-16 18:41:35 +0000267 raise
jadmanskicf8c4d62008-05-27 22:09:14 +0000268 except Exception, e:
269 msg = "Unhandled %s error occured during test\n"
270 msg %= str(e.__class__.__name__)
271 raise error.UnhandledError(msg)
apwf1a81162006-04-25 10:10:29 +0000272
mblighcaa605c2006-10-02 00:37:35 +0000273
mblighd016ecc2006-11-25 21:41:07 +0000274 def run_test(self, url, *args, **dargs):
mblighc86b0b42006-07-28 17:35:28 +0000275 """Summon a test object and run it.
276
277 tag
278 tag to add to testname
mbligh12a7df72006-10-06 03:54:33 +0000279 url
280 url of the test to run
mblighc86b0b42006-07-28 17:35:28 +0000281 """
mbligh12a7df72006-10-06 03:54:33 +0000282
mblighd016ecc2006-11-25 21:41:07 +0000283 if not url:
mbligh302482e2008-05-01 20:06:16 +0000284 raise TypeError("Test name is invalid. "
285 "Switched arguments?")
mbligh09f288a2007-09-18 21:34:57 +0000286 (group, testname) = test.testname(url)
mbligh7dd510c2007-11-13 17:11:22 +0000287 tag = dargs.pop('tag', None)
mbligh65938a22007-12-10 16:58:52 +0000288 container = dargs.pop('container', None)
mbligh09f288a2007-09-18 21:34:57 +0000289 subdir = testname
mbligh7dd510c2007-11-13 17:11:22 +0000290 if tag:
291 subdir += '.' + tag
292
mbligh65938a22007-12-10 16:58:52 +0000293 if container:
mbligh68119582008-01-25 18:16:41 +0000294 cname = container.get('name', None)
295 if not cname: # get old name
296 cname = container.get('container_name', None)
297 mbytes = container.get('mbytes', None)
298 if not mbytes: # get old name
299 mbytes = container.get('mem', None)
300 cpus = container.get('cpus', None)
301 if not cpus: # get old name
302 cpus = container.get('cpu', None)
jadmanski87cbc7f2008-05-13 18:17:10 +0000303 root = container.get('root', None)
mbligh68119582008-01-25 18:16:41 +0000304 self.new_container(mbytes=mbytes, cpus=cpus,
305 root=root, name=cname)
mbligh65938a22007-12-10 16:58:52 +0000306 # We are running in a container now...
307
jadmanski8415f962008-05-06 20:38:53 +0000308 def log_warning(reason):
309 self.record("WARN", subdir, testname, reason)
310 @disk_usage_monitor.watch(log_warning, "/",
311 self.max_disk_usage_rate)
mbligh7dd510c2007-11-13 17:11:22 +0000312 def group_func():
apwf1a81162006-04-25 10:10:29 +0000313 try:
mblighd016ecc2006-11-25 21:41:07 +0000314 self.__runtest(url, tag, args, dargs)
mbligh302482e2008-05-01 20:06:16 +0000315 except error.TestNAError, detail:
316 self.record('TEST_NA', subdir, testname,
317 str(detail))
318 raise
apwf1a81162006-04-25 10:10:29 +0000319 except Exception, detail:
mbligh7dd510c2007-11-13 17:11:22 +0000320 self.record('FAIL', subdir, testname,
321 str(detail))
apwf1a81162006-04-25 10:10:29 +0000322 raise
323 else:
mbligh7dd510c2007-11-13 17:11:22 +0000324 self.record('GOOD', subdir, testname,
325 'completed successfully')
jadmanski8415f962008-05-06 20:38:53 +0000326
mblighcfc6dd32007-11-20 00:44:35 +0000327 result, exc_info = self.__rungroup(subdir, group_func)
mbligh68119582008-01-25 18:16:41 +0000328 if container:
329 self.release_container()
mbligh302482e2008-05-01 20:06:16 +0000330 if exc_info and isinstance(exc_info[1], error.TestError):
mbligh7dd510c2007-11-13 17:11:22 +0000331 return False
332 elif exc_info:
mbligh71ea2492008-01-15 20:35:52 +0000333 raise exc_info[0], exc_info[1], exc_info[2]
apwf1a81162006-04-25 10:10:29 +0000334 else:
mbligh7dd510c2007-11-13 17:11:22 +0000335 return True
336
337
338 def __rungroup(self, name, function, *args, **dargs):
339 """\
340 name:
341 name of the group
342 function:
343 subroutine to run
344 *args:
345 arguments for the function
346
347 Returns a 2-tuple (result, exc_info) where result
348 is the return value of function, and exc_info is
349 the sys.exc_info() of the exception thrown by the
350 function (which may be None).
351 """
352
353 result, exc_info = None, None
354 try:
355 self.record('START', None, name)
jadmanskia9c75c42008-05-01 22:05:31 +0000356 self._increment_group_level()
mbligh7dd510c2007-11-13 17:11:22 +0000357 result = function(*args, **dargs)
jadmanskia9c75c42008-05-01 22:05:31 +0000358 self._decrement_group_level()
mbligh7dd510c2007-11-13 17:11:22 +0000359 self.record('END GOOD', None, name)
mbligh302482e2008-05-01 20:06:16 +0000360 except error.TestNAError, e:
jadmanskia9c75c42008-05-01 22:05:31 +0000361 self._decrement_group_level()
mbligh302482e2008-05-01 20:06:16 +0000362 self.record('END TEST_NA', None, name, str(e))
mbligh7dd510c2007-11-13 17:11:22 +0000363 except Exception, e:
364 exc_info = sys.exc_info()
jadmanskia9c75c42008-05-01 22:05:31 +0000365 self._decrement_group_level()
mbligh302482e2008-05-01 20:06:16 +0000366 err_msg = str(e) + '\n' + traceback.format_exc()
mbligh51144e02007-11-20 20:38:18 +0000367 self.record('END FAIL', None, name, err_msg)
mbligh7dd510c2007-11-13 17:11:22 +0000368
369 return result, exc_info
apw0865f482006-03-30 18:50:19 +0000370
mblighd7fb4a62006-10-01 00:57:53 +0000371
apw1da244b2007-09-27 17:18:01 +0000372 def run_group(self, function, *args, **dargs):
mbligh88ab90f2007-08-29 15:52:49 +0000373 """\
374 function:
375 subroutine to run
376 *args:
377 arguments for the function
378 """
379
mbligh7dd510c2007-11-13 17:11:22 +0000380 # Allow the tag for the group to be specified
mbligh88ab90f2007-08-29 15:52:49 +0000381 name = function.__name__
mbligh7dd510c2007-11-13 17:11:22 +0000382 tag = dargs.pop('tag', None)
383 if tag:
384 name = tag
apw1da244b2007-09-27 17:18:01 +0000385
mbligh7dd510c2007-11-13 17:11:22 +0000386 result, exc_info = self.__rungroup(name, function,
387 *args, **dargs)
apw1da244b2007-09-27 17:18:01 +0000388
mbligh7dd510c2007-11-13 17:11:22 +0000389 # if there was a non-TestError exception, raise it
mbligh302482e2008-05-01 20:06:16 +0000390 if exc_info and not isinstance(exc_info[1], error.TestError):
mbligh7dd510c2007-11-13 17:11:22 +0000391 err = ''.join(traceback.format_exception(*exc_info))
mbligh302482e2008-05-01 20:06:16 +0000392 raise error.TestError(name + ' failed\n' + err)
mbligh88ab90f2007-08-29 15:52:49 +0000393
mbligh7dd510c2007-11-13 17:11:22 +0000394 # pass back the actual return value from the function
apw08403ca2007-09-27 17:17:22 +0000395 return result
396
mbligh88ab90f2007-08-29 15:52:49 +0000397
jadmanski87cbc7f2008-05-13 18:17:10 +0000398 def new_container(self, mbytes=None, cpus=None, root=None, name=None):
mbligh8ea61e22008-05-09 18:09:37 +0000399 if not autotest_utils.grep('cpuset', '/proc/filesystems'):
mbligh68119582008-01-25 18:16:41 +0000400 print "Containers not enabled by latest reboot"
401 return # containers weren't enabled in this kernel boot
402 pid = os.getpid()
mbligh68119582008-01-25 18:16:41 +0000403 if not name:
404 name = 'test%d' % pid # make arbitrary unique name
405 self.container = cpuset.cpuset(name, job_size=mbytes,
mbligh337bb762008-04-16 21:23:10 +0000406 job_pid=pid, cpus=cpus, root=root)
mbligh68119582008-01-25 18:16:41 +0000407 # This job's python shell is now running in the new container
408 # and all forked test processes will inherit that container
409
410
411 def release_container(self):
412 if self.container:
mbligh337bb762008-04-16 21:23:10 +0000413 self.container.release()
mbligh68119582008-01-25 18:16:41 +0000414 self.container = None
415
416
417 def cpu_count(self):
418 if self.container:
419 return len(self.container.cpus)
jadmanskia9c75c42008-05-01 22:05:31 +0000420 return autotest_utils.count_cpus() # use total system count
mbligh68119582008-01-25 18:16:41 +0000421
422
apwce73d892007-09-25 16:53:05 +0000423 # Check the passed kernel identifier against the command line
424 # and the running kernel, abort the job on missmatch.
mbligh38a4a112008-03-19 13:11:34 +0000425 def kernel_check_ident(self, expected_when, expected_id, subdir,
jadmanskia9c75c42008-05-01 22:05:31 +0000426 type = 'src', patches=[]):
mbligh38a4a112008-03-19 13:11:34 +0000427 print (("POST BOOT: checking booted kernel " +
428 "mark=%d identity='%s' type='%s'") %
429 (expected_when, expected_id, type))
apwce73d892007-09-25 16:53:05 +0000430
jadmanskia9c75c42008-05-01 22:05:31 +0000431 running_id = autotest_utils.running_os_ident()
apwce73d892007-09-25 16:53:05 +0000432
mblighe829ba52008-06-03 15:04:08 +0000433 cmdline = utils.read_one_line("/proc/cmdline")
apwce73d892007-09-25 16:53:05 +0000434
435 find_sum = re.compile(r'.*IDENT=(\d+)')
436 m = find_sum.match(cmdline)
437 cmdline_when = -1
438 if m:
439 cmdline_when = int(m.groups()[0])
440
441 # We have all the facts, see if they indicate we
442 # booted the requested kernel or not.
443 bad = False
mblighda0311e2007-10-25 16:03:33 +0000444 if (type == 'src' and expected_id != running_id or
jadmanskia9c75c42008-05-01 22:05:31 +0000445 type == 'rpm' and
446 not running_id.startswith(expected_id + '::')):
apwce73d892007-09-25 16:53:05 +0000447 print "check_kernel_ident: kernel identifier mismatch"
448 bad = True
449 if expected_when != cmdline_when:
450 print "check_kernel_ident: kernel command line mismatch"
451 bad = True
452
453 if bad:
454 print " Expected Ident: " + expected_id
455 print " Running Ident: " + running_id
456 print " Expected Mark: %d" % (expected_when)
457 print "Command Line Mark: %d" % (cmdline_when)
458 print " Command Line: " + cmdline
459
mbligh302482e2008-05-01 20:06:16 +0000460 raise error.JobError("boot failure", "reboot.verify")
apwce73d892007-09-25 16:53:05 +0000461
jadmanskia9c75c42008-05-01 22:05:31 +0000462 kernel_info = {'kernel': expected_id}
463 for i, patch in enumerate(patches):
464 kernel_info["patch%d" % i] = patch
mblighb7fd2702008-03-25 14:57:08 +0000465 self.record('GOOD', subdir, 'reboot.verify', expected_id)
jadmanskia9c75c42008-05-01 22:05:31 +0000466 self._decrement_group_level()
467 self.record('END GOOD', subdir, 'reboot',
468 optional_fields=kernel_info)
apwce73d892007-09-25 16:53:05 +0000469
470
mblighc2359852007-08-28 18:11:48 +0000471 def filesystem(self, device, mountpoint = None, loop_size = 0):
mblighd7fb4a62006-10-01 00:57:53 +0000472 if not mountpoint:
473 mountpoint = self.tmpdir
mblighc2359852007-08-28 18:11:48 +0000474 return filesystem.filesystem(self, device, mountpoint,loop_size)
mblighd7fb4a62006-10-01 00:57:53 +0000475
mblighcaa62c22008-04-07 21:51:17 +0000476
477 def enable_external_logging(self):
478 pass
479
480
481 def disable_external_logging(self):
482 pass
483
484
485 def reboot_setup(self):
486 pass
487
mblighcaa605c2006-10-02 00:37:35 +0000488
489 def reboot(self, tag='autotest'):
mblighcaa62c22008-04-07 21:51:17 +0000490 self.reboot_setup()
jadmanskia9c75c42008-05-01 22:05:31 +0000491 self.record('START', None, 'reboot')
492 self._increment_group_level()
mbligh30270302007-11-05 20:33:52 +0000493 self.record('GOOD', None, 'reboot.start')
apwde1503a2006-10-10 08:34:21 +0000494 self.harness.run_reboot()
apw11985b72007-10-04 15:44:47 +0000495 default = self.config_get('boot.set_default')
496 if default:
497 self.bootloader.set_default(tag)
498 else:
499 self.bootloader.boot_once(tag)
mbligh302482e2008-05-01 20:06:16 +0000500 cmd = "(sleep 5; reboot) </dev/null >/dev/null 2>&1 &"
mblighe829ba52008-06-03 15:04:08 +0000501 utils.system(cmd)
apw0778a2f2006-10-06 03:11:40 +0000502 self.quit()
mblighcaa605c2006-10-02 00:37:35 +0000503
504
apw0865f482006-03-30 18:50:19 +0000505 def noop(self, text):
506 print "job: noop: " + text
507
mblighcaa605c2006-10-02 00:37:35 +0000508
mblighc86b0b42006-07-28 17:35:28 +0000509 def parallel(self, *tasklist):
510 """Run tasks in parallel"""
apw8fef4ac2006-10-10 22:53:37 +0000511
512 pids = []
mblighd528d302007-12-19 16:19:05 +0000513 old_log_filename = self.log_filename
514 for i, task in enumerate(tasklist):
515 self.log_filename = old_log_filename + (".%d" % i)
516 task_func = lambda: task[0](*task[1:])
mbligh302482e2008-05-01 20:06:16 +0000517 pids.append(parallel.fork_start(self.resultdir,
518 task_func))
mblighd528d302007-12-19 16:19:05 +0000519
520 old_log_path = os.path.join(self.resultdir, old_log_filename)
521 old_log = open(old_log_path, "a")
mblighd509b712008-01-14 17:41:25 +0000522 exceptions = []
mblighd528d302007-12-19 16:19:05 +0000523 for i, pid in enumerate(pids):
524 # wait for the task to finish
mblighd509b712008-01-14 17:41:25 +0000525 try:
mbligh302482e2008-05-01 20:06:16 +0000526 parallel.fork_waitfor(self.resultdir, pid)
mblighd509b712008-01-14 17:41:25 +0000527 except Exception, e:
528 exceptions.append(e)
mblighd528d302007-12-19 16:19:05 +0000529 # copy the logs from the subtask into the main log
530 new_log_path = old_log_path + (".%d" % i)
531 if os.path.exists(new_log_path):
532 new_log = open(new_log_path)
533 old_log.write(new_log.read())
534 new_log.close()
535 old_log.flush()
536 os.remove(new_log_path)
537 old_log.close()
538
539 self.log_filename = old_log_filename
apw0865f482006-03-30 18:50:19 +0000540
mblighd509b712008-01-14 17:41:25 +0000541 # handle any exceptions raised by the parallel tasks
542 if exceptions:
543 msg = "%d task(s) failed" % len(exceptions)
mbligh302482e2008-05-01 20:06:16 +0000544 raise error.JobError(msg, str(exceptions), exceptions)
mblighd509b712008-01-14 17:41:25 +0000545
mblighcaa605c2006-10-02 00:37:35 +0000546
apw0865f482006-03-30 18:50:19 +0000547 def quit(self):
mblighc86b0b42006-07-28 17:35:28 +0000548 # XXX: should have a better name.
apwde1503a2006-10-10 08:34:21 +0000549 self.harness.run_pause()
mbligh302482e2008-05-01 20:06:16 +0000550 raise error.JobContinue("more to come")
apw0865f482006-03-30 18:50:19 +0000551
mblighcaa605c2006-10-02 00:37:35 +0000552
apw0865f482006-03-30 18:50:19 +0000553 def complete(self, status):
mblighc86b0b42006-07-28 17:35:28 +0000554 """Clean up and exit"""
apw0865f482006-03-30 18:50:19 +0000555 # We are about to exit 'complete' so clean up the control file.
556 try:
mbligh366ff1b2008-04-25 16:07:56 +0000557 os.unlink(self.state_file)
apw0865f482006-03-30 18:50:19 +0000558 except:
559 pass
mblighc0b10d32008-03-03 16:03:28 +0000560
mbligh61a6c1a2006-12-25 01:26:38 +0000561 self.harness.run_complete()
mblighcaa62c22008-04-07 21:51:17 +0000562 self.disable_external_logging()
apw1b021902006-04-03 17:02:56 +0000563 sys.exit(status)
apw0865f482006-03-30 18:50:19 +0000564
mblighcaa605c2006-10-02 00:37:35 +0000565
mbligh366ff1b2008-04-25 16:07:56 +0000566 def set_state(self, var, val):
567 # Deep copies make sure that the state can't be altered
568 # without it being re-written. Perf wise, deep copies
569 # are overshadowed by pickling/loading.
570 self.state[var] = copy.deepcopy(val)
571 pickle.dump(self.state, open(self.state_file, 'w'))
572
573
574 def __load_state(self):
jadmanskia9c75c42008-05-01 22:05:31 +0000575 assert not hasattr(self, "state")
mbligh366ff1b2008-04-25 16:07:56 +0000576 try:
577 self.state = pickle.load(open(self.state_file, 'r'))
jadmanskia9c75c42008-05-01 22:05:31 +0000578 self.state_existed = True
mbligh366ff1b2008-04-25 16:07:56 +0000579 except Exception:
580 print "Initializing the state engine."
581 self.state = {}
mblighf1ae0a42008-04-25 16:09:20 +0000582 self.set_state('__steps', []) # writes pickle file
jadmanskia9c75c42008-05-01 22:05:31 +0000583 self.state_existed = False
mbligh366ff1b2008-04-25 16:07:56 +0000584
585
586 def get_state(self, var, default=None):
587 if var in self.state or default == None:
588 val = self.state[var]
589 else:
590 val = default
591 return copy.deepcopy(val)
592
593
mbligh12a04cb2008-04-25 16:07:20 +0000594 def __create_step_tuple(self, fn, args, dargs):
595 # Legacy code passes in an array where the first arg is
596 # the function or its name.
597 if isinstance(fn, list):
598 assert(len(args) == 0)
599 assert(len(dargs) == 0)
600 args = fn[1:]
601 fn = fn[0]
602 # Pickling actual functions is harry, thus we have to call
603 # them by name. Unfortunately, this means only functions
604 # defined globally can be used as a next step.
mblighb274ef52008-06-02 19:40:01 +0000605 if callable(fn):
mbligh12a04cb2008-04-25 16:07:20 +0000606 fn = fn.__name__
607 if not isinstance(fn, types.StringTypes):
608 raise StepError("Next steps must be functions or "
609 "strings containing the function name")
mblighb274ef52008-06-02 19:40:01 +0000610 ancestry = copy.copy(self.current_step_ancestry)
611 return (ancestry, fn, args, dargs)
mbligh12a04cb2008-04-25 16:07:20 +0000612
613
mbligh8f4d0432008-06-02 19:42:50 +0000614 def next_step_append(self, fn, *args, **dargs):
615 """Define the next step and place it at the end"""
mblighf1ae0a42008-04-25 16:09:20 +0000616 steps = self.get_state('__steps')
mbligh366ff1b2008-04-25 16:07:56 +0000617 steps.append(self.__create_step_tuple(fn, args, dargs))
mblighf1ae0a42008-04-25 16:09:20 +0000618 self.set_state('__steps', steps)
apw0865f482006-03-30 18:50:19 +0000619
mblighcaa605c2006-10-02 00:37:35 +0000620
mbligh8f4d0432008-06-02 19:42:50 +0000621 def next_step(self, fn, *args, **dargs):
622 """Create a new step and place it after any steps added
623 while running the current step but before any steps added in
624 previous steps"""
625 steps = self.get_state('__steps')
626 steps.insert(self.next_step_index,
627 self.__create_step_tuple(fn, args, dargs))
628 self.next_step_index += 1
629 self.set_state('__steps', steps)
630
631
mbligh12a04cb2008-04-25 16:07:20 +0000632 def next_step_prepend(self, fn, *args, **dargs):
mbligh237bed32007-09-05 13:05:57 +0000633 """Insert a new step, executing first"""
mblighf1ae0a42008-04-25 16:09:20 +0000634 steps = self.get_state('__steps')
mbligh366ff1b2008-04-25 16:07:56 +0000635 steps.insert(0, self.__create_step_tuple(fn, args, dargs))
mbligh8f4d0432008-06-02 19:42:50 +0000636 self.next_step_index += 1
mblighf1ae0a42008-04-25 16:09:20 +0000637 self.set_state('__steps', steps)
mbligh237bed32007-09-05 13:05:57 +0000638
639
mblighb274ef52008-06-02 19:40:01 +0000640 def _run_step_fn(self, local_vars, fn, args, dargs):
641 """Run a (step) function within the given context"""
642
643 local_vars['__args'] = args
644 local_vars['__dargs'] = dargs
645 exec('__ret = %s(*__args, **__dargs)' % fn,
646 local_vars, local_vars)
647 return local_vars['__ret']
648
649
650 def _create_frame(self, global_vars, ancestry, fn_name):
651 """Set up the environment like it would have been when this
652 function was first defined.
653
654 Child step engine 'implementations' must have 'return locals()'
655 at end end of their steps. Because of this, we can call the
656 parent function and get back all child functions (i.e. those
657 defined within it).
658
659 Unfortunately, the call stack of the function calling
660 job.next_step might have been deeper than the function it
661 added. In order to make sure that the environment is what it
662 should be, we need to then pop off the frames we built until
663 we find the frame where the function was first defined."""
664
665 # The copies ensure that the parent frames are not modified
666 # while building child frames. This matters if we then
667 # pop some frames in the next part of this function.
668 current_frame = copy.copy(global_vars)
669 frames = [current_frame]
670 for steps_fn_name in ancestry:
671 ret = self._run_step_fn(current_frame,
672 steps_fn_name, [], {})
673 current_frame = copy.copy(ret)
674 frames.append(current_frame)
675
676 while len(frames) > 2:
677 if fn_name not in frames[-2]:
678 break
679 if frames[-2][fn_name] != frames[-1][fn_name]:
680 break
681 frames.pop()
682 ancestry.pop()
683
684 return (frames[-1], ancestry)
685
686
687 def _add_step_init(self, local_vars, current_function):
688 """If the function returned a dictionary that includes a
689 function named 'step_init', prepend it to our list of steps.
690 This will only get run the first time a function with a nested
691 use of the step engine is run."""
692
693 if (isinstance(local_vars, dict) and
694 'step_init' in local_vars and
695 callable(local_vars['step_init'])):
696 # The init step is a child of the function
697 # we were just running.
698 self.current_step_ancestry.append(current_function)
699 self.next_step_prepend('step_init')
700
701
apw83f8d772006-04-27 14:12:56 +0000702 def step_engine(self):
mblighc86b0b42006-07-28 17:35:28 +0000703 """the stepping engine -- if the control file defines
704 step_init we will be using this engine to drive multiple runs.
705 """
706 """Do the next step"""
apw83f8d772006-04-27 14:12:56 +0000707
mbligh366ff1b2008-04-25 16:07:56 +0000708 # Set up the environment and then interpret the control file.
709 # Some control files will have code outside of functions,
710 # which means we need to have our state engine initialized
711 # before reading in the file.
mblighb274ef52008-06-02 19:40:01 +0000712 global_control_vars = {'job': self}
713 exec(JOB_PREAMBLE, global_control_vars, global_control_vars)
714 execfile(self.control, global_control_vars, global_control_vars)
apw83f8d772006-04-27 14:12:56 +0000715
mbligh366ff1b2008-04-25 16:07:56 +0000716 # If we loaded in a mid-job state file, then we presumably
717 # know what steps we have yet to run.
jadmanskia9c75c42008-05-01 22:05:31 +0000718 if not self.state_existed:
mblighb274ef52008-06-02 19:40:01 +0000719 if global_control_vars.has_key('step_init'):
720 self.next_step(global_control_vars['step_init'])
apw0865f482006-03-30 18:50:19 +0000721
mbligh366ff1b2008-04-25 16:07:56 +0000722 # Iterate through the steps. If we reboot, we'll simply
723 # continue iterating on the next step.
mblighf1ae0a42008-04-25 16:09:20 +0000724 while len(self.get_state('__steps')) > 0:
725 steps = self.get_state('__steps')
mblighb274ef52008-06-02 19:40:01 +0000726 (ancestry, fn_name, args, dargs) = steps.pop(0)
mblighf1ae0a42008-04-25 16:09:20 +0000727 self.set_state('__steps', steps)
apw0865f482006-03-30 18:50:19 +0000728
mbligh8f4d0432008-06-02 19:42:50 +0000729 self.next_step_index = 0
mblighb274ef52008-06-02 19:40:01 +0000730 ret = self._create_frame(global_control_vars, ancestry,
731 fn_name)
732 local_vars, self.current_step_ancestry = ret
733 local_vars = self._run_step_fn(local_vars, fn_name,
734 args, dargs)
735 self._add_step_init(local_vars, fn_name)
apw0865f482006-03-30 18:50:19 +0000736
mblighcaa605c2006-10-02 00:37:35 +0000737
jadmanskia9c75c42008-05-01 22:05:31 +0000738 def _init_group_level(self):
739 self.group_level = self.get_state("__group_level", default=0)
740
741
742 def _increment_group_level(self):
743 self.group_level += 1
744 self.set_state("__group_level", self.group_level)
745
746
747 def _decrement_group_level(self):
748 self.group_level -= 1
749 self.set_state("__group_level", self.group_level)
750
751
752 def record(self, status_code, subdir, operation, status = '',
753 optional_fields=None):
mbligh09f288a2007-09-18 21:34:57 +0000754 """
755 Record job-level status
apw7db8d0b2006-10-09 08:10:25 +0000756
mbligh09f288a2007-09-18 21:34:57 +0000757 The intent is to make this file both machine parseable and
758 human readable. That involves a little more complexity, but
759 really isn't all that bad ;-)
760
761 Format is <status code>\t<subdir>\t<operation>\t<status>
762
763 status code: (GOOD|WARN|FAIL|ABORT)
764 or START
765 or END (GOOD|WARN|FAIL|ABORT)
766
767 subdir: MUST be a relevant subdirectory in the results,
768 or None, which will be represented as '----'
769
770 operation: description of what you ran (e.g. "dbench", or
771 "mkfs -t foobar /dev/sda9")
772
773 status: error message or "completed sucessfully"
774
775 ------------------------------------------------------------
776
777 Initial tabs indicate indent levels for grouping, and is
mbligh7dd510c2007-11-13 17:11:22 +0000778 governed by self.group_level
mbligh09f288a2007-09-18 21:34:57 +0000779
780 multiline messages have secondary lines prefaced by a double
781 space (' ')
782 """
783
mblighb0570ad2007-09-19 18:18:11 +0000784 if subdir:
785 if re.match(r'[\n\t]', subdir):
jadmanskia9c75c42008-05-01 22:05:31 +0000786 raise ValueError("Invalid character in "
787 "subdir string")
mblighb0570ad2007-09-19 18:18:11 +0000788 substr = subdir
789 else:
790 substr = '----'
mbligh09f288a2007-09-18 21:34:57 +0000791
mbligh302482e2008-05-01 20:06:16 +0000792 if not logging.is_valid_status(status_code):
jadmanskia9c75c42008-05-01 22:05:31 +0000793 raise ValueError("Invalid status code supplied: %s" %
794 status_code)
mbligh9c5ac322007-10-31 18:01:59 +0000795 if not operation:
796 operation = '----'
jadmanskia9c75c42008-05-01 22:05:31 +0000797
mbligh09f288a2007-09-18 21:34:57 +0000798 if re.match(r'[\n\t]', operation):
jadmanskia9c75c42008-05-01 22:05:31 +0000799 raise ValueError("Invalid character in "
800 "operation string")
mbligh09f288a2007-09-18 21:34:57 +0000801 operation = operation.rstrip()
jadmanskia9c75c42008-05-01 22:05:31 +0000802
803 if not optional_fields:
804 optional_fields = {}
805
mbligh09f288a2007-09-18 21:34:57 +0000806 status = status.rstrip()
807 status = re.sub(r"\t", " ", status)
apw7db8d0b2006-10-09 08:10:25 +0000808 # Ensure any continuation lines are marked so we can
809 # detect them in the status file to ensure it is parsable.
jadmanskia9c75c42008-05-01 22:05:31 +0000810 status = re.sub(r"\n", "\n" + "\t" * self.group_level + " ",
811 status)
mbligh09f288a2007-09-18 21:34:57 +0000812
mbligh30270302007-11-05 20:33:52 +0000813 # Generate timestamps for inclusion in the logs
814 epoch_time = int(time.time()) # seconds since epoch, in UTC
815 local_time = time.localtime(epoch_time)
jadmanskia9c75c42008-05-01 22:05:31 +0000816 optional_fields["timestamp"] = str(epoch_time)
817 optional_fields["localtime"] = time.strftime("%b %d %H:%M:%S",
818 local_time)
mbligh30270302007-11-05 20:33:52 +0000819
jadmanskia9c75c42008-05-01 22:05:31 +0000820 fields = [status_code, substr, operation]
821 fields += ["%s=%s" % x for x in optional_fields.iteritems()]
822 fields.append(status)
823
824 msg = '\t'.join(str(x) for x in fields)
mbligh7dd510c2007-11-13 17:11:22 +0000825 msg = '\t' * self.group_level + msg
apw7db8d0b2006-10-09 08:10:25 +0000826
mblighd528d302007-12-19 16:19:05 +0000827 msg_tag = ""
828 if "." in self.log_filename:
829 msg_tag = self.log_filename.split(".", 1)[1]
830
jadmanskia9c75c42008-05-01 22:05:31 +0000831 self.harness.test_status_detail(status_code, substr,
832 operation, status, msg_tag)
mblighd528d302007-12-19 16:19:05 +0000833 self.harness.test_status(msg, msg_tag)
834
835 # log to stdout (if enabled)
836 #if self.log_filename == self.DEFAULT_LOG_FILENAME:
apwf1a81162006-04-25 10:10:29 +0000837 print msg
mblighd528d302007-12-19 16:19:05 +0000838
839 # log to the "root" status log
840 status_file = os.path.join(self.resultdir, self.log_filename)
mbligh7dd510c2007-11-13 17:11:22 +0000841 open(status_file, "a").write(msg + "\n")
mblighd528d302007-12-19 16:19:05 +0000842
843 # log to the subdir status log (if subdir is set)
mblighb0570ad2007-09-19 18:18:11 +0000844 if subdir:
mblighadff6ca2008-01-22 16:38:25 +0000845 dir = os.path.join(self.resultdir, subdir)
846 if not os.path.exists(dir):
847 os.mkdir(dir)
848
849 status_file = os.path.join(dir,
mblighd528d302007-12-19 16:19:05 +0000850 self.DEFAULT_LOG_FILENAME)
mblighb0570ad2007-09-19 18:18:11 +0000851 open(status_file, "a").write(msg + "\n")
apwce9abe92006-04-27 14:14:04 +0000852
853
jadmanski8415f962008-05-06 20:38:53 +0000854class disk_usage_monitor:
855 def __init__(self, logging_func, device, max_mb_per_hour):
856 self.func = logging_func
857 self.device = device
858 self.max_mb_per_hour = max_mb_per_hour
859
860
861 def start(self):
862 self.initial_space = autotest_utils.freespace(self.device)
863 self.start_time = time.time()
864
865
866 def stop(self):
867 # if no maximum usage rate was set, we don't need to
868 # generate any warnings
869 if not self.max_mb_per_hour:
870 return
871
872 final_space = autotest_utils.freespace(self.device)
873 used_space = self.initial_space - final_space
874 stop_time = time.time()
875 total_time = stop_time - self.start_time
876 # round up the time to one minute, to keep extremely short
877 # tests from generating false positives due to short, badly
878 # timed bursts of activity
879 total_time = max(total_time, 60.0)
880
881 # determine the usage rate
882 bytes_per_sec = used_space / total_time
883 mb_per_sec = bytes_per_sec / 1024**2
884 mb_per_hour = mb_per_sec * 60 * 60
885
886 if mb_per_hour > self.max_mb_per_hour:
887 msg = ("disk space on %s was consumed at a rate of "
888 "%.2f MB/hour")
889 msg %= (self.device, mb_per_hour)
890 self.func(msg)
891
892
893 @classmethod
894 def watch(cls, *monitor_args, **monitor_dargs):
895 """ Generic decorator to wrap a function call with the
896 standard create-monitor -> start -> call -> stop idiom."""
897 def decorator(func):
898 def watched_func(*args, **dargs):
899 monitor = cls(*monitor_args, **monitor_dargs)
900 monitor.start()
901 try:
902 func(*args, **dargs)
903 finally:
904 monitor.stop()
905 return watched_func
906 return decorator
907
908
mblighcaa62c22008-04-07 21:51:17 +0000909def runjob(control, cont = False, tag = "default", harness_type = '',
910 use_external_logging = False):
mblighc86b0b42006-07-28 17:35:28 +0000911 """The main interface to this module
912
mbligh72b88fc2006-12-16 18:41:35 +0000913 control
mblighc86b0b42006-07-28 17:35:28 +0000914 The control file to use for this job.
915 cont
916 Whether this is the continuation of a previously started job
917 """
mblighb4eef242007-07-23 18:22:49 +0000918 control = os.path.abspath(control)
apwce9abe92006-04-27 14:14:04 +0000919 state = control + '.state'
920
921 # instantiate the job object ready for the control file.
922 myjob = None
923 try:
924 # Check that the control file is valid
925 if not os.path.exists(control):
mbligh302482e2008-05-01 20:06:16 +0000926 raise error.JobError(control +
927 ": control file not found")
apwce9abe92006-04-27 14:14:04 +0000928
929 # When continuing, the job is complete when there is no
930 # state file, ensure we don't try and continue.
mblighf3fef462006-09-13 16:05:05 +0000931 if cont and not os.path.exists(state):
mbligh302482e2008-05-01 20:06:16 +0000932 raise error.JobComplete("all done")
mblighf3fef462006-09-13 16:05:05 +0000933 if cont == False and os.path.exists(state):
apwce9abe92006-04-27 14:14:04 +0000934 os.unlink(state)
935
mblighcaa62c22008-04-07 21:51:17 +0000936 myjob = job(control, tag, cont, harness_type,
937 use_external_logging)
apwce9abe92006-04-27 14:14:04 +0000938
939 # Load in the users control file, may do any one of:
940 # 1) execute in toto
941 # 2) define steps, and select the first via next_step()
942 myjob.step_engine()
943
mbligh302482e2008-05-01 20:06:16 +0000944 except error.JobContinue:
apwce9abe92006-04-27 14:14:04 +0000945 sys.exit(5)
946
mbligh302482e2008-05-01 20:06:16 +0000947 except error.JobComplete:
apwb832e1b2007-11-24 20:24:38 +0000948 sys.exit(1)
949
mbligh302482e2008-05-01 20:06:16 +0000950 except error.JobError, instance:
apwce9abe92006-04-27 14:14:04 +0000951 print "JOB ERROR: " + instance.args[0]
mbligh9c5ac322007-10-31 18:01:59 +0000952 if myjob:
mbligh30270302007-11-05 20:33:52 +0000953 command = None
954 if len(instance.args) > 1:
955 command = instance.args[1]
956 myjob.record('ABORT', None, command, instance.args[0])
jadmanskia9c75c42008-05-01 22:05:31 +0000957 myjob._decrement_group_level()
mblighc3430162007-11-14 23:57:19 +0000958 myjob.record('END ABORT', None, None)
jadmanskia9c75c42008-05-01 22:05:31 +0000959 assert(myjob.group_level == 0)
apwce9abe92006-04-27 14:14:04 +0000960 myjob.complete(1)
apwb832e1b2007-11-24 20:24:38 +0000961 else:
962 sys.exit(1)
apwce9abe92006-04-27 14:14:04 +0000963
mblighc3430162007-11-14 23:57:19 +0000964 except Exception, e:
mbligh302482e2008-05-01 20:06:16 +0000965 msg = str(e) + '\n' + traceback.format_exc()
mblighc3430162007-11-14 23:57:19 +0000966 print "JOB ERROR: " + msg
mblighfbfb77d2007-02-15 18:54:03 +0000967 if myjob:
mblighc3430162007-11-14 23:57:19 +0000968 myjob.record('ABORT', None, None, msg)
jadmanskia9c75c42008-05-01 22:05:31 +0000969 myjob._decrement_group_level()
mblighc3430162007-11-14 23:57:19 +0000970 myjob.record('END ABORT', None, None)
jadmanskia9c75c42008-05-01 22:05:31 +0000971 assert(myjob.group_level == 0)
mbligh9c5ac322007-10-31 18:01:59 +0000972 myjob.complete(1)
apwb832e1b2007-11-24 20:24:38 +0000973 else:
974 sys.exit(1)
mbligh892d37f2007-03-01 17:03:25 +0000975
mbligh0144e5a2008-03-07 18:17:53 +0000976 # If we get here, then we assume the job is complete and good.
jadmanskia9c75c42008-05-01 22:05:31 +0000977 myjob._decrement_group_level()
mbligh0144e5a2008-03-07 18:17:53 +0000978 myjob.record('END GOOD', None, None)
jadmanskia9c75c42008-05-01 22:05:31 +0000979 assert(myjob.group_level == 0)
mbligh0144e5a2008-03-07 18:17:53 +0000980
mbligh892d37f2007-03-01 17:03:25 +0000981 myjob.complete(0)
mblighcaa62c22008-04-07 21:51:17 +0000982
983
984# site_job.py may be non-existant or empty, make sure that an appropriate
985# site_job class is created nevertheless
986try:
987 from site_job import site_job
988except ImportError:
989 class site_job(base_job):
990 pass
991
992class job(site_job):
993 pass
jadmanski87cbc7f2008-05-13 18:17:10 +0000994