blob: 76ae3daac85973dc10f20b03543fab122fdef98f [file] [log] [blame]
Oleg Loskutoff75cc9b22019-10-16 17:28:12 -07001# Copyright (c) 2007 The Chromium OS Authors. All rights reserved.
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
mbligh05269362007-10-16 16:58:11 +00005# Copyright Martin J. Bligh, Andy Whitcroft, 2007
6#
mblighea397bb2008-02-02 19:17:51 +00007# Define the server-side test class
mbligh05269362007-10-16 16:58:11 +00008#
Xixuan Wue8ce6542017-11-07 17:22:10 -08009# pylint: disable=missing-docstring
mbligh05269362007-10-16 16:58:11 +000010
Xixuan Wu5019d692017-11-10 15:58:33 -080011import logging
12import os
13import tempfile
mblighea397bb2008-02-02 19:17:51 +000014
Xixuan Wu5019d692017-11-10 15:58:33 -080015from autotest_lib.client.common_lib import log
16from autotest_lib.client.common_lib import test as common_test
17from autotest_lib.client.common_lib import utils
mbligh05269362007-10-16 16:58:11 +000018
19
mblighccb9e182008-04-17 15:42:10 +000020class test(common_test.base_test):
mbligh089f1e32009-06-22 18:26:45 +000021 disable_sysinfo_install_cache = False
mbligh43dd5492009-09-03 20:20:51 +000022 host_parameter = None
mblighdccabe32008-02-01 17:39:32 +000023
24
mbligh4395bbd2009-03-25 19:34:17 +000025_sysinfo_before_test_script = """\
jadmanski54f90af2008-10-10 16:20:55 +000026import pickle
27from autotest_lib.client.bin import test
28mytest = test.test(job, '', %r)
29job.sysinfo.log_before_each_test(mytest)
30sysinfo_pickle = os.path.join(mytest.outputdir, 'sysinfo.pickle')
31pickle.dump(job.sysinfo, open(sysinfo_pickle, 'w'))
32job.record('GOOD', '', 'sysinfo.before')
33"""
34
mbligh4395bbd2009-03-25 19:34:17 +000035_sysinfo_after_test_script = """\
jadmanski54f90af2008-10-10 16:20:55 +000036import pickle
37from autotest_lib.client.bin import test
38mytest = test.test(job, '', %r)
Dan Shi4eba9f02014-06-18 13:58:05 -070039# success is passed in so diffable_logdir can decide if or not to collect
40# full log content.
41mytest.success = %s
jadmanski54f90af2008-10-10 16:20:55 +000042sysinfo_pickle = os.path.join(mytest.outputdir, 'sysinfo.pickle')
43if os.path.exists(sysinfo_pickle):
44 job.sysinfo = pickle.load(open(sysinfo_pickle))
45 job.sysinfo.__init__(job.resultdir)
46job.sysinfo.log_after_each_test(mytest)
47job.record('GOOD', '', 'sysinfo.after')
48"""
49
mbligh4395bbd2009-03-25 19:34:17 +000050# this script is ran after _sysinfo_before_test_script and before
51# _sysinfo_after_test_script which means the pickle file exists
52# already and should be dumped with updated state for
53# _sysinfo_after_test_script to pick it up later
54_sysinfo_iteration_script = """\
55import pickle
56from autotest_lib.client.bin import test
57mytest = test.test(job, '', %r)
58sysinfo_pickle = os.path.join(mytest.outputdir, 'sysinfo.pickle')
59if os.path.exists(sysinfo_pickle):
60 job.sysinfo = pickle.load(open(sysinfo_pickle))
61 job.sysinfo.__init__(job.resultdir)
62job.sysinfo.%s(mytest, iteration=%d)
63pickle.dump(job.sysinfo, open(sysinfo_pickle, 'w'))
64job.record('GOOD', '', 'sysinfo.iteration.%s')
65"""
66
jadmanski54f90af2008-10-10 16:20:55 +000067
jadmanski7c7aff32009-03-25 22:43:07 +000068def install_autotest_and_run(func):
69 def wrapper(self, mytest):
mbligh35245892009-06-08 16:43:21 +000070 host, at, outputdir = self._install()
mbligh089f1e32009-06-22 18:26:45 +000071 try:
72 host.erase_dir_contents(outputdir)
73 func(self, mytest, host, at, outputdir)
74 finally:
75 # the test class can define this flag to make us remove the
76 # sysinfo install files and outputdir contents after each run
77 if mytest.disable_sysinfo_install_cache:
78 self.cleanup(host_close=False)
mbligh35245892009-06-08 16:43:21 +000079
jadmanski7c7aff32009-03-25 22:43:07 +000080 return wrapper
81
82
jadmanski54f90af2008-10-10 16:20:55 +000083class _sysinfo_logger(object):
Alex Khouderchahc44e7772018-07-16 10:53:14 -070084 AUTOTEST_PARENT_DIR = '/tmp/sysinfo'
85 OUTPUT_PARENT_DIR = '/tmp'
86
jadmanski54f90af2008-10-10 16:20:55 +000087 def __init__(self, job):
88 self.job = job
89 self.pickle = None
mbligh35245892009-06-08 16:43:21 +000090
91 # for now support a single host
92 self.host = None
93 self.autotest = None
94 self.outputdir = None
95
jadmanski54f90af2008-10-10 16:20:55 +000096 if len(job.machines) != 1:
97 # disable logging on multi-machine tests
98 self.before_hook = self.after_hook = None
mbligh35245892009-06-08 16:43:21 +000099 self.before_iteration_hook = self.after_iteration_hook = None
jadmanski54f90af2008-10-10 16:20:55 +0000100
101
102 def _install(self):
mbligh35245892009-06-08 16:43:21 +0000103 if not self.host:
104 from autotest_lib.server import hosts, autotest
Dan Shid7f12422016-01-04 11:51:32 -0800105 self.host = hosts.create_target_machine(
106 self.job.machine_dict_list[0])
mblighf780bbf2009-09-03 20:45:46 +0000107 try:
Alex Khouderchahc44e7772018-07-16 10:53:14 -0700108 # Remove existing autoserv-* directories before creating more
109 self.host.delete_all_tmp_dirs(self.AUTOTEST_PARENT_DIR)
110 self.host.delete_all_tmp_dirs(self.OUTPUT_PARENT_DIR)
111
112 tmp_dir = self.host.get_tmp_dir(self.AUTOTEST_PARENT_DIR)
mblighf780bbf2009-09-03 20:45:46 +0000113 self.autotest = autotest.Autotest(self.host)
Dale Curtis74a314b2011-06-23 14:55:46 -0700114 self.autotest.install(autodir=tmp_dir)
Alex Khouderchahc44e7772018-07-16 10:53:14 -0700115 self.outputdir = self.host.get_tmp_dir(self.OUTPUT_PARENT_DIR)
mblighf780bbf2009-09-03 20:45:46 +0000116 except:
117 # if installation fails roll back the host
118 try:
119 self.host.close()
120 except:
121 logging.exception("Unable to close host %s",
122 self.host.hostname)
123 self.host = None
124 self.autotest = None
125 raise
mbligh089f1e32009-06-22 18:26:45 +0000126 else:
mbligh089f1e32009-06-22 18:26:45 +0000127 # if autotest client dir does not exist, reinstall (it may have
128 # been removed by the test code)
Kevin Cheng70936692015-10-13 14:57:26 -0700129 autodir = self.host.get_autodir()
130 if not autodir or not self.host.path_exists(autodir):
jadmanski27b52912009-08-14 17:09:15 +0000131 self.autotest.install(autodir=autodir)
mbligh089f1e32009-06-22 18:26:45 +0000132
133 # if the output dir does not exist, recreate it
Kevin Cheng70936692015-10-13 14:57:26 -0700134 if not self.host.path_exists(self.outputdir):
135 self.host.run('mkdir -p %s' % self.outputdir)
mbligh35245892009-06-08 16:43:21 +0000136
137 return self.host, self.autotest, self.outputdir
jadmanski54f90af2008-10-10 16:20:55 +0000138
139
mbligh4395bbd2009-03-25 19:34:17 +0000140 def _pull_pickle(self, host, outputdir):
141 """Pulls from the client the pickle file with the saved sysinfo state.
142 """
jadmanski54f90af2008-10-10 16:20:55 +0000143 fd, path = tempfile.mkstemp(dir=self.job.tmpdir)
144 os.close(fd)
145 host.get_file(os.path.join(outputdir, "sysinfo.pickle"), path)
146 self.pickle = path
147
148
mbligh4395bbd2009-03-25 19:34:17 +0000149 def _push_pickle(self, host, outputdir):
150 """Pushes the server saved sysinfo pickle file to the client.
151 """
jadmanski54f90af2008-10-10 16:20:55 +0000152 if self.pickle:
153 host.send_file(self.pickle,
154 os.path.join(outputdir, "sysinfo.pickle"))
155 os.remove(self.pickle)
156 self.pickle = None
157
jadmanski54f90af2008-10-10 16:20:55 +0000158
mbligh4395bbd2009-03-25 19:34:17 +0000159 def _pull_sysinfo_keyval(self, host, outputdir, mytest):
160 """Pulls sysinfo and keyval data from the client.
161 """
jadmanski54f90af2008-10-10 16:20:55 +0000162 # pull the sysinfo data back on to the server
163 host.get_file(os.path.join(outputdir, "sysinfo"), mytest.outputdir)
164
165 # pull the keyval data back into the local one
166 fd, path = tempfile.mkstemp(dir=self.job.tmpdir)
167 os.close(fd)
168 host.get_file(os.path.join(outputdir, "keyval"), path)
169 keyval = utils.read_keyval(path)
170 os.remove(path)
171 mytest.write_test_keyval(keyval)
172
173
mbligh4395bbd2009-03-25 19:34:17 +0000174 @log.log_and_ignore_errors("pre-test server sysinfo error:")
jadmanski7c7aff32009-03-25 22:43:07 +0000175 @install_autotest_and_run
176 def before_hook(self, mytest, host, at, outputdir):
mbligh4395bbd2009-03-25 19:34:17 +0000177 # run the pre-test sysinfo script
178 at.run(_sysinfo_before_test_script % outputdir,
Dale Curtis9285ddf2011-01-05 11:47:24 -0800179 results_dir=self.job.resultdir)
mbligh4395bbd2009-03-25 19:34:17 +0000180
181 self._pull_pickle(host, outputdir)
182
183
184 @log.log_and_ignore_errors("pre-test iteration server sysinfo error:")
jadmanski7c7aff32009-03-25 22:43:07 +0000185 @install_autotest_and_run
186 def before_iteration_hook(self, mytest, host, at, outputdir):
mbligh4395bbd2009-03-25 19:34:17 +0000187 # this function is called after before_hook() se we have sysinfo state
188 # to push to the server
189 self._push_pickle(host, outputdir);
190 # run the pre-test iteration sysinfo script
191 at.run(_sysinfo_iteration_script %
192 (outputdir, 'log_before_each_iteration', mytest.iteration,
193 'before'),
Dale Curtis9285ddf2011-01-05 11:47:24 -0800194 results_dir=self.job.resultdir)
mbligh4395bbd2009-03-25 19:34:17 +0000195
196 # get the new sysinfo state from the client
197 self._pull_pickle(host, outputdir)
198
199
200 @log.log_and_ignore_errors("post-test iteration server sysinfo error:")
jadmanski7c7aff32009-03-25 22:43:07 +0000201 @install_autotest_and_run
202 def after_iteration_hook(self, mytest, host, at, outputdir):
mbligh4395bbd2009-03-25 19:34:17 +0000203 # push latest sysinfo state to the client
204 self._push_pickle(host, outputdir);
205 # run the post-test iteration sysinfo script
206 at.run(_sysinfo_iteration_script %
207 (outputdir, 'log_after_each_iteration', mytest.iteration,
208 'after'),
Dale Curtis9285ddf2011-01-05 11:47:24 -0800209 results_dir=self.job.resultdir)
mbligh4395bbd2009-03-25 19:34:17 +0000210
211 # get the new sysinfo state from the client
212 self._pull_pickle(host, outputdir)
mbligh4395bbd2009-03-25 19:34:17 +0000213
214
215 @log.log_and_ignore_errors("post-test server sysinfo error:")
jadmanski7c7aff32009-03-25 22:43:07 +0000216 @install_autotest_and_run
217 def after_hook(self, mytest, host, at, outputdir):
mbligh4395bbd2009-03-25 19:34:17 +0000218 self._push_pickle(host, outputdir);
219 # run the post-test sysinfo script
Dan Shi4eba9f02014-06-18 13:58:05 -0700220 at.run(_sysinfo_after_test_script % (outputdir, mytest.success),
Dale Curtis9285ddf2011-01-05 11:47:24 -0800221 results_dir=self.job.resultdir)
mbligh4395bbd2009-03-25 19:34:17 +0000222
223 self._pull_sysinfo_keyval(host, outputdir, mytest)
224
225
mbligh089f1e32009-06-22 18:26:45 +0000226 def cleanup(self, host_close=True):
mbligh35245892009-06-08 16:43:21 +0000227 if self.host and self.autotest:
228 try:
mbligh089f1e32009-06-22 18:26:45 +0000229 try:
230 self.autotest.uninstall()
231 finally:
232 if host_close:
233 self.host.close()
234 else:
235 self.host.erase_dir_contents(self.outputdir)
236
237 except Exception:
238 # ignoring exceptions here so that we don't hide the true
239 # reason of failure from runtest
240 logging.exception('Error cleaning up the sysinfo autotest/host '
241 'objects, ignoring it')
mbligh35245892009-06-08 16:43:21 +0000242
243
mbligh05269362007-10-16 16:58:11 +0000244def runtest(job, url, tag, args, dargs):
Fang Dengc2282212015-01-26 13:10:01 -0800245 """Server-side runtest.
246
247 @param job: A server_job instance.
248 @param url: URL to the test.
249 @param tag: Test tag that will be appended to the test name.
250 See client/common_lib/test.py:runtest
251 @param args: args to pass to the test.
252 @param dargs: key-val based args to pass to the test.
253 """
254
255 disable_before_test_hook = dargs.pop('disable_before_test_sysinfo', False)
256 disable_after_test_hook = dargs.pop('disable_after_test_sysinfo', False)
257 disable_before_iteration_hook = dargs.pop(
258 'disable_before_iteration_sysinfo', False)
259 disable_after_iteration_hook = dargs.pop(
260 'disable_after_iteration_sysinfo', False)
261
Xixuan Wu5019d692017-11-10 15:58:33 -0800262 disable_sysinfo = dargs.pop('disable_sysinfo', False)
263 if job.fast and not disable_sysinfo:
264 # Server job will be executed in fast mode, which means
265 # 1) if job succeeds, no hook will be executed.
266 # 2) if job failed, after_hook will be executed.
267 logger = _sysinfo_logger(job)
Xixuan Wuf4fb11b2017-11-13 19:54:38 -0800268 logging_args = [None, logger.after_hook, None,
269 logger.after_iteration_hook]
Xixuan Wu5019d692017-11-10 15:58:33 -0800270 elif not disable_sysinfo:
mbligh79a2ee12008-11-05 22:07:38 +0000271 logger = _sysinfo_logger(job)
Fang Dengc2282212015-01-26 13:10:01 -0800272 logging_args = [
273 logger.before_hook if not disable_before_test_hook else None,
274 logger.after_hook if not disable_after_test_hook else None,
275 (logger.before_iteration_hook
276 if not disable_before_iteration_hook else None),
277 (logger.after_iteration_hook
278 if not disable_after_iteration_hook else None),
279 ]
mbligh79a2ee12008-11-05 22:07:38 +0000280 else:
mbligh35245892009-06-08 16:43:21 +0000281 logger = None
282 logging_args = [None, None, None, None]
283
mbligh43dd5492009-09-03 20:20:51 +0000284 # add in a hook that calls host.log_kernel if we can
285 def log_kernel_hook(mytest, existing_hook=logging_args[0]):
286 if mytest.host_parameter:
287 host = dargs[mytest.host_parameter]
288 if host:
289 host.log_kernel()
290 # chain this call with any existing hook
291 if existing_hook:
292 existing_hook(mytest)
293 logging_args[0] = log_kernel_hook
294
mbligh35245892009-06-08 16:43:21 +0000295 try:
296 common_test.runtest(job, url, tag, args, dargs, locals(), globals(),
297 *logging_args)
298 finally:
299 if logger:
300 logger.cleanup()