| 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 |