blob: caeecfd7b069d482ffe39db8d23ac880abc9a7f4 [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
mblighe829ba52008-06-03 15:04:08 +000013from autotest_lib.client.common_lib import error, barrier, logging, utils
mbligh302482e2008-05-01 20:06:16 +000014
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 = []
mbligh8f4d0432008-06-02 19:42:50 +000089 self.next_step_index = 0
jadmanskia9c75c42008-05-01 22:05:31 +000090 self.__load_state()
mbligha2508052006-05-28 21:29:53 +000091
apw96da1a42006-11-02 00:23:18 +000092 if not cont:
mblighc1cbc992008-05-27 20:01:45 +000093 """
94 Don't cleanup the tmp dir (which contains the lockfile)
95 in the constructor, this would be a problem for multiple
96 jobs starting at the same time on the same client. Instead
97 do the delete at the server side. We simply create the tmp
98 directory here if it does not already exist.
99 """
100 if not os.path.exists(self.tmpdir):
101 os.mkdir(self.tmpdir)
apw96da1a42006-11-02 00:23:18 +0000102
apw870988b2007-09-25 16:50:53 +0000103 results = os.path.join(self.autodir, 'results')
104 if not os.path.exists(results):
105 os.mkdir(results)
mblighfbfb77d2007-02-15 18:54:03 +0000106
apwf3d28622007-09-25 16:49:17 +0000107 download = os.path.join(self.testdir, 'download')
mblighc1cbc992008-05-27 20:01:45 +0000108 if not os.path.exists(download):
109 os.mkdir(download)
110
apw96da1a42006-11-02 00:23:18 +0000111 if os.path.exists(self.resultdir):
mblighe829ba52008-06-03 15:04:08 +0000112 utils.system('rm -rf '
mbligh302482e2008-05-01 20:06:16 +0000113 + self.resultdir)
apw96da1a42006-11-02 00:23:18 +0000114 os.mkdir(self.resultdir)
mbligh0fb83972008-01-10 16:30:02 +0000115 os.mkdir(self.sysinfodir)
apw96da1a42006-11-02 00:23:18 +0000116
apw870988b2007-09-25 16:50:53 +0000117 os.mkdir(os.path.join(self.resultdir, 'debug'))
118 os.mkdir(os.path.join(self.resultdir, 'analysis'))
apw870988b2007-09-25 16:50:53 +0000119
mbligh8d83cdc2007-12-03 18:09:18 +0000120 shutil.copyfile(self.control,
121 os.path.join(self.resultdir, 'control'))
mblighf4ca14f2008-03-03 16:03:05 +0000122
mbligh4b089662006-06-14 22:34:58 +0000123
apwecf41b72006-03-31 14:00:55 +0000124 self.control = control
mbligh27113602007-10-31 21:07:51 +0000125 self.jobtag = jobtag
mblighd528d302007-12-19 16:19:05 +0000126 self.log_filename = self.DEFAULT_LOG_FILENAME
mbligh68119582008-01-25 18:16:41 +0000127 self.container = None
mblighf4c35322006-03-13 01:01:10 +0000128
mbligh56f1fbb2006-10-01 15:10:56 +0000129 self.stdout = fd_stack.fd_stack(1, sys.stdout)
130 self.stderr = fd_stack.fd_stack(2, sys.stderr)
jadmanskia9c75c42008-05-01 22:05:31 +0000131
132 self._init_group_level()
mblighf4c35322006-03-13 01:01:10 +0000133
apw059e1b12006-10-12 17:18:26 +0000134 self.config = config.config(self)
135
apwd27e55f2006-12-01 11:22:08 +0000136 self.harness = harness.select(harness_type, self)
137
mbligha35553b2006-04-23 15:52:25 +0000138 self.profilers = profilers.profilers(self)
mbligh72905562006-05-25 01:30:49 +0000139
mblighcaa605c2006-10-02 00:37:35 +0000140 try:
apw90154af2006-12-01 11:23:36 +0000141 tool = self.config_get('boottool.executable')
142 self.bootloader = boottool.boottool(tool)
mblighcaa605c2006-10-02 00:37:35 +0000143 except:
144 pass
145
mbligh0fb83972008-01-10 16:30:02 +0000146 sysinfo.log_per_reboot_data(self.sysinfodir)
mbligh3a6d6ca2006-04-23 15:50:24 +0000147
mbligh30270302007-11-05 20:33:52 +0000148 if not cont:
mblighc3430162007-11-14 23:57:19 +0000149 self.record('START', None, None)
jadmanskia9c75c42008-05-01 22:05:31 +0000150 self._increment_group_level()
apw357f50f2006-12-01 11:22:39 +0000151
apwf91efaf2007-11-24 17:32:13 +0000152 self.harness.run_start()
mblighcaa62c22008-04-07 21:51:17 +0000153
154 if use_external_logging:
155 self.enable_external_logging()
apwf91efaf2007-11-24 17:32:13 +0000156
jadmanski8415f962008-05-06 20:38:53 +0000157 # load the max disk usage rate - default to no monitoring
158 self.max_disk_usage_rate = self.get_state('__monitor_disk',
159 default=0.0)
160
161
162 def monitor_disk_usage(self, max_rate):
163 """\
164 Signal that the job should monitor disk space usage on /
165 and generate a warning if a test uses up disk space at a
166 rate exceeding 'max_rate'.
167
168 Parameters:
169 max_rate - the maximium allowed rate of disk consumption
170 during a test, in MB/hour, or 0 to indicate
171 no limit.
172 """
173 self.set_state('__monitor_disk', max_rate)
174 self.max_disk_usage_rate = max_rate
175
mbligh0692e472007-08-30 16:07:53 +0000176
177 def relative_path(self, path):
178 """\
179 Return a patch relative to the job results directory
180 """
mbligh1c250ca2007-08-30 16:31:38 +0000181 head = len(self.resultdir) + 1 # remove the / inbetween
182 return path[head:]
mbligh0692e472007-08-30 16:07:53 +0000183
184
mbligh362ab3d2007-08-30 11:24:04 +0000185 def control_get(self):
186 return self.control
187
mblighcaa605c2006-10-02 00:37:35 +0000188
mbligh8d83cdc2007-12-03 18:09:18 +0000189 def control_set(self, control):
190 self.control = os.path.abspath(control)
191
192
apwde1503a2006-10-10 08:34:21 +0000193 def harness_select(self, which):
194 self.harness = harness.select(which, self)
195
196
apw059e1b12006-10-12 17:18:26 +0000197 def config_set(self, name, value):
198 self.config.set(name, value)
199
200
201 def config_get(self, name):
202 return self.config.get(name)
203
mbligh8baa2ea2006-12-17 23:01:24 +0000204 def setup_dirs(self, results_dir, tmp_dir):
mbligh1e8858e2006-11-24 22:18:35 +0000205 if not tmp_dir:
apw870988b2007-09-25 16:50:53 +0000206 tmp_dir = os.path.join(self.tmpdir, 'build')
mbligh1e8858e2006-11-24 22:18:35 +0000207 if not os.path.exists(tmp_dir):
208 os.mkdir(tmp_dir)
209 if not os.path.isdir(tmp_dir):
mbligh642b03e2008-01-14 16:53:15 +0000210 e_msg = "Temp dir (%s) is not a dir - args backwards?" % self.tmpdir
211 raise ValueError(e_msg)
mbligh1e8858e2006-11-24 22:18:35 +0000212
213 # We label the first build "build" and then subsequent ones
214 # as "build.2", "build.3", etc. Whilst this is a little bit
215 # inconsistent, 99.9% of jobs will only have one build
216 # (that's not done as kernbench, sparse, or buildtest),
217 # so it works out much cleaner. One of life's comprimises.
218 if not results_dir:
219 results_dir = os.path.join(self.resultdir, 'build')
220 i = 2
221 while os.path.exists(results_dir):
222 results_dir = os.path.join(self.resultdir, 'build.%d' % i)
mblighd9223fc2006-11-26 17:19:54 +0000223 i += 1
mbligh1e8858e2006-11-24 22:18:35 +0000224 if not os.path.exists(results_dir):
225 os.mkdir(results_dir)
mbligh72b88fc2006-12-16 18:41:35 +0000226
mbligh8baa2ea2006-12-17 23:01:24 +0000227 return (results_dir, tmp_dir)
228
229
230 def xen(self, base_tree, results_dir = '', tmp_dir = '', leave = False, \
231 kjob = None ):
232 """Summon a xen object"""
233 (results_dir, tmp_dir) = self.setup_dirs(results_dir, tmp_dir)
234 build_dir = 'xen'
235 return xen.xen(self, base_tree, results_dir, tmp_dir, build_dir, leave, kjob)
236
237
238 def kernel(self, base_tree, results_dir = '', tmp_dir = '', leave = False):
239 """Summon a kernel object"""
mbligh669caa12007-11-05 18:32:13 +0000240 (results_dir, tmp_dir) = self.setup_dirs(results_dir, tmp_dir)
mbligh8baa2ea2006-12-17 23:01:24 +0000241 build_dir = 'linux'
mbligh6ee7ee02007-11-13 23:49:05 +0000242 return kernel.auto_kernel(self, base_tree, results_dir,
243 tmp_dir, build_dir, leave)
mblighf4c35322006-03-13 01:01:10 +0000244
mblighcaa605c2006-10-02 00:37:35 +0000245
mbligh6b504ff2007-12-12 21:03:49 +0000246 def barrier(self, *args, **kwds):
mblighfadca202006-09-23 04:40:01 +0000247 """Create a barrier object"""
mbligh6b504ff2007-12-12 21:03:49 +0000248 return barrier.barrier(*args, **kwds)
mblighfadca202006-09-23 04:40:01 +0000249
mblighcaa605c2006-10-02 00:37:35 +0000250
mbligh4b089662006-06-14 22:34:58 +0000251 def setup_dep(self, deps):
mblighc86b0b42006-07-28 17:35:28 +0000252 """Set up the dependencies for this test.
253
254 deps is a list of libraries required for this test.
255 """
mbligh4b089662006-06-14 22:34:58 +0000256 for dep in deps:
257 try:
apw870988b2007-09-25 16:50:53 +0000258 os.chdir(os.path.join(self.autodir, 'deps', dep))
mblighe829ba52008-06-03 15:04:08 +0000259 utils.system('./' + dep + '.py')
mbligh4b089662006-06-14 22:34:58 +0000260 except:
mbligh302482e2008-05-01 20:06:16 +0000261 err = "setting up dependency " + dep + "\n"
262 raise error.UnhandledError(err)
mbligh4b089662006-06-14 22:34:58 +0000263
264
mbligh72b88fc2006-12-16 18:41:35 +0000265 def __runtest(self, url, tag, args, dargs):
266 try:
mbligh53c41502007-10-23 20:45:04 +0000267 l = lambda : test.runtest(self, url, tag, args, dargs)
mbligh302482e2008-05-01 20:06:16 +0000268 pid = parallel.fork_start(self.resultdir, l)
269 parallel.fork_waitfor(self.resultdir, pid)
270 except error.AutotestError:
mbligh72b88fc2006-12-16 18:41:35 +0000271 raise
jadmanskicf8c4d62008-05-27 22:09:14 +0000272 except Exception, e:
273 msg = "Unhandled %s error occured during test\n"
274 msg %= str(e.__class__.__name__)
275 raise error.UnhandledError(msg)
apwf1a81162006-04-25 10:10:29 +0000276
mblighcaa605c2006-10-02 00:37:35 +0000277
mblighd016ecc2006-11-25 21:41:07 +0000278 def run_test(self, url, *args, **dargs):
mblighc86b0b42006-07-28 17:35:28 +0000279 """Summon a test object and run it.
280
281 tag
282 tag to add to testname
mbligh12a7df72006-10-06 03:54:33 +0000283 url
284 url of the test to run
mblighc86b0b42006-07-28 17:35:28 +0000285 """
mbligh12a7df72006-10-06 03:54:33 +0000286
mblighd016ecc2006-11-25 21:41:07 +0000287 if not url:
mbligh302482e2008-05-01 20:06:16 +0000288 raise TypeError("Test name is invalid. "
289 "Switched arguments?")
mbligh09f288a2007-09-18 21:34:57 +0000290 (group, testname) = test.testname(url)
mbligh7dd510c2007-11-13 17:11:22 +0000291 tag = dargs.pop('tag', None)
mbligh65938a22007-12-10 16:58:52 +0000292 container = dargs.pop('container', None)
mbligh09f288a2007-09-18 21:34:57 +0000293 subdir = testname
mbligh7dd510c2007-11-13 17:11:22 +0000294 if tag:
295 subdir += '.' + tag
296
mbligh65938a22007-12-10 16:58:52 +0000297 if container:
mbligh68119582008-01-25 18:16:41 +0000298 cname = container.get('name', None)
299 if not cname: # get old name
300 cname = container.get('container_name', None)
301 mbytes = container.get('mbytes', None)
302 if not mbytes: # get old name
303 mbytes = container.get('mem', None)
304 cpus = container.get('cpus', None)
305 if not cpus: # get old name
306 cpus = container.get('cpu', None)
jadmanski87cbc7f2008-05-13 18:17:10 +0000307 root = container.get('root', None)
mbligh68119582008-01-25 18:16:41 +0000308 self.new_container(mbytes=mbytes, cpus=cpus,
309 root=root, name=cname)
mbligh65938a22007-12-10 16:58:52 +0000310 # We are running in a container now...
311
jadmanski8415f962008-05-06 20:38:53 +0000312 def log_warning(reason):
313 self.record("WARN", subdir, testname, reason)
314 @disk_usage_monitor.watch(log_warning, "/",
315 self.max_disk_usage_rate)
mbligh7dd510c2007-11-13 17:11:22 +0000316 def group_func():
apwf1a81162006-04-25 10:10:29 +0000317 try:
mblighd016ecc2006-11-25 21:41:07 +0000318 self.__runtest(url, tag, args, dargs)
mbligh302482e2008-05-01 20:06:16 +0000319 except error.TestNAError, detail:
320 self.record('TEST_NA', subdir, testname,
321 str(detail))
322 raise
apwf1a81162006-04-25 10:10:29 +0000323 except Exception, detail:
mbligh7dd510c2007-11-13 17:11:22 +0000324 self.record('FAIL', subdir, testname,
325 str(detail))
apwf1a81162006-04-25 10:10:29 +0000326 raise
327 else:
mbligh7dd510c2007-11-13 17:11:22 +0000328 self.record('GOOD', subdir, testname,
329 'completed successfully')
jadmanski8415f962008-05-06 20:38:53 +0000330
mblighcfc6dd32007-11-20 00:44:35 +0000331 result, exc_info = self.__rungroup(subdir, group_func)
mbligh68119582008-01-25 18:16:41 +0000332 if container:
333 self.release_container()
mbligh302482e2008-05-01 20:06:16 +0000334 if exc_info and isinstance(exc_info[1], error.TestError):
mbligh7dd510c2007-11-13 17:11:22 +0000335 return False
336 elif exc_info:
mbligh71ea2492008-01-15 20:35:52 +0000337 raise exc_info[0], exc_info[1], exc_info[2]
apwf1a81162006-04-25 10:10:29 +0000338 else:
mbligh7dd510c2007-11-13 17:11:22 +0000339 return True
340
341
342 def __rungroup(self, name, function, *args, **dargs):
343 """\
344 name:
345 name of the group
346 function:
347 subroutine to run
348 *args:
349 arguments for the function
350
351 Returns a 2-tuple (result, exc_info) where result
352 is the return value of function, and exc_info is
353 the sys.exc_info() of the exception thrown by the
354 function (which may be None).
355 """
356
357 result, exc_info = None, None
358 try:
359 self.record('START', None, name)
jadmanskia9c75c42008-05-01 22:05:31 +0000360 self._increment_group_level()
mbligh7dd510c2007-11-13 17:11:22 +0000361 result = function(*args, **dargs)
jadmanskia9c75c42008-05-01 22:05:31 +0000362 self._decrement_group_level()
mbligh7dd510c2007-11-13 17:11:22 +0000363 self.record('END GOOD', None, name)
mbligh302482e2008-05-01 20:06:16 +0000364 except error.TestNAError, e:
jadmanskia9c75c42008-05-01 22:05:31 +0000365 self._decrement_group_level()
mbligh302482e2008-05-01 20:06:16 +0000366 self.record('END TEST_NA', None, name, str(e))
mbligh7dd510c2007-11-13 17:11:22 +0000367 except Exception, e:
368 exc_info = sys.exc_info()
jadmanskia9c75c42008-05-01 22:05:31 +0000369 self._decrement_group_level()
mbligh302482e2008-05-01 20:06:16 +0000370 err_msg = str(e) + '\n' + traceback.format_exc()
mbligh51144e02007-11-20 20:38:18 +0000371 self.record('END FAIL', None, name, err_msg)
mbligh7dd510c2007-11-13 17:11:22 +0000372
373 return result, exc_info
apw0865f482006-03-30 18:50:19 +0000374
mblighd7fb4a62006-10-01 00:57:53 +0000375
apw1da244b2007-09-27 17:18:01 +0000376 def run_group(self, function, *args, **dargs):
mbligh88ab90f2007-08-29 15:52:49 +0000377 """\
378 function:
379 subroutine to run
380 *args:
381 arguments for the function
382 """
383
mbligh7dd510c2007-11-13 17:11:22 +0000384 # Allow the tag for the group to be specified
mbligh88ab90f2007-08-29 15:52:49 +0000385 name = function.__name__
mbligh7dd510c2007-11-13 17:11:22 +0000386 tag = dargs.pop('tag', None)
387 if tag:
388 name = tag
apw1da244b2007-09-27 17:18:01 +0000389
mbligh7dd510c2007-11-13 17:11:22 +0000390 result, exc_info = self.__rungroup(name, function,
391 *args, **dargs)
apw1da244b2007-09-27 17:18:01 +0000392
mbligh7dd510c2007-11-13 17:11:22 +0000393 # if there was a non-TestError exception, raise it
mbligh302482e2008-05-01 20:06:16 +0000394 if exc_info and not isinstance(exc_info[1], error.TestError):
mbligh7dd510c2007-11-13 17:11:22 +0000395 err = ''.join(traceback.format_exception(*exc_info))
mbligh302482e2008-05-01 20:06:16 +0000396 raise error.TestError(name + ' failed\n' + err)
mbligh88ab90f2007-08-29 15:52:49 +0000397
mbligh7dd510c2007-11-13 17:11:22 +0000398 # pass back the actual return value from the function
apw08403ca2007-09-27 17:17:22 +0000399 return result
400
mbligh88ab90f2007-08-29 15:52:49 +0000401
jadmanski87cbc7f2008-05-13 18:17:10 +0000402 def new_container(self, mbytes=None, cpus=None, root=None, name=None):
mbligh8ea61e22008-05-09 18:09:37 +0000403 if not autotest_utils.grep('cpuset', '/proc/filesystems'):
mbligh68119582008-01-25 18:16:41 +0000404 print "Containers not enabled by latest reboot"
405 return # containers weren't enabled in this kernel boot
406 pid = os.getpid()
mbligh68119582008-01-25 18:16:41 +0000407 if not name:
408 name = 'test%d' % pid # make arbitrary unique name
409 self.container = cpuset.cpuset(name, job_size=mbytes,
mbligh337bb762008-04-16 21:23:10 +0000410 job_pid=pid, cpus=cpus, root=root)
mbligh68119582008-01-25 18:16:41 +0000411 # This job's python shell is now running in the new container
412 # and all forked test processes will inherit that container
413
414
415 def release_container(self):
416 if self.container:
mbligh337bb762008-04-16 21:23:10 +0000417 self.container.release()
mbligh68119582008-01-25 18:16:41 +0000418 self.container = None
419
420
421 def cpu_count(self):
422 if self.container:
423 return len(self.container.cpus)
jadmanskia9c75c42008-05-01 22:05:31 +0000424 return autotest_utils.count_cpus() # use total system count
mbligh68119582008-01-25 18:16:41 +0000425
426
apwce73d892007-09-25 16:53:05 +0000427 # Check the passed kernel identifier against the command line
428 # and the running kernel, abort the job on missmatch.
mbligh38a4a112008-03-19 13:11:34 +0000429 def kernel_check_ident(self, expected_when, expected_id, subdir,
jadmanskia9c75c42008-05-01 22:05:31 +0000430 type = 'src', patches=[]):
mbligh38a4a112008-03-19 13:11:34 +0000431 print (("POST BOOT: checking booted kernel " +
432 "mark=%d identity='%s' type='%s'") %
433 (expected_when, expected_id, type))
apwce73d892007-09-25 16:53:05 +0000434
jadmanskia9c75c42008-05-01 22:05:31 +0000435 running_id = autotest_utils.running_os_ident()
apwce73d892007-09-25 16:53:05 +0000436
mblighe829ba52008-06-03 15:04:08 +0000437 cmdline = utils.read_one_line("/proc/cmdline")
apwce73d892007-09-25 16:53:05 +0000438
439 find_sum = re.compile(r'.*IDENT=(\d+)')
440 m = find_sum.match(cmdline)
441 cmdline_when = -1
442 if m:
443 cmdline_when = int(m.groups()[0])
444
445 # We have all the facts, see if they indicate we
446 # booted the requested kernel or not.
447 bad = False
mblighda0311e2007-10-25 16:03:33 +0000448 if (type == 'src' and expected_id != running_id or
jadmanskia9c75c42008-05-01 22:05:31 +0000449 type == 'rpm' and
450 not running_id.startswith(expected_id + '::')):
apwce73d892007-09-25 16:53:05 +0000451 print "check_kernel_ident: kernel identifier mismatch"
452 bad = True
453 if expected_when != cmdline_when:
454 print "check_kernel_ident: kernel command line mismatch"
455 bad = True
456
457 if bad:
458 print " Expected Ident: " + expected_id
459 print " Running Ident: " + running_id
460 print " Expected Mark: %d" % (expected_when)
461 print "Command Line Mark: %d" % (cmdline_when)
462 print " Command Line: " + cmdline
463
mbligh302482e2008-05-01 20:06:16 +0000464 raise error.JobError("boot failure", "reboot.verify")
apwce73d892007-09-25 16:53:05 +0000465
jadmanskia9c75c42008-05-01 22:05:31 +0000466 kernel_info = {'kernel': expected_id}
467 for i, patch in enumerate(patches):
468 kernel_info["patch%d" % i] = patch
mblighb7fd2702008-03-25 14:57:08 +0000469 self.record('GOOD', subdir, 'reboot.verify', expected_id)
jadmanskia9c75c42008-05-01 22:05:31 +0000470 self._decrement_group_level()
471 self.record('END GOOD', subdir, 'reboot',
472 optional_fields=kernel_info)
apwce73d892007-09-25 16:53:05 +0000473
474
mblighc2359852007-08-28 18:11:48 +0000475 def filesystem(self, device, mountpoint = None, loop_size = 0):
mblighd7fb4a62006-10-01 00:57:53 +0000476 if not mountpoint:
477 mountpoint = self.tmpdir
mblighc2359852007-08-28 18:11:48 +0000478 return filesystem.filesystem(self, device, mountpoint,loop_size)
mblighd7fb4a62006-10-01 00:57:53 +0000479
mblighcaa62c22008-04-07 21:51:17 +0000480
481 def enable_external_logging(self):
482 pass
483
484
485 def disable_external_logging(self):
486 pass
487
488
489 def reboot_setup(self):
490 pass
491
mblighcaa605c2006-10-02 00:37:35 +0000492
493 def reboot(self, tag='autotest'):
mblighcaa62c22008-04-07 21:51:17 +0000494 self.reboot_setup()
jadmanskia9c75c42008-05-01 22:05:31 +0000495 self.record('START', None, 'reboot')
496 self._increment_group_level()
mbligh30270302007-11-05 20:33:52 +0000497 self.record('GOOD', None, 'reboot.start')
apwde1503a2006-10-10 08:34:21 +0000498 self.harness.run_reboot()
apw11985b72007-10-04 15:44:47 +0000499 default = self.config_get('boot.set_default')
500 if default:
501 self.bootloader.set_default(tag)
502 else:
503 self.bootloader.boot_once(tag)
mbligh302482e2008-05-01 20:06:16 +0000504 cmd = "(sleep 5; reboot) </dev/null >/dev/null 2>&1 &"
mblighe829ba52008-06-03 15:04:08 +0000505 utils.system(cmd)
apw0778a2f2006-10-06 03:11:40 +0000506 self.quit()
mblighcaa605c2006-10-02 00:37:35 +0000507
508
apw0865f482006-03-30 18:50:19 +0000509 def noop(self, text):
510 print "job: noop: " + text
511
mblighcaa605c2006-10-02 00:37:35 +0000512
mblighc86b0b42006-07-28 17:35:28 +0000513 def parallel(self, *tasklist):
514 """Run tasks in parallel"""
apw8fef4ac2006-10-10 22:53:37 +0000515
516 pids = []
mblighd528d302007-12-19 16:19:05 +0000517 old_log_filename = self.log_filename
518 for i, task in enumerate(tasklist):
519 self.log_filename = old_log_filename + (".%d" % i)
520 task_func = lambda: task[0](*task[1:])
mbligh302482e2008-05-01 20:06:16 +0000521 pids.append(parallel.fork_start(self.resultdir,
522 task_func))
mblighd528d302007-12-19 16:19:05 +0000523
524 old_log_path = os.path.join(self.resultdir, old_log_filename)
525 old_log = open(old_log_path, "a")
mblighd509b712008-01-14 17:41:25 +0000526 exceptions = []
mblighd528d302007-12-19 16:19:05 +0000527 for i, pid in enumerate(pids):
528 # wait for the task to finish
mblighd509b712008-01-14 17:41:25 +0000529 try:
mbligh302482e2008-05-01 20:06:16 +0000530 parallel.fork_waitfor(self.resultdir, pid)
mblighd509b712008-01-14 17:41:25 +0000531 except Exception, e:
532 exceptions.append(e)
mblighd528d302007-12-19 16:19:05 +0000533 # copy the logs from the subtask into the main log
534 new_log_path = old_log_path + (".%d" % i)
535 if os.path.exists(new_log_path):
536 new_log = open(new_log_path)
537 old_log.write(new_log.read())
538 new_log.close()
539 old_log.flush()
540 os.remove(new_log_path)
541 old_log.close()
542
543 self.log_filename = old_log_filename
apw0865f482006-03-30 18:50:19 +0000544
mblighd509b712008-01-14 17:41:25 +0000545 # handle any exceptions raised by the parallel tasks
546 if exceptions:
547 msg = "%d task(s) failed" % len(exceptions)
mbligh302482e2008-05-01 20:06:16 +0000548 raise error.JobError(msg, str(exceptions), exceptions)
mblighd509b712008-01-14 17:41:25 +0000549
mblighcaa605c2006-10-02 00:37:35 +0000550
apw0865f482006-03-30 18:50:19 +0000551 def quit(self):
mblighc86b0b42006-07-28 17:35:28 +0000552 # XXX: should have a better name.
apwde1503a2006-10-10 08:34:21 +0000553 self.harness.run_pause()
mbligh302482e2008-05-01 20:06:16 +0000554 raise error.JobContinue("more to come")
apw0865f482006-03-30 18:50:19 +0000555
mblighcaa605c2006-10-02 00:37:35 +0000556
apw0865f482006-03-30 18:50:19 +0000557 def complete(self, status):
mblighc86b0b42006-07-28 17:35:28 +0000558 """Clean up and exit"""
apw0865f482006-03-30 18:50:19 +0000559 # We are about to exit 'complete' so clean up the control file.
560 try:
mbligh366ff1b2008-04-25 16:07:56 +0000561 os.unlink(self.state_file)
apw0865f482006-03-30 18:50:19 +0000562 except:
563 pass
mblighc0b10d32008-03-03 16:03:28 +0000564
mbligh61a6c1a2006-12-25 01:26:38 +0000565 self.harness.run_complete()
mblighcaa62c22008-04-07 21:51:17 +0000566 self.disable_external_logging()
apw1b021902006-04-03 17:02:56 +0000567 sys.exit(status)
apw0865f482006-03-30 18:50:19 +0000568
mblighcaa605c2006-10-02 00:37:35 +0000569
mbligh366ff1b2008-04-25 16:07:56 +0000570 def set_state(self, var, val):
571 # Deep copies make sure that the state can't be altered
572 # without it being re-written. Perf wise, deep copies
573 # are overshadowed by pickling/loading.
574 self.state[var] = copy.deepcopy(val)
575 pickle.dump(self.state, open(self.state_file, 'w'))
576
577
578 def __load_state(self):
jadmanskia9c75c42008-05-01 22:05:31 +0000579 assert not hasattr(self, "state")
mbligh366ff1b2008-04-25 16:07:56 +0000580 try:
581 self.state = pickle.load(open(self.state_file, 'r'))
jadmanskia9c75c42008-05-01 22:05:31 +0000582 self.state_existed = True
mbligh366ff1b2008-04-25 16:07:56 +0000583 except Exception:
584 print "Initializing the state engine."
585 self.state = {}
mblighf1ae0a42008-04-25 16:09:20 +0000586 self.set_state('__steps', []) # writes pickle file
jadmanskia9c75c42008-05-01 22:05:31 +0000587 self.state_existed = False
mbligh366ff1b2008-04-25 16:07:56 +0000588
589
590 def get_state(self, var, default=None):
591 if var in self.state or default == None:
592 val = self.state[var]
593 else:
594 val = default
595 return copy.deepcopy(val)
596
597
mbligh12a04cb2008-04-25 16:07:20 +0000598 def __create_step_tuple(self, fn, args, dargs):
599 # Legacy code passes in an array where the first arg is
600 # the function or its name.
601 if isinstance(fn, list):
602 assert(len(args) == 0)
603 assert(len(dargs) == 0)
604 args = fn[1:]
605 fn = fn[0]
606 # Pickling actual functions is harry, thus we have to call
607 # them by name. Unfortunately, this means only functions
608 # defined globally can be used as a next step.
mblighb274ef52008-06-02 19:40:01 +0000609 if callable(fn):
mbligh12a04cb2008-04-25 16:07:20 +0000610 fn = fn.__name__
611 if not isinstance(fn, types.StringTypes):
612 raise StepError("Next steps must be functions or "
613 "strings containing the function name")
mblighb274ef52008-06-02 19:40:01 +0000614 ancestry = copy.copy(self.current_step_ancestry)
615 return (ancestry, fn, args, dargs)
mbligh12a04cb2008-04-25 16:07:20 +0000616
617
mbligh8f4d0432008-06-02 19:42:50 +0000618 def next_step_append(self, fn, *args, **dargs):
619 """Define the next step and place it at the end"""
mblighf1ae0a42008-04-25 16:09:20 +0000620 steps = self.get_state('__steps')
mbligh366ff1b2008-04-25 16:07:56 +0000621 steps.append(self.__create_step_tuple(fn, args, dargs))
mblighf1ae0a42008-04-25 16:09:20 +0000622 self.set_state('__steps', steps)
apw0865f482006-03-30 18:50:19 +0000623
mblighcaa605c2006-10-02 00:37:35 +0000624
mbligh8f4d0432008-06-02 19:42:50 +0000625 def next_step(self, fn, *args, **dargs):
626 """Create a new step and place it after any steps added
627 while running the current step but before any steps added in
628 previous steps"""
629 steps = self.get_state('__steps')
630 steps.insert(self.next_step_index,
631 self.__create_step_tuple(fn, args, dargs))
632 self.next_step_index += 1
633 self.set_state('__steps', steps)
634
635
mbligh12a04cb2008-04-25 16:07:20 +0000636 def next_step_prepend(self, fn, *args, **dargs):
mbligh237bed32007-09-05 13:05:57 +0000637 """Insert a new step, executing first"""
mblighf1ae0a42008-04-25 16:09:20 +0000638 steps = self.get_state('__steps')
mbligh366ff1b2008-04-25 16:07:56 +0000639 steps.insert(0, self.__create_step_tuple(fn, args, dargs))
mbligh8f4d0432008-06-02 19:42:50 +0000640 self.next_step_index += 1
mblighf1ae0a42008-04-25 16:09:20 +0000641 self.set_state('__steps', steps)
mbligh237bed32007-09-05 13:05:57 +0000642
643
mblighb274ef52008-06-02 19:40:01 +0000644 def _run_step_fn(self, local_vars, fn, args, dargs):
645 """Run a (step) function within the given context"""
646
647 local_vars['__args'] = args
648 local_vars['__dargs'] = dargs
649 exec('__ret = %s(*__args, **__dargs)' % fn,
650 local_vars, local_vars)
651 return local_vars['__ret']
652
653
654 def _create_frame(self, global_vars, ancestry, fn_name):
655 """Set up the environment like it would have been when this
656 function was first defined.
657
658 Child step engine 'implementations' must have 'return locals()'
659 at end end of their steps. Because of this, we can call the
660 parent function and get back all child functions (i.e. those
661 defined within it).
662
663 Unfortunately, the call stack of the function calling
664 job.next_step might have been deeper than the function it
665 added. In order to make sure that the environment is what it
666 should be, we need to then pop off the frames we built until
667 we find the frame where the function was first defined."""
668
669 # The copies ensure that the parent frames are not modified
670 # while building child frames. This matters if we then
671 # pop some frames in the next part of this function.
672 current_frame = copy.copy(global_vars)
673 frames = [current_frame]
674 for steps_fn_name in ancestry:
675 ret = self._run_step_fn(current_frame,
676 steps_fn_name, [], {})
677 current_frame = copy.copy(ret)
678 frames.append(current_frame)
679
680 while len(frames) > 2:
681 if fn_name not in frames[-2]:
682 break
683 if frames[-2][fn_name] != frames[-1][fn_name]:
684 break
685 frames.pop()
686 ancestry.pop()
687
688 return (frames[-1], ancestry)
689
690
691 def _add_step_init(self, local_vars, current_function):
692 """If the function returned a dictionary that includes a
693 function named 'step_init', prepend it to our list of steps.
694 This will only get run the first time a function with a nested
695 use of the step engine is run."""
696
697 if (isinstance(local_vars, dict) and
698 'step_init' in local_vars and
699 callable(local_vars['step_init'])):
700 # The init step is a child of the function
701 # we were just running.
702 self.current_step_ancestry.append(current_function)
703 self.next_step_prepend('step_init')
704
705
apw83f8d772006-04-27 14:12:56 +0000706 def step_engine(self):
mblighc86b0b42006-07-28 17:35:28 +0000707 """the stepping engine -- if the control file defines
708 step_init we will be using this engine to drive multiple runs.
709 """
710 """Do the next step"""
apw83f8d772006-04-27 14:12:56 +0000711
mbligh366ff1b2008-04-25 16:07:56 +0000712 # Set up the environment and then interpret the control file.
713 # Some control files will have code outside of functions,
714 # which means we need to have our state engine initialized
715 # before reading in the file.
mblighb274ef52008-06-02 19:40:01 +0000716 global_control_vars = {'job': self}
717 exec(JOB_PREAMBLE, global_control_vars, global_control_vars)
718 execfile(self.control, global_control_vars, global_control_vars)
apw83f8d772006-04-27 14:12:56 +0000719
mbligh366ff1b2008-04-25 16:07:56 +0000720 # If we loaded in a mid-job state file, then we presumably
721 # know what steps we have yet to run.
jadmanskia9c75c42008-05-01 22:05:31 +0000722 if not self.state_existed:
mblighb274ef52008-06-02 19:40:01 +0000723 if global_control_vars.has_key('step_init'):
724 self.next_step(global_control_vars['step_init'])
apw0865f482006-03-30 18:50:19 +0000725
mbligh366ff1b2008-04-25 16:07:56 +0000726 # Iterate through the steps. If we reboot, we'll simply
727 # continue iterating on the next step.
mblighf1ae0a42008-04-25 16:09:20 +0000728 while len(self.get_state('__steps')) > 0:
729 steps = self.get_state('__steps')
mblighb274ef52008-06-02 19:40:01 +0000730 (ancestry, fn_name, args, dargs) = steps.pop(0)
mblighf1ae0a42008-04-25 16:09:20 +0000731 self.set_state('__steps', steps)
apw0865f482006-03-30 18:50:19 +0000732
mbligh8f4d0432008-06-02 19:42:50 +0000733 self.next_step_index = 0
mblighb274ef52008-06-02 19:40:01 +0000734 ret = self._create_frame(global_control_vars, ancestry,
735 fn_name)
736 local_vars, self.current_step_ancestry = ret
737 local_vars = self._run_step_fn(local_vars, fn_name,
738 args, dargs)
739 self._add_step_init(local_vars, fn_name)
apw0865f482006-03-30 18:50:19 +0000740
mblighcaa605c2006-10-02 00:37:35 +0000741
jadmanskia9c75c42008-05-01 22:05:31 +0000742 def _init_group_level(self):
743 self.group_level = self.get_state("__group_level", default=0)
744
745
746 def _increment_group_level(self):
747 self.group_level += 1
748 self.set_state("__group_level", self.group_level)
749
750
751 def _decrement_group_level(self):
752 self.group_level -= 1
753 self.set_state("__group_level", self.group_level)
754
755
756 def record(self, status_code, subdir, operation, status = '',
757 optional_fields=None):
mbligh09f288a2007-09-18 21:34:57 +0000758 """
759 Record job-level status
apw7db8d0b2006-10-09 08:10:25 +0000760
mbligh09f288a2007-09-18 21:34:57 +0000761 The intent is to make this file both machine parseable and
762 human readable. That involves a little more complexity, but
763 really isn't all that bad ;-)
764
765 Format is <status code>\t<subdir>\t<operation>\t<status>
766
767 status code: (GOOD|WARN|FAIL|ABORT)
768 or START
769 or END (GOOD|WARN|FAIL|ABORT)
770
771 subdir: MUST be a relevant subdirectory in the results,
772 or None, which will be represented as '----'
773
774 operation: description of what you ran (e.g. "dbench", or
775 "mkfs -t foobar /dev/sda9")
776
777 status: error message or "completed sucessfully"
778
779 ------------------------------------------------------------
780
781 Initial tabs indicate indent levels for grouping, and is
mbligh7dd510c2007-11-13 17:11:22 +0000782 governed by self.group_level
mbligh09f288a2007-09-18 21:34:57 +0000783
784 multiline messages have secondary lines prefaced by a double
785 space (' ')
786 """
787
mblighb0570ad2007-09-19 18:18:11 +0000788 if subdir:
789 if re.match(r'[\n\t]', subdir):
jadmanskia9c75c42008-05-01 22:05:31 +0000790 raise ValueError("Invalid character in "
791 "subdir string")
mblighb0570ad2007-09-19 18:18:11 +0000792 substr = subdir
793 else:
794 substr = '----'
mbligh09f288a2007-09-18 21:34:57 +0000795
mbligh302482e2008-05-01 20:06:16 +0000796 if not logging.is_valid_status(status_code):
jadmanskia9c75c42008-05-01 22:05:31 +0000797 raise ValueError("Invalid status code supplied: %s" %
798 status_code)
mbligh9c5ac322007-10-31 18:01:59 +0000799 if not operation:
800 operation = '----'
jadmanskia9c75c42008-05-01 22:05:31 +0000801
mbligh09f288a2007-09-18 21:34:57 +0000802 if re.match(r'[\n\t]', operation):
jadmanskia9c75c42008-05-01 22:05:31 +0000803 raise ValueError("Invalid character in "
804 "operation string")
mbligh09f288a2007-09-18 21:34:57 +0000805 operation = operation.rstrip()
jadmanskia9c75c42008-05-01 22:05:31 +0000806
807 if not optional_fields:
808 optional_fields = {}
809
mbligh09f288a2007-09-18 21:34:57 +0000810 status = status.rstrip()
811 status = re.sub(r"\t", " ", status)
apw7db8d0b2006-10-09 08:10:25 +0000812 # Ensure any continuation lines are marked so we can
813 # detect them in the status file to ensure it is parsable.
jadmanskia9c75c42008-05-01 22:05:31 +0000814 status = re.sub(r"\n", "\n" + "\t" * self.group_level + " ",
815 status)
mbligh09f288a2007-09-18 21:34:57 +0000816
mbligh30270302007-11-05 20:33:52 +0000817 # Generate timestamps for inclusion in the logs
818 epoch_time = int(time.time()) # seconds since epoch, in UTC
819 local_time = time.localtime(epoch_time)
jadmanskia9c75c42008-05-01 22:05:31 +0000820 optional_fields["timestamp"] = str(epoch_time)
821 optional_fields["localtime"] = time.strftime("%b %d %H:%M:%S",
822 local_time)
mbligh30270302007-11-05 20:33:52 +0000823
jadmanskia9c75c42008-05-01 22:05:31 +0000824 fields = [status_code, substr, operation]
825 fields += ["%s=%s" % x for x in optional_fields.iteritems()]
826 fields.append(status)
827
828 msg = '\t'.join(str(x) for x in fields)
mbligh7dd510c2007-11-13 17:11:22 +0000829 msg = '\t' * self.group_level + msg
apw7db8d0b2006-10-09 08:10:25 +0000830
mblighd528d302007-12-19 16:19:05 +0000831 msg_tag = ""
832 if "." in self.log_filename:
833 msg_tag = self.log_filename.split(".", 1)[1]
834
jadmanskia9c75c42008-05-01 22:05:31 +0000835 self.harness.test_status_detail(status_code, substr,
836 operation, status, msg_tag)
mblighd528d302007-12-19 16:19:05 +0000837 self.harness.test_status(msg, msg_tag)
838
839 # log to stdout (if enabled)
840 #if self.log_filename == self.DEFAULT_LOG_FILENAME:
apwf1a81162006-04-25 10:10:29 +0000841 print msg
mblighd528d302007-12-19 16:19:05 +0000842
843 # log to the "root" status log
844 status_file = os.path.join(self.resultdir, self.log_filename)
mbligh7dd510c2007-11-13 17:11:22 +0000845 open(status_file, "a").write(msg + "\n")
mblighd528d302007-12-19 16:19:05 +0000846
847 # log to the subdir status log (if subdir is set)
mblighb0570ad2007-09-19 18:18:11 +0000848 if subdir:
mblighadff6ca2008-01-22 16:38:25 +0000849 dir = os.path.join(self.resultdir, subdir)
850 if not os.path.exists(dir):
851 os.mkdir(dir)
852
853 status_file = os.path.join(dir,
mblighd528d302007-12-19 16:19:05 +0000854 self.DEFAULT_LOG_FILENAME)
mblighb0570ad2007-09-19 18:18:11 +0000855 open(status_file, "a").write(msg + "\n")
apwce9abe92006-04-27 14:14:04 +0000856
857
jadmanski8415f962008-05-06 20:38:53 +0000858class disk_usage_monitor:
859 def __init__(self, logging_func, device, max_mb_per_hour):
860 self.func = logging_func
861 self.device = device
862 self.max_mb_per_hour = max_mb_per_hour
863
864
865 def start(self):
866 self.initial_space = autotest_utils.freespace(self.device)
867 self.start_time = time.time()
868
869
870 def stop(self):
871 # if no maximum usage rate was set, we don't need to
872 # generate any warnings
873 if not self.max_mb_per_hour:
874 return
875
876 final_space = autotest_utils.freespace(self.device)
877 used_space = self.initial_space - final_space
878 stop_time = time.time()
879 total_time = stop_time - self.start_time
880 # round up the time to one minute, to keep extremely short
881 # tests from generating false positives due to short, badly
882 # timed bursts of activity
883 total_time = max(total_time, 60.0)
884
885 # determine the usage rate
886 bytes_per_sec = used_space / total_time
887 mb_per_sec = bytes_per_sec / 1024**2
888 mb_per_hour = mb_per_sec * 60 * 60
889
890 if mb_per_hour > self.max_mb_per_hour:
891 msg = ("disk space on %s was consumed at a rate of "
892 "%.2f MB/hour")
893 msg %= (self.device, mb_per_hour)
894 self.func(msg)
895
896
897 @classmethod
898 def watch(cls, *monitor_args, **monitor_dargs):
899 """ Generic decorator to wrap a function call with the
900 standard create-monitor -> start -> call -> stop idiom."""
901 def decorator(func):
902 def watched_func(*args, **dargs):
903 monitor = cls(*monitor_args, **monitor_dargs)
904 monitor.start()
905 try:
906 func(*args, **dargs)
907 finally:
908 monitor.stop()
909 return watched_func
910 return decorator
911
912
mblighcaa62c22008-04-07 21:51:17 +0000913def runjob(control, cont = False, tag = "default", harness_type = '',
914 use_external_logging = False):
mblighc86b0b42006-07-28 17:35:28 +0000915 """The main interface to this module
916
mbligh72b88fc2006-12-16 18:41:35 +0000917 control
mblighc86b0b42006-07-28 17:35:28 +0000918 The control file to use for this job.
919 cont
920 Whether this is the continuation of a previously started job
921 """
mblighb4eef242007-07-23 18:22:49 +0000922 control = os.path.abspath(control)
apwce9abe92006-04-27 14:14:04 +0000923 state = control + '.state'
924
925 # instantiate the job object ready for the control file.
926 myjob = None
927 try:
928 # Check that the control file is valid
929 if not os.path.exists(control):
mbligh302482e2008-05-01 20:06:16 +0000930 raise error.JobError(control +
931 ": control file not found")
apwce9abe92006-04-27 14:14:04 +0000932
933 # When continuing, the job is complete when there is no
934 # state file, ensure we don't try and continue.
mblighf3fef462006-09-13 16:05:05 +0000935 if cont and not os.path.exists(state):
mbligh302482e2008-05-01 20:06:16 +0000936 raise error.JobComplete("all done")
mblighf3fef462006-09-13 16:05:05 +0000937 if cont == False and os.path.exists(state):
apwce9abe92006-04-27 14:14:04 +0000938 os.unlink(state)
939
mblighcaa62c22008-04-07 21:51:17 +0000940 myjob = job(control, tag, cont, harness_type,
941 use_external_logging)
apwce9abe92006-04-27 14:14:04 +0000942
943 # 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()
947
mbligh302482e2008-05-01 20:06:16 +0000948 except error.JobContinue:
apwce9abe92006-04-27 14:14:04 +0000949 sys.exit(5)
950
mbligh302482e2008-05-01 20:06:16 +0000951 except error.JobComplete:
apwb832e1b2007-11-24 20:24:38 +0000952 sys.exit(1)
953
mbligh302482e2008-05-01 20:06:16 +0000954 except error.JobError, instance:
apwce9abe92006-04-27 14:14:04 +0000955 print "JOB ERROR: " + instance.args[0]
mbligh9c5ac322007-10-31 18:01:59 +0000956 if myjob:
mbligh30270302007-11-05 20:33:52 +0000957 command = None
958 if len(instance.args) > 1:
959 command = instance.args[1]
960 myjob.record('ABORT', None, command, instance.args[0])
jadmanskia9c75c42008-05-01 22:05:31 +0000961 myjob._decrement_group_level()
mblighc3430162007-11-14 23:57:19 +0000962 myjob.record('END ABORT', None, None)
jadmanskia9c75c42008-05-01 22:05:31 +0000963 assert(myjob.group_level == 0)
apwce9abe92006-04-27 14:14:04 +0000964 myjob.complete(1)
apwb832e1b2007-11-24 20:24:38 +0000965 else:
966 sys.exit(1)
apwce9abe92006-04-27 14:14:04 +0000967
mblighc3430162007-11-14 23:57:19 +0000968 except Exception, e:
mbligh302482e2008-05-01 20:06:16 +0000969 msg = str(e) + '\n' + traceback.format_exc()
mblighc3430162007-11-14 23:57:19 +0000970 print "JOB ERROR: " + msg
mblighfbfb77d2007-02-15 18:54:03 +0000971 if myjob:
mblighc3430162007-11-14 23:57:19 +0000972 myjob.record('ABORT', None, None, msg)
jadmanskia9c75c42008-05-01 22:05:31 +0000973 myjob._decrement_group_level()
mblighc3430162007-11-14 23:57:19 +0000974 myjob.record('END ABORT', None, None)
jadmanskia9c75c42008-05-01 22:05:31 +0000975 assert(myjob.group_level == 0)
mbligh9c5ac322007-10-31 18:01:59 +0000976 myjob.complete(1)
apwb832e1b2007-11-24 20:24:38 +0000977 else:
978 sys.exit(1)
mbligh892d37f2007-03-01 17:03:25 +0000979
mbligh0144e5a2008-03-07 18:17:53 +0000980 # If we get here, then we assume the job is complete and good.
jadmanskia9c75c42008-05-01 22:05:31 +0000981 myjob._decrement_group_level()
mbligh0144e5a2008-03-07 18:17:53 +0000982 myjob.record('END GOOD', None, None)
jadmanskia9c75c42008-05-01 22:05:31 +0000983 assert(myjob.group_level == 0)
mbligh0144e5a2008-03-07 18:17:53 +0000984
mbligh892d37f2007-03-01 17:03:25 +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:
991 from site_job import site_job
992except ImportError:
993 class site_job(base_job):
994 pass
995
996class job(site_job):
997 pass
jadmanski87cbc7f2008-05-13 18:17:10 +0000998