blob: 960bda93a34c2860d7cbb945ddf9bb0b5d93b463 [file] [log] [blame]
lmre9f528e2009-08-12 15:18:10 +00001import sys, os, time, commands, re, logging, signal, glob
lmr6f669ce2009-05-31 19:02:42 +00002from autotest_lib.client.bin import test
3from autotest_lib.client.common_lib import error
lmra4967622009-07-23 01:36:32 +00004import kvm_vm, kvm_utils, kvm_subprocess
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
14def preprocess_image(test, params):
15 """
16 Preprocess a single QEMU image according to the instructions in params.
17
18 @param test: Autotest test object.
19 @param params: A dict containing image preprocessing parameters.
20 @note: Currently this function just creates an image if requested.
21 """
22 qemu_img_path = os.path.join(test.bindir, "qemu-img")
lmr90b9fd52009-08-17 20:48:18 +000023 image_filename = kvm_vm.get_image_filename(params, test.bindir)
lmr6f669ce2009-05-31 19:02:42 +000024
25 create_image = False
26
27 if params.get("force_create_image") == "yes":
28 logging.debug("'force_create_image' specified; creating image...")
29 create_image = True
30 elif params.get("create_image") == "yes" and not \
31 os.path.exists(image_filename):
32 logging.debug("Creating image...")
33 create_image = True
34
35 if create_image:
lmr90b9fd52009-08-17 20:48:18 +000036 if not kvm_vm.create_image(params, qemu_img_path, test.bindir):
lmr6f669ce2009-05-31 19:02:42 +000037 message = "Could not create image"
38 logging.error(message)
39 raise error.TestError(message)
40
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 """
52 qemu_path = os.path.join(test.bindir, "qemu")
lmr6f669ce2009-05-31 19:02:42 +000053
54 logging.debug("Preprocessing VM '%s'..." % name)
55 vm = kvm_utils.env_get_vm(env, name)
56 if vm:
57 logging.debug("VM object found in environment")
58 else:
59 logging.debug("VM object does not exist; creating it")
lmr90b9fd52009-08-17 20:48:18 +000060 vm = kvm_vm.VM(name, params, qemu_path, test.bindir,
lmree90dd92009-08-13 04:13:39 +000061 env.get("address_cache"))
lmr6f669ce2009-05-31 19:02:42 +000062 kvm_utils.env_register_vm(env, name, vm)
63
64 start_vm = False
65 for_migration = False
66
67 if params.get("start_vm_for_migration") == "yes":
68 logging.debug("'start_vm_for_migration' specified; (re)starting VM with"
69 " -incoming option...")
70 start_vm = True
71 for_migration = True
72 elif params.get("restart_vm") == "yes":
73 logging.debug("'restart_vm' specified; (re)starting VM...")
74 start_vm = True
75 elif params.get("start_vm") == "yes":
76 if not vm.is_alive():
77 logging.debug("VM is not alive; starting it...")
78 start_vm = True
79 elif vm.make_qemu_command() != vm.make_qemu_command(name, params,
80 qemu_path,
lmr90b9fd52009-08-17 20:48:18 +000081 test.bindir):
lmra4967622009-07-23 01:36:32 +000082 logging.debug("VM's qemu command differs from requested one; "
lmr6f669ce2009-05-31 19:02:42 +000083 "restarting it...")
84 start_vm = True
85
86 if start_vm:
lmr90b9fd52009-08-17 20:48:18 +000087 if not vm.create(name, params, qemu_path, test.bindir, for_migration):
lmr6f669ce2009-05-31 19:02:42 +000088 message = "Could not start VM"
89 logging.error(message)
90 raise error.TestError(message)
91
92 scrdump_filename = os.path.join(test.debugdir, "pre_%s.ppm" % name)
93 vm.send_monitor_cmd("screendump %s" % scrdump_filename)
94
95
96def postprocess_image(test, params):
97 """
98 Postprocess a single QEMU image according to the instructions in params.
99 Currently this function just removes an image if requested.
100
101 @param test: An Autotest test object.
102 @param params: A dict containing image postprocessing parameters.
103 """
lmr6f669ce2009-05-31 19:02:42 +0000104 if params.get("remove_image") == "yes":
lmr90b9fd52009-08-17 20:48:18 +0000105 kvm_vm.remove_image(params, test.bindir)
lmr6f669ce2009-05-31 19:02:42 +0000106
107
108def postprocess_vm(test, params, env, name):
109 """
110 Postprocess a single VM object according to the instructions in params.
111 Kill the VM if requested and get a screendump.
112
113 @param test: An Autotest test object.
114 @param params: A dict containing VM postprocessing parameters.
115 @param env: The environment (a dict-like object).
116 @param name: The name of the VM object.
117 """
118 logging.debug("Postprocessing VM '%s'..." % name)
119 vm = kvm_utils.env_get_vm(env, name)
120 if vm:
121 logging.debug("VM object found in environment")
122 else:
123 logging.debug("VM object does not exist in environment")
124 return
125
126 scrdump_filename = os.path.join(test.debugdir, "post_%s.ppm" % name)
127 vm.send_monitor_cmd("screendump %s" % scrdump_filename)
128
129 if params.get("kill_vm") == "yes":
130 if not kvm_utils.wait_for(vm.is_dead,
131 float(params.get("kill_vm_timeout", 0)), 0.0, 1.0,
132 "Waiting for VM to kill itself..."):
133 logging.debug("'kill_vm' specified; killing VM...")
134 vm.destroy(gracefully = params.get("kill_vm_gracefully") == "yes")
135
136
lmr86d1ea52009-06-15 20:34:39 +0000137def process_command(test, params, env, command, command_timeout,
138 command_noncritical):
139 """
140 Pre- or post- custom commands to be executed before/after a test is run
141
142 @param test: An Autotest test object.
143 @param params: A dict containing all VM and image parameters.
144 @param env: The environment (a dict-like object).
lmr0bc6efc2009-07-27 13:27:42 +0000145 @param command: Command to be run.
146 @param command_timeout: Timeout for command execution.
147 @param command_noncritical: If True test will not fail if command fails.
lmr86d1ea52009-06-15 20:34:39 +0000148 """
lmr0bc6efc2009-07-27 13:27:42 +0000149 # Export environment vars
lmr86d1ea52009-06-15 20:34:39 +0000150 for k in params.keys():
lmr0bc6efc2009-07-27 13:27:42 +0000151 os.putenv("KVM_TEST_%s" % k, str(params[k]))
152 # Execute command
lmr86d1ea52009-06-15 20:34:39 +0000153 logging.info("Executing command '%s'..." % command)
lmra4967622009-07-23 01:36:32 +0000154 (status, output) = kvm_subprocess.run_fg("cd %s; %s" % (test.bindir,
lmr86d1ea52009-06-15 20:34:39 +0000155 command),
lmra4967622009-07-23 01:36:32 +0000156 logging.debug, "(command) ",
lmr0bc6efc2009-07-27 13:27:42 +0000157 timeout=command_timeout)
lmr86d1ea52009-06-15 20:34:39 +0000158 if status != 0:
lmr0bc6efc2009-07-27 13:27:42 +0000159 logging.warn("Custom processing command failed: '%s'" % command)
160 if not command_noncritical:
lmr86d1ea52009-06-15 20:34:39 +0000161 raise error.TestError("Custom processing command failed")
162
163
lmr6f669ce2009-05-31 19:02:42 +0000164def process(test, params, env, image_func, vm_func):
165 """
166 Pre- or post-process VMs and images according to the instructions in params.
167 Call image_func for each image listed in params and vm_func for each VM.
168
169 @param test: An Autotest test object.
170 @param params: A dict containing all VM and image parameters.
171 @param env: The environment (a dict-like object).
172 @param image_func: A function to call for each image.
173 @param vm_func: A function to call for each VM.
174 """
175 # Get list of VMs specified for this test
176 vm_names = kvm_utils.get_sub_dict_names(params, "vms")
177 for vm_name in vm_names:
178 vm_params = kvm_utils.get_sub_dict(params, vm_name)
179 # Get list of images specified for this VM
180 image_names = kvm_utils.get_sub_dict_names(vm_params, "images")
181 for image_name in image_names:
182 image_params = kvm_utils.get_sub_dict(vm_params, image_name)
183 # Call image_func for each image
184 image_func(test, image_params)
185 # Call vm_func for each vm
186 vm_func(test, vm_params, env, vm_name)
187
188
189def preprocess(test, params, env):
190 """
191 Preprocess all VMs and images according to the instructions in params.
192 Also, collect some host information, such as the KVM version.
193
194 @param test: An Autotest test object.
195 @param params: A dict containing all VM and image parameters.
196 @param env: The environment (a dict-like object).
197 """
lmr965bcd22009-08-13 04:12:19 +0000198 # Start tcpdump if it isn't already running
199 if not env.has_key("address_cache"):
200 env["address_cache"] = {}
201 if env.has_key("tcpdump") and not env["tcpdump"].is_alive():
202 env["tcpdump"].close()
203 del env["tcpdump"]
204 if not env.has_key("tcpdump"):
205 command = "/usr/sbin/tcpdump -npvi any 'dst port 68'"
206 logging.debug("Starting tcpdump (%s)...", command)
207 env["tcpdump"] = kvm_subprocess.kvm_tail(
208 command=command,
209 output_func=_update_address_cache,
210 output_params=(env["address_cache"],))
211 if kvm_utils.wait_for(lambda: not env["tcpdump"].is_alive(),
212 0.1, 0.1, 1.0):
213 logging.warn("Could not start tcpdump")
214 logging.warn("Status: %s" % env["tcpdump"].get_status())
215 logging.warn("Output:" + kvm_utils.format_str_for_message(
216 env["tcpdump"].get_output()))
217
lmr6f669ce2009-05-31 19:02:42 +0000218 # Destroy and remove VMs that are no longer needed in the environment
219 requested_vms = kvm_utils.get_sub_dict_names(params, "vms")
220 for key in env.keys():
221 vm = env[key]
222 if not kvm_utils.is_vm(vm):
223 continue
224 if not vm.name in requested_vms:
225 logging.debug("VM '%s' found in environment but not required for"
226 " test; removing it..." % vm.name)
227 vm.destroy()
228 del env[key]
229
lmr0bc6efc2009-07-27 13:27:42 +0000230 # Execute any pre_commands
lmr86d1ea52009-06-15 20:34:39 +0000231 if params.get("pre_command"):
232 process_command(test, params, env, params.get("pre_command"),
lmr0bc6efc2009-07-27 13:27:42 +0000233 int(params.get("pre_command_timeout", "600")),
234 params.get("pre_command_noncritical") == "yes")
lmr86d1ea52009-06-15 20:34:39 +0000235
lmr6f669ce2009-05-31 19:02:42 +0000236 # Preprocess all VMs and images
237 process(test, params, env, preprocess_image, preprocess_vm)
238
239 # Get the KVM kernel module version and write it as a keyval
240 logging.debug("Fetching KVM module version...")
241 if os.path.exists("/dev/kvm"):
242 kvm_version = os.uname()[2]
243 try:
244 file = open("/sys/module/kvm/version", "r")
245 kvm_version = file.read().strip()
246 file.close()
247 except:
248 pass
249 else:
250 kvm_version = "Unknown"
251 logging.debug("KVM module not loaded")
252 logging.debug("KVM version: %s" % kvm_version)
253 test.write_test_keyval({"kvm_version": kvm_version})
254
255 # Get the KVM userspace version and write it as a keyval
256 logging.debug("Fetching KVM userspace version...")
257 qemu_path = os.path.join(test.bindir, "qemu")
258 version_line = commands.getoutput("%s -help | head -n 1" % qemu_path)
259 exp = re.compile("[Vv]ersion .*?,")
260 match = exp.search(version_line)
261 if match:
262 kvm_userspace_version = " ".join(match.group().split()[1:]).strip(",")
263 else:
264 kvm_userspace_version = "Unknown"
265 logging.debug("Could not fetch KVM userspace version")
266 logging.debug("KVM userspace version: %s" % kvm_userspace_version)
267 test.write_test_keyval({"kvm_userspace_version": kvm_userspace_version})
268
269
270def postprocess(test, params, env):
271 """
272 Postprocess all VMs and images according to the instructions in params.
273
274 @param test: An Autotest test object.
275 @param params: Dict containing all VM and image parameters.
276 @param env: The environment (a dict-like object).
277 """
278 process(test, params, env, postprocess_image, postprocess_vm)
279
lmr0ee0a9c2009-07-24 19:23:29 +0000280 # Should we convert PPM files to PNG format?
281 if params.get("convert_ppm_files_to_png") == "yes":
282 logging.debug("'convert_ppm_files_to_png' specified; converting PPM"
283 " files to PNG format...")
lmre9f528e2009-08-12 15:18:10 +0000284 try:
285 for f in glob.glob(os.path.join(test.debugdir, "*.ppm")):
lmr8e7929b2009-08-13 04:26:53 +0000286 new_path = f.replace(".ppm", ".png")
lmre9f528e2009-08-12 15:18:10 +0000287 image = PIL.Image.open(f)
lmr8e7929b2009-08-13 04:26:53 +0000288 image.save(new_path, format = 'PNG')
lmre9f528e2009-08-12 15:18:10 +0000289 except NameError:
290 pass
lmr0ee0a9c2009-07-24 19:23:29 +0000291
292 # Should we keep the PPM files?
293 if params.get("keep_ppm_files") != "yes":
lmr6f669ce2009-05-31 19:02:42 +0000294 logging.debug("'keep_ppm_files' not specified; removing all PPM files"
lmr0ee0a9c2009-07-24 19:23:29 +0000295 " from debug dir...")
lmre9f528e2009-08-12 15:18:10 +0000296 for f in glob.glob(os.path.join(test.debugdir, '*.ppm')):
297 os.unlink(f)
lmr6f669ce2009-05-31 19:02:42 +0000298
lmr0bc6efc2009-07-27 13:27:42 +0000299 # Execute any post_commands
lmr86d1ea52009-06-15 20:34:39 +0000300 if params.get("post_command"):
301 process_command(test, params, env, params.get("post_command"),
lmr0bc6efc2009-07-27 13:27:42 +0000302 int(params.get("post_command_timeout", "600")),
303 params.get("post_command_noncritical") == "yes")
lmr86d1ea52009-06-15 20:34:39 +0000304
lmra4197002009-08-13 05:00:51 +0000305 # Kill the tailing threads of all VMs
306 for vm in kvm_utils.env_get_all_vms(env):
307 vm.kill_tail_thread()
308
lmr965bcd22009-08-13 04:12:19 +0000309 # Terminate tcpdump if no VMs are alive
310 living_vms = [vm for vm in kvm_utils.env_get_all_vms(env) if vm.is_alive()]
311 if not living_vms and env.has_key("tcpdump"):
312 env["tcpdump"].close()
313 del env["tcpdump"]
314
lmr6f669ce2009-05-31 19:02:42 +0000315
316def postprocess_on_error(test, params, env):
317 """
318 Perform postprocessing operations required only if the test failed.
319
320 @param test: An Autotest test object.
321 @param params: A dict containing all VM and image parameters.
322 @param env: The environment (a dict-like object).
323 """
324 params.update(kvm_utils.get_sub_dict(params, "on_error"))
lmr965bcd22009-08-13 04:12:19 +0000325
326
327def _update_address_cache(address_cache, line):
328 if re.search("Your.IP", line, re.IGNORECASE):
329 matches = re.findall(r"\d*\.\d*\.\d*\.\d*", line)
330 if matches:
331 address_cache["last_seen"] = matches[0]
332 if re.search("Client.Ethernet.Address", line, re.IGNORECASE):
333 matches = re.findall(r"\w*:\w*:\w*:\w*:\w*:\w*", line)
334 if matches and address_cache.get("last_seen"):
335 mac_address = matches[0].lower()
336 logging.debug("(address cache) Adding cache entry: %s ---> %s",
337 mac_address, address_cache.get("last_seen"))
338 address_cache[mac_address] = address_cache.get("last_seen")
339 del address_cache["last_seen"]