blob: 7c4968360725490445368d6024be13bec2f920f8 [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
mbligh302482e2008-05-01 20:06:16 +000012from autotest_lib.client.bin import autotest_utils
13from autotest_lib.client.common_lib import error, barrier, logging
14
15import parallel, kernel, xen, test, profilers, filesystem, fd_stack, boottool
16import harness, config, sysinfo, cpuset
17
mblighf4c35322006-03-13 01:01:10 +000018
mbligh12a04cb2008-04-25 16:07:20 +000019
mbligh366ff1b2008-04-25 16:07:56 +000020JOB_PREAMBLE = """
21from common.error import *
22from autotest_utils import *
23"""
24
25
mbligh302482e2008-05-01 20:06:16 +000026class StepError(error.AutotestError):
mbligh12a04cb2008-04-25 16:07:20 +000027 pass
28
29
mblighcaa62c22008-04-07 21:51:17 +000030class base_job:
mblighc86b0b42006-07-28 17:35:28 +000031 """The actual job against which we do everything.
32
33 Properties:
mbligh72b88fc2006-12-16 18:41:35 +000034 autodir
mblighc86b0b42006-07-28 17:35:28 +000035 The top level autotest directory (/usr/local/autotest).
36 Comes from os.environ['AUTODIR'].
mbligh72b88fc2006-12-16 18:41:35 +000037 bindir
mblighc86b0b42006-07-28 17:35:28 +000038 <autodir>/bin/
mblighd5a38832008-01-25 18:15:39 +000039 libdir
40 <autodir>/lib/
mbligh72b88fc2006-12-16 18:41:35 +000041 testdir
mblighc86b0b42006-07-28 17:35:28 +000042 <autodir>/tests/
mbligh84bafdb2008-01-26 19:43:34 +000043 site_testdir
44 <autodir>/site_tests/
mblighc86b0b42006-07-28 17:35:28 +000045 profdir
46 <autodir>/profilers/
47 tmpdir
48 <autodir>/tmp/
49 resultdir
50 <autodir>/results/<jobtag>
51 stdout
52 fd_stack object for stdout
53 stderr
54 fd_stack object for stderr
55 profilers
56 the profilers object for this job
apw504a7dd2006-10-12 17:18:37 +000057 harness
58 the server harness object for this job
apw059e1b12006-10-12 17:18:26 +000059 config
60 the job configuration for this job
mblighc86b0b42006-07-28 17:35:28 +000061 """
62
mblighd528d302007-12-19 16:19:05 +000063 DEFAULT_LOG_FILENAME = "status"
64
mblighcaa62c22008-04-07 21:51:17 +000065 def __init__(self, control, jobtag, cont, harness_type=None,
66 use_external_logging = False):
mblighc86b0b42006-07-28 17:35:28 +000067 """
68 control
69 The control file (pathname of)
70 jobtag
71 The job tag string (eg "default")
apw96da1a42006-11-02 00:23:18 +000072 cont
73 If this is the continuation of this job
apwe68a7132006-12-01 11:21:37 +000074 harness_type
75 An alternative server harness
mblighc86b0b42006-07-28 17:35:28 +000076 """
mblighf4c35322006-03-13 01:01:10 +000077 self.autodir = os.environ['AUTODIR']
apw870988b2007-09-25 16:50:53 +000078 self.bindir = os.path.join(self.autodir, 'bin')
mblighd5a38832008-01-25 18:15:39 +000079 self.libdir = os.path.join(self.autodir, 'lib')
apw870988b2007-09-25 16:50:53 +000080 self.testdir = os.path.join(self.autodir, 'tests')
mbligh84bafdb2008-01-26 19:43:34 +000081 self.site_testdir = os.path.join(self.autodir, 'site_tests')
apw870988b2007-09-25 16:50:53 +000082 self.profdir = os.path.join(self.autodir, 'profilers')
83 self.tmpdir = os.path.join(self.autodir, 'tmp')
84 self.resultdir = os.path.join(self.autodir, 'results', jobtag)
mbligh0fb83972008-01-10 16:30:02 +000085 self.sysinfodir = os.path.join(self.resultdir, 'sysinfo')
mbligh8d83cdc2007-12-03 18:09:18 +000086 self.control = os.path.abspath(control)
mbligh366ff1b2008-04-25 16:07:56 +000087 self.state_file = self.control + '.state'
mblighb274ef52008-06-02 19:40:01 +000088 self.current_step_ancestry = []
jadmanskia9c75c42008-05-01 22:05:31 +000089 self.__load_state()
mbligha2508052006-05-28 21:29:53 +000090
apw96da1a42006-11-02 00:23:18 +000091 if not cont:
mblighc1cbc992008-05-27 20:01:45 +000092 """
93 Don't cleanup the tmp dir (which contains the lockfile)
94 in the constructor, this would be a problem for multiple
95 jobs starting at the same time on the same client. Instead
96 do the delete at the server side. We simply create the tmp
97 directory here if it does not already exist.
98 """
99 if not os.path.exists(self.tmpdir):
100 os.mkdir(self.tmpdir)
apw96da1a42006-11-02 00:23:18 +0000101
apw870988b2007-09-25 16:50:53 +0000102 results = os.path.join(self.autodir, 'results')
103 if not os.path.exists(results):
104 os.mkdir(results)
mblighfbfb77d2007-02-15 18:54:03 +0000105
apwf3d28622007-09-25 16:49:17 +0000106 download = os.path.join(self.testdir, 'download')
mblighc1cbc992008-05-27 20:01:45 +0000107 if not os.path.exists(download):
108 os.mkdir(download)
109
apw96da1a42006-11-02 00:23:18 +0000110 if os.path.exists(self.resultdir):
mbligh302482e2008-05-01 20:06:16 +0000111 autotest_utils.system('rm -rf '
112 + self.resultdir)
apw96da1a42006-11-02 00:23:18 +0000113 os.mkdir(self.resultdir)
mbligh0fb83972008-01-10 16:30:02 +0000114 os.mkdir(self.sysinfodir)
apw96da1a42006-11-02 00:23:18 +0000115
apw870988b2007-09-25 16:50:53 +0000116 os.mkdir(os.path.join(self.resultdir, 'debug'))
117 os.mkdir(os.path.join(self.resultdir, 'analysis'))
apw870988b2007-09-25 16:50:53 +0000118
mbligh8d83cdc2007-12-03 18:09:18 +0000119 shutil.copyfile(self.control,
120 os.path.join(self.resultdir, 'control'))
mblighf4ca14f2008-03-03 16:03:05 +0000121
mbligh4b089662006-06-14 22:34:58 +0000122
apwecf41b72006-03-31 14:00:55 +0000123 self.control = control
mbligh27113602007-10-31 21:07:51 +0000124 self.jobtag = jobtag
mblighd528d302007-12-19 16:19:05 +0000125 self.log_filename = self.DEFAULT_LOG_FILENAME
mbligh68119582008-01-25 18:16:41 +0000126 self.container = None
mblighf4c35322006-03-13 01:01:10 +0000127
mbligh56f1fbb2006-10-01 15:10:56 +0000128 self.stdout = fd_stack.fd_stack(1, sys.stdout)
129 self.stderr = fd_stack.fd_stack(2, sys.stderr)
jadmanskia9c75c42008-05-01 22:05:31 +0000130
131 self._init_group_level()
mblighf4c35322006-03-13 01:01:10 +0000132
apw059e1b12006-10-12 17:18:26 +0000133 self.config = config.config(self)
134
apwd27e55f2006-12-01 11:22:08 +0000135 self.harness = harness.select(harness_type, self)
136
mbligha35553b2006-04-23 15:52:25 +0000137 self.profilers = profilers.profilers(self)
mbligh72905562006-05-25 01:30:49 +0000138
mblighcaa605c2006-10-02 00:37:35 +0000139 try:
apw90154af2006-12-01 11:23:36 +0000140 tool = self.config_get('boottool.executable')
141 self.bootloader = boottool.boottool(tool)
mblighcaa605c2006-10-02 00:37:35 +0000142 except:
143 pass
144
mbligh0fb83972008-01-10 16:30:02 +0000145 sysinfo.log_per_reboot_data(self.sysinfodir)
mbligh3a6d6ca2006-04-23 15:50:24 +0000146
mbligh30270302007-11-05 20:33:52 +0000147 if not cont:
mblighc3430162007-11-14 23:57:19 +0000148 self.record('START', None, None)
jadmanskia9c75c42008-05-01 22:05:31 +0000149 self._increment_group_level()
apw357f50f2006-12-01 11:22:39 +0000150
apwf91efaf2007-11-24 17:32:13 +0000151 self.harness.run_start()
mblighcaa62c22008-04-07 21:51:17 +0000152
153 if use_external_logging:
154 self.enable_external_logging()
apwf91efaf2007-11-24 17:32:13 +0000155
jadmanski8415f962008-05-06 20:38:53 +0000156 # load the max disk usage rate - default to no monitoring
157 self.max_disk_usage_rate = self.get_state('__monitor_disk',
158 default=0.0)
159
160
161 def monitor_disk_usage(self, max_rate):
162 """\
163 Signal that the job should monitor disk space usage on /
164 and generate a warning if a test uses up disk space at a
165 rate exceeding 'max_rate'.
166
167 Parameters:
168 max_rate - the maximium allowed rate of disk consumption
169 during a test, in MB/hour, or 0 to indicate
170 no limit.
171 """
172 self.set_state('__monitor_disk', max_rate)
173 self.max_disk_usage_rate = max_rate
174
mbligh0692e472007-08-30 16:07:53 +0000175
176 def relative_path(self, path):
177 """\
178 Return a patch relative to the job results directory
179 """
mbligh1c250ca2007-08-30 16:31:38 +0000180 head = len(self.resultdir) + 1 # remove the / inbetween
181 return path[head:]
mbligh0692e472007-08-30 16:07:53 +0000182
183
mbligh362ab3d2007-08-30 11:24:04 +0000184 def control_get(self):
185 return self.control
186
mblighcaa605c2006-10-02 00:37:35 +0000187
mbligh8d83cdc2007-12-03 18:09:18 +0000188 def control_set(self, control):
189 self.control = os.path.abspath(control)
190
191
apwde1503a2006-10-10 08:34:21 +0000192 def harness_select(self, which):
193 self.harness = harness.select(which, self)
194
195
apw059e1b12006-10-12 17:18:26 +0000196 def config_set(self, name, value):
197 self.config.set(name, value)
198
199
200 def config_get(self, name):
201 return self.config.get(name)
202
mbligh8baa2ea2006-12-17 23:01:24 +0000203 def setup_dirs(self, results_dir, tmp_dir):
mbligh1e8858e2006-11-24 22:18:35 +0000204 if not tmp_dir:
apw870988b2007-09-25 16:50:53 +0000205 tmp_dir = os.path.join(self.tmpdir, 'build')
mbligh1e8858e2006-11-24 22:18:35 +0000206 if not os.path.exists(tmp_dir):
207 os.mkdir(tmp_dir)
208 if not os.path.isdir(tmp_dir):
mbligh642b03e2008-01-14 16:53:15 +0000209 e_msg = "Temp dir (%s) is not a dir - args backwards?" % self.tmpdir
210 raise ValueError(e_msg)
mbligh1e8858e2006-11-24 22:18:35 +0000211
212 # We label the first build "build" and then subsequent ones
213 # as "build.2", "build.3", etc. Whilst this is a little bit
214 # inconsistent, 99.9% of jobs will only have one build
215 # (that's not done as kernbench, sparse, or buildtest),
216 # so it works out much cleaner. One of life's comprimises.
217 if not results_dir:
218 results_dir = os.path.join(self.resultdir, 'build')
219 i = 2
220 while os.path.exists(results_dir):
221 results_dir = os.path.join(self.resultdir, 'build.%d' % i)
mblighd9223fc2006-11-26 17:19:54 +0000222 i += 1
mbligh1e8858e2006-11-24 22:18:35 +0000223 if not os.path.exists(results_dir):
224 os.mkdir(results_dir)
mbligh72b88fc2006-12-16 18:41:35 +0000225
mbligh8baa2ea2006-12-17 23:01:24 +0000226 return (results_dir, tmp_dir)
227
228
229 def xen(self, base_tree, results_dir = '', tmp_dir = '', leave = False, \
230 kjob = None ):
231 """Summon a xen object"""
232 (results_dir, tmp_dir) = self.setup_dirs(results_dir, tmp_dir)
233 build_dir = 'xen'
234 return xen.xen(self, base_tree, results_dir, tmp_dir, build_dir, leave, kjob)
235
236
237 def kernel(self, base_tree, results_dir = '', tmp_dir = '', leave = False):
238 """Summon a kernel object"""
mbligh669caa12007-11-05 18:32:13 +0000239 (results_dir, tmp_dir) = self.setup_dirs(results_dir, tmp_dir)
mbligh8baa2ea2006-12-17 23:01:24 +0000240 build_dir = 'linux'
mbligh6ee7ee02007-11-13 23:49:05 +0000241 return kernel.auto_kernel(self, base_tree, results_dir,
242 tmp_dir, build_dir, leave)
mblighf4c35322006-03-13 01:01:10 +0000243
mblighcaa605c2006-10-02 00:37:35 +0000244
mbligh6b504ff2007-12-12 21:03:49 +0000245 def barrier(self, *args, **kwds):
mblighfadca202006-09-23 04:40:01 +0000246 """Create a barrier object"""
mbligh6b504ff2007-12-12 21:03:49 +0000247 return barrier.barrier(*args, **kwds)
mblighfadca202006-09-23 04:40:01 +0000248
mblighcaa605c2006-10-02 00:37:35 +0000249
mbligh4b089662006-06-14 22:34:58 +0000250 def setup_dep(self, deps):
mblighc86b0b42006-07-28 17:35:28 +0000251 """Set up the dependencies for this test.
252
253 deps is a list of libraries required for this test.
254 """
mbligh4b089662006-06-14 22:34:58 +0000255 for dep in deps:
256 try:
apw870988b2007-09-25 16:50:53 +0000257 os.chdir(os.path.join(self.autodir, 'deps', dep))
mbligh302482e2008-05-01 20:06:16 +0000258 autotest_utils.system('./' + dep + '.py')
mbligh4b089662006-06-14 22:34:58 +0000259 except:
mbligh302482e2008-05-01 20:06:16 +0000260 err = "setting up dependency " + dep + "\n"
261 raise error.UnhandledError(err)
mbligh4b089662006-06-14 22:34:58 +0000262
263
mbligh72b88fc2006-12-16 18:41:35 +0000264 def __runtest(self, url, tag, args, dargs):
265 try:
mbligh53c41502007-10-23 20:45:04 +0000266 l = lambda : test.runtest(self, url, tag, args, dargs)
mbligh302482e2008-05-01 20:06:16 +0000267 pid = parallel.fork_start(self.resultdir, l)
268 parallel.fork_waitfor(self.resultdir, pid)
269 except error.AutotestError:
mbligh72b88fc2006-12-16 18:41:35 +0000270 raise
jadmanskicf8c4d62008-05-27 22:09:14 +0000271 except Exception, e:
272 msg = "Unhandled %s error occured during test\n"
273 msg %= str(e.__class__.__name__)
274 raise error.UnhandledError(msg)
apwf1a81162006-04-25 10:10:29 +0000275
mblighcaa605c2006-10-02 00:37:35 +0000276
mblighd016ecc2006-11-25 21:41:07 +0000277 def run_test(self, url, *args, **dargs):
mblighc86b0b42006-07-28 17:35:28 +0000278 """Summon a test object and run it.
279
280 tag
281 tag to add to testname
mbligh12a7df72006-10-06 03:54:33 +0000282 url
283 url of the test to run
mblighc86b0b42006-07-28 17:35:28 +0000284 """
mbligh12a7df72006-10-06 03:54:33 +0000285
mblighd016ecc2006-11-25 21:41:07 +0000286 if not url:
mbligh302482e2008-05-01 20:06:16 +0000287 raise TypeError("Test name is invalid. "
288 "Switched arguments?")
mbligh09f288a2007-09-18 21:34:57 +0000289 (group, testname) = test.testname(url)
mbligh7dd510c2007-11-13 17:11:22 +0000290 tag = dargs.pop('tag', None)
mbligh65938a22007-12-10 16:58:52 +0000291 container = dargs.pop('container', None)
mbligh09f288a2007-09-18 21:34:57 +0000292 subdir = testname
mbligh7dd510c2007-11-13 17:11:22 +0000293 if tag:
294 subdir += '.' + tag
295
mbligh65938a22007-12-10 16:58:52 +0000296 if container:
mbligh68119582008-01-25 18:16:41 +0000297 cname = container.get('name', None)
298 if not cname: # get old name
299 cname = container.get('container_name', None)
300 mbytes = container.get('mbytes', None)
301 if not mbytes: # get old name
302 mbytes = container.get('mem', None)
303 cpus = container.get('cpus', None)
304 if not cpus: # get old name
305 cpus = container.get('cpu', None)
jadmanski87cbc7f2008-05-13 18:17:10 +0000306 root = container.get('root', None)
mbligh68119582008-01-25 18:16:41 +0000307 self.new_container(mbytes=mbytes, cpus=cpus,
308 root=root, name=cname)
mbligh65938a22007-12-10 16:58:52 +0000309 # We are running in a container now...
310
jadmanski8415f962008-05-06 20:38:53 +0000311 def log_warning(reason):
312 self.record("WARN", subdir, testname, reason)
313 @disk_usage_monitor.watch(log_warning, "/",
314 self.max_disk_usage_rate)
mbligh7dd510c2007-11-13 17:11:22 +0000315 def group_func():
apwf1a81162006-04-25 10:10:29 +0000316 try:
mblighd016ecc2006-11-25 21:41:07 +0000317 self.__runtest(url, tag, args, dargs)
mbligh302482e2008-05-01 20:06:16 +0000318 except error.TestNAError, detail:
319 self.record('TEST_NA', subdir, testname,
320 str(detail))
321 raise
apwf1a81162006-04-25 10:10:29 +0000322 except Exception, detail:
mbligh7dd510c2007-11-13 17:11:22 +0000323 self.record('FAIL', subdir, testname,
324 str(detail))
apwf1a81162006-04-25 10:10:29 +0000325 raise
326 else:
mbligh7dd510c2007-11-13 17:11:22 +0000327 self.record('GOOD', subdir, testname,
328 'completed successfully')
jadmanski8415f962008-05-06 20:38:53 +0000329
mblighcfc6dd32007-11-20 00:44:35 +0000330 result, exc_info = self.__rungroup(subdir, group_func)
mbligh68119582008-01-25 18:16:41 +0000331 if container:
332 self.release_container()
mbligh302482e2008-05-01 20:06:16 +0000333 if exc_info and isinstance(exc_info[1], error.TestError):
mbligh7dd510c2007-11-13 17:11:22 +0000334 return False
335 elif exc_info:
mbligh71ea2492008-01-15 20:35:52 +0000336 raise exc_info[0], exc_info[1], exc_info[2]
apwf1a81162006-04-25 10:10:29 +0000337 else:
mbligh7dd510c2007-11-13 17:11:22 +0000338 return True
339
340
341 def __rungroup(self, name, function, *args, **dargs):
342 """\
343 name:
344 name of the group
345 function:
346 subroutine to run
347 *args:
348 arguments for the function
349
350 Returns a 2-tuple (result, exc_info) where result
351 is the return value of function, and exc_info is
352 the sys.exc_info() of the exception thrown by the
353 function (which may be None).
354 """
355
356 result, exc_info = None, None
357 try:
358 self.record('START', None, name)
jadmanskia9c75c42008-05-01 22:05:31 +0000359 self._increment_group_level()
mbligh7dd510c2007-11-13 17:11:22 +0000360 result = function(*args, **dargs)
jadmanskia9c75c42008-05-01 22:05:31 +0000361 self._decrement_group_level()
mbligh7dd510c2007-11-13 17:11:22 +0000362 self.record('END GOOD', None, name)
mbligh302482e2008-05-01 20:06:16 +0000363 except error.TestNAError, e:
jadmanskia9c75c42008-05-01 22:05:31 +0000364 self._decrement_group_level()
mbligh302482e2008-05-01 20:06:16 +0000365 self.record('END TEST_NA', None, name, str(e))
mbligh7dd510c2007-11-13 17:11:22 +0000366 except Exception, e:
367 exc_info = sys.exc_info()
jadmanskia9c75c42008-05-01 22:05:31 +0000368 self._decrement_group_level()
mbligh302482e2008-05-01 20:06:16 +0000369 err_msg = str(e) + '\n' + traceback.format_exc()
mbligh51144e02007-11-20 20:38:18 +0000370 self.record('END FAIL', None, name, err_msg)
mbligh7dd510c2007-11-13 17:11:22 +0000371
372 return result, exc_info
apw0865f482006-03-30 18:50:19 +0000373
mblighd7fb4a62006-10-01 00:57:53 +0000374
apw1da244b2007-09-27 17:18:01 +0000375 def run_group(self, function, *args, **dargs):
mbligh88ab90f2007-08-29 15:52:49 +0000376 """\
377 function:
378 subroutine to run
379 *args:
380 arguments for the function
381 """
382
mbligh7dd510c2007-11-13 17:11:22 +0000383 # Allow the tag for the group to be specified
mbligh88ab90f2007-08-29 15:52:49 +0000384 name = function.__name__
mbligh7dd510c2007-11-13 17:11:22 +0000385 tag = dargs.pop('tag', None)
386 if tag:
387 name = tag
apw1da244b2007-09-27 17:18:01 +0000388
mbligh7dd510c2007-11-13 17:11:22 +0000389 result, exc_info = self.__rungroup(name, function,
390 *args, **dargs)
apw1da244b2007-09-27 17:18:01 +0000391
mbligh7dd510c2007-11-13 17:11:22 +0000392 # if there was a non-TestError exception, raise it
mbligh302482e2008-05-01 20:06:16 +0000393 if exc_info and not isinstance(exc_info[1], error.TestError):
mbligh7dd510c2007-11-13 17:11:22 +0000394 err = ''.join(traceback.format_exception(*exc_info))
mbligh302482e2008-05-01 20:06:16 +0000395 raise error.TestError(name + ' failed\n' + err)
mbligh88ab90f2007-08-29 15:52:49 +0000396
mbligh7dd510c2007-11-13 17:11:22 +0000397 # pass back the actual return value from the function
apw08403ca2007-09-27 17:17:22 +0000398 return result
399
mbligh88ab90f2007-08-29 15:52:49 +0000400
jadmanski87cbc7f2008-05-13 18:17:10 +0000401 def new_container(self, mbytes=None, cpus=None, root=None, name=None):
mbligh8ea61e22008-05-09 18:09:37 +0000402 if not autotest_utils.grep('cpuset', '/proc/filesystems'):
mbligh68119582008-01-25 18:16:41 +0000403 print "Containers not enabled by latest reboot"
404 return # containers weren't enabled in this kernel boot
405 pid = os.getpid()
mbligh68119582008-01-25 18:16:41 +0000406 if not name:
407 name = 'test%d' % pid # make arbitrary unique name
408 self.container = cpuset.cpuset(name, job_size=mbytes,
mbligh337bb762008-04-16 21:23:10 +0000409 job_pid=pid, cpus=cpus, root=root)
mbligh68119582008-01-25 18:16:41 +0000410 # This job's python shell is now running in the new container
411 # and all forked test processes will inherit that container
412
413
414 def release_container(self):
415 if self.container:
mbligh337bb762008-04-16 21:23:10 +0000416 self.container.release()
mbligh68119582008-01-25 18:16:41 +0000417 self.container = None
418
419
420 def cpu_count(self):
421 if self.container:
422 return len(self.container.cpus)
jadmanskia9c75c42008-05-01 22:05:31 +0000423 return autotest_utils.count_cpus() # use total system count
mbligh68119582008-01-25 18:16:41 +0000424
425
apwce73d892007-09-25 16:53:05 +0000426 # Check the passed kernel identifier against the command line
427 # and the running kernel, abort the job on missmatch.
mbligh38a4a112008-03-19 13:11:34 +0000428 def kernel_check_ident(self, expected_when, expected_id, subdir,
jadmanskia9c75c42008-05-01 22:05:31 +0000429 type = 'src', patches=[]):
mbligh38a4a112008-03-19 13:11:34 +0000430 print (("POST BOOT: checking booted kernel " +
431 "mark=%d identity='%s' type='%s'") %
432 (expected_when, expected_id, type))
apwce73d892007-09-25 16:53:05 +0000433
jadmanskia9c75c42008-05-01 22:05:31 +0000434 running_id = autotest_utils.running_os_ident()
apwce73d892007-09-25 16:53:05 +0000435
jadmanskia9c75c42008-05-01 22:05:31 +0000436 cmdline = autotest_utils.read_one_line("/proc/cmdline")
apwce73d892007-09-25 16:53:05 +0000437
438 find_sum = re.compile(r'.*IDENT=(\d+)')
439 m = find_sum.match(cmdline)
440 cmdline_when = -1
441 if m:
442 cmdline_when = int(m.groups()[0])
443
444 # We have all the facts, see if they indicate we
445 # booted the requested kernel or not.
446 bad = False
mblighda0311e2007-10-25 16:03:33 +0000447 if (type == 'src' and expected_id != running_id or
jadmanskia9c75c42008-05-01 22:05:31 +0000448 type == 'rpm' and
449 not running_id.startswith(expected_id + '::')):
apwce73d892007-09-25 16:53:05 +0000450 print "check_kernel_ident: kernel identifier mismatch"
451 bad = True
452 if expected_when != cmdline_when:
453 print "check_kernel_ident: kernel command line mismatch"
454 bad = True
455
456 if bad:
457 print " Expected Ident: " + expected_id
458 print " Running Ident: " + running_id
459 print " Expected Mark: %d" % (expected_when)
460 print "Command Line Mark: %d" % (cmdline_when)
461 print " Command Line: " + cmdline
462
mbligh302482e2008-05-01 20:06:16 +0000463 raise error.JobError("boot failure", "reboot.verify")
apwce73d892007-09-25 16:53:05 +0000464
jadmanskia9c75c42008-05-01 22:05:31 +0000465 kernel_info = {'kernel': expected_id}
466 for i, patch in enumerate(patches):
467 kernel_info["patch%d" % i] = patch
mblighb7fd2702008-03-25 14:57:08 +0000468 self.record('GOOD', subdir, 'reboot.verify', expected_id)
jadmanskia9c75c42008-05-01 22:05:31 +0000469 self._decrement_group_level()
470 self.record('END GOOD', subdir, 'reboot',
471 optional_fields=kernel_info)
apwce73d892007-09-25 16:53:05 +0000472
473
mblighc2359852007-08-28 18:11:48 +0000474 def filesystem(self, device, mountpoint = None, loop_size = 0):
mblighd7fb4a62006-10-01 00:57:53 +0000475 if not mountpoint:
476 mountpoint = self.tmpdir
mblighc2359852007-08-28 18:11:48 +0000477 return filesystem.filesystem(self, device, mountpoint,loop_size)
mblighd7fb4a62006-10-01 00:57:53 +0000478
mblighcaa62c22008-04-07 21:51:17 +0000479
480 def enable_external_logging(self):
481 pass
482
483
484 def disable_external_logging(self):
485 pass
486
487
488 def reboot_setup(self):
489 pass
490
mblighcaa605c2006-10-02 00:37:35 +0000491
492 def reboot(self, tag='autotest'):
mblighcaa62c22008-04-07 21:51:17 +0000493 self.reboot_setup()
jadmanskia9c75c42008-05-01 22:05:31 +0000494 self.record('START', None, 'reboot')
495 self._increment_group_level()
mbligh30270302007-11-05 20:33:52 +0000496 self.record('GOOD', None, 'reboot.start')
apwde1503a2006-10-10 08:34:21 +0000497 self.harness.run_reboot()
apw11985b72007-10-04 15:44:47 +0000498 default = self.config_get('boot.set_default')
499 if default:
500 self.bootloader.set_default(tag)
501 else:
502 self.bootloader.boot_once(tag)
mbligh302482e2008-05-01 20:06:16 +0000503 cmd = "(sleep 5; reboot) </dev/null >/dev/null 2>&1 &"
504 autotest_utils.system(cmd)
apw0778a2f2006-10-06 03:11:40 +0000505 self.quit()
mblighcaa605c2006-10-02 00:37:35 +0000506
507
apw0865f482006-03-30 18:50:19 +0000508 def noop(self, text):
509 print "job: noop: " + text
510
mblighcaa605c2006-10-02 00:37:35 +0000511
mblighc86b0b42006-07-28 17:35:28 +0000512 def parallel(self, *tasklist):
513 """Run tasks in parallel"""
apw8fef4ac2006-10-10 22:53:37 +0000514
515 pids = []
mblighd528d302007-12-19 16:19:05 +0000516 old_log_filename = self.log_filename
517 for i, task in enumerate(tasklist):
518 self.log_filename = old_log_filename + (".%d" % i)
519 task_func = lambda: task[0](*task[1:])
mbligh302482e2008-05-01 20:06:16 +0000520 pids.append(parallel.fork_start(self.resultdir,
521 task_func))
mblighd528d302007-12-19 16:19:05 +0000522
523 old_log_path = os.path.join(self.resultdir, old_log_filename)
524 old_log = open(old_log_path, "a")
mblighd509b712008-01-14 17:41:25 +0000525 exceptions = []
mblighd528d302007-12-19 16:19:05 +0000526 for i, pid in enumerate(pids):
527 # wait for the task to finish
mblighd509b712008-01-14 17:41:25 +0000528 try:
mbligh302482e2008-05-01 20:06:16 +0000529 parallel.fork_waitfor(self.resultdir, pid)
mblighd509b712008-01-14 17:41:25 +0000530 except Exception, e:
531 exceptions.append(e)
mblighd528d302007-12-19 16:19:05 +0000532 # copy the logs from the subtask into the main log
533 new_log_path = old_log_path + (".%d" % i)
534 if os.path.exists(new_log_path):
535 new_log = open(new_log_path)
536 old_log.write(new_log.read())
537 new_log.close()
538 old_log.flush()
539 os.remove(new_log_path)
540 old_log.close()
541
542 self.log_filename = old_log_filename
apw0865f482006-03-30 18:50:19 +0000543
mblighd509b712008-01-14 17:41:25 +0000544 # handle any exceptions raised by the parallel tasks
545 if exceptions:
546 msg = "%d task(s) failed" % len(exceptions)
mbligh302482e2008-05-01 20:06:16 +0000547 raise error.JobError(msg, str(exceptions), exceptions)
mblighd509b712008-01-14 17:41:25 +0000548
mblighcaa605c2006-10-02 00:37:35 +0000549
apw0865f482006-03-30 18:50:19 +0000550 def quit(self):
mblighc86b0b42006-07-28 17:35:28 +0000551 # XXX: should have a better name.
apwde1503a2006-10-10 08:34:21 +0000552 self.harness.run_pause()
mbligh302482e2008-05-01 20:06:16 +0000553 raise error.JobContinue("more to come")
apw0865f482006-03-30 18:50:19 +0000554
mblighcaa605c2006-10-02 00:37:35 +0000555
apw0865f482006-03-30 18:50:19 +0000556 def complete(self, status):
mblighc86b0b42006-07-28 17:35:28 +0000557 """Clean up and exit"""
apw0865f482006-03-30 18:50:19 +0000558 # We are about to exit 'complete' so clean up the control file.
559 try:
mbligh366ff1b2008-04-25 16:07:56 +0000560 os.unlink(self.state_file)
apw0865f482006-03-30 18:50:19 +0000561 except:
562 pass
mblighc0b10d32008-03-03 16:03:28 +0000563
mbligh61a6c1a2006-12-25 01:26:38 +0000564 self.harness.run_complete()
mblighcaa62c22008-04-07 21:51:17 +0000565 self.disable_external_logging()
apw1b021902006-04-03 17:02:56 +0000566 sys.exit(status)
apw0865f482006-03-30 18:50:19 +0000567
mblighcaa605c2006-10-02 00:37:35 +0000568
mbligh366ff1b2008-04-25 16:07:56 +0000569 def set_state(self, var, val):
570 # Deep copies make sure that the state can't be altered
571 # without it being re-written. Perf wise, deep copies
572 # are overshadowed by pickling/loading.
573 self.state[var] = copy.deepcopy(val)
574 pickle.dump(self.state, open(self.state_file, 'w'))
575
576
577 def __load_state(self):
jadmanskia9c75c42008-05-01 22:05:31 +0000578 assert not hasattr(self, "state")
mbligh366ff1b2008-04-25 16:07:56 +0000579 try:
580 self.state = pickle.load(open(self.state_file, 'r'))
jadmanskia9c75c42008-05-01 22:05:31 +0000581 self.state_existed = True
mbligh366ff1b2008-04-25 16:07:56 +0000582 except Exception:
583 print "Initializing the state engine."
584 self.state = {}
mblighf1ae0a42008-04-25 16:09:20 +0000585 self.set_state('__steps', []) # writes pickle file
jadmanskia9c75c42008-05-01 22:05:31 +0000586 self.state_existed = False
mbligh366ff1b2008-04-25 16:07:56 +0000587
588
589 def get_state(self, var, default=None):
590 if var in self.state or default == None:
591 val = self.state[var]
592 else:
593 val = default
594 return copy.deepcopy(val)
595
596
mbligh12a04cb2008-04-25 16:07:20 +0000597 def __create_step_tuple(self, fn, args, dargs):
598 # Legacy code passes in an array where the first arg is
599 # the function or its name.
600 if isinstance(fn, list):
601 assert(len(args) == 0)
602 assert(len(dargs) == 0)
603 args = fn[1:]
604 fn = fn[0]
605 # Pickling actual functions is harry, thus we have to call
606 # them by name. Unfortunately, this means only functions
607 # defined globally can be used as a next step.
mblighb274ef52008-06-02 19:40:01 +0000608 if callable(fn):
mbligh12a04cb2008-04-25 16:07:20 +0000609 fn = fn.__name__
610 if not isinstance(fn, types.StringTypes):
611 raise StepError("Next steps must be functions or "
612 "strings containing the function name")
mblighb274ef52008-06-02 19:40:01 +0000613 ancestry = copy.copy(self.current_step_ancestry)
614 return (ancestry, fn, args, dargs)
mbligh12a04cb2008-04-25 16:07:20 +0000615
616
mbligh12a04cb2008-04-25 16:07:20 +0000617 def next_step(self, fn, *args, **dargs):
mblighc86b0b42006-07-28 17:35:28 +0000618 """Define the next step"""
mblighf1ae0a42008-04-25 16:09:20 +0000619 steps = self.get_state('__steps')
mbligh366ff1b2008-04-25 16:07:56 +0000620 steps.append(self.__create_step_tuple(fn, args, dargs))
mblighf1ae0a42008-04-25 16:09:20 +0000621 self.set_state('__steps', steps)
apw0865f482006-03-30 18:50:19 +0000622
mblighcaa605c2006-10-02 00:37:35 +0000623
mbligh12a04cb2008-04-25 16:07:20 +0000624 def next_step_prepend(self, fn, *args, **dargs):
mbligh237bed32007-09-05 13:05:57 +0000625 """Insert a new step, executing first"""
mblighf1ae0a42008-04-25 16:09:20 +0000626 steps = self.get_state('__steps')
mbligh366ff1b2008-04-25 16:07:56 +0000627 steps.insert(0, self.__create_step_tuple(fn, args, dargs))
mblighf1ae0a42008-04-25 16:09:20 +0000628 self.set_state('__steps', steps)
mbligh237bed32007-09-05 13:05:57 +0000629
630
mblighb274ef52008-06-02 19:40:01 +0000631 def _run_step_fn(self, local_vars, fn, args, dargs):
632 """Run a (step) function within the given context"""
633
634 local_vars['__args'] = args
635 local_vars['__dargs'] = dargs
636 exec('__ret = %s(*__args, **__dargs)' % fn,
637 local_vars, local_vars)
638 return local_vars['__ret']
639
640
641 def _create_frame(self, global_vars, ancestry, fn_name):
642 """Set up the environment like it would have been when this
643 function was first defined.
644
645 Child step engine 'implementations' must have 'return locals()'
646 at end end of their steps. Because of this, we can call the
647 parent function and get back all child functions (i.e. those
648 defined within it).
649
650 Unfortunately, the call stack of the function calling
651 job.next_step might have been deeper than the function it
652 added. In order to make sure that the environment is what it
653 should be, we need to then pop off the frames we built until
654 we find the frame where the function was first defined."""
655
656 # The copies ensure that the parent frames are not modified
657 # while building child frames. This matters if we then
658 # pop some frames in the next part of this function.
659 current_frame = copy.copy(global_vars)
660 frames = [current_frame]
661 for steps_fn_name in ancestry:
662 ret = self._run_step_fn(current_frame,
663 steps_fn_name, [], {})
664 current_frame = copy.copy(ret)
665 frames.append(current_frame)
666
667 while len(frames) > 2:
668 if fn_name not in frames[-2]:
669 break
670 if frames[-2][fn_name] != frames[-1][fn_name]:
671 break
672 frames.pop()
673 ancestry.pop()
674
675 return (frames[-1], ancestry)
676
677
678 def _add_step_init(self, local_vars, current_function):
679 """If the function returned a dictionary that includes a
680 function named 'step_init', prepend it to our list of steps.
681 This will only get run the first time a function with a nested
682 use of the step engine is run."""
683
684 if (isinstance(local_vars, dict) and
685 'step_init' in local_vars and
686 callable(local_vars['step_init'])):
687 # The init step is a child of the function
688 # we were just running.
689 self.current_step_ancestry.append(current_function)
690 self.next_step_prepend('step_init')
691
692
apw83f8d772006-04-27 14:12:56 +0000693 def step_engine(self):
mblighc86b0b42006-07-28 17:35:28 +0000694 """the stepping engine -- if the control file defines
695 step_init we will be using this engine to drive multiple runs.
696 """
697 """Do the next step"""
apw83f8d772006-04-27 14:12:56 +0000698
mbligh366ff1b2008-04-25 16:07:56 +0000699 # Set up the environment and then interpret the control file.
700 # Some control files will have code outside of functions,
701 # which means we need to have our state engine initialized
702 # before reading in the file.
mblighb274ef52008-06-02 19:40:01 +0000703 global_control_vars = {'job': self}
704 exec(JOB_PREAMBLE, global_control_vars, global_control_vars)
705 execfile(self.control, global_control_vars, global_control_vars)
apw83f8d772006-04-27 14:12:56 +0000706
mbligh366ff1b2008-04-25 16:07:56 +0000707 # If we loaded in a mid-job state file, then we presumably
708 # know what steps we have yet to run.
jadmanskia9c75c42008-05-01 22:05:31 +0000709 if not self.state_existed:
mblighb274ef52008-06-02 19:40:01 +0000710 if global_control_vars.has_key('step_init'):
711 self.next_step(global_control_vars['step_init'])
apw0865f482006-03-30 18:50:19 +0000712
mbligh366ff1b2008-04-25 16:07:56 +0000713 # Iterate through the steps. If we reboot, we'll simply
714 # continue iterating on the next step.
mblighf1ae0a42008-04-25 16:09:20 +0000715 while len(self.get_state('__steps')) > 0:
716 steps = self.get_state('__steps')
mblighb274ef52008-06-02 19:40:01 +0000717 (ancestry, fn_name, args, dargs) = steps.pop(0)
mblighf1ae0a42008-04-25 16:09:20 +0000718 self.set_state('__steps', steps)
apw0865f482006-03-30 18:50:19 +0000719
mblighb274ef52008-06-02 19:40:01 +0000720 ret = self._create_frame(global_control_vars, ancestry,
721 fn_name)
722 local_vars, self.current_step_ancestry = ret
723 local_vars = self._run_step_fn(local_vars, fn_name,
724 args, dargs)
725 self._add_step_init(local_vars, fn_name)
apw0865f482006-03-30 18:50:19 +0000726
mblighcaa605c2006-10-02 00:37:35 +0000727
jadmanskia9c75c42008-05-01 22:05:31 +0000728 def _init_group_level(self):
729 self.group_level = self.get_state("__group_level", default=0)
730
731
732 def _increment_group_level(self):
733 self.group_level += 1
734 self.set_state("__group_level", self.group_level)
735
736
737 def _decrement_group_level(self):
738 self.group_level -= 1
739 self.set_state("__group_level", self.group_level)
740
741
742 def record(self, status_code, subdir, operation, status = '',
743 optional_fields=None):
mbligh09f288a2007-09-18 21:34:57 +0000744 """
745 Record job-level status
apw7db8d0b2006-10-09 08:10:25 +0000746
mbligh09f288a2007-09-18 21:34:57 +0000747 The intent is to make this file both machine parseable and
748 human readable. That involves a little more complexity, but
749 really isn't all that bad ;-)
750
751 Format is <status code>\t<subdir>\t<operation>\t<status>
752
753 status code: (GOOD|WARN|FAIL|ABORT)
754 or START
755 or END (GOOD|WARN|FAIL|ABORT)
756
757 subdir: MUST be a relevant subdirectory in the results,
758 or None, which will be represented as '----'
759
760 operation: description of what you ran (e.g. "dbench", or
761 "mkfs -t foobar /dev/sda9")
762
763 status: error message or "completed sucessfully"
764
765 ------------------------------------------------------------
766
767 Initial tabs indicate indent levels for grouping, and is
mbligh7dd510c2007-11-13 17:11:22 +0000768 governed by self.group_level
mbligh09f288a2007-09-18 21:34:57 +0000769
770 multiline messages have secondary lines prefaced by a double
771 space (' ')
772 """
773
mblighb0570ad2007-09-19 18:18:11 +0000774 if subdir:
775 if re.match(r'[\n\t]', subdir):
jadmanskia9c75c42008-05-01 22:05:31 +0000776 raise ValueError("Invalid character in "
777 "subdir string")
mblighb0570ad2007-09-19 18:18:11 +0000778 substr = subdir
779 else:
780 substr = '----'
mbligh09f288a2007-09-18 21:34:57 +0000781
mbligh302482e2008-05-01 20:06:16 +0000782 if not logging.is_valid_status(status_code):
jadmanskia9c75c42008-05-01 22:05:31 +0000783 raise ValueError("Invalid status code supplied: %s" %
784 status_code)
mbligh9c5ac322007-10-31 18:01:59 +0000785 if not operation:
786 operation = '----'
jadmanskia9c75c42008-05-01 22:05:31 +0000787
mbligh09f288a2007-09-18 21:34:57 +0000788 if re.match(r'[\n\t]', operation):
jadmanskia9c75c42008-05-01 22:05:31 +0000789 raise ValueError("Invalid character in "
790 "operation string")
mbligh09f288a2007-09-18 21:34:57 +0000791 operation = operation.rstrip()
jadmanskia9c75c42008-05-01 22:05:31 +0000792
793 if not optional_fields:
794 optional_fields = {}
795
mbligh09f288a2007-09-18 21:34:57 +0000796 status = status.rstrip()
797 status = re.sub(r"\t", " ", status)
apw7db8d0b2006-10-09 08:10:25 +0000798 # Ensure any continuation lines are marked so we can
799 # detect them in the status file to ensure it is parsable.
jadmanskia9c75c42008-05-01 22:05:31 +0000800 status = re.sub(r"\n", "\n" + "\t" * self.group_level + " ",
801 status)
mbligh09f288a2007-09-18 21:34:57 +0000802
mbligh30270302007-11-05 20:33:52 +0000803 # Generate timestamps for inclusion in the logs
804 epoch_time = int(time.time()) # seconds since epoch, in UTC
805 local_time = time.localtime(epoch_time)
jadmanskia9c75c42008-05-01 22:05:31 +0000806 optional_fields["timestamp"] = str(epoch_time)
807 optional_fields["localtime"] = time.strftime("%b %d %H:%M:%S",
808 local_time)
mbligh30270302007-11-05 20:33:52 +0000809
jadmanskia9c75c42008-05-01 22:05:31 +0000810 fields = [status_code, substr, operation]
811 fields += ["%s=%s" % x for x in optional_fields.iteritems()]
812 fields.append(status)
813
814 msg = '\t'.join(str(x) for x in fields)
mbligh7dd510c2007-11-13 17:11:22 +0000815 msg = '\t' * self.group_level + msg
apw7db8d0b2006-10-09 08:10:25 +0000816
mblighd528d302007-12-19 16:19:05 +0000817 msg_tag = ""
818 if "." in self.log_filename:
819 msg_tag = self.log_filename.split(".", 1)[1]
820
jadmanskia9c75c42008-05-01 22:05:31 +0000821 self.harness.test_status_detail(status_code, substr,
822 operation, status, msg_tag)
mblighd528d302007-12-19 16:19:05 +0000823 self.harness.test_status(msg, msg_tag)
824
825 # log to stdout (if enabled)
826 #if self.log_filename == self.DEFAULT_LOG_FILENAME:
apwf1a81162006-04-25 10:10:29 +0000827 print msg
mblighd528d302007-12-19 16:19:05 +0000828
829 # log to the "root" status log
830 status_file = os.path.join(self.resultdir, self.log_filename)
mbligh7dd510c2007-11-13 17:11:22 +0000831 open(status_file, "a").write(msg + "\n")
mblighd528d302007-12-19 16:19:05 +0000832
833 # log to the subdir status log (if subdir is set)
mblighb0570ad2007-09-19 18:18:11 +0000834 if subdir:
mblighadff6ca2008-01-22 16:38:25 +0000835 dir = os.path.join(self.resultdir, subdir)
836 if not os.path.exists(dir):
837 os.mkdir(dir)
838
839 status_file = os.path.join(dir,
mblighd528d302007-12-19 16:19:05 +0000840 self.DEFAULT_LOG_FILENAME)
mblighb0570ad2007-09-19 18:18:11 +0000841 open(status_file, "a").write(msg + "\n")
apwce9abe92006-04-27 14:14:04 +0000842
843
jadmanski8415f962008-05-06 20:38:53 +0000844class disk_usage_monitor:
845 def __init__(self, logging_func, device, max_mb_per_hour):
846 self.func = logging_func
847 self.device = device
848 self.max_mb_per_hour = max_mb_per_hour
849
850
851 def start(self):
852 self.initial_space = autotest_utils.freespace(self.device)
853 self.start_time = time.time()
854
855
856 def stop(self):
857 # if no maximum usage rate was set, we don't need to
858 # generate any warnings
859 if not self.max_mb_per_hour:
860 return
861
862 final_space = autotest_utils.freespace(self.device)
863 used_space = self.initial_space - final_space
864 stop_time = time.time()
865 total_time = stop_time - self.start_time
866 # round up the time to one minute, to keep extremely short
867 # tests from generating false positives due to short, badly
868 # timed bursts of activity
869 total_time = max(total_time, 60.0)
870
871 # determine the usage rate
872 bytes_per_sec = used_space / total_time
873 mb_per_sec = bytes_per_sec / 1024**2
874 mb_per_hour = mb_per_sec * 60 * 60
875
876 if mb_per_hour > self.max_mb_per_hour:
877 msg = ("disk space on %s was consumed at a rate of "
878 "%.2f MB/hour")
879 msg %= (self.device, mb_per_hour)
880 self.func(msg)
881
882
883 @classmethod
884 def watch(cls, *monitor_args, **monitor_dargs):
885 """ Generic decorator to wrap a function call with the
886 standard create-monitor -> start -> call -> stop idiom."""
887 def decorator(func):
888 def watched_func(*args, **dargs):
889 monitor = cls(*monitor_args, **monitor_dargs)
890 monitor.start()
891 try:
892 func(*args, **dargs)
893 finally:
894 monitor.stop()
895 return watched_func
896 return decorator
897
898
mblighcaa62c22008-04-07 21:51:17 +0000899def runjob(control, cont = False, tag = "default", harness_type = '',
900 use_external_logging = False):
mblighc86b0b42006-07-28 17:35:28 +0000901 """The main interface to this module
902
mbligh72b88fc2006-12-16 18:41:35 +0000903 control
mblighc86b0b42006-07-28 17:35:28 +0000904 The control file to use for this job.
905 cont
906 Whether this is the continuation of a previously started job
907 """
mblighb4eef242007-07-23 18:22:49 +0000908 control = os.path.abspath(control)
apwce9abe92006-04-27 14:14:04 +0000909 state = control + '.state'
910
911 # instantiate the job object ready for the control file.
912 myjob = None
913 try:
914 # Check that the control file is valid
915 if not os.path.exists(control):
mbligh302482e2008-05-01 20:06:16 +0000916 raise error.JobError(control +
917 ": control file not found")
apwce9abe92006-04-27 14:14:04 +0000918
919 # When continuing, the job is complete when there is no
920 # state file, ensure we don't try and continue.
mblighf3fef462006-09-13 16:05:05 +0000921 if cont and not os.path.exists(state):
mbligh302482e2008-05-01 20:06:16 +0000922 raise error.JobComplete("all done")
mblighf3fef462006-09-13 16:05:05 +0000923 if cont == False and os.path.exists(state):
apwce9abe92006-04-27 14:14:04 +0000924 os.unlink(state)
925
mblighcaa62c22008-04-07 21:51:17 +0000926 myjob = job(control, tag, cont, harness_type,
927 use_external_logging)
apwce9abe92006-04-27 14:14:04 +0000928
929 # Load in the users control file, may do any one of:
930 # 1) execute in toto
931 # 2) define steps, and select the first via next_step()
932 myjob.step_engine()
933
mbligh302482e2008-05-01 20:06:16 +0000934 except error.JobContinue:
apwce9abe92006-04-27 14:14:04 +0000935 sys.exit(5)
936
mbligh302482e2008-05-01 20:06:16 +0000937 except error.JobComplete:
apwb832e1b2007-11-24 20:24:38 +0000938 sys.exit(1)
939
mbligh302482e2008-05-01 20:06:16 +0000940 except error.JobError, instance:
apwce9abe92006-04-27 14:14:04 +0000941 print "JOB ERROR: " + instance.args[0]
mbligh9c5ac322007-10-31 18:01:59 +0000942 if myjob:
mbligh30270302007-11-05 20:33:52 +0000943 command = None
944 if len(instance.args) > 1:
945 command = instance.args[1]
946 myjob.record('ABORT', None, command, instance.args[0])
jadmanskia9c75c42008-05-01 22:05:31 +0000947 myjob._decrement_group_level()
mblighc3430162007-11-14 23:57:19 +0000948 myjob.record('END ABORT', None, None)
jadmanskia9c75c42008-05-01 22:05:31 +0000949 assert(myjob.group_level == 0)
apwce9abe92006-04-27 14:14:04 +0000950 myjob.complete(1)
apwb832e1b2007-11-24 20:24:38 +0000951 else:
952 sys.exit(1)
apwce9abe92006-04-27 14:14:04 +0000953
mblighc3430162007-11-14 23:57:19 +0000954 except Exception, e:
mbligh302482e2008-05-01 20:06:16 +0000955 msg = str(e) + '\n' + traceback.format_exc()
mblighc3430162007-11-14 23:57:19 +0000956 print "JOB ERROR: " + msg
mblighfbfb77d2007-02-15 18:54:03 +0000957 if myjob:
mblighc3430162007-11-14 23:57:19 +0000958 myjob.record('ABORT', None, None, msg)
jadmanskia9c75c42008-05-01 22:05:31 +0000959 myjob._decrement_group_level()
mblighc3430162007-11-14 23:57:19 +0000960 myjob.record('END ABORT', None, None)
jadmanskia9c75c42008-05-01 22:05:31 +0000961 assert(myjob.group_level == 0)
mbligh9c5ac322007-10-31 18:01:59 +0000962 myjob.complete(1)
apwb832e1b2007-11-24 20:24:38 +0000963 else:
964 sys.exit(1)
mbligh892d37f2007-03-01 17:03:25 +0000965
mbligh0144e5a2008-03-07 18:17:53 +0000966 # If we get here, then we assume the job is complete and good.
jadmanskia9c75c42008-05-01 22:05:31 +0000967 myjob._decrement_group_level()
mbligh0144e5a2008-03-07 18:17:53 +0000968 myjob.record('END GOOD', None, None)
jadmanskia9c75c42008-05-01 22:05:31 +0000969 assert(myjob.group_level == 0)
mbligh0144e5a2008-03-07 18:17:53 +0000970
mbligh892d37f2007-03-01 17:03:25 +0000971 myjob.complete(0)
mblighcaa62c22008-04-07 21:51:17 +0000972
973
974# site_job.py may be non-existant or empty, make sure that an appropriate
975# site_job class is created nevertheless
976try:
977 from site_job import site_job
978except ImportError:
979 class site_job(base_job):
980 pass
981
982class job(site_job):
983 pass
jadmanski87cbc7f2008-05-13 18:17:10 +0000984