blob: ee3e9b2686e4798861bf6cf32510e457bc8575dd [file] [log] [blame]
lmr0e4056d2010-04-01 02:58:01 +00001import sys, os, time, commands, re, logging, signal, glob, threading, shutil
lmr47a853b2010-02-04 13:56:48 +00002from autotest_lib.client.bin import test, utils
lmr6f669ce2009-05-31 19:02:42 +00003from autotest_lib.client.common_lib import error
lmr9e964a02010-06-18 03:46:21 +00004import kvm_vm, kvm_utils, kvm_subprocess, kvm_monitor, ppm_utils
lmre9f528e2009-08-12 15:18:10 +00005try:
6 import PIL.Image
7except ImportError:
8 logging.warning('No python imaging library installed. PPM image '
9 'conversion to JPEG disabled. In order to enable it, '
10 'please install python-imaging or the equivalent for your '
11 'distro.')
lmr6f669ce2009-05-31 19:02:42 +000012
13
lmr0e4056d2010-04-01 02:58:01 +000014_screendump_thread = None
15_screendump_thread_termination_event = None
16
17
lmr6f669ce2009-05-31 19:02:42 +000018def preprocess_image(test, params):
19 """
20 Preprocess a single QEMU image according to the instructions in params.
21
22 @param test: Autotest test object.
23 @param params: A dict containing image preprocessing parameters.
24 @note: Currently this function just creates an image if requested.
25 """
lmr90b9fd52009-08-17 20:48:18 +000026 image_filename = kvm_vm.get_image_filename(params, test.bindir)
lmr6f669ce2009-05-31 19:02:42 +000027
28 create_image = False
29
30 if params.get("force_create_image") == "yes":
31 logging.debug("'force_create_image' specified; creating image...")
32 create_image = True
lmr52800ba2009-08-17 20:49:58 +000033 elif (params.get("create_image") == "yes" and not
34 os.path.exists(image_filename)):
lmr6f669ce2009-05-31 19:02:42 +000035 logging.debug("Creating image...")
36 create_image = True
37
lmr0294de32009-09-09 22:44:28 +000038 if create_image and not kvm_vm.create_image(params, test.bindir):
39 raise error.TestError("Could not create image")
lmr6f669ce2009-05-31 19:02:42 +000040
41
42def preprocess_vm(test, params, env, name):
43 """
44 Preprocess a single VM object according to the instructions in params.
45 Start the VM if requested and get a screendump.
46
47 @param test: An Autotest test object.
48 @param params: A dict containing VM preprocessing parameters.
49 @param env: The environment (a dict-like object).
50 @param name: The name of the VM object.
51 """
lmr6f669ce2009-05-31 19:02:42 +000052 logging.debug("Preprocessing VM '%s'..." % name)
53 vm = kvm_utils.env_get_vm(env, name)
54 if vm:
55 logging.debug("VM object found in environment")
56 else:
57 logging.debug("VM object does not exist; creating it")
lmr52800ba2009-08-17 20:49:58 +000058 vm = kvm_vm.VM(name, params, test.bindir, env.get("address_cache"))
lmr6f669ce2009-05-31 19:02:42 +000059 kvm_utils.env_register_vm(env, name, vm)
60
61 start_vm = False
62 for_migration = False
63
64 if params.get("start_vm_for_migration") == "yes":
lmr8a47ce32010-03-23 15:27:12 +000065 logging.debug("'start_vm_for_migration' specified; (re)starting VM "
66 "with -incoming option...")
lmr6f669ce2009-05-31 19:02:42 +000067 start_vm = True
68 for_migration = True
69 elif params.get("restart_vm") == "yes":
70 logging.debug("'restart_vm' specified; (re)starting VM...")
71 start_vm = True
72 elif params.get("start_vm") == "yes":
73 if not vm.is_alive():
74 logging.debug("VM is not alive; starting it...")
75 start_vm = True
76 elif vm.make_qemu_command() != vm.make_qemu_command(name, params,
lmr90b9fd52009-08-17 20:48:18 +000077 test.bindir):
lmra4967622009-07-23 01:36:32 +000078 logging.debug("VM's qemu command differs from requested one; "
lmr6f669ce2009-05-31 19:02:42 +000079 "restarting it...")
80 start_vm = True
81
lmr0294de32009-09-09 22:44:28 +000082 if start_vm and not vm.create(name, params, test.bindir, for_migration):
83 raise error.TestError("Could not start VM")
lmr6f669ce2009-05-31 19:02:42 +000084
85 scrdump_filename = os.path.join(test.debugdir, "pre_%s.ppm" % name)
lmr9e964a02010-06-18 03:46:21 +000086 try:
87 if vm.monitor:
88 vm.monitor.screendump(scrdump_filename)
89 except kvm_monitor.MonitorError, e:
90 logging.warn(e)
lmr6f669ce2009-05-31 19:02:42 +000091
92
93def postprocess_image(test, params):
94 """
95 Postprocess a single QEMU image according to the instructions in params.
96 Currently this function just removes an image if requested.
97
98 @param test: An Autotest test object.
99 @param params: A dict containing image postprocessing parameters.
100 """
lmr6f669ce2009-05-31 19:02:42 +0000101 if params.get("remove_image") == "yes":
lmr90b9fd52009-08-17 20:48:18 +0000102 kvm_vm.remove_image(params, test.bindir)
lmr6f669ce2009-05-31 19:02:42 +0000103
104
105def postprocess_vm(test, params, env, name):
106 """
107 Postprocess a single VM object according to the instructions in params.
108 Kill the VM if requested and get a screendump.
109
110 @param test: An Autotest test object.
111 @param params: A dict containing VM postprocessing parameters.
112 @param env: The environment (a dict-like object).
113 @param name: The name of the VM object.
114 """
115 logging.debug("Postprocessing VM '%s'..." % name)
116 vm = kvm_utils.env_get_vm(env, name)
117 if vm:
118 logging.debug("VM object found in environment")
119 else:
120 logging.debug("VM object does not exist in environment")
121 return
122
123 scrdump_filename = os.path.join(test.debugdir, "post_%s.ppm" % name)
lmr9e964a02010-06-18 03:46:21 +0000124 try:
125 if vm.monitor:
126 vm.monitor.screendump(scrdump_filename)
127 except kvm_monitor.MonitorError, e:
128 logging.warn(e)
lmr6f669ce2009-05-31 19:02:42 +0000129
130 if params.get("kill_vm") == "yes":
lmradbfb1a2009-10-13 13:44:14 +0000131 kill_vm_timeout = float(params.get("kill_vm_timeout", 0))
132 if kill_vm_timeout:
133 logging.debug("'kill_vm' specified; waiting for VM to shut down "
134 "before killing it...")
135 kvm_utils.wait_for(vm.is_dead, kill_vm_timeout, 0, 1)
136 else:
lmr6f669ce2009-05-31 19:02:42 +0000137 logging.debug("'kill_vm' specified; killing VM...")
138 vm.destroy(gracefully = params.get("kill_vm_gracefully") == "yes")
139
140
lmr12b98a72010-02-09 11:13:57 +0000141def process_command(test, params, env, command, command_timeout,
lmr86d1ea52009-06-15 20:34:39 +0000142 command_noncritical):
143 """
144 Pre- or post- custom commands to be executed before/after a test is run
145
146 @param test: An Autotest test object.
147 @param params: A dict containing all VM and image parameters.
148 @param env: The environment (a dict-like object).
lmr12b98a72010-02-09 11:13:57 +0000149 @param command: Command to be run.
lmr0bc6efc2009-07-27 13:27:42 +0000150 @param command_timeout: Timeout for command execution.
151 @param command_noncritical: If True test will not fail if command fails.
lmr86d1ea52009-06-15 20:34:39 +0000152 """
lmr0bc6efc2009-07-27 13:27:42 +0000153 # Export environment vars
lmr0ce407d2010-03-23 15:46:30 +0000154 for k in params:
lmr0bc6efc2009-07-27 13:27:42 +0000155 os.putenv("KVM_TEST_%s" % k, str(params[k]))
lmrb1ef8472010-02-05 11:21:13 +0000156 # Execute commands
lmr12b98a72010-02-09 11:13:57 +0000157 try:
158 utils.system("cd %s; %s" % (test.bindir, command))
159 except error.CmdError, e:
160 logging.warn("Custom processing command '%s' failed, output is: %s",
161 command, str(e))
162 if not command_noncritical:
163 raise error.TestError("Custom processing command failed: %s" %
164 str(e))
lmr86d1ea52009-06-15 20:34:39 +0000165
166
lmr6f669ce2009-05-31 19:02:42 +0000167def process(test, params, env, image_func, vm_func):
168 """
169 Pre- or post-process VMs and images according to the instructions in params.
170 Call image_func for each image listed in params and vm_func for each VM.
171
172 @param test: An Autotest test object.
173 @param params: A dict containing all VM and image parameters.
174 @param env: The environment (a dict-like object).
175 @param image_func: A function to call for each image.
176 @param vm_func: A function to call for each VM.
177 """
178 # Get list of VMs specified for this test
179 vm_names = kvm_utils.get_sub_dict_names(params, "vms")
180 for vm_name in vm_names:
181 vm_params = kvm_utils.get_sub_dict(params, vm_name)
182 # Get list of images specified for this VM
183 image_names = kvm_utils.get_sub_dict_names(vm_params, "images")
184 for image_name in image_names:
185 image_params = kvm_utils.get_sub_dict(vm_params, image_name)
186 # Call image_func for each image
187 image_func(test, image_params)
188 # Call vm_func for each vm
189 vm_func(test, vm_params, env, vm_name)
190
191
192def preprocess(test, params, env):
193 """
194 Preprocess all VMs and images according to the instructions in params.
195 Also, collect some host information, such as the KVM version.
196
197 @param test: An Autotest test object.
198 @param params: A dict containing all VM and image parameters.
199 @param env: The environment (a dict-like object).
200 """
lmr965bcd22009-08-13 04:12:19 +0000201 # Start tcpdump if it isn't already running
lmr8a47ce32010-03-23 15:27:12 +0000202 if "address_cache" not in env:
lmr965bcd22009-08-13 04:12:19 +0000203 env["address_cache"] = {}
lmr8a47ce32010-03-23 15:27:12 +0000204 if "tcpdump" in env and not env["tcpdump"].is_alive():
lmr965bcd22009-08-13 04:12:19 +0000205 env["tcpdump"].close()
206 del env["tcpdump"]
lmrb1f76462010-05-25 23:44:03 +0000207 if "tcpdump" not in env and params.get("run_tcpdump", "yes") == "yes":
lmr965bcd22009-08-13 04:12:19 +0000208 command = "/usr/sbin/tcpdump -npvi any 'dst port 68'"
209 logging.debug("Starting tcpdump (%s)...", command)
210 env["tcpdump"] = kvm_subprocess.kvm_tail(
211 command=command,
212 output_func=_update_address_cache,
213 output_params=(env["address_cache"],))
214 if kvm_utils.wait_for(lambda: not env["tcpdump"].is_alive(),
215 0.1, 0.1, 1.0):
216 logging.warn("Could not start tcpdump")
217 logging.warn("Status: %s" % env["tcpdump"].get_status())
218 logging.warn("Output:" + kvm_utils.format_str_for_message(
219 env["tcpdump"].get_output()))
220
lmr6f669ce2009-05-31 19:02:42 +0000221 # Destroy and remove VMs that are no longer needed in the environment
222 requested_vms = kvm_utils.get_sub_dict_names(params, "vms")
lmr0ce407d2010-03-23 15:46:30 +0000223 for key in env.keys():
lmr6f669ce2009-05-31 19:02:42 +0000224 vm = env[key]
225 if not kvm_utils.is_vm(vm):
226 continue
227 if not vm.name in requested_vms:
lmr8a47ce32010-03-23 15:27:12 +0000228 logging.debug("VM '%s' found in environment but not required for "
229 "test; removing it..." % vm.name)
lmr6f669ce2009-05-31 19:02:42 +0000230 vm.destroy()
231 del env[key]
232
lmr6f669ce2009-05-31 19:02:42 +0000233 # Get the KVM kernel module version and write it as a keyval
234 logging.debug("Fetching KVM module version...")
235 if os.path.exists("/dev/kvm"):
lmr6f669ce2009-05-31 19:02:42 +0000236 try:
lmr8a47ce32010-03-23 15:27:12 +0000237 kvm_version = open("/sys/module/kvm/version").read().strip()
lmr6f669ce2009-05-31 19:02:42 +0000238 except:
lmr8a47ce32010-03-23 15:27:12 +0000239 kvm_version = os.uname()[2]
lmr6f669ce2009-05-31 19:02:42 +0000240 else:
241 kvm_version = "Unknown"
242 logging.debug("KVM module not loaded")
243 logging.debug("KVM version: %s" % kvm_version)
244 test.write_test_keyval({"kvm_version": kvm_version})
245
246 # Get the KVM userspace version and write it as a keyval
247 logging.debug("Fetching KVM userspace version...")
lmr52800ba2009-08-17 20:49:58 +0000248 qemu_path = kvm_utils.get_path(test.bindir, params.get("qemu_binary",
249 "qemu"))
lmr6f669ce2009-05-31 19:02:42 +0000250 version_line = commands.getoutput("%s -help | head -n 1" % qemu_path)
lmr8a47ce32010-03-23 15:27:12 +0000251 matches = re.findall("[Vv]ersion .*?,", version_line)
252 if matches:
253 kvm_userspace_version = " ".join(matches[0].split()[1:]).strip(",")
lmr6f669ce2009-05-31 19:02:42 +0000254 else:
255 kvm_userspace_version = "Unknown"
256 logging.debug("Could not fetch KVM userspace version")
257 logging.debug("KVM userspace version: %s" % kvm_userspace_version)
258 test.write_test_keyval({"kvm_userspace_version": kvm_userspace_version})
259
lmr8a47ce32010-03-23 15:27:12 +0000260 # Execute any pre_commands
261 if params.get("pre_command"):
262 process_command(test, params, env, params.get("pre_command"),
263 int(params.get("pre_command_timeout", "600")),
264 params.get("pre_command_noncritical") == "yes")
265
266 # Preprocess all VMs and images
267 process(test, params, env, preprocess_image, preprocess_vm)
268
lmr0e4056d2010-04-01 02:58:01 +0000269 # Start the screendump thread
270 if params.get("take_regular_screendumps") == "yes":
271 logging.debug("Starting screendump thread")
272 global _screendump_thread, _screendump_thread_termination_event
273 _screendump_thread_termination_event = threading.Event()
274 _screendump_thread = threading.Thread(target=_take_screendumps,
275 args=(test, params, env))
276 _screendump_thread.start()
277
lmr6f669ce2009-05-31 19:02:42 +0000278
279def postprocess(test, params, env):
280 """
281 Postprocess all VMs and images according to the instructions in params.
282
283 @param test: An Autotest test object.
284 @param params: Dict containing all VM and image parameters.
285 @param env: The environment (a dict-like object).
286 """
lmr0e4056d2010-04-01 02:58:01 +0000287 # Postprocess all VMs and images
lmr6f669ce2009-05-31 19:02:42 +0000288 process(test, params, env, postprocess_image, postprocess_vm)
289
lmr0e4056d2010-04-01 02:58:01 +0000290 # Terminate the screendump thread
291 global _screendump_thread, _screendump_thread_termination_event
292 if _screendump_thread:
293 logging.debug("Terminating screendump thread...")
294 _screendump_thread_termination_event.set()
295 _screendump_thread.join(10)
lmr327729b2010-06-14 17:14:00 +0000296 _screendump_thread = None
297 _screendump_thread_termination_event = None
lmr0e4056d2010-04-01 02:58:01 +0000298
lmr665975c2009-12-27 20:01:41 +0000299 # Warn about corrupt PPM files
300 for f in glob.glob(os.path.join(test.debugdir, "*.ppm")):
301 if not ppm_utils.image_verify_ppm_file(f):
302 logging.warn("Found corrupt PPM file: %s", f)
303
lmr0ee0a9c2009-07-24 19:23:29 +0000304 # Should we convert PPM files to PNG format?
305 if params.get("convert_ppm_files_to_png") == "yes":
lmr8a47ce32010-03-23 15:27:12 +0000306 logging.debug("'convert_ppm_files_to_png' specified; converting PPM "
307 "files to PNG format...")
lmre9f528e2009-08-12 15:18:10 +0000308 try:
309 for f in glob.glob(os.path.join(test.debugdir, "*.ppm")):
lmr15c44862009-09-10 03:23:45 +0000310 if ppm_utils.image_verify_ppm_file(f):
311 new_path = f.replace(".ppm", ".png")
312 image = PIL.Image.open(f)
313 image.save(new_path, format='PNG')
lmre9f528e2009-08-12 15:18:10 +0000314 except NameError:
315 pass
lmr0ee0a9c2009-07-24 19:23:29 +0000316
317 # Should we keep the PPM files?
318 if params.get("keep_ppm_files") != "yes":
lmr8a47ce32010-03-23 15:27:12 +0000319 logging.debug("'keep_ppm_files' not specified; removing all PPM files "
320 "from debug dir...")
lmre9f528e2009-08-12 15:18:10 +0000321 for f in glob.glob(os.path.join(test.debugdir, '*.ppm')):
322 os.unlink(f)
lmr6f669ce2009-05-31 19:02:42 +0000323
lmr0e4056d2010-04-01 02:58:01 +0000324 # Should we keep the screendump dirs?
325 if params.get("keep_screendumps") != "yes":
326 logging.debug("'keep_screendumps' not specified; removing screendump "
327 "dirs...")
328 for d in glob.glob(os.path.join(test.debugdir, "screendumps_*")):
329 if os.path.isdir(d) and not os.path.islink(d):
330 shutil.rmtree(d, ignore_errors=True)
lmr86d1ea52009-06-15 20:34:39 +0000331
lmr588f4972009-10-13 13:43:10 +0000332 # Kill all unresponsive VMs
333 if params.get("kill_unresponsive_vms") == "yes":
334 logging.debug("'kill_unresponsive_vms' specified; killing all VMs "
335 "that fail to respond to a remote login request...")
336 for vm in kvm_utils.env_get_all_vms(env):
337 if vm.is_alive():
338 session = vm.remote_login()
339 if session:
340 session.close()
341 else:
342 vm.destroy(gracefully=False)
343
lmra4197002009-08-13 05:00:51 +0000344 # Kill the tailing threads of all VMs
345 for vm in kvm_utils.env_get_all_vms(env):
346 vm.kill_tail_thread()
347
lmr965bcd22009-08-13 04:12:19 +0000348 # Terminate tcpdump if no VMs are alive
349 living_vms = [vm for vm in kvm_utils.env_get_all_vms(env) if vm.is_alive()]
lmr8a47ce32010-03-23 15:27:12 +0000350 if not living_vms and "tcpdump" in env:
lmr965bcd22009-08-13 04:12:19 +0000351 env["tcpdump"].close()
352 del env["tcpdump"]
353
lmr0e4056d2010-04-01 02:58:01 +0000354 # Execute any post_commands
355 if params.get("post_command"):
356 process_command(test, params, env, params.get("post_command"),
357 int(params.get("post_command_timeout", "600")),
358 params.get("post_command_noncritical") == "yes")
359
lmrfef27e12010-04-01 02:59:31 +0000360 # Abort on error?
361 if params.get("abort") == "yes":
362 exc_string = str(sys.exc_info()[1])
363 logging.info("Aborting job (%s)", exc_string)
364 for vm in kvm_utils.env_get_all_vms(env):
365 if not vm.is_dead():
366 logging.info("VM '%s' is alive.", vm.name)
lmr9e964a02010-06-18 03:46:21 +0000367 for m in vm.monitors:
368 logging.info("'%s' has a %s monitor unix socket at: %s",
369 vm.name, m.protocol, m.filename)
lmrfef27e12010-04-01 02:59:31 +0000370 logging.info("The command line used to start '%s' was:\n%s",
371 vm.name, vm.make_qemu_command())
372 raise error.JobError("Abort requested (%s)" % exc_string)
373
lmr6f669ce2009-05-31 19:02:42 +0000374
375def postprocess_on_error(test, params, env):
376 """
377 Perform postprocessing operations required only if the test failed.
378
379 @param test: An Autotest test object.
380 @param params: A dict containing all VM and image parameters.
381 @param env: The environment (a dict-like object).
382 """
383 params.update(kvm_utils.get_sub_dict(params, "on_error"))
lmr965bcd22009-08-13 04:12:19 +0000384
385
386def _update_address_cache(address_cache, line):
387 if re.search("Your.IP", line, re.IGNORECASE):
388 matches = re.findall(r"\d*\.\d*\.\d*\.\d*", line)
389 if matches:
390 address_cache["last_seen"] = matches[0]
391 if re.search("Client.Ethernet.Address", line, re.IGNORECASE):
392 matches = re.findall(r"\w*:\w*:\w*:\w*:\w*:\w*", line)
393 if matches and address_cache.get("last_seen"):
394 mac_address = matches[0].lower()
395 logging.debug("(address cache) Adding cache entry: %s ---> %s",
396 mac_address, address_cache.get("last_seen"))
397 address_cache[mac_address] = address_cache.get("last_seen")
398 del address_cache["last_seen"]
lmr0e4056d2010-04-01 02:58:01 +0000399
400
401def _take_screendumps(test, params, env):
402 global _screendump_thread_termination_event
403 temp_dir = test.debugdir
404 if params.get("screendump_temp_dir"):
405 temp_dir = kvm_utils.get_path(test.bindir,
406 params.get("screendump_temp_dir"))
407 try:
408 os.makedirs(temp_dir)
409 except OSError:
410 pass
411 temp_filename = os.path.join(temp_dir, "scrdump-%s.ppm" %
412 kvm_utils.generate_random_string(6))
413 delay = float(params.get("screendump_delay", 5))
414 quality = int(params.get("screendump_quality", 30))
415
416 cache = {}
417
418 while True:
419 for vm in kvm_utils.env_get_all_vms(env):
420 if vm.is_dead():
421 continue
lmr9e964a02010-06-18 03:46:21 +0000422 try:
423 vm.monitor.screendump(temp_filename)
424 except kvm_monitor.MonitorError, e:
425 logging.warn(e)
426 continue
lmr0e4056d2010-04-01 02:58:01 +0000427 if not os.path.exists(temp_filename):
428 logging.warn("VM '%s' failed to produce a screendump", vm.name)
429 continue
430 if not ppm_utils.image_verify_ppm_file(temp_filename):
431 logging.warn("VM '%s' produced an invalid screendump", vm.name)
432 os.unlink(temp_filename)
433 continue
434 screendump_dir = os.path.join(test.debugdir,
435 "screendumps_%s" % vm.name)
436 try:
437 os.makedirs(screendump_dir)
438 except OSError:
439 pass
440 screendump_filename = os.path.join(screendump_dir,
441 "%s_%s.jpg" % (vm.name,
442 time.strftime("%Y-%m-%d_%H-%M-%S")))
443 hash = utils.hash_file(temp_filename)
444 if hash in cache:
445 try:
446 os.link(cache[hash], screendump_filename)
447 except OSError:
448 pass
449 else:
450 try:
451 image = PIL.Image.open(temp_filename)
452 image.save(screendump_filename, format="JPEG", quality=quality)
453 cache[hash] = screendump_filename
454 except NameError:
455 pass
456 os.unlink(temp_filename)
457 if _screendump_thread_termination_event.isSet():
458 break
459 _screendump_thread_termination_event.wait(delay)