lmr | 6f669ce | 2009-05-31 19:02:42 +0000 | [diff] [blame] | 1 | import time, os, logging |
| 2 | from autotest_lib.client.common_lib import utils, error |
| 3 | import kvm_utils, ppm_utils, scan_results |
| 4 | |
| 5 | """ |
| 6 | KVM test definitions. |
| 7 | |
| 8 | @copyright: 2008-2009 Red Hat Inc. |
| 9 | """ |
| 10 | |
| 11 | |
| 12 | def run_boot(test, params, env): |
| 13 | """ |
| 14 | KVM reboot test: |
| 15 | 1) Log into a guest |
| 16 | 2) Send a reboot command to the guest |
| 17 | 3) Wait until it's up. |
| 18 | 4) Log into the guest to verify it's up again. |
| 19 | |
| 20 | @param test: kvm test object |
| 21 | @param params: Dictionary with the test parameters |
| 22 | @param env: Dictionary with test environment. |
| 23 | """ |
| 24 | vm = kvm_utils.env_get_vm(env, params.get("main_vm")) |
| 25 | if not vm: |
| 26 | raise error.TestError("VM object not found in environment") |
| 27 | if not vm.is_alive(): |
| 28 | raise error.TestError("VM seems to be dead; Test requires a living VM") |
| 29 | |
| 30 | logging.info("Waiting for guest to be up...") |
| 31 | |
| 32 | session = kvm_utils.wait_for(vm.ssh_login, 240, 0, 2) |
| 33 | if not session: |
| 34 | raise error.TestFail("Could not log into guest") |
| 35 | |
| 36 | logging.info("Logged in") |
| 37 | |
| 38 | if params.get("reboot") == "yes": |
| 39 | # Send the VM's reboot command |
| 40 | session.sendline(vm.get_params().get("cmd_reboot")) |
| 41 | logging.info("Reboot command sent; waiting for guest to go down...") |
| 42 | |
| 43 | if not kvm_utils.wait_for(lambda: not session.is_responsive(), |
| 44 | 120, 0, 1): |
| 45 | raise error.TestFail("Guest refuses to go down") |
| 46 | |
| 47 | session.close() |
| 48 | |
| 49 | logging.info("Guest is down; waiting for it to go up again...") |
| 50 | |
| 51 | session = kvm_utils.wait_for(vm.ssh_login, 120, 0, 2) |
| 52 | if not session: |
| 53 | raise error.TestFail("Could not log into guest after reboot") |
| 54 | |
| 55 | logging.info("Guest is up again") |
| 56 | |
| 57 | session.close() |
| 58 | |
| 59 | |
| 60 | def run_migration(test, params, env): |
| 61 | """ |
| 62 | KVM migration test: |
| 63 | |
| 64 | 1) Get two live VMs. One will be the 'source', the other will be the |
| 65 | 'destination'. |
| 66 | 2) Verify if the source VM supports migration. If it does, proceed with |
| 67 | the test |
| 68 | 3) Send a migration command to the source vm and wait until it's finished. |
| 69 | 4) Kill off the source vm |
| 70 | 3) Log into the destination vm after the migration is finished. |
| 71 | 4) Compare the output of a reference command executed on the source with |
| 72 | the output of the same command on the destination machine |
| 73 | |
| 74 | @param test: kvm test object. |
| 75 | @param params: Dictionary with test parameters. |
| 76 | @param env: Dictionary with the test environment. |
| 77 | """ |
| 78 | src_vm_name = params.get("migration_src") |
| 79 | vm = kvm_utils.env_get_vm(env, src_vm_name) |
| 80 | if not vm: |
| 81 | raise error.TestError("VM '%s' not found in environment" % src_vm_name) |
| 82 | if not vm.is_alive(): |
| 83 | raise error.TestError("VM '%s' seems to be dead; Test requires a" |
| 84 | " living VM" % src_vm_name) |
| 85 | |
| 86 | dest_vm_name = params.get("migration_dst") |
| 87 | dest_vm = kvm_utils.env_get_vm(env, dest_vm_name) |
| 88 | if not dest_vm: |
| 89 | raise error.TestError("VM '%s' not found in environment" % dest_vm_name) |
| 90 | if not dest_vm.is_alive(): |
| 91 | raise error.TestError("VM '%s' seems to be dead; Test requires a" |
| 92 | " living VM" % dest_vm_name) |
| 93 | |
| 94 | pre_scrdump_filename = os.path.join(test.debugdir, "migration_pre.ppm") |
| 95 | post_scrdump_filename = os.path.join(test.debugdir, "migration_post.ppm") |
| 96 | |
| 97 | # See if migration is supported |
| 98 | s, o = vm.send_monitor_cmd("help info") |
| 99 | if not "info migrate" in o: |
| 100 | raise error.TestError("Migration is not supported") |
| 101 | |
| 102 | # Log into guest and get the output of migration_test_command |
| 103 | logging.info("Waiting for guest to be up...") |
| 104 | |
| 105 | session = kvm_utils.wait_for(vm.ssh_login, 240, 0, 2) |
| 106 | if not session: |
| 107 | raise error.TestFail("Could not log into guest") |
| 108 | |
| 109 | logging.info("Logged in") |
| 110 | |
| 111 | reference_output = session.get_command_output(params.get("migration_test_" |
| 112 | "command")) |
| 113 | session.close() |
| 114 | |
| 115 | # Define the migration command |
| 116 | cmd = "migrate -d tcp:localhost:%d" % dest_vm.migration_port |
| 117 | logging.debug("Migration command: %s" % cmd) |
| 118 | |
| 119 | # Migrate |
| 120 | s, o = vm.send_monitor_cmd(cmd) |
| 121 | if s: |
| 122 | logging.error("Migration command failed (command: %r, output: %r)" % |
| 123 | (cmd, o)) |
| 124 | raise error.TestFail("Migration command failed") |
| 125 | |
| 126 | # Define some helper functions |
| 127 | def mig_finished(): |
| 128 | s, o = vm.send_monitor_cmd("info migrate") |
| 129 | if s: |
| 130 | return False |
| 131 | if "Migration status: active" in o: |
| 132 | return False |
| 133 | return True |
| 134 | |
| 135 | def mig_succeeded(): |
| 136 | s, o = vm.send_monitor_cmd("info migrate") |
| 137 | if s == 0 and "Migration status: completed" in o: |
| 138 | return True |
| 139 | return False |
| 140 | |
| 141 | def mig_failed(): |
| 142 | s, o = vm.send_monitor_cmd("info migrate") |
| 143 | if s == 0 and "Migration status: failed" in o: |
| 144 | return True |
| 145 | return False |
| 146 | |
| 147 | # Wait for migration to finish |
| 148 | if not kvm_utils.wait_for(mig_finished, 90, 2, 2, |
| 149 | "Waiting for migration to finish..."): |
| 150 | raise error.TestFail("Timeout elapsed while waiting for migration to" |
| 151 | "finish") |
| 152 | |
| 153 | # Report migration status |
| 154 | if mig_succeeded(): |
| 155 | logging.info("Migration finished successfully") |
| 156 | else: |
| 157 | if mig_failed(): |
| 158 | message = "Migration failed" |
| 159 | else: |
| 160 | message = "Migration ended with unknown status" |
| 161 | raise error.TestFail(message) |
| 162 | |
| 163 | # Get 'post' screendump |
| 164 | dest_vm.send_monitor_cmd("screendump %s" % post_scrdump_filename) |
| 165 | |
| 166 | # Get 'pre' screendump |
| 167 | vm.send_monitor_cmd("screendump %s" % pre_scrdump_filename) |
| 168 | |
| 169 | # Kill the source VM |
| 170 | vm.send_monitor_cmd("quit", block=False) |
| 171 | |
| 172 | # Hack: it seems that the first attempt to communicate with the SSH port |
| 173 | # following migration always fails (or succeeds after a very long time). |
| 174 | # So just connect to the port once so the following call to ssh_login |
| 175 | # succeeds. |
| 176 | dest_vm.is_sshd_running(timeout=0.0) |
| 177 | |
| 178 | # Log into guest and get the output of migration_test_command |
| 179 | logging.info("Logging into guest after migration...") |
| 180 | |
| 181 | session = dest_vm.ssh_login() |
| 182 | if not session: |
| 183 | raise error.TestFail("Could not log into guest after migration") |
| 184 | |
| 185 | logging.info("Logged in after migration") |
| 186 | |
| 187 | output = session.get_command_output(params.get("migration_test_command")) |
| 188 | session.close() |
| 189 | |
| 190 | # Compare output to reference output |
| 191 | if output != reference_output: |
| 192 | logging.info("Command output before migration differs from command" |
| 193 | " output after migration") |
| 194 | logging.info("Command: %s" % params.get("migration_test_command")) |
| 195 | logging.info("Output before:" + |
| 196 | kvm_utils.format_str_for_message(reference_output)) |
| 197 | logging.info("Output after:" + kvm_utils.format_str_for_message(output)) |
| 198 | raise error.TestFail("Command produced different output before and" |
| 199 | " after migration") |
| 200 | |
| 201 | |
| 202 | def run_autotest(test, params, env): |
| 203 | """ |
| 204 | Run an autotest test inside a guest. |
| 205 | |
| 206 | @param test: kvm test object. |
| 207 | @param params: Dictionary with test parameters. |
| 208 | @param env: Dictionary with the test environment. |
| 209 | """ |
| 210 | vm = kvm_utils.env_get_vm(env, params.get("main_vm")) |
| 211 | if not vm: |
| 212 | raise error.TestError("VM object not found in environment") |
| 213 | if not vm.is_alive(): |
| 214 | raise error.TestError("VM seems to be dead; Test requires a living VM") |
| 215 | |
| 216 | logging.info("Logging into guest...") |
| 217 | |
| 218 | session = kvm_utils.wait_for(vm.ssh_login, 240, 0, 2) |
| 219 | if not session: |
| 220 | raise error.TestFail("Could not log into guest") |
| 221 | |
| 222 | logging.info("Logged in") |
| 223 | |
| 224 | # Collect some info |
| 225 | test_name = params.get("test_name") |
| 226 | test_timeout = int(params.get("test_timeout", 300)) |
| 227 | test_control_file = params.get("test_control_file", "control") |
| 228 | tarred_autotest_path = "/tmp/autotest.tar.bz2" |
| 229 | tarred_test_path = "/tmp/%s.tar.bz2" % test_name |
| 230 | |
| 231 | # tar the contents of bindir/autotest |
| 232 | cmd = "cd %s; tar cvjf %s autotest/*" |
| 233 | cmd += " --exclude=autotest/tests" |
| 234 | cmd += " --exclude=autotest/results" |
| 235 | cmd += " --exclude=autotest/tmp" |
| 236 | cmd += " --exclude=autotest/control" |
| 237 | cmd += " --exclude=*.pyc" |
| 238 | cmd += " --exclude=*.svn" |
| 239 | cmd += " --exclude=*.git" |
| 240 | kvm_utils.run_bg(cmd % (test.bindir, tarred_autotest_path), timeout=30) |
| 241 | |
| 242 | # tar the contents of bindir/autotest/tests/<test_name> |
| 243 | cmd = "cd %s; tar cvjf %s %s/*" |
| 244 | cmd += " --exclude=*.pyc" |
| 245 | cmd += " --exclude=*.svn" |
| 246 | cmd += " --exclude=*.git" |
| 247 | kvm_utils.run_bg(cmd % (os.path.join(test.bindir, "autotest", "tests"), |
| 248 | tarred_test_path, test_name), timeout=30) |
| 249 | |
| 250 | # Check if we need to copy autotest.tar.bz2 |
| 251 | copy = False |
| 252 | output = session.get_command_output("ls -l autotest.tar.bz2") |
| 253 | if "such file" in output: |
| 254 | copy = True |
| 255 | else: |
| 256 | size = int(output.split()[4]) |
| 257 | if size != os.path.getsize(tarred_autotest_path): |
| 258 | copy = True |
| 259 | # Perform the copy |
| 260 | if copy: |
| 261 | logging.info("Copying autotest.tar.bz2 to guest" |
| 262 | " (file is missing or has a different size)...") |
| 263 | if not vm.scp_to_remote(tarred_autotest_path, ""): |
| 264 | raise error.TestFail("Could not copy autotest.tar.bz2 to guest") |
| 265 | |
| 266 | # Check if we need to copy <test_name>.tar.bz2 |
| 267 | copy = False |
| 268 | output = session.get_command_output("ls -l %s.tar.bz2" % test_name) |
| 269 | if "such file" in output: |
| 270 | copy = True |
| 271 | else: |
| 272 | size = int(output.split()[4]) |
| 273 | if size != os.path.getsize(tarred_test_path): |
| 274 | copy = True |
| 275 | # Perform the copy |
| 276 | if copy: |
| 277 | logging.info("Copying %s.tar.bz2 to guest (file is missing or has a" |
| 278 | " different size)..." % test_name) |
| 279 | if not vm.scp_to_remote(tarred_test_path, ""): |
| 280 | raise error.TestFail("Could not copy %s.tar.bz2 to guest" % |
| 281 | test_name) |
| 282 | |
| 283 | # Extract autotest.tar.bz2 |
| 284 | logging.info("Extracting autotest.tar.bz2...") |
| 285 | status = session.get_command_status("tar xvfj autotest.tar.bz2") |
| 286 | if status != 0: |
| 287 | raise error.TestFail("Could not extract autotest.tar.bz2") |
| 288 | |
| 289 | # mkdir autotest/tests |
| 290 | session.sendline("mkdir autotest/tests") |
| 291 | |
| 292 | # Extract <test_name>.tar.bz2 into autotest/tests |
| 293 | logging.info("Extracting %s.tar.bz2..." % test_name) |
| 294 | status = session.get_command_status("tar xvfj %s.tar.bz2 -C " |
| 295 | "autotest/tests" % test_name) |
| 296 | if status != 0: |
| 297 | raise error.TestFail("Could not extract %s.tar.bz2" % test_name) |
| 298 | |
| 299 | # Run the test |
| 300 | logging.info("Running test '%s'..." % test_name) |
| 301 | session.sendline("cd autotest/tests/%s" % test_name) |
| 302 | session.sendline("rm -f ./%s.state" % test_control_file) |
| 303 | session.read_up_to_prompt() |
| 304 | session.sendline("../../bin/autotest ./%s" % test_control_file) |
| 305 | logging.info("---------------- Test output ----------------") |
| 306 | match, output = session.read_up_to_prompt(timeout=test_timeout, |
| 307 | print_func=logging.info) |
| 308 | logging.info("---------------- End of test output ----------------") |
| 309 | if not match: |
| 310 | raise error.TestFail("Timeout elapsed while waiting for test to" |
| 311 | " complete") |
| 312 | |
| 313 | session.close() |
| 314 | |
| 315 | # Parse test results |
| 316 | result_list = scan_results.parse_results(output) |
| 317 | |
| 318 | # Report test results and check for FAIL/ERROR status |
| 319 | logging.info("Results (test, status, duration, info):") |
| 320 | status_error = False |
| 321 | status_fail = False |
| 322 | if result_list == []: |
| 323 | status_fail = True |
lmr | d16a67d | 2009-06-10 19:52:59 +0000 | [diff] [blame^] | 324 | message_fail = ("Test '%s' did not produce any recognizable " |
| 325 | "results" % test_name) |
lmr | 6f669ce | 2009-05-31 19:02:42 +0000 | [diff] [blame] | 326 | for result in result_list: |
| 327 | logging.info(str(result)) |
| 328 | if result[1] == "FAIL": |
| 329 | status_fail = True |
| 330 | message_fail = "Test '%s' ended with FAIL" |
| 331 | " (info: '%s')" % (result[0], result[3]) |
| 332 | if result[1] == "ERROR": |
| 333 | status_error = True |
| 334 | message_error = "Test '%s' ended with ERROR" |
| 335 | " (info: '%s')" % (result[0], result[3]) |
| 336 | if result[1] == "ABORT": |
| 337 | status_error = True |
| 338 | message_error = "Test '%s' ended with ABORT" |
| 339 | " (info: '%s')" % (result[0], result[3]) |
| 340 | |
| 341 | # Copy test results to the local bindir/guest_results |
| 342 | logging.info("Copying results back from guest...") |
| 343 | guest_results_dir = os.path.join(test.outputdir, "guest_results") |
| 344 | if not os.path.exists(guest_results_dir): |
| 345 | os.mkdir(guest_results_dir) |
| 346 | if not vm.scp_from_remote("autotest/results/default/*", guest_results_dir): |
| 347 | logging.error("Could not copy results back from guest") |
| 348 | |
| 349 | # Fail the test if necessary |
| 350 | if status_fail: |
| 351 | raise error.TestFail(message_fail) |
| 352 | elif status_error: |
| 353 | raise error.TestError(message_error) |
| 354 | |
| 355 | |
| 356 | def internal_yum_update(session, command, prompt, timeout): |
| 357 | """ |
| 358 | Helper function to perform the yum update test. |
| 359 | |
| 360 | @param session: SSH session stablished to the host |
| 361 | @param command: Command to be sent to the SSH connection |
| 362 | @param prompt: Machine prompt |
| 363 | @param timeout: How long to wait until we get an appropriate output from |
| 364 | the SSH session. |
| 365 | """ |
| 366 | session.sendline(command) |
| 367 | end_time = time.time() + timeout |
| 368 | while time.time() < end_time: |
| 369 | (match, text) = session.read_until_last_line_matches( |
| 370 | ["[Ii]s this [Oo][Kk]", prompt], timeout=timeout) |
| 371 | if match == 0: |
| 372 | logging.info("Got 'Is this ok'; sending 'y'") |
| 373 | session.sendline("y") |
| 374 | elif match == 1: |
| 375 | logging.info("Got shell prompt") |
| 376 | return True |
| 377 | else: |
| 378 | logging.info("Timeout or process exited") |
| 379 | return False |
| 380 | |
| 381 | |
| 382 | def run_yum_update(test, params, env): |
| 383 | """ |
| 384 | Runs yum update and yum update kernel on the remote host (yum enabled |
| 385 | hosts only). |
| 386 | |
| 387 | @param test: kvm test object. |
| 388 | @param params: Dictionary with test parameters. |
| 389 | @param env: Dictionary with the test environment. |
| 390 | """ |
| 391 | vm = kvm_utils.env_get_vm(env, params.get("main_vm")) |
| 392 | if not vm: |
| 393 | message = "VM object not found in environment" |
| 394 | logging.error(message) |
| 395 | raise error.TestError(message) |
| 396 | if not vm.is_alive(): |
| 397 | message = "VM seems to be dead; Test requires a living VM" |
| 398 | logging.error(message) |
| 399 | raise error.TestError(message) |
| 400 | |
| 401 | logging.info("Logging into guest...") |
| 402 | |
| 403 | session = kvm_utils.wait_for(vm.ssh_login, 120, 0, 2) |
| 404 | if not session: |
| 405 | message = "Could not log into guest" |
| 406 | logging.error(message) |
| 407 | raise error.TestFail(message) |
| 408 | |
| 409 | logging.info("Logged in") |
| 410 | |
| 411 | internal_yum_update(session, "yum update", params.get("ssh_prompt"), 600) |
| 412 | internal_yum_update(session, "yum update kernel", |
| 413 | params.get("ssh_prompt"), 600) |
| 414 | |
| 415 | session.close() |
| 416 | |
| 417 | |
| 418 | def run_linux_s3(test, params, env): |
| 419 | """ |
| 420 | Suspend a guest Linux OS to memory. |
| 421 | |
| 422 | @param test: kvm test object. |
| 423 | @param params: Dictionary with test parameters. |
| 424 | @param env: Dictionary with the test environment. |
| 425 | """ |
| 426 | vm = kvm_utils.env_get_vm(env, params.get("main_vm")) |
| 427 | if not vm: |
| 428 | raise error.TestError("VM object not found in environment") |
| 429 | if not vm.is_alive(): |
| 430 | raise error.TestError("VM seems to be dead; Test requires a living VM") |
| 431 | |
| 432 | logging.info("Waiting for guest to be up...") |
| 433 | |
| 434 | session = kvm_utils.wait_for(vm.ssh_login, 240, 0, 2) |
| 435 | if not session: |
| 436 | raise error.TestFail("Could not log into guest") |
| 437 | |
| 438 | logging.info("Logged in") |
| 439 | logging.info("Checking that VM supports S3") |
| 440 | |
| 441 | status = session.get_command_status("grep -q mem /sys/power/state") |
| 442 | if status == None: |
| 443 | logging.error("Failed to check if S3 exists") |
| 444 | elif status != 0: |
| 445 | raise error.TestFail("Guest does not support S3") |
| 446 | |
| 447 | logging.info("Waiting for a while for X to start") |
| 448 | time.sleep(10) |
| 449 | |
| 450 | src_tty = session.get_command_output("fgconsole").strip() |
| 451 | logging.info("Current virtual terminal is %s" % src_tty) |
| 452 | if src_tty not in map(str, range(1,10)): |
| 453 | raise error.TestFail("Got a strange current vt (%s)" % src_tty) |
| 454 | |
| 455 | dst_tty = "1" |
| 456 | if src_tty == "1": |
| 457 | dst_tty = "2" |
| 458 | |
| 459 | logging.info("Putting VM into S3") |
| 460 | command = "chvt %s && echo mem > /sys/power/state && chvt %s" % (dst_tty, |
| 461 | src_tty) |
| 462 | status = session.get_command_status(command, timeout=120) |
| 463 | if status != 0: |
| 464 | raise error.TestFail("Suspend to mem failed") |
| 465 | |
| 466 | logging.info("VM resumed after S3") |
| 467 | |
| 468 | session.close() |