blob: 99d155f675760ca94dde3cbce4e9321424b2c2fd [file] [log] [blame]
mbligh05269362007-10-16 16:58:11 +00001# Copyright Martin J. Bligh, Andy Whitcroft, 2007
2#
mblighea397bb2008-02-02 19:17:51 +00003# Define the server-side test class
mbligh05269362007-10-16 16:58:11 +00004#
Xixuan Wue8ce6542017-11-07 17:22:10 -08005# pylint: disable=missing-docstring
mbligh05269362007-10-16 16:58:11 +00006
Xixuan Wu5019d692017-11-10 15:58:33 -08007import logging
8import os
9import tempfile
mblighea397bb2008-02-02 19:17:51 +000010
Xixuan Wu5019d692017-11-10 15:58:33 -080011from autotest_lib.client.common_lib import log
12from autotest_lib.client.common_lib import test as common_test
13from autotest_lib.client.common_lib import utils
mbligh05269362007-10-16 16:58:11 +000014
15
mblighccb9e182008-04-17 15:42:10 +000016class test(common_test.base_test):
mbligh089f1e32009-06-22 18:26:45 +000017 disable_sysinfo_install_cache = False
mbligh43dd5492009-09-03 20:20:51 +000018 host_parameter = None
mblighdccabe32008-02-01 17:39:32 +000019
20
mbligh4395bbd2009-03-25 19:34:17 +000021_sysinfo_before_test_script = """\
jadmanski54f90af2008-10-10 16:20:55 +000022import pickle
23from autotest_lib.client.bin import test
24mytest = test.test(job, '', %r)
25job.sysinfo.log_before_each_test(mytest)
26sysinfo_pickle = os.path.join(mytest.outputdir, 'sysinfo.pickle')
27pickle.dump(job.sysinfo, open(sysinfo_pickle, 'w'))
28job.record('GOOD', '', 'sysinfo.before')
29"""
30
mbligh4395bbd2009-03-25 19:34:17 +000031_sysinfo_after_test_script = """\
jadmanski54f90af2008-10-10 16:20:55 +000032import pickle
33from autotest_lib.client.bin import test
34mytest = test.test(job, '', %r)
Dan Shi4eba9f02014-06-18 13:58:05 -070035# success is passed in so diffable_logdir can decide if or not to collect
36# full log content.
37mytest.success = %s
jadmanski54f90af2008-10-10 16:20:55 +000038sysinfo_pickle = os.path.join(mytest.outputdir, 'sysinfo.pickle')
39if os.path.exists(sysinfo_pickle):
40 job.sysinfo = pickle.load(open(sysinfo_pickle))
41 job.sysinfo.__init__(job.resultdir)
42job.sysinfo.log_after_each_test(mytest)
43job.record('GOOD', '', 'sysinfo.after')
44"""
45
mbligh4395bbd2009-03-25 19:34:17 +000046# this script is ran after _sysinfo_before_test_script and before
47# _sysinfo_after_test_script which means the pickle file exists
48# already and should be dumped with updated state for
49# _sysinfo_after_test_script to pick it up later
50_sysinfo_iteration_script = """\
51import pickle
52from autotest_lib.client.bin import test
53mytest = test.test(job, '', %r)
54sysinfo_pickle = os.path.join(mytest.outputdir, 'sysinfo.pickle')
55if os.path.exists(sysinfo_pickle):
56 job.sysinfo = pickle.load(open(sysinfo_pickle))
57 job.sysinfo.__init__(job.resultdir)
58job.sysinfo.%s(mytest, iteration=%d)
59pickle.dump(job.sysinfo, open(sysinfo_pickle, 'w'))
60job.record('GOOD', '', 'sysinfo.iteration.%s')
61"""
62
jadmanski54f90af2008-10-10 16:20:55 +000063
jadmanski7c7aff32009-03-25 22:43:07 +000064def install_autotest_and_run(func):
65 def wrapper(self, mytest):
mbligh35245892009-06-08 16:43:21 +000066 host, at, outputdir = self._install()
Kevin Cheng70936692015-10-13 14:57:26 -070067 # TODO(kevcheng): remove when host client install is supported for
68 # ADBHost. crbug.com/543702
69 if not host.is_client_install_supported:
70 logging.debug('host client install not supported, skipping %s:',
71 func.__name__)
72 return
73
mbligh089f1e32009-06-22 18:26:45 +000074 try:
75 host.erase_dir_contents(outputdir)
76 func(self, mytest, host, at, outputdir)
77 finally:
78 # the test class can define this flag to make us remove the
79 # sysinfo install files and outputdir contents after each run
80 if mytest.disable_sysinfo_install_cache:
81 self.cleanup(host_close=False)
mbligh35245892009-06-08 16:43:21 +000082
jadmanski7c7aff32009-03-25 22:43:07 +000083 return wrapper
84
85
jadmanski54f90af2008-10-10 16:20:55 +000086class _sysinfo_logger(object):
Alex Khouderchahc44e7772018-07-16 10:53:14 -070087 AUTOTEST_PARENT_DIR = '/tmp/sysinfo'
88 OUTPUT_PARENT_DIR = '/tmp'
89
jadmanski54f90af2008-10-10 16:20:55 +000090 def __init__(self, job):
91 self.job = job
92 self.pickle = None
mbligh35245892009-06-08 16:43:21 +000093
94 # for now support a single host
95 self.host = None
96 self.autotest = None
97 self.outputdir = None
98
jadmanski54f90af2008-10-10 16:20:55 +000099 if len(job.machines) != 1:
100 # disable logging on multi-machine tests
101 self.before_hook = self.after_hook = None
mbligh35245892009-06-08 16:43:21 +0000102 self.before_iteration_hook = self.after_iteration_hook = None
jadmanski54f90af2008-10-10 16:20:55 +0000103
104
105 def _install(self):
mbligh35245892009-06-08 16:43:21 +0000106 if not self.host:
107 from autotest_lib.server import hosts, autotest
Dan Shid7f12422016-01-04 11:51:32 -0800108 self.host = hosts.create_target_machine(
109 self.job.machine_dict_list[0])
Kevin Cheng70936692015-10-13 14:57:26 -0700110 # TODO(kevcheng): remove when host client install is supported for
111 # ADBHost. crbug.com/543702
112 if not self.host.is_client_install_supported:
113 return self.host, None, None
mblighf780bbf2009-09-03 20:45:46 +0000114 try:
Alex Khouderchahc44e7772018-07-16 10:53:14 -0700115 # Remove existing autoserv-* directories before creating more
116 self.host.delete_all_tmp_dirs(self.AUTOTEST_PARENT_DIR)
117 self.host.delete_all_tmp_dirs(self.OUTPUT_PARENT_DIR)
118
119 tmp_dir = self.host.get_tmp_dir(self.AUTOTEST_PARENT_DIR)
mblighf780bbf2009-09-03 20:45:46 +0000120 self.autotest = autotest.Autotest(self.host)
Dale Curtis74a314b2011-06-23 14:55:46 -0700121 self.autotest.install(autodir=tmp_dir)
Alex Khouderchahc44e7772018-07-16 10:53:14 -0700122 self.outputdir = self.host.get_tmp_dir(self.OUTPUT_PARENT_DIR)
mblighf780bbf2009-09-03 20:45:46 +0000123 except:
124 # if installation fails roll back the host
125 try:
126 self.host.close()
127 except:
128 logging.exception("Unable to close host %s",
129 self.host.hostname)
130 self.host = None
131 self.autotest = None
132 raise
mbligh089f1e32009-06-22 18:26:45 +0000133 else:
Kevin Cheng70936692015-10-13 14:57:26 -0700134 # TODO(kevcheng): remove when host client install is supported for
135 # ADBHost. crbug.com/543702
136 if not self.host.is_client_install_supported:
137 return self.host, None, None
mbligh089f1e32009-06-22 18:26:45 +0000138
139 # if autotest client dir does not exist, reinstall (it may have
140 # been removed by the test code)
Kevin Cheng70936692015-10-13 14:57:26 -0700141 autodir = self.host.get_autodir()
142 if not autodir or not self.host.path_exists(autodir):
jadmanski27b52912009-08-14 17:09:15 +0000143 self.autotest.install(autodir=autodir)
mbligh089f1e32009-06-22 18:26:45 +0000144
145 # if the output dir does not exist, recreate it
Kevin Cheng70936692015-10-13 14:57:26 -0700146 if not self.host.path_exists(self.outputdir):
147 self.host.run('mkdir -p %s' % self.outputdir)
mbligh35245892009-06-08 16:43:21 +0000148
149 return self.host, self.autotest, self.outputdir
jadmanski54f90af2008-10-10 16:20:55 +0000150
151
mbligh4395bbd2009-03-25 19:34:17 +0000152 def _pull_pickle(self, host, outputdir):
153 """Pulls from the client the pickle file with the saved sysinfo state.
154 """
jadmanski54f90af2008-10-10 16:20:55 +0000155 fd, path = tempfile.mkstemp(dir=self.job.tmpdir)
156 os.close(fd)
157 host.get_file(os.path.join(outputdir, "sysinfo.pickle"), path)
158 self.pickle = path
159
160
mbligh4395bbd2009-03-25 19:34:17 +0000161 def _push_pickle(self, host, outputdir):
162 """Pushes the server saved sysinfo pickle file to the client.
163 """
jadmanski54f90af2008-10-10 16:20:55 +0000164 if self.pickle:
165 host.send_file(self.pickle,
166 os.path.join(outputdir, "sysinfo.pickle"))
167 os.remove(self.pickle)
168 self.pickle = None
169
jadmanski54f90af2008-10-10 16:20:55 +0000170
mbligh4395bbd2009-03-25 19:34:17 +0000171 def _pull_sysinfo_keyval(self, host, outputdir, mytest):
172 """Pulls sysinfo and keyval data from the client.
173 """
jadmanski54f90af2008-10-10 16:20:55 +0000174 # pull the sysinfo data back on to the server
175 host.get_file(os.path.join(outputdir, "sysinfo"), mytest.outputdir)
176
177 # pull the keyval data back into the local one
178 fd, path = tempfile.mkstemp(dir=self.job.tmpdir)
179 os.close(fd)
180 host.get_file(os.path.join(outputdir, "keyval"), path)
181 keyval = utils.read_keyval(path)
182 os.remove(path)
183 mytest.write_test_keyval(keyval)
184
185
mbligh4395bbd2009-03-25 19:34:17 +0000186 @log.log_and_ignore_errors("pre-test server sysinfo error:")
jadmanski7c7aff32009-03-25 22:43:07 +0000187 @install_autotest_and_run
188 def before_hook(self, mytest, host, at, outputdir):
mbligh4395bbd2009-03-25 19:34:17 +0000189 # run the pre-test sysinfo script
190 at.run(_sysinfo_before_test_script % outputdir,
Dale Curtis9285ddf2011-01-05 11:47:24 -0800191 results_dir=self.job.resultdir)
mbligh4395bbd2009-03-25 19:34:17 +0000192
193 self._pull_pickle(host, outputdir)
194
195
196 @log.log_and_ignore_errors("pre-test iteration server sysinfo error:")
jadmanski7c7aff32009-03-25 22:43:07 +0000197 @install_autotest_and_run
198 def before_iteration_hook(self, mytest, host, at, outputdir):
mbligh4395bbd2009-03-25 19:34:17 +0000199 # this function is called after before_hook() se we have sysinfo state
200 # to push to the server
201 self._push_pickle(host, outputdir);
202 # run the pre-test iteration sysinfo script
203 at.run(_sysinfo_iteration_script %
204 (outputdir, 'log_before_each_iteration', mytest.iteration,
205 'before'),
Dale Curtis9285ddf2011-01-05 11:47:24 -0800206 results_dir=self.job.resultdir)
mbligh4395bbd2009-03-25 19:34:17 +0000207
208 # get the new sysinfo state from the client
209 self._pull_pickle(host, outputdir)
210
211
212 @log.log_and_ignore_errors("post-test iteration server sysinfo error:")
jadmanski7c7aff32009-03-25 22:43:07 +0000213 @install_autotest_and_run
214 def after_iteration_hook(self, mytest, host, at, outputdir):
mbligh4395bbd2009-03-25 19:34:17 +0000215 # push latest sysinfo state to the client
216 self._push_pickle(host, outputdir);
217 # run the post-test iteration sysinfo script
218 at.run(_sysinfo_iteration_script %
219 (outputdir, 'log_after_each_iteration', mytest.iteration,
220 'after'),
Dale Curtis9285ddf2011-01-05 11:47:24 -0800221 results_dir=self.job.resultdir)
mbligh4395bbd2009-03-25 19:34:17 +0000222
223 # get the new sysinfo state from the client
224 self._pull_pickle(host, outputdir)
mbligh4395bbd2009-03-25 19:34:17 +0000225
226
227 @log.log_and_ignore_errors("post-test server sysinfo error:")
jadmanski7c7aff32009-03-25 22:43:07 +0000228 @install_autotest_and_run
229 def after_hook(self, mytest, host, at, outputdir):
mbligh4395bbd2009-03-25 19:34:17 +0000230 self._push_pickle(host, outputdir);
231 # run the post-test sysinfo script
Dan Shi4eba9f02014-06-18 13:58:05 -0700232 at.run(_sysinfo_after_test_script % (outputdir, mytest.success),
Dale Curtis9285ddf2011-01-05 11:47:24 -0800233 results_dir=self.job.resultdir)
mbligh4395bbd2009-03-25 19:34:17 +0000234
235 self._pull_sysinfo_keyval(host, outputdir, mytest)
236
237
mbligh089f1e32009-06-22 18:26:45 +0000238 def cleanup(self, host_close=True):
mbligh35245892009-06-08 16:43:21 +0000239 if self.host and self.autotest:
240 try:
mbligh089f1e32009-06-22 18:26:45 +0000241 try:
242 self.autotest.uninstall()
243 finally:
244 if host_close:
245 self.host.close()
246 else:
247 self.host.erase_dir_contents(self.outputdir)
248
249 except Exception:
250 # ignoring exceptions here so that we don't hide the true
251 # reason of failure from runtest
252 logging.exception('Error cleaning up the sysinfo autotest/host '
253 'objects, ignoring it')
mbligh35245892009-06-08 16:43:21 +0000254
255
mbligh05269362007-10-16 16:58:11 +0000256def runtest(job, url, tag, args, dargs):
Fang Dengc2282212015-01-26 13:10:01 -0800257 """Server-side runtest.
258
259 @param job: A server_job instance.
260 @param url: URL to the test.
261 @param tag: Test tag that will be appended to the test name.
262 See client/common_lib/test.py:runtest
263 @param args: args to pass to the test.
264 @param dargs: key-val based args to pass to the test.
265 """
266
267 disable_before_test_hook = dargs.pop('disable_before_test_sysinfo', False)
268 disable_after_test_hook = dargs.pop('disable_after_test_sysinfo', False)
269 disable_before_iteration_hook = dargs.pop(
270 'disable_before_iteration_sysinfo', False)
271 disable_after_iteration_hook = dargs.pop(
272 'disable_after_iteration_sysinfo', False)
273
Xixuan Wu5019d692017-11-10 15:58:33 -0800274 disable_sysinfo = dargs.pop('disable_sysinfo', False)
275 if job.fast and not disable_sysinfo:
276 # Server job will be executed in fast mode, which means
277 # 1) if job succeeds, no hook will be executed.
278 # 2) if job failed, after_hook will be executed.
279 logger = _sysinfo_logger(job)
Xixuan Wuf4fb11b2017-11-13 19:54:38 -0800280 logging_args = [None, logger.after_hook, None,
281 logger.after_iteration_hook]
Xixuan Wu5019d692017-11-10 15:58:33 -0800282 elif not disable_sysinfo:
mbligh79a2ee12008-11-05 22:07:38 +0000283 logger = _sysinfo_logger(job)
Fang Dengc2282212015-01-26 13:10:01 -0800284 logging_args = [
285 logger.before_hook if not disable_before_test_hook else None,
286 logger.after_hook if not disable_after_test_hook else None,
287 (logger.before_iteration_hook
288 if not disable_before_iteration_hook else None),
289 (logger.after_iteration_hook
290 if not disable_after_iteration_hook else None),
291 ]
mbligh79a2ee12008-11-05 22:07:38 +0000292 else:
mbligh35245892009-06-08 16:43:21 +0000293 logger = None
294 logging_args = [None, None, None, None]
295
mbligh43dd5492009-09-03 20:20:51 +0000296 # add in a hook that calls host.log_kernel if we can
297 def log_kernel_hook(mytest, existing_hook=logging_args[0]):
298 if mytest.host_parameter:
299 host = dargs[mytest.host_parameter]
300 if host:
301 host.log_kernel()
302 # chain this call with any existing hook
303 if existing_hook:
304 existing_hook(mytest)
305 logging_args[0] = log_kernel_hook
306
mbligh35245892009-06-08 16:43:21 +0000307 try:
308 common_test.runtest(job, url, tag, args, dargs, locals(), globals(),
309 *logging_args)
310 finally:
311 if logger:
312 logger.cleanup()