blob: 9d1b45be46513a29a8c96de04aefb5a43a4fcee0 [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)
mblighd660afe2008-06-05 22:17:53 +0000287 namelen = len(testname)
288 dargs = dargs.copy()
289 tntag = dargs.pop('tag', None)
290 if tntag: # testname tag is included in reported test name
291 testname += '.' + tntag
mbligh09f288a2007-09-18 21:34:57 +0000292 subdir = testname
mblighd660afe2008-06-05 22:17:53 +0000293 sdtag = dargs.pop('subdir_tag', None)
294 if sdtag: # subdir-only tag is not included in reports
295 subdir = subdir + '.' + sdtag
296 tag = subdir[namelen+1:] # '' if none
mbligh7dd510c2007-11-13 17:11:22 +0000297
mblighd660afe2008-06-05 22:17:53 +0000298 outputdir = os.path.join(self.resultdir, subdir)
299 if os.path.exists(outputdir):
300 msg = ("%s already exists, test <%s> may have"
301 " already run with tag <%s>"
302 % (outputdir, testname, tag) )
303 raise error.TestError(msg)
304 os.mkdir(outputdir)
305
306 container = dargs.pop('container', None)
mbligh65938a22007-12-10 16:58:52 +0000307 if container:
mbligh68119582008-01-25 18:16:41 +0000308 cname = container.get('name', None)
309 if not cname: # get old name
310 cname = container.get('container_name', None)
311 mbytes = container.get('mbytes', None)
312 if not mbytes: # get old name
313 mbytes = container.get('mem', None)
314 cpus = container.get('cpus', None)
315 if not cpus: # get old name
316 cpus = container.get('cpu', None)
jadmanski87cbc7f2008-05-13 18:17:10 +0000317 root = container.get('root', None)
mbligh68119582008-01-25 18:16:41 +0000318 self.new_container(mbytes=mbytes, cpus=cpus,
319 root=root, name=cname)
mbligh65938a22007-12-10 16:58:52 +0000320 # We are running in a container now...
321
jadmanski8415f962008-05-06 20:38:53 +0000322 def log_warning(reason):
323 self.record("WARN", subdir, testname, reason)
324 @disk_usage_monitor.watch(log_warning, "/",
325 self.max_disk_usage_rate)
mbligh7dd510c2007-11-13 17:11:22 +0000326 def group_func():
apwf1a81162006-04-25 10:10:29 +0000327 try:
mblighd016ecc2006-11-25 21:41:07 +0000328 self.__runtest(url, tag, args, dargs)
mbligh302482e2008-05-01 20:06:16 +0000329 except error.TestNAError, detail:
330 self.record('TEST_NA', subdir, testname,
331 str(detail))
332 raise
apwf1a81162006-04-25 10:10:29 +0000333 except Exception, detail:
mbligh7dd510c2007-11-13 17:11:22 +0000334 self.record('FAIL', subdir, testname,
335 str(detail))
apwf1a81162006-04-25 10:10:29 +0000336 raise
337 else:
mbligh7dd510c2007-11-13 17:11:22 +0000338 self.record('GOOD', subdir, testname,
339 'completed successfully')
jadmanski8415f962008-05-06 20:38:53 +0000340
mblighd660afe2008-06-05 22:17:53 +0000341 result, exc_info = self.__rungroup(subdir, testname, group_func)
mbligh68119582008-01-25 18:16:41 +0000342 if container:
343 self.release_container()
mbligh302482e2008-05-01 20:06:16 +0000344 if exc_info and isinstance(exc_info[1], error.TestError):
mbligh7dd510c2007-11-13 17:11:22 +0000345 return False
346 elif exc_info:
mbligh71ea2492008-01-15 20:35:52 +0000347 raise exc_info[0], exc_info[1], exc_info[2]
apwf1a81162006-04-25 10:10:29 +0000348 else:
mbligh7dd510c2007-11-13 17:11:22 +0000349 return True
350
351
mblighd660afe2008-06-05 22:17:53 +0000352 def __rungroup(self, subdir, testname, function, *args, **dargs):
mbligh7dd510c2007-11-13 17:11:22 +0000353 """\
mblighd660afe2008-06-05 22:17:53 +0000354 subdir:
mbligh7dd510c2007-11-13 17:11:22 +0000355 name of the group
mblighd660afe2008-06-05 22:17:53 +0000356 testname:
357 name of the test to run, or support step
mbligh7dd510c2007-11-13 17:11:22 +0000358 function:
359 subroutine to run
360 *args:
361 arguments for the function
362
363 Returns a 2-tuple (result, exc_info) where result
364 is the return value of function, and exc_info is
365 the sys.exc_info() of the exception thrown by the
366 function (which may be None).
367 """
368
369 result, exc_info = None, None
370 try:
mblighd660afe2008-06-05 22:17:53 +0000371 self.record('START', subdir, testname)
jadmanskia9c75c42008-05-01 22:05:31 +0000372 self._increment_group_level()
mbligh7dd510c2007-11-13 17:11:22 +0000373 result = function(*args, **dargs)
jadmanskia9c75c42008-05-01 22:05:31 +0000374 self._decrement_group_level()
mblighd660afe2008-06-05 22:17:53 +0000375 self.record('END GOOD', subdir, testname)
mbligh302482e2008-05-01 20:06:16 +0000376 except error.TestNAError, e:
jadmanskia9c75c42008-05-01 22:05:31 +0000377 self._decrement_group_level()
mblighd660afe2008-06-05 22:17:53 +0000378 self.record('END TEST_NA', subdir, testname, str(e))
mbligh7dd510c2007-11-13 17:11:22 +0000379 except Exception, e:
380 exc_info = sys.exc_info()
jadmanskia9c75c42008-05-01 22:05:31 +0000381 self._decrement_group_level()
mbligh302482e2008-05-01 20:06:16 +0000382 err_msg = str(e) + '\n' + traceback.format_exc()
mblighd660afe2008-06-05 22:17:53 +0000383 self.record('END FAIL', subdir, testname, err_msg)
mbligh7dd510c2007-11-13 17:11:22 +0000384
385 return result, exc_info
apw0865f482006-03-30 18:50:19 +0000386
mblighd7fb4a62006-10-01 00:57:53 +0000387
apw1da244b2007-09-27 17:18:01 +0000388 def run_group(self, function, *args, **dargs):
mbligh88ab90f2007-08-29 15:52:49 +0000389 """\
390 function:
391 subroutine to run
392 *args:
393 arguments for the function
394 """
395
mbligh7dd510c2007-11-13 17:11:22 +0000396 # Allow the tag for the group to be specified
mbligh88ab90f2007-08-29 15:52:49 +0000397 name = function.__name__
mbligh7dd510c2007-11-13 17:11:22 +0000398 tag = dargs.pop('tag', None)
399 if tag:
400 name = tag
apw1da244b2007-09-27 17:18:01 +0000401
mblighd660afe2008-06-05 22:17:53 +0000402 outputdir = os.path.join(self.resultdir, name)
403 if os.path.exists(outputdir):
404 msg = ("%s already exists, test <%s> may have"
405 " already run with tag <%s>"
406 % (outputdir, name, name) )
407 raise error.TestError(msg)
408 os.mkdir(outputdir)
409
410 result, exc_info = self.__rungroup(name, name, function,
mbligh7dd510c2007-11-13 17:11:22 +0000411 *args, **dargs)
apw1da244b2007-09-27 17:18:01 +0000412
mbligh7dd510c2007-11-13 17:11:22 +0000413 # if there was a non-TestError exception, raise it
mbligh302482e2008-05-01 20:06:16 +0000414 if exc_info and not isinstance(exc_info[1], error.TestError):
mbligh7dd510c2007-11-13 17:11:22 +0000415 err = ''.join(traceback.format_exception(*exc_info))
mbligh302482e2008-05-01 20:06:16 +0000416 raise error.TestError(name + ' failed\n' + err)
mbligh88ab90f2007-08-29 15:52:49 +0000417
mbligh7dd510c2007-11-13 17:11:22 +0000418 # pass back the actual return value from the function
apw08403ca2007-09-27 17:17:22 +0000419 return result
420
mbligh88ab90f2007-08-29 15:52:49 +0000421
jadmanski87cbc7f2008-05-13 18:17:10 +0000422 def new_container(self, mbytes=None, cpus=None, root=None, name=None):
mbligh8ea61e22008-05-09 18:09:37 +0000423 if not autotest_utils.grep('cpuset', '/proc/filesystems'):
mbligh68119582008-01-25 18:16:41 +0000424 print "Containers not enabled by latest reboot"
425 return # containers weren't enabled in this kernel boot
426 pid = os.getpid()
mbligh68119582008-01-25 18:16:41 +0000427 if not name:
428 name = 'test%d' % pid # make arbitrary unique name
429 self.container = cpuset.cpuset(name, job_size=mbytes,
mbligh337bb762008-04-16 21:23:10 +0000430 job_pid=pid, cpus=cpus, root=root)
mbligh68119582008-01-25 18:16:41 +0000431 # This job's python shell is now running in the new container
432 # and all forked test processes will inherit that container
433
434
435 def release_container(self):
436 if self.container:
mbligh337bb762008-04-16 21:23:10 +0000437 self.container.release()
mbligh68119582008-01-25 18:16:41 +0000438 self.container = None
439
440
441 def cpu_count(self):
442 if self.container:
443 return len(self.container.cpus)
jadmanskia9c75c42008-05-01 22:05:31 +0000444 return autotest_utils.count_cpus() # use total system count
mbligh68119582008-01-25 18:16:41 +0000445
446
apwce73d892007-09-25 16:53:05 +0000447 # Check the passed kernel identifier against the command line
448 # and the running kernel, abort the job on missmatch.
mbligh38a4a112008-03-19 13:11:34 +0000449 def kernel_check_ident(self, expected_when, expected_id, subdir,
jadmanskia9c75c42008-05-01 22:05:31 +0000450 type = 'src', patches=[]):
mbligh38a4a112008-03-19 13:11:34 +0000451 print (("POST BOOT: checking booted kernel " +
452 "mark=%d identity='%s' type='%s'") %
453 (expected_when, expected_id, type))
apwce73d892007-09-25 16:53:05 +0000454
jadmanskia9c75c42008-05-01 22:05:31 +0000455 running_id = autotest_utils.running_os_ident()
apwce73d892007-09-25 16:53:05 +0000456
mblighe829ba52008-06-03 15:04:08 +0000457 cmdline = utils.read_one_line("/proc/cmdline")
apwce73d892007-09-25 16:53:05 +0000458
459 find_sum = re.compile(r'.*IDENT=(\d+)')
460 m = find_sum.match(cmdline)
461 cmdline_when = -1
462 if m:
463 cmdline_when = int(m.groups()[0])
464
465 # We have all the facts, see if they indicate we
466 # booted the requested kernel or not.
467 bad = False
mblighda0311e2007-10-25 16:03:33 +0000468 if (type == 'src' and expected_id != running_id or
jadmanskia9c75c42008-05-01 22:05:31 +0000469 type == 'rpm' and
470 not running_id.startswith(expected_id + '::')):
apwce73d892007-09-25 16:53:05 +0000471 print "check_kernel_ident: kernel identifier mismatch"
472 bad = True
473 if expected_when != cmdline_when:
474 print "check_kernel_ident: kernel command line mismatch"
475 bad = True
476
477 if bad:
478 print " Expected Ident: " + expected_id
479 print " Running Ident: " + running_id
480 print " Expected Mark: %d" % (expected_when)
481 print "Command Line Mark: %d" % (cmdline_when)
482 print " Command Line: " + cmdline
483
mbligh302482e2008-05-01 20:06:16 +0000484 raise error.JobError("boot failure", "reboot.verify")
apwce73d892007-09-25 16:53:05 +0000485
jadmanskia9c75c42008-05-01 22:05:31 +0000486 kernel_info = {'kernel': expected_id}
487 for i, patch in enumerate(patches):
488 kernel_info["patch%d" % i] = patch
mblighb7fd2702008-03-25 14:57:08 +0000489 self.record('GOOD', subdir, 'reboot.verify', expected_id)
jadmanskia9c75c42008-05-01 22:05:31 +0000490 self._decrement_group_level()
491 self.record('END GOOD', subdir, 'reboot',
492 optional_fields=kernel_info)
apwce73d892007-09-25 16:53:05 +0000493
494
mblighc2359852007-08-28 18:11:48 +0000495 def filesystem(self, device, mountpoint = None, loop_size = 0):
mblighd7fb4a62006-10-01 00:57:53 +0000496 if not mountpoint:
497 mountpoint = self.tmpdir
mblighc2359852007-08-28 18:11:48 +0000498 return filesystem.filesystem(self, device, mountpoint,loop_size)
mblighd7fb4a62006-10-01 00:57:53 +0000499
mblighcaa62c22008-04-07 21:51:17 +0000500
501 def enable_external_logging(self):
502 pass
503
504
505 def disable_external_logging(self):
506 pass
507
508
509 def reboot_setup(self):
510 pass
511
mblighcaa605c2006-10-02 00:37:35 +0000512
513 def reboot(self, tag='autotest'):
mblighcaa62c22008-04-07 21:51:17 +0000514 self.reboot_setup()
jadmanskia9c75c42008-05-01 22:05:31 +0000515 self.record('START', None, 'reboot')
516 self._increment_group_level()
mbligh30270302007-11-05 20:33:52 +0000517 self.record('GOOD', None, 'reboot.start')
apwde1503a2006-10-10 08:34:21 +0000518 self.harness.run_reboot()
apw11985b72007-10-04 15:44:47 +0000519 default = self.config_get('boot.set_default')
520 if default:
521 self.bootloader.set_default(tag)
522 else:
523 self.bootloader.boot_once(tag)
mbligh302482e2008-05-01 20:06:16 +0000524 cmd = "(sleep 5; reboot) </dev/null >/dev/null 2>&1 &"
mblighe829ba52008-06-03 15:04:08 +0000525 utils.system(cmd)
apw0778a2f2006-10-06 03:11:40 +0000526 self.quit()
mblighcaa605c2006-10-02 00:37:35 +0000527
528
apw0865f482006-03-30 18:50:19 +0000529 def noop(self, text):
530 print "job: noop: " + text
531
mblighcaa605c2006-10-02 00:37:35 +0000532
mblighc86b0b42006-07-28 17:35:28 +0000533 def parallel(self, *tasklist):
534 """Run tasks in parallel"""
apw8fef4ac2006-10-10 22:53:37 +0000535
536 pids = []
mblighd528d302007-12-19 16:19:05 +0000537 old_log_filename = self.log_filename
538 for i, task in enumerate(tasklist):
539 self.log_filename = old_log_filename + (".%d" % i)
540 task_func = lambda: task[0](*task[1:])
mbligh302482e2008-05-01 20:06:16 +0000541 pids.append(parallel.fork_start(self.resultdir,
542 task_func))
mblighd528d302007-12-19 16:19:05 +0000543
544 old_log_path = os.path.join(self.resultdir, old_log_filename)
545 old_log = open(old_log_path, "a")
mblighd509b712008-01-14 17:41:25 +0000546 exceptions = []
mblighd528d302007-12-19 16:19:05 +0000547 for i, pid in enumerate(pids):
548 # wait for the task to finish
mblighd509b712008-01-14 17:41:25 +0000549 try:
mbligh302482e2008-05-01 20:06:16 +0000550 parallel.fork_waitfor(self.resultdir, pid)
mblighd509b712008-01-14 17:41:25 +0000551 except Exception, e:
552 exceptions.append(e)
mblighd528d302007-12-19 16:19:05 +0000553 # copy the logs from the subtask into the main log
554 new_log_path = old_log_path + (".%d" % i)
555 if os.path.exists(new_log_path):
556 new_log = open(new_log_path)
557 old_log.write(new_log.read())
558 new_log.close()
559 old_log.flush()
560 os.remove(new_log_path)
561 old_log.close()
562
563 self.log_filename = old_log_filename
apw0865f482006-03-30 18:50:19 +0000564
mblighd509b712008-01-14 17:41:25 +0000565 # handle any exceptions raised by the parallel tasks
566 if exceptions:
567 msg = "%d task(s) failed" % len(exceptions)
mbligh302482e2008-05-01 20:06:16 +0000568 raise error.JobError(msg, str(exceptions), exceptions)
mblighd509b712008-01-14 17:41:25 +0000569
mblighcaa605c2006-10-02 00:37:35 +0000570
apw0865f482006-03-30 18:50:19 +0000571 def quit(self):
mblighc86b0b42006-07-28 17:35:28 +0000572 # XXX: should have a better name.
apwde1503a2006-10-10 08:34:21 +0000573 self.harness.run_pause()
mbligh302482e2008-05-01 20:06:16 +0000574 raise error.JobContinue("more to come")
apw0865f482006-03-30 18:50:19 +0000575
mblighcaa605c2006-10-02 00:37:35 +0000576
apw0865f482006-03-30 18:50:19 +0000577 def complete(self, status):
mblighc86b0b42006-07-28 17:35:28 +0000578 """Clean up and exit"""
apw0865f482006-03-30 18:50:19 +0000579 # We are about to exit 'complete' so clean up the control file.
580 try:
mbligh366ff1b2008-04-25 16:07:56 +0000581 os.unlink(self.state_file)
apw0865f482006-03-30 18:50:19 +0000582 except:
583 pass
mblighc0b10d32008-03-03 16:03:28 +0000584
mbligh61a6c1a2006-12-25 01:26:38 +0000585 self.harness.run_complete()
mblighcaa62c22008-04-07 21:51:17 +0000586 self.disable_external_logging()
apw1b021902006-04-03 17:02:56 +0000587 sys.exit(status)
apw0865f482006-03-30 18:50:19 +0000588
mblighcaa605c2006-10-02 00:37:35 +0000589
mbligh366ff1b2008-04-25 16:07:56 +0000590 def set_state(self, var, val):
591 # Deep copies make sure that the state can't be altered
592 # without it being re-written. Perf wise, deep copies
593 # are overshadowed by pickling/loading.
594 self.state[var] = copy.deepcopy(val)
595 pickle.dump(self.state, open(self.state_file, 'w'))
596
597
598 def __load_state(self):
jadmanskia9c75c42008-05-01 22:05:31 +0000599 assert not hasattr(self, "state")
mbligh366ff1b2008-04-25 16:07:56 +0000600 try:
601 self.state = pickle.load(open(self.state_file, 'r'))
jadmanskia9c75c42008-05-01 22:05:31 +0000602 self.state_existed = True
mbligh366ff1b2008-04-25 16:07:56 +0000603 except Exception:
604 print "Initializing the state engine."
605 self.state = {}
mblighf1ae0a42008-04-25 16:09:20 +0000606 self.set_state('__steps', []) # writes pickle file
jadmanskia9c75c42008-05-01 22:05:31 +0000607 self.state_existed = False
mbligh366ff1b2008-04-25 16:07:56 +0000608
609
610 def get_state(self, var, default=None):
611 if var in self.state or default == None:
612 val = self.state[var]
613 else:
614 val = default
615 return copy.deepcopy(val)
616
617
mbligh12a04cb2008-04-25 16:07:20 +0000618 def __create_step_tuple(self, fn, args, dargs):
619 # Legacy code passes in an array where the first arg is
620 # the function or its name.
621 if isinstance(fn, list):
622 assert(len(args) == 0)
623 assert(len(dargs) == 0)
624 args = fn[1:]
625 fn = fn[0]
626 # Pickling actual functions is harry, thus we have to call
627 # them by name. Unfortunately, this means only functions
628 # defined globally can be used as a next step.
mblighb274ef52008-06-02 19:40:01 +0000629 if callable(fn):
mbligh12a04cb2008-04-25 16:07:20 +0000630 fn = fn.__name__
631 if not isinstance(fn, types.StringTypes):
632 raise StepError("Next steps must be functions or "
633 "strings containing the function name")
mblighb274ef52008-06-02 19:40:01 +0000634 ancestry = copy.copy(self.current_step_ancestry)
635 return (ancestry, fn, args, dargs)
mbligh12a04cb2008-04-25 16:07:20 +0000636
637
mbligh8f4d0432008-06-02 19:42:50 +0000638 def next_step_append(self, fn, *args, **dargs):
639 """Define the next step and place it at the end"""
mblighf1ae0a42008-04-25 16:09:20 +0000640 steps = self.get_state('__steps')
mbligh366ff1b2008-04-25 16:07:56 +0000641 steps.append(self.__create_step_tuple(fn, args, dargs))
mblighf1ae0a42008-04-25 16:09:20 +0000642 self.set_state('__steps', steps)
apw0865f482006-03-30 18:50:19 +0000643
mblighcaa605c2006-10-02 00:37:35 +0000644
mbligh8f4d0432008-06-02 19:42:50 +0000645 def next_step(self, fn, *args, **dargs):
646 """Create a new step and place it after any steps added
647 while running the current step but before any steps added in
648 previous steps"""
649 steps = self.get_state('__steps')
650 steps.insert(self.next_step_index,
651 self.__create_step_tuple(fn, args, dargs))
652 self.next_step_index += 1
653 self.set_state('__steps', steps)
654
655
mbligh12a04cb2008-04-25 16:07:20 +0000656 def next_step_prepend(self, fn, *args, **dargs):
mbligh237bed32007-09-05 13:05:57 +0000657 """Insert a new step, executing first"""
mblighf1ae0a42008-04-25 16:09:20 +0000658 steps = self.get_state('__steps')
mbligh366ff1b2008-04-25 16:07:56 +0000659 steps.insert(0, self.__create_step_tuple(fn, args, dargs))
mbligh8f4d0432008-06-02 19:42:50 +0000660 self.next_step_index += 1
mblighf1ae0a42008-04-25 16:09:20 +0000661 self.set_state('__steps', steps)
mbligh237bed32007-09-05 13:05:57 +0000662
663
mblighb274ef52008-06-02 19:40:01 +0000664 def _run_step_fn(self, local_vars, fn, args, dargs):
665 """Run a (step) function within the given context"""
666
667 local_vars['__args'] = args
668 local_vars['__dargs'] = dargs
669 exec('__ret = %s(*__args, **__dargs)' % fn,
670 local_vars, local_vars)
671 return local_vars['__ret']
672
673
674 def _create_frame(self, global_vars, ancestry, fn_name):
675 """Set up the environment like it would have been when this
676 function was first defined.
677
678 Child step engine 'implementations' must have 'return locals()'
679 at end end of their steps. Because of this, we can call the
680 parent function and get back all child functions (i.e. those
681 defined within it).
682
683 Unfortunately, the call stack of the function calling
684 job.next_step might have been deeper than the function it
685 added. In order to make sure that the environment is what it
686 should be, we need to then pop off the frames we built until
687 we find the frame where the function was first defined."""
688
689 # The copies ensure that the parent frames are not modified
690 # while building child frames. This matters if we then
691 # pop some frames in the next part of this function.
692 current_frame = copy.copy(global_vars)
693 frames = [current_frame]
694 for steps_fn_name in ancestry:
695 ret = self._run_step_fn(current_frame,
696 steps_fn_name, [], {})
697 current_frame = copy.copy(ret)
698 frames.append(current_frame)
699
700 while len(frames) > 2:
701 if fn_name not in frames[-2]:
702 break
703 if frames[-2][fn_name] != frames[-1][fn_name]:
704 break
705 frames.pop()
706 ancestry.pop()
707
708 return (frames[-1], ancestry)
709
710
711 def _add_step_init(self, local_vars, current_function):
712 """If the function returned a dictionary that includes a
713 function named 'step_init', prepend it to our list of steps.
714 This will only get run the first time a function with a nested
715 use of the step engine is run."""
716
717 if (isinstance(local_vars, dict) and
718 'step_init' in local_vars and
719 callable(local_vars['step_init'])):
720 # The init step is a child of the function
721 # we were just running.
722 self.current_step_ancestry.append(current_function)
723 self.next_step_prepend('step_init')
724
725
apw83f8d772006-04-27 14:12:56 +0000726 def step_engine(self):
mblighc86b0b42006-07-28 17:35:28 +0000727 """the stepping engine -- if the control file defines
728 step_init we will be using this engine to drive multiple runs.
729 """
730 """Do the next step"""
apw83f8d772006-04-27 14:12:56 +0000731
mbligh366ff1b2008-04-25 16:07:56 +0000732 # Set up the environment and then interpret the control file.
733 # Some control files will have code outside of functions,
734 # which means we need to have our state engine initialized
735 # before reading in the file.
mblighb274ef52008-06-02 19:40:01 +0000736 global_control_vars = {'job': self}
737 exec(JOB_PREAMBLE, global_control_vars, global_control_vars)
738 execfile(self.control, global_control_vars, global_control_vars)
apw83f8d772006-04-27 14:12:56 +0000739
mbligh366ff1b2008-04-25 16:07:56 +0000740 # If we loaded in a mid-job state file, then we presumably
741 # know what steps we have yet to run.
jadmanskia9c75c42008-05-01 22:05:31 +0000742 if not self.state_existed:
mblighb274ef52008-06-02 19:40:01 +0000743 if global_control_vars.has_key('step_init'):
744 self.next_step(global_control_vars['step_init'])
apw0865f482006-03-30 18:50:19 +0000745
mbligh366ff1b2008-04-25 16:07:56 +0000746 # Iterate through the steps. If we reboot, we'll simply
747 # continue iterating on the next step.
mblighf1ae0a42008-04-25 16:09:20 +0000748 while len(self.get_state('__steps')) > 0:
749 steps = self.get_state('__steps')
mblighb274ef52008-06-02 19:40:01 +0000750 (ancestry, fn_name, args, dargs) = steps.pop(0)
mblighf1ae0a42008-04-25 16:09:20 +0000751 self.set_state('__steps', steps)
apw0865f482006-03-30 18:50:19 +0000752
mbligh8f4d0432008-06-02 19:42:50 +0000753 self.next_step_index = 0
mblighb274ef52008-06-02 19:40:01 +0000754 ret = self._create_frame(global_control_vars, ancestry,
755 fn_name)
756 local_vars, self.current_step_ancestry = ret
757 local_vars = self._run_step_fn(local_vars, fn_name,
758 args, dargs)
759 self._add_step_init(local_vars, fn_name)
apw0865f482006-03-30 18:50:19 +0000760
mblighcaa605c2006-10-02 00:37:35 +0000761
jadmanskia9c75c42008-05-01 22:05:31 +0000762 def _init_group_level(self):
763 self.group_level = self.get_state("__group_level", default=0)
764
765
766 def _increment_group_level(self):
767 self.group_level += 1
768 self.set_state("__group_level", self.group_level)
769
770
771 def _decrement_group_level(self):
772 self.group_level -= 1
773 self.set_state("__group_level", self.group_level)
774
775
776 def record(self, status_code, subdir, operation, status = '',
777 optional_fields=None):
mbligh09f288a2007-09-18 21:34:57 +0000778 """
779 Record job-level status
apw7db8d0b2006-10-09 08:10:25 +0000780
mbligh09f288a2007-09-18 21:34:57 +0000781 The intent is to make this file both machine parseable and
782 human readable. That involves a little more complexity, but
783 really isn't all that bad ;-)
784
785 Format is <status code>\t<subdir>\t<operation>\t<status>
786
787 status code: (GOOD|WARN|FAIL|ABORT)
788 or START
789 or END (GOOD|WARN|FAIL|ABORT)
790
791 subdir: MUST be a relevant subdirectory in the results,
792 or None, which will be represented as '----'
793
794 operation: description of what you ran (e.g. "dbench", or
795 "mkfs -t foobar /dev/sda9")
796
797 status: error message or "completed sucessfully"
798
799 ------------------------------------------------------------
800
801 Initial tabs indicate indent levels for grouping, and is
mbligh7dd510c2007-11-13 17:11:22 +0000802 governed by self.group_level
mbligh09f288a2007-09-18 21:34:57 +0000803
804 multiline messages have secondary lines prefaced by a double
805 space (' ')
806 """
807
mblighb0570ad2007-09-19 18:18:11 +0000808 if subdir:
809 if re.match(r'[\n\t]', subdir):
jadmanskia9c75c42008-05-01 22:05:31 +0000810 raise ValueError("Invalid character in "
811 "subdir string")
mblighb0570ad2007-09-19 18:18:11 +0000812 substr = subdir
813 else:
814 substr = '----'
mbligh09f288a2007-09-18 21:34:57 +0000815
mbligh302482e2008-05-01 20:06:16 +0000816 if not logging.is_valid_status(status_code):
jadmanskia9c75c42008-05-01 22:05:31 +0000817 raise ValueError("Invalid status code supplied: %s" %
818 status_code)
mbligh9c5ac322007-10-31 18:01:59 +0000819 if not operation:
820 operation = '----'
jadmanskia9c75c42008-05-01 22:05:31 +0000821
mbligh09f288a2007-09-18 21:34:57 +0000822 if re.match(r'[\n\t]', operation):
jadmanskia9c75c42008-05-01 22:05:31 +0000823 raise ValueError("Invalid character in "
824 "operation string")
mbligh09f288a2007-09-18 21:34:57 +0000825 operation = operation.rstrip()
jadmanskia9c75c42008-05-01 22:05:31 +0000826
827 if not optional_fields:
828 optional_fields = {}
829
mbligh09f288a2007-09-18 21:34:57 +0000830 status = status.rstrip()
831 status = re.sub(r"\t", " ", status)
apw7db8d0b2006-10-09 08:10:25 +0000832 # Ensure any continuation lines are marked so we can
833 # detect them in the status file to ensure it is parsable.
jadmanskia9c75c42008-05-01 22:05:31 +0000834 status = re.sub(r"\n", "\n" + "\t" * self.group_level + " ",
835 status)
mbligh09f288a2007-09-18 21:34:57 +0000836
mbligh30270302007-11-05 20:33:52 +0000837 # Generate timestamps for inclusion in the logs
838 epoch_time = int(time.time()) # seconds since epoch, in UTC
839 local_time = time.localtime(epoch_time)
jadmanskia9c75c42008-05-01 22:05:31 +0000840 optional_fields["timestamp"] = str(epoch_time)
841 optional_fields["localtime"] = time.strftime("%b %d %H:%M:%S",
842 local_time)
mbligh30270302007-11-05 20:33:52 +0000843
jadmanskia9c75c42008-05-01 22:05:31 +0000844 fields = [status_code, substr, operation]
845 fields += ["%s=%s" % x for x in optional_fields.iteritems()]
846 fields.append(status)
847
848 msg = '\t'.join(str(x) for x in fields)
mbligh7dd510c2007-11-13 17:11:22 +0000849 msg = '\t' * self.group_level + msg
apw7db8d0b2006-10-09 08:10:25 +0000850
mblighd528d302007-12-19 16:19:05 +0000851 msg_tag = ""
852 if "." in self.log_filename:
853 msg_tag = self.log_filename.split(".", 1)[1]
854
jadmanskia9c75c42008-05-01 22:05:31 +0000855 self.harness.test_status_detail(status_code, substr,
856 operation, status, msg_tag)
mblighd528d302007-12-19 16:19:05 +0000857 self.harness.test_status(msg, msg_tag)
858
859 # log to stdout (if enabled)
860 #if self.log_filename == self.DEFAULT_LOG_FILENAME:
apwf1a81162006-04-25 10:10:29 +0000861 print msg
mblighd528d302007-12-19 16:19:05 +0000862
863 # log to the "root" status log
864 status_file = os.path.join(self.resultdir, self.log_filename)
mbligh7dd510c2007-11-13 17:11:22 +0000865 open(status_file, "a").write(msg + "\n")
mblighd528d302007-12-19 16:19:05 +0000866
867 # log to the subdir status log (if subdir is set)
mblighb0570ad2007-09-19 18:18:11 +0000868 if subdir:
mblighadff6ca2008-01-22 16:38:25 +0000869 dir = os.path.join(self.resultdir, subdir)
mblighadff6ca2008-01-22 16:38:25 +0000870 status_file = os.path.join(dir,
mblighd528d302007-12-19 16:19:05 +0000871 self.DEFAULT_LOG_FILENAME)
mblighb0570ad2007-09-19 18:18:11 +0000872 open(status_file, "a").write(msg + "\n")
apwce9abe92006-04-27 14:14:04 +0000873
874
jadmanski8415f962008-05-06 20:38:53 +0000875class disk_usage_monitor:
876 def __init__(self, logging_func, device, max_mb_per_hour):
877 self.func = logging_func
878 self.device = device
879 self.max_mb_per_hour = max_mb_per_hour
880
881
882 def start(self):
883 self.initial_space = autotest_utils.freespace(self.device)
884 self.start_time = time.time()
885
886
887 def stop(self):
888 # if no maximum usage rate was set, we don't need to
889 # generate any warnings
890 if not self.max_mb_per_hour:
891 return
892
893 final_space = autotest_utils.freespace(self.device)
894 used_space = self.initial_space - final_space
895 stop_time = time.time()
896 total_time = stop_time - self.start_time
897 # round up the time to one minute, to keep extremely short
898 # tests from generating false positives due to short, badly
899 # timed bursts of activity
900 total_time = max(total_time, 60.0)
901
902 # determine the usage rate
903 bytes_per_sec = used_space / total_time
904 mb_per_sec = bytes_per_sec / 1024**2
905 mb_per_hour = mb_per_sec * 60 * 60
906
907 if mb_per_hour > self.max_mb_per_hour:
908 msg = ("disk space on %s was consumed at a rate of "
909 "%.2f MB/hour")
910 msg %= (self.device, mb_per_hour)
911 self.func(msg)
912
913
914 @classmethod
915 def watch(cls, *monitor_args, **monitor_dargs):
916 """ Generic decorator to wrap a function call with the
917 standard create-monitor -> start -> call -> stop idiom."""
918 def decorator(func):
919 def watched_func(*args, **dargs):
920 monitor = cls(*monitor_args, **monitor_dargs)
921 monitor.start()
922 try:
923 func(*args, **dargs)
924 finally:
925 monitor.stop()
926 return watched_func
927 return decorator
928
929
mblighcaa62c22008-04-07 21:51:17 +0000930def runjob(control, cont = False, tag = "default", harness_type = '',
931 use_external_logging = False):
mblighc86b0b42006-07-28 17:35:28 +0000932 """The main interface to this module
933
mbligh72b88fc2006-12-16 18:41:35 +0000934 control
mblighc86b0b42006-07-28 17:35:28 +0000935 The control file to use for this job.
936 cont
937 Whether this is the continuation of a previously started job
938 """
mblighb4eef242007-07-23 18:22:49 +0000939 control = os.path.abspath(control)
apwce9abe92006-04-27 14:14:04 +0000940 state = control + '.state'
941
942 # instantiate the job object ready for the control file.
943 myjob = None
944 try:
945 # Check that the control file is valid
946 if not os.path.exists(control):
mbligh302482e2008-05-01 20:06:16 +0000947 raise error.JobError(control +
948 ": control file not found")
apwce9abe92006-04-27 14:14:04 +0000949
950 # When continuing, the job is complete when there is no
951 # state file, ensure we don't try and continue.
mblighf3fef462006-09-13 16:05:05 +0000952 if cont and not os.path.exists(state):
mbligh302482e2008-05-01 20:06:16 +0000953 raise error.JobComplete("all done")
mblighf3fef462006-09-13 16:05:05 +0000954 if cont == False and os.path.exists(state):
apwce9abe92006-04-27 14:14:04 +0000955 os.unlink(state)
956
mblighcaa62c22008-04-07 21:51:17 +0000957 myjob = job(control, tag, cont, harness_type,
958 use_external_logging)
apwce9abe92006-04-27 14:14:04 +0000959
960 # Load in the users control file, may do any one of:
961 # 1) execute in toto
962 # 2) define steps, and select the first via next_step()
963 myjob.step_engine()
964
mbligh302482e2008-05-01 20:06:16 +0000965 except error.JobContinue:
apwce9abe92006-04-27 14:14:04 +0000966 sys.exit(5)
967
mbligh302482e2008-05-01 20:06:16 +0000968 except error.JobComplete:
apwb832e1b2007-11-24 20:24:38 +0000969 sys.exit(1)
970
mbligh302482e2008-05-01 20:06:16 +0000971 except error.JobError, instance:
apwce9abe92006-04-27 14:14:04 +0000972 print "JOB ERROR: " + instance.args[0]
mbligh9c5ac322007-10-31 18:01:59 +0000973 if myjob:
mbligh30270302007-11-05 20:33:52 +0000974 command = None
975 if len(instance.args) > 1:
976 command = instance.args[1]
977 myjob.record('ABORT', None, command, instance.args[0])
jadmanskia9c75c42008-05-01 22:05:31 +0000978 myjob._decrement_group_level()
mblighc3430162007-11-14 23:57:19 +0000979 myjob.record('END ABORT', None, None)
jadmanskia9c75c42008-05-01 22:05:31 +0000980 assert(myjob.group_level == 0)
apwce9abe92006-04-27 14:14:04 +0000981 myjob.complete(1)
apwb832e1b2007-11-24 20:24:38 +0000982 else:
983 sys.exit(1)
apwce9abe92006-04-27 14:14:04 +0000984
mblighc3430162007-11-14 23:57:19 +0000985 except Exception, e:
mbligh302482e2008-05-01 20:06:16 +0000986 msg = str(e) + '\n' + traceback.format_exc()
mblighc3430162007-11-14 23:57:19 +0000987 print "JOB ERROR: " + msg
mblighfbfb77d2007-02-15 18:54:03 +0000988 if myjob:
mblighc3430162007-11-14 23:57:19 +0000989 myjob.record('ABORT', None, None, msg)
jadmanskia9c75c42008-05-01 22:05:31 +0000990 myjob._decrement_group_level()
mblighc3430162007-11-14 23:57:19 +0000991 myjob.record('END ABORT', None, None)
jadmanskia9c75c42008-05-01 22:05:31 +0000992 assert(myjob.group_level == 0)
mbligh9c5ac322007-10-31 18:01:59 +0000993 myjob.complete(1)
apwb832e1b2007-11-24 20:24:38 +0000994 else:
995 sys.exit(1)
mbligh892d37f2007-03-01 17:03:25 +0000996
mbligh0144e5a2008-03-07 18:17:53 +0000997 # If we get here, then we assume the job is complete and good.
jadmanskia9c75c42008-05-01 22:05:31 +0000998 myjob._decrement_group_level()
mbligh0144e5a2008-03-07 18:17:53 +0000999 myjob.record('END GOOD', None, None)
jadmanskia9c75c42008-05-01 22:05:31 +00001000 assert(myjob.group_level == 0)
mbligh0144e5a2008-03-07 18:17:53 +00001001
mbligh892d37f2007-03-01 17:03:25 +00001002 myjob.complete(0)
mblighcaa62c22008-04-07 21:51:17 +00001003
1004
1005# site_job.py may be non-existant or empty, make sure that an appropriate
1006# site_job class is created nevertheless
1007try:
1008 from site_job import site_job
1009except ImportError:
1010 class site_job(base_job):
1011 pass
1012
1013class job(site_job):
1014 pass
jadmanski87cbc7f2008-05-13 18:17:10 +00001015
mblighd660afe2008-06-05 22:17:53 +00001016