OLD | NEW |
1 """ | 1 """ |
2 High-level KVM test utility functions. | 2 High-level KVM test utility functions. |
3 | 3 |
4 This module is meant to reduce code size by performing common test procedures. | 4 This module is meant to reduce code size by performing common test procedures. |
5 Generally, code here should look like test code. | 5 Generally, code here should look like test code. |
6 More specifically: | 6 More specifically: |
7 - Functions in this module should raise exceptions if things go wrong | 7 - Functions in this module should raise exceptions if things go wrong |
8 (unlike functions in kvm_utils.py and kvm_vm.py which report failure via | 8 (unlike functions in kvm_utils.py and kvm_vm.py which report failure via |
9 their returned values). | 9 their returned values). |
10 - Functions in this module may use logging.info(), in addition to | 10 - Functions in this module may use logging.info(), in addition to |
11 logging.debug() and logging.error(), to log messages the user may be | 11 logging.debug() and logging.error(), to log messages the user may be |
12 interested in (unlike kvm_utils.py and kvm_vm.py which use | 12 interested in (unlike kvm_utils.py and kvm_vm.py which use |
13 logging.debug() for anything that isn't an error). | 13 logging.debug() for anything that isn't an error). |
14 - Functions in this module typically use functions and classes from | 14 - Functions in this module typically use functions and classes from |
15 lower-level modules (e.g. kvm_utils.py, kvm_vm.py, kvm_subprocess.py). | 15 lower-level modules (e.g. kvm_utils.py, kvm_vm.py, kvm_subprocess.py). |
16 - Functions in this module should not be used by lower-level modules. | 16 - Functions in this module should not be used by lower-level modules. |
17 - Functions in this module should be used in the right context. | 17 - Functions in this module should be used in the right context. |
18 For example, a function should not be used where it may display | 18 For example, a function should not be used where it may display |
19 misleading or inaccurate info or debug messages. | 19 misleading or inaccurate info or debug messages. |
20 | 20 |
21 @copyright: 2008-2009 Red Hat Inc. | 21 @copyright: 2008-2009 Red Hat Inc. |
22 """ | 22 """ |
23 | 23 |
24 import time, os, logging, re, commands, signal | 24 import time, os, logging, re, signal |
25 from autotest_lib.client.common_lib import error | 25 from autotest_lib.client.common_lib import error |
26 from autotest_lib.client.bin import utils | 26 from autotest_lib.client.bin import utils |
27 import kvm_utils, kvm_vm, kvm_subprocess, scan_results | 27 import kvm_utils, kvm_vm, kvm_subprocess, scan_results |
28 | 28 |
29 | 29 |
30 def get_living_vm(env, vm_name): | 30 def get_living_vm(env, vm_name): |
31 """ | 31 """ |
32 Get a VM object from the environment and make sure it's alive. | 32 Get a VM object from the environment and make sure it's alive. |
33 | 33 |
34 @param env: Dictionary with test environment. | 34 @param env: Dictionary with test environment. |
35 @param vm_name: Name of the desired VM object. | 35 @param vm_name: Name of the desired VM object. |
36 @return: A VM object. | 36 @return: A VM object. |
37 """ | 37 """ |
38 vm = kvm_utils.env_get_vm(env, vm_name) | 38 vm = env.get_vm(vm_name) |
39 if not vm: | 39 if not vm: |
40 raise error.TestError("VM '%s' not found in environment" % vm_name) | 40 raise error.TestError("VM '%s' not found in environment" % vm_name) |
41 if not vm.is_alive(): | 41 if not vm.is_alive(): |
42 raise error.TestError("VM '%s' seems to be dead; test requires a " | 42 raise error.TestError("VM '%s' seems to be dead; test requires a " |
43 "living VM" % vm_name) | 43 "living VM" % vm_name) |
44 return vm | 44 return vm |
45 | 45 |
46 | 46 |
47 def wait_for_login(vm, nic_index=0, timeout=240, start=0, step=2): | 47 def wait_for_login(vm, nic_index=0, timeout=240, start=0, step=2, serial=None): |
48 """ | 48 """ |
49 Try logging into a VM repeatedly. Stop on success or when timeout expires. | 49 Try logging into a VM repeatedly. Stop on success or when timeout expires. |
50 | 50 |
51 @param vm: VM object. | 51 @param vm: VM object. |
52 @param nic_index: Index of NIC to access in the VM. | 52 @param nic_index: Index of NIC to access in the VM. |
53 @param timeout: Time to wait before giving up. | 53 @param timeout: Time to wait before giving up. |
| 54 @param serial: Whether to use a serial connection instead of a remote |
| 55 (ssh, rss) one. |
54 @return: A shell session object. | 56 @return: A shell session object. |
55 """ | 57 """ |
56 logging.info("Trying to log into guest '%s', timeout %ds", vm.name, timeout) | 58 end_time = time.time() + timeout |
57 session = kvm_utils.wait_for(lambda: vm.remote_login(nic_index=nic_index), | 59 session = None |
58 timeout, start, step) | 60 if serial: |
| 61 type = 'serial' |
| 62 logging.info("Trying to log into guest %s using serial connection," |
| 63 " timeout %ds", vm.name, timeout) |
| 64 time.sleep(start) |
| 65 while time.time() < end_time: |
| 66 try: |
| 67 session = vm.serial_login() |
| 68 break |
| 69 except kvm_utils.LoginError, e: |
| 70 logging.debug(e) |
| 71 time.sleep(step) |
| 72 else: |
| 73 type = 'remote' |
| 74 logging.info("Trying to log into guest %s using remote connection," |
| 75 " timeout %ds", vm.name, timeout) |
| 76 time.sleep(start) |
| 77 while time.time() < end_time: |
| 78 try: |
| 79 session = vm.login(nic_index=nic_index) |
| 80 break |
| 81 except (kvm_utils.LoginError, kvm_vm.VMError), e: |
| 82 logging.debug(e) |
| 83 time.sleep(step) |
59 if not session: | 84 if not session: |
60 raise error.TestFail("Could not log into guest '%s'" % vm.name) | 85 raise error.TestFail("Could not log into guest %s using %s connection" % |
61 logging.info("Logged into guest '%s'" % vm.name) | 86 (vm.name, type)) |
| 87 logging.info("Logged into guest %s using %s connection", vm.name, type) |
62 return session | 88 return session |
63 | 89 |
64 | 90 |
65 def reboot(vm, session, method="shell", sleep_before_reset=10, nic_index=0, | 91 def reboot(vm, session, method="shell", sleep_before_reset=10, nic_index=0, |
66 timeout=240): | 92 timeout=240): |
67 """ | 93 """ |
68 Reboot the VM and wait for it to come back up by trying to log in until | 94 Reboot the VM and wait for it to come back up by trying to log in until |
69 timeout expires. | 95 timeout expires. |
70 | 96 |
71 @param vm: VM object. | 97 @param vm: VM object. |
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
105 | 131 |
106 # Wait for the session to become unresponsive and close it | 132 # Wait for the session to become unresponsive and close it |
107 if not kvm_utils.wait_for(lambda: not session.is_responsive(timeout=30), | 133 if not kvm_utils.wait_for(lambda: not session.is_responsive(timeout=30), |
108 120, 0, 1): | 134 120, 0, 1): |
109 raise error.TestFail("Guest refuses to go down") | 135 raise error.TestFail("Guest refuses to go down") |
110 session.close() | 136 session.close() |
111 | 137 |
112 # Try logging into the guest until timeout expires | 138 # Try logging into the guest until timeout expires |
113 logging.info("Guest is down. Waiting for it to go up again, timeout %ds", | 139 logging.info("Guest is down. Waiting for it to go up again, timeout %ds", |
114 timeout) | 140 timeout) |
115 session = kvm_utils.wait_for(lambda: vm.remote_login(nic_index=nic_index), | 141 session = vm.wait_for_login(nic_index, timeout=timeout) |
116 timeout, 0, 2) | |
117 if not session: | |
118 raise error.TestFail("Could not log into guest after reboot") | |
119 logging.info("Guest is up again") | 142 logging.info("Guest is up again") |
120 return session | 143 return session |
121 | 144 |
122 | 145 |
123 def migrate(vm, env=None, mig_timeout=3600, mig_protocol="tcp", | 146 def migrate(vm, env=None, mig_timeout=3600, mig_protocol="tcp", |
124 mig_cancel=False): | 147 mig_cancel=False, offline=False, stable_check=False, |
| 148 clean=False, save_path=None, dest_host='localhost', mig_port=None): |
125 """ | 149 """ |
126 Migrate a VM locally and re-register it in the environment. | 150 Migrate a VM locally and re-register it in the environment. |
127 | 151 |
128 @param vm: The VM to migrate. | 152 @param vm: The VM to migrate. |
129 @param env: The environment dictionary. If omitted, the migrated VM will | 153 @param env: The environment dictionary. If omitted, the migrated VM will |
130 not be registered. | 154 not be registered. |
131 @param mig_timeout: timeout value for migration. | 155 @param mig_timeout: timeout value for migration. |
132 @param mig_protocol: migration protocol | 156 @param mig_protocol: migration protocol |
133 @param mig_cancel: Test migrate_cancel or not when protocol is tcp. | 157 @param mig_cancel: Test migrate_cancel or not when protocol is tcp. |
134 @return: The post-migration VM. | 158 @param dest_host: Destination host (defaults to 'localhost'). |
| 159 @param mig_port: Port that will be used for migration. |
| 160 @return: The post-migration VM, in case of same host migration, True in |
| 161 case of multi-host migration. |
135 """ | 162 """ |
136 def mig_finished(): | 163 def mig_finished(): |
137 o = vm.monitor.info("migrate") | 164 o = vm.monitor.info("migrate") |
138 if isinstance(o, str): | 165 if isinstance(o, str): |
139 return "status: active" not in o | 166 return "status: active" not in o |
140 else: | 167 else: |
141 return o.get("status") != "active" | 168 return o.get("status") != "active" |
142 | 169 |
143 def mig_succeeded(): | 170 def mig_succeeded(): |
144 o = vm.monitor.info("migrate") | 171 o = vm.monitor.info("migrate") |
(...skipping 17 matching lines...) Expand all Loading... |
162 else: | 189 else: |
163 return (o.get("status") == "cancelled" or | 190 return (o.get("status") == "cancelled" or |
164 o.get("status") == "canceled") | 191 o.get("status") == "canceled") |
165 | 192 |
166 def wait_for_migration(): | 193 def wait_for_migration(): |
167 if not kvm_utils.wait_for(mig_finished, mig_timeout, 2, 2, | 194 if not kvm_utils.wait_for(mig_finished, mig_timeout, 2, 2, |
168 "Waiting for migration to finish..."): | 195 "Waiting for migration to finish..."): |
169 raise error.TestFail("Timeout expired while waiting for migration " | 196 raise error.TestFail("Timeout expired while waiting for migration " |
170 "to finish") | 197 "to finish") |
171 | 198 |
172 dest_vm = vm.clone() | 199 if dest_host == 'localhost': |
| 200 dest_vm = vm.clone() |
173 | 201 |
174 if mig_protocol == "exec": | 202 if (dest_host == 'localhost') and stable_check: |
175 # Exec is a little different from other migrate methods - first we | 203 # Pause the dest vm after creation |
176 # ask the monitor the migration, then the vm state is dumped to a | 204 dest_vm.params['extra_params'] = (dest_vm.params.get('extra_params','') |
177 # compressed file, then we start the dest vm with -incoming pointing | 205 + ' -S') |
178 # to it | |
179 try: | |
180 exec_file = "/tmp/exec-%s.gz" % kvm_utils.generate_random_string(8) | |
181 exec_cmd = "gzip -c -d %s" % exec_file | |
182 uri = '"exec:gzip -c > %s"' % exec_file | |
183 vm.monitor.cmd("stop") | |
184 vm.monitor.migrate(uri) | |
185 wait_for_migration() | |
186 | 206 |
187 if not dest_vm.create(migration_mode=mig_protocol, | 207 if dest_host == 'localhost': |
188 migration_exec_cmd=exec_cmd, mac_source=vm): | 208 dest_vm.create(migration_mode=mig_protocol, mac_source=vm) |
189 raise error.TestError("Could not create dest VM") | 209 |
190 finally: | 210 try: |
191 logging.debug("Removing migration file %s", exec_file) | |
192 try: | |
193 os.remove(exec_file) | |
194 except OSError: | |
195 pass | |
196 else: | |
197 if not dest_vm.create(migration_mode=mig_protocol, mac_source=vm): | |
198 raise error.TestError("Could not create dest VM") | |
199 try: | 211 try: |
200 if mig_protocol == "tcp": | 212 if mig_protocol == "tcp": |
201 uri = "tcp:localhost:%d" % dest_vm.migration_port | 213 if dest_host == 'localhost': |
| 214 uri = "tcp:localhost:%d" % dest_vm.migration_port |
| 215 else: |
| 216 uri = 'tcp:%s:%d' % (dest_host, mig_port) |
202 elif mig_protocol == "unix": | 217 elif mig_protocol == "unix": |
203 uri = "unix:%s" % dest_vm.migration_file | 218 uri = "unix:%s" % dest_vm.migration_file |
| 219 elif mig_protocol == "exec": |
| 220 uri = '"exec:nc localhost %s"' % dest_vm.migration_port |
| 221 |
| 222 if offline: |
| 223 vm.monitor.cmd("stop") |
204 vm.monitor.migrate(uri) | 224 vm.monitor.migrate(uri) |
205 | 225 |
206 if mig_cancel: | 226 if mig_cancel: |
207 time.sleep(2) | 227 time.sleep(2) |
208 vm.monitor.cmd("migrate_cancel") | 228 vm.monitor.cmd("migrate_cancel") |
209 if not kvm_utils.wait_for(mig_cancelled, 60, 2, 2, | 229 if not kvm_utils.wait_for(mig_cancelled, 60, 2, 2, |
210 "Waiting for migration " | 230 "Waiting for migration " |
211 "cancellation"): | 231 "cancellation"): |
212 raise error.TestFail("Failed to cancel migration") | 232 raise error.TestFail("Failed to cancel migration") |
213 dest_vm.destroy(gracefully=False) | 233 if offline: |
| 234 vm.monitor.cmd("cont") |
| 235 if dest_host == 'localhost': |
| 236 dest_vm.destroy(gracefully=False) |
214 return vm | 237 return vm |
215 else: | 238 else: |
216 wait_for_migration() | 239 wait_for_migration() |
| 240 if (dest_host == 'localhost') and stable_check: |
| 241 save_path = None or "/tmp" |
| 242 save1 = os.path.join(save_path, "src") |
| 243 save2 = os.path.join(save_path, "dst") |
| 244 |
| 245 vm.save_to_file(save1) |
| 246 dest_vm.save_to_file(save2) |
| 247 |
| 248 # Fail if we see deltas |
| 249 md5_save1 = utils.hash_file(save1) |
| 250 md5_save2 = utils.hash_file(save2) |
| 251 if md5_save1 != md5_save2: |
| 252 raise error.TestFail("Mismatch of VM state before " |
| 253 "and after migration") |
| 254 |
| 255 if (dest_host == 'localhost') and offline: |
| 256 dest_vm.monitor.cmd("cont") |
217 except: | 257 except: |
218 dest_vm.destroy() | 258 if dest_host == 'localhost': |
| 259 dest_vm.destroy() |
219 raise | 260 raise |
220 | 261 |
| 262 finally: |
| 263 if (dest_host == 'localhost') and stable_check and clean: |
| 264 logging.debug("Cleaning the state files") |
| 265 if os.path.isfile(save1): |
| 266 os.remove(save1) |
| 267 if os.path.isfile(save2): |
| 268 os.remove(save2) |
| 269 |
221 # Report migration status | 270 # Report migration status |
222 if mig_succeeded(): | 271 if mig_succeeded(): |
223 logging.info("Migration finished successfully") | 272 logging.info("Migration finished successfully") |
224 elif mig_failed(): | 273 elif mig_failed(): |
225 raise error.TestFail("Migration failed") | 274 raise error.TestFail("Migration failed") |
226 else: | 275 else: |
227 raise error.TestFail("Migration ended with unknown status") | 276 raise error.TestFail("Migration ended with unknown status") |
228 | 277 |
229 if "paused" in dest_vm.monitor.info("status"): | 278 if dest_host == 'localhost': |
230 logging.debug("Destination VM is paused, resuming it...") | 279 if "paused" in dest_vm.monitor.info("status"): |
231 dest_vm.monitor.cmd("cont") | 280 logging.debug("Destination VM is paused, resuming it...") |
| 281 dest_vm.monitor.cmd("cont") |
232 | 282 |
233 # Kill the source VM | 283 # Kill the source VM |
234 vm.destroy(gracefully=False) | 284 vm.destroy(gracefully=False) |
235 | 285 |
236 # Replace the source VM with the new cloned VM | 286 # Replace the source VM with the new cloned VM |
237 if env is not None: | 287 if (dest_host == 'localhost') and (env is not None): |
238 kvm_utils.env_register_vm(env, vm.name, dest_vm) | 288 env.register_vm(vm.name, dest_vm) |
239 | 289 |
240 # Return the new cloned VM | 290 # Return the new cloned VM |
241 return dest_vm | 291 if dest_host == 'localhost': |
| 292 return dest_vm |
| 293 else: |
| 294 return vm |
242 | 295 |
243 | 296 |
244 def stop_windows_service(session, service, timeout=120): | 297 def stop_windows_service(session, service, timeout=120): |
245 """ | 298 """ |
246 Stop a Windows service using sc. | 299 Stop a Windows service using sc. |
247 If the service is already stopped or is not installed, do nothing. | 300 If the service is already stopped or is not installed, do nothing. |
248 | 301 |
249 @param service: The name of the service | 302 @param service: The name of the service |
250 @param timeout: Time duration to wait for service to stop | 303 @param timeout: Time duration to wait for service to stop |
251 @raise error.TestError: Raised if the service can't be stopped | 304 @raise error.TestError: Raised if the service can't be stopped |
252 """ | 305 """ |
253 end_time = time.time() + timeout | 306 end_time = time.time() + timeout |
254 while time.time() < end_time: | 307 while time.time() < end_time: |
255 o = session.get_command_output("sc stop %s" % service, timeout=60) | 308 o = session.cmd_output("sc stop %s" % service, timeout=60) |
256 # FAILED 1060 means the service isn't installed. | 309 # FAILED 1060 means the service isn't installed. |
257 # FAILED 1062 means the service hasn't been started. | 310 # FAILED 1062 means the service hasn't been started. |
258 if re.search(r"\bFAILED (1060|1062)\b", o, re.I): | 311 if re.search(r"\bFAILED (1060|1062)\b", o, re.I): |
259 break | 312 break |
260 time.sleep(1) | 313 time.sleep(1) |
261 else: | 314 else: |
262 raise error.TestError("Could not stop service '%s'" % service) | 315 raise error.TestError("Could not stop service '%s'" % service) |
263 | 316 |
264 | 317 |
265 def start_windows_service(session, service, timeout=120): | 318 def start_windows_service(session, service, timeout=120): |
266 """ | 319 """ |
267 Start a Windows service using sc. | 320 Start a Windows service using sc. |
268 If the service is already running, do nothing. | 321 If the service is already running, do nothing. |
269 If the service isn't installed, fail. | 322 If the service isn't installed, fail. |
270 | 323 |
271 @param service: The name of the service | 324 @param service: The name of the service |
272 @param timeout: Time duration to wait for service to start | 325 @param timeout: Time duration to wait for service to start |
273 @raise error.TestError: Raised if the service can't be started | 326 @raise error.TestError: Raised if the service can't be started |
274 """ | 327 """ |
275 end_time = time.time() + timeout | 328 end_time = time.time() + timeout |
276 while time.time() < end_time: | 329 while time.time() < end_time: |
277 o = session.get_command_output("sc start %s" % service, timeout=60) | 330 o = session.cmd_output("sc start %s" % service, timeout=60) |
278 # FAILED 1060 means the service isn't installed. | 331 # FAILED 1060 means the service isn't installed. |
279 if re.search(r"\bFAILED 1060\b", o, re.I): | 332 if re.search(r"\bFAILED 1060\b", o, re.I): |
280 raise error.TestError("Could not start service '%s' " | 333 raise error.TestError("Could not start service '%s' " |
281 "(service not installed)" % service) | 334 "(service not installed)" % service) |
282 # FAILED 1056 means the service is already running. | 335 # FAILED 1056 means the service is already running. |
283 if re.search(r"\bFAILED 1056\b", o, re.I): | 336 if re.search(r"\bFAILED 1056\b", o, re.I): |
284 break | 337 break |
285 time.sleep(1) | 338 time.sleep(1) |
286 else: | 339 else: |
287 raise error.TestError("Could not start service '%s'" % service) | 340 raise error.TestError("Could not start service '%s'" % service) |
(...skipping 11 matching lines...) Expand all Loading... |
299 @param session: A shell session. | 352 @param session: A shell session. |
300 @param time_command: Command to issue to get the current guest time. | 353 @param time_command: Command to issue to get the current guest time. |
301 @param time_filter_re: Regex filter to apply on the output of | 354 @param time_filter_re: Regex filter to apply on the output of |
302 time_command in order to get the current time. | 355 time_command in order to get the current time. |
303 @param time_format: Format string to pass to time.strptime() with the | 356 @param time_format: Format string to pass to time.strptime() with the |
304 result of the regex filter. | 357 result of the regex filter. |
305 @return: A tuple containing the host time and guest time. | 358 @return: A tuple containing the host time and guest time. |
306 """ | 359 """ |
307 if len(re.findall("ntpdate|w32tm", time_command)) == 0: | 360 if len(re.findall("ntpdate|w32tm", time_command)) == 0: |
308 host_time = time.time() | 361 host_time = time.time() |
309 session.sendline(time_command) | 362 s = session.cmd_output(time_command) |
310 (match, s) = session.read_up_to_prompt() | |
311 if not match: | |
312 raise error.TestError("Could not get guest time") | |
313 | 363 |
314 try: | 364 try: |
315 s = re.findall(time_filter_re, s)[0] | 365 s = re.findall(time_filter_re, s)[0] |
316 except IndexError: | 366 except IndexError: |
317 logging.debug("The time string from guest is:\n%s" % s) | 367 logging.debug("The time string from guest is:\n%s", s) |
318 raise error.TestError("The time string from guest is unexpected.") | 368 raise error.TestError("The time string from guest is unexpected.") |
319 except Exception, e: | 369 except Exception, e: |
320 logging.debug("(time_filter_re, time_string): (%s, %s)" % | 370 logging.debug("(time_filter_re, time_string): (%s, %s)", |
321 (time_filter_re, s)) | 371 time_filter_re, s) |
322 raise e | 372 raise e |
323 | 373 |
324 guest_time = time.mktime(time.strptime(s, time_format)) | 374 guest_time = time.mktime(time.strptime(s, time_format)) |
325 else: | 375 else: |
326 s , o = session.get_command_status_output(time_command) | 376 o = session.cmd(time_command) |
327 if s != 0: | |
328 raise error.TestError("Could not get guest time") | |
329 if re.match('ntpdate', time_command): | 377 if re.match('ntpdate', time_command): |
330 offset = re.findall('offset (.*) sec',o)[0] | 378 offset = re.findall('offset (.*) sec', o)[0] |
331 host_main, host_mantissa = re.findall(time_filter_re, o)[0] | 379 host_main, host_mantissa = re.findall(time_filter_re, o)[0] |
332 host_time = time.mktime(time.strptime(host_main, time_format)) \ | 380 host_time = (time.mktime(time.strptime(host_main, time_format)) + |
333 + float("0.%s" % host_mantissa) | 381 float("0.%s" % host_mantissa)) |
334 guest_time = host_time + float(offset) | 382 guest_time = host_time + float(offset) |
335 else: | 383 else: |
336 guest_time = re.findall(time_filter_re, o)[0] | 384 guest_time = re.findall(time_filter_re, o)[0] |
337 offset = re.findall("o:(.*)s", o)[0] | 385 offset = re.findall("o:(.*)s", o)[0] |
338 if re.match('PM', guest_time): | 386 if re.match('PM', guest_time): |
339 hour = re.findall('\d+ (\d+):', guest_time)[0] | 387 hour = re.findall('\d+ (\d+):', guest_time)[0] |
340 hour = str(int(hour) + 12) | 388 hour = str(int(hour) + 12) |
341 guest_time = re.sub('\d+\s\d+:', "\d+\s%s:" % hour, | 389 guest_time = re.sub('\d+\s\d+:', "\d+\s%s:" % hour, |
342 guest_time)[:-3] | 390 guest_time)[:-3] |
343 else: | 391 else: |
(...skipping 30 matching lines...) Expand all Loading... |
374 shm = vm.get_shared_meminfo() | 422 shm = vm.get_shared_meminfo() |
375 if shm is None: | 423 if shm is None: |
376 raise error.TestError("Could not get shared meminfo from " | 424 raise error.TestError("Could not get shared meminfo from " |
377 "VM %s" % vm) | 425 "VM %s" % vm) |
378 meminfo += "%dM; " % shm | 426 meminfo += "%dM; " % shm |
379 meminfo = meminfo[0:-2] + "}" | 427 meminfo = meminfo[0:-2] + "}" |
380 | 428 |
381 return meminfo | 429 return meminfo |
382 | 430 |
383 | 431 |
384 def run_autotest(vm, session, control_path, timeout, outputdir): | 432 def run_autotest(vm, session, control_path, timeout, outputdir, params): |
385 """ | 433 """ |
386 Run an autotest control file inside a guest (linux only utility). | 434 Run an autotest control file inside a guest (linux only utility). |
387 | 435 |
388 @param vm: VM object. | 436 @param vm: VM object. |
389 @param session: A shell session on the VM provided. | 437 @param session: A shell session on the VM provided. |
390 @param control_path: A path to an autotest control file. | 438 @param control_path: A path to an autotest control file. |
391 @param timeout: Timeout under which the autotest control file must complete. | 439 @param timeout: Timeout under which the autotest control file must complete. |
392 @param outputdir: Path on host where we should copy the guest autotest | 440 @param outputdir: Path on host where we should copy the guest autotest |
393 results to. | 441 results to. |
| 442 |
| 443 The following params is used by the migration |
| 444 @param params: Test params used in the migration test |
394 """ | 445 """ |
395 def copy_if_hash_differs(vm, local_path, remote_path): | 446 def copy_if_hash_differs(vm, local_path, remote_path): |
396 """ | 447 """ |
397 Copy a file to a guest if it doesn't exist or if its MD5sum differs. | 448 Copy a file to a guest if it doesn't exist or if its MD5sum differs. |
398 | 449 |
399 @param vm: VM object. | 450 @param vm: VM object. |
400 @param local_path: Local path. | 451 @param local_path: Local path. |
401 @param remote_path: Remote path. | 452 @param remote_path: Remote path. |
402 """ | 453 """ |
403 copy = False | |
404 local_hash = utils.hash_file(local_path) | 454 local_hash = utils.hash_file(local_path) |
405 basename = os.path.basename(local_path) | 455 basename = os.path.basename(local_path) |
406 output = session.get_command_output("md5sum %s" % remote_path) | 456 output = session.cmd_output("md5sum %s" % remote_path) |
407 if "such file" in output: | 457 if "such file" in output: |
408 remote_hash = "0" | 458 remote_hash = "0" |
409 elif output: | 459 elif output: |
410 remote_hash = output.split()[0] | 460 remote_hash = output.split()[0] |
411 else: | 461 else: |
412 logging.warning("MD5 check for remote path %s did not return.", | 462 logging.warning("MD5 check for remote path %s did not return.", |
413 remote_path) | 463 remote_path) |
414 # Let's be a little more lenient here and see if it wasn't a | 464 # Let's be a little more lenient here and see if it wasn't a |
415 # temporary problem | 465 # temporary problem |
416 remote_hash = "0" | 466 remote_hash = "0" |
417 | |
418 if remote_hash != local_hash: | 467 if remote_hash != local_hash: |
419 logging.debug("Copying %s to guest", basename) | 468 logging.debug("Copying %s to guest", basename) |
420 copy = True | 469 vm.copy_files_to(local_path, remote_path) |
421 | |
422 if copy: | |
423 if not vm.copy_files_to(local_path, remote_path): | |
424 raise error.TestFail("Could not copy %s to guest" % local_path) | |
425 | 470 |
426 | 471 |
427 def extract(vm, remote_path, dest_dir="."): | 472 def extract(vm, remote_path, dest_dir="."): |
428 """ | 473 """ |
429 Extract a .tar.bz2 file on the guest. | 474 Extract a .tar.bz2 file on the guest. |
430 | 475 |
431 @param vm: VM object | 476 @param vm: VM object |
432 @param remote_path: Remote file path | 477 @param remote_path: Remote file path |
433 @param dest_dir: Destination dir for the contents | 478 @param dest_dir: Destination dir for the contents |
434 """ | 479 """ |
435 basename = os.path.basename(remote_path) | 480 basename = os.path.basename(remote_path) |
436 logging.info("Extracting %s...", basename) | 481 logging.info("Extracting %s...", basename) |
437 e_cmd = "tar xjvf %s -C %s" % (remote_path, dest_dir) | 482 e_cmd = "tar xjvf %s -C %s" % (remote_path, dest_dir) |
438 s, o = session.get_command_status_output(e_cmd, timeout=120) | 483 session.cmd(e_cmd, timeout=120) |
439 if s != 0: | |
440 logging.error("Uncompress output:\n%s", o) | |
441 raise error.TestFail("Failed to extract %s on guest" % basename) | |
442 | 484 |
443 | 485 |
444 def get_results(): | 486 def get_results(): |
445 """ | 487 """ |
446 Copy autotest results present on the guest back to the host. | 488 Copy autotest results present on the guest back to the host. |
447 """ | 489 """ |
448 logging.info("Trying to copy autotest results from guest") | 490 logging.info("Trying to copy autotest results from guest") |
449 guest_results_dir = os.path.join(outputdir, "guest_autotest_results") | 491 guest_results_dir = os.path.join(outputdir, "guest_autotest_results") |
450 if not os.path.exists(guest_results_dir): | 492 if not os.path.exists(guest_results_dir): |
451 os.mkdir(guest_results_dir) | 493 os.mkdir(guest_results_dir) |
452 if not vm.copy_files_from("%s/results/default/*" % autotest_path, | 494 vm.copy_files_from("%s/results/default/*" % autotest_path, |
453 guest_results_dir): | 495 guest_results_dir) |
454 logging.error("Could not copy autotest results from guest") | |
455 | 496 |
456 | 497 |
457 def get_results_summary(): | 498 def get_results_summary(): |
458 """ | 499 """ |
459 Get the status of the tests that were executed on the host and close | 500 Get the status of the tests that were executed on the host and close |
460 the session where autotest was being executed. | 501 the session where autotest was being executed. |
461 """ | 502 """ |
462 output = session.get_command_output("cat results/*/status") | 503 output = session.cmd_output("cat results/*/status") |
463 try: | 504 try: |
464 results = scan_results.parse_results(output) | 505 results = scan_results.parse_results(output) |
465 # Report test results | 506 # Report test results |
466 logging.info("Results (test, status, duration, info):") | 507 logging.info("Results (test, status, duration, info):") |
467 for result in results: | 508 for result in results: |
468 logging.info(str(result)) | 509 logging.info(str(result)) |
469 session.close() | 510 session.close() |
470 return results | 511 return results |
471 except Exception, e: | 512 except Exception, e: |
472 logging.error("Error processing guest autotest results: %s", e) | 513 logging.error("Error processing guest autotest results: %s", e) |
473 return None | 514 return None |
474 | 515 |
475 | 516 |
476 if not os.path.isfile(control_path): | 517 if not os.path.isfile(control_path): |
477 raise error.TestError("Invalid path to autotest control file: %s" % | 518 raise error.TestError("Invalid path to autotest control file: %s" % |
478 control_path) | 519 control_path) |
479 | 520 |
| 521 migrate_background = params.get("migrate_background") == "yes" |
| 522 if migrate_background: |
| 523 mig_timeout = float(params.get("mig_timeout", "3600")) |
| 524 mig_protocol = params.get("migration_protocol", "tcp") |
| 525 |
480 compressed_autotest_path = "/tmp/autotest.tar.bz2" | 526 compressed_autotest_path = "/tmp/autotest.tar.bz2" |
481 | 527 |
482 # To avoid problems, let's make the test use the current AUTODIR | 528 # To avoid problems, let's make the test use the current AUTODIR |
483 # (autotest client path) location | 529 # (autotest client path) location |
484 autotest_path = os.environ['AUTODIR'] | 530 autotest_path = os.environ['AUTODIR'] |
485 | 531 |
486 # tar the contents of bindir/autotest | 532 # tar the contents of bindir/autotest |
487 cmd = "tar cvjf %s %s/*" % (compressed_autotest_path, autotest_path) | 533 cmd = "tar cvjf %s %s/*" % (compressed_autotest_path, autotest_path) |
488 # Until we have nested virtualization, we don't need the kvm test :) | 534 # Until we have nested virtualization, we don't need the kvm test :) |
489 cmd += " --exclude=%s/tests/kvm" % autotest_path | 535 cmd += " --exclude=%s/tests/kvm" % autotest_path |
490 cmd += " --exclude=%s/results" % autotest_path | 536 cmd += " --exclude=%s/results" % autotest_path |
491 cmd += " --exclude=%s/tmp" % autotest_path | 537 cmd += " --exclude=%s/tmp" % autotest_path |
492 cmd += " --exclude=%s/control*" % autotest_path | 538 cmd += " --exclude=%s/control*" % autotest_path |
493 cmd += " --exclude=*.pyc" | 539 cmd += " --exclude=*.pyc" |
494 cmd += " --exclude=*.svn" | 540 cmd += " --exclude=*.svn" |
495 cmd += " --exclude=*.git" | 541 cmd += " --exclude=*.git" |
496 utils.run(cmd) | 542 utils.run(cmd) |
497 | 543 |
498 # Copy autotest.tar.bz2 | 544 # Copy autotest.tar.bz2 |
499 copy_if_hash_differs(vm, compressed_autotest_path, compressed_autotest_path) | 545 copy_if_hash_differs(vm, compressed_autotest_path, compressed_autotest_path) |
500 | 546 |
501 # Extract autotest.tar.bz2 | 547 # Extract autotest.tar.bz2 |
502 extract(vm, compressed_autotest_path, "/") | 548 extract(vm, compressed_autotest_path, "/") |
503 | 549 |
504 if not vm.copy_files_to(control_path, | 550 vm.copy_files_to(control_path, os.path.join(autotest_path, 'control')) |
505 os.path.join(autotest_path, 'control')): | |
506 raise error.TestFail("Could not copy the test control file to guest") | |
507 | 551 |
508 # Run the test | 552 # Run the test |
509 logging.info("Running autotest control file %s on guest, timeout %ss", | 553 logging.info("Running autotest control file %s on guest, timeout %ss", |
510 os.path.basename(control_path), timeout) | 554 os.path.basename(control_path), timeout) |
511 session.get_command_output("cd %s" % autotest_path) | 555 session.cmd("cd %s" % autotest_path) |
512 session.get_command_output("rm -f control.state") | 556 try: |
513 session.get_command_output("rm -rf results/*") | 557 session.cmd("rm -f control.state") |
514 logging.info("---------------- Test output ----------------") | 558 session.cmd("rm -rf results/*") |
515 status = session.get_command_status("bin/autotest control", | 559 except kvm_subprocess.ShellError: |
516 timeout=timeout, | 560 pass |
517 print_func=logging.info) | 561 try: |
518 logging.info("------------- End of test output ------------") | 562 bg = None |
519 if status is None: | 563 try: |
520 if not vm.is_alive(): | 564 logging.info("---------------- Test output ----------------") |
| 565 if migrate_background: |
| 566 mig_timeout = float(params.get("mig_timeout", "3600")) |
| 567 mig_protocol = params.get("migration_protocol", "tcp") |
| 568 |
| 569 bg = kvm_utils.Thread(session.cmd_output, |
| 570 kwargs={'cmd': "bin/autotest control", |
| 571 'timeout': timeout, |
| 572 'print_func': logging.info}) |
| 573 |
| 574 bg.start() |
| 575 |
| 576 while bg.is_alive(): |
| 577 logging.info("Tests is not ended, start a round of" |
| 578 "migration ...") |
| 579 vm.migrate(timeout=mig_timeout, protocol=mig_protocol) |
| 580 else: |
| 581 session.cmd_output("bin/autotest control", timeout=timeout, |
| 582 print_func=logging.info) |
| 583 finally: |
| 584 logging.info("------------- End of test output ------------") |
| 585 if migrate_background and bg: |
| 586 bg.join() |
| 587 except kvm_subprocess.ShellTimeoutError: |
| 588 if vm.is_alive(): |
| 589 get_results() |
| 590 get_results_summary() |
| 591 raise error.TestError("Timeout elapsed while waiting for job to " |
| 592 "complete") |
| 593 else: |
521 raise error.TestError("Autotest job on guest failed " | 594 raise error.TestError("Autotest job on guest failed " |
522 "(VM terminated during job)") | 595 "(VM terminated during job)") |
523 if not session.is_alive(): | 596 except kvm_subprocess.ShellProcessTerminatedError: |
524 get_results() | |
525 raise error.TestError("Autotest job on guest failed " | |
526 "(Remote session terminated during job)") | |
527 get_results() | 597 get_results() |
528 get_results_summary() | 598 raise error.TestError("Autotest job on guest failed " |
529 raise error.TestError("Timeout elapsed while waiting for job to " | 599 "(Remote session terminated during job)") |
530 "complete") | |
531 | 600 |
532 results = get_results_summary() | 601 results = get_results_summary() |
533 get_results() | 602 get_results() |
534 | 603 |
535 # Make a list of FAIL/ERROR/ABORT results (make sure FAIL results appear | 604 # Make a list of FAIL/ERROR/ABORT results (make sure FAIL results appear |
536 # before ERROR results, and ERROR results appear before ABORT results) | 605 # before ERROR results, and ERROR results appear before ABORT results) |
537 bad_results = [r[0] for r in results if r[1] == "FAIL"] | 606 bad_results = [r[0] for r in results if r[1] == "FAIL"] |
538 bad_results += [r[0] for r in results if r[1] == "ERROR"] | 607 bad_results += [r[0] for r in results if r[1] == "ERROR"] |
539 bad_results += [r[0] for r in results if r[1] == "ABORT"] | 608 bad_results += [r[0] for r in results if r[1] == "ABORT"] |
540 | 609 |
(...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
582 # always get the packet loss ratio even if timeout. | 651 # always get the packet loss ratio even if timeout. |
583 if process.is_alive(): | 652 if process.is_alive(): |
584 kvm_utils.kill_process_tree(process.get_pid(), signal.SIGINT) | 653 kvm_utils.kill_process_tree(process.get_pid(), signal.SIGINT) |
585 | 654 |
586 status = process.get_status() | 655 status = process.get_status() |
587 output = process.get_output() | 656 output = process.get_output() |
588 | 657 |
589 process.close() | 658 process.close() |
590 return status, output | 659 return status, output |
591 else: | 660 else: |
592 session.sendline(command) | 661 output = "" |
593 status, output = session.read_up_to_prompt(timeout=timeout, | 662 try: |
594 print_func=output_func) | 663 output = session.cmd_output(command, timeout=timeout, |
595 if not status: | 664 print_func=output_func) |
| 665 except kvm_subprocess.ShellTimeoutError: |
596 # Send ctrl+c (SIGINT) through ssh session | 666 # Send ctrl+c (SIGINT) through ssh session |
597 session.send("\003") | 667 session.send("\003") |
598 status, output2 = session.read_up_to_prompt(print_func=output_func) | 668 try: |
599 output += output2 | 669 output2 = session.read_up_to_prompt(print_func=output_func) |
600 if not status: | 670 output += output2 |
| 671 except kvm_subprocess.ExpectTimeoutError, e: |
| 672 output += e.output |
601 # We also need to use this session to query the return value | 673 # We also need to use this session to query the return value |
602 session.send("\003") | 674 session.send("\003") |
603 | 675 |
604 session.sendline(session.status_test_command) | 676 session.sendline(session.status_test_command) |
605 s2, o2 = session.read_up_to_prompt() | 677 try: |
606 if not s2: | 678 o2 = session.read_up_to_prompt() |
| 679 except kvm_subprocess.ExpectError: |
607 status = -1 | 680 status = -1 |
608 else: | 681 else: |
609 try: | 682 try: |
610 status = int(re.findall("\d+", o2)[0]) | 683 status = int(re.findall("\d+", o2)[0]) |
611 except: | 684 except: |
612 status = -1 | 685 status = -1 |
613 | 686 |
614 return status, output | 687 return status, output |
615 | 688 |
616 | 689 |
(...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
663 | 736 |
664 | 737 |
665 def get_linux_ifname(session, mac_address): | 738 def get_linux_ifname(session, mac_address): |
666 """ | 739 """ |
667 Get the interface name through the mac address. | 740 Get the interface name through the mac address. |
668 | 741 |
669 @param session: session to the virtual machine | 742 @param session: session to the virtual machine |
670 @mac_address: the macaddress of nic | 743 @mac_address: the macaddress of nic |
671 """ | 744 """ |
672 | 745 |
673 output = session.get_command_output("ifconfig -a") | 746 output = session.cmd_output("ifconfig -a") |
674 | 747 |
675 try: | 748 try: |
676 ethname = re.findall("(\w+)\s+Link.*%s" % mac_address, output, | 749 ethname = re.findall("(\w+)\s+Link.*%s" % mac_address, output, |
677 re.IGNORECASE)[0] | 750 re.IGNORECASE)[0] |
678 return ethname | 751 return ethname |
679 except: | 752 except: |
680 return None | 753 return None |
OLD | NEW |