Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 #!/usr/bin/python | 1 #!/usr/bin/python |
| 2 | 2 |
| 3 # Copyright (c) 2011 The Chromium OS Authors. All rights reserved. | 3 # Copyright (c) 2011 The Chromium OS Authors. All rights reserved. |
| 4 # Use of this source code is governed by a BSD-style license that can be | 4 # Use of this source code is governed by a BSD-style license that can be |
| 5 # found in the LICENSE file. | 5 # found in the LICENSE file. |
| 6 | 6 |
| 7 """This module runs a suite of Auto Update tests. | 7 """This module runs a suite of Auto Update tests. |
| 8 | 8 |
| 9 The tests can be run on either a virtual machine or actual device depending | 9 The tests can be run on either a virtual machine or actual device depending |
| 10 on parameters given. Specific tests can be run by invoking --test_prefix. | 10 on parameters given. Specific tests can be run by invoking --test_prefix. |
| 11 Verbose is useful for many of the tests if you want to see individual commands | 11 Verbose is useful for many of the tests if you want to see individual commands |
| 12 being run during the update process. | 12 being run during the update process. |
| 13 """ | 13 """ |
| 14 | 14 |
| 15 import optparse | 15 import optparse |
| 16 import os | 16 import os |
| 17 import re | 17 import re |
| 18 import shutil | |
| 18 import subprocess | 19 import subprocess |
| 19 import sys | 20 import sys |
| 21 import tempfile | |
| 20 import threading | 22 import threading |
| 21 import time | 23 import time |
| 22 import unittest | 24 import unittest |
| 23 import urllib | 25 import urllib |
| 24 | 26 |
| 25 sys.path.append(os.path.join(os.path.dirname(__file__), '../lib')) | 27 sys.path.append(os.path.join(os.path.dirname(__file__), '../lib')) |
| 26 from cros_build_lib import Die | 28 from cros_build_lib import Die |
| 27 from cros_build_lib import GetIPAddress | 29 from cros_build_lib import GetIPAddress |
| 28 from cros_build_lib import Info | 30 from cros_build_lib import Info |
| 29 from cros_build_lib import ReinterpretPathForChroot | 31 from cros_build_lib import ReinterpretPathForChroot |
| (...skipping 16 matching lines...) Expand all Loading... | |
| 46 class AUTest(object): | 48 class AUTest(object): |
| 47 """Abstract interface that defines an Auto Update test.""" | 49 """Abstract interface that defines an Auto Update test.""" |
| 48 verbose = False | 50 verbose = False |
| 49 | 51 |
| 50 def setUp(self): | 52 def setUp(self): |
| 51 unittest.TestCase.setUp(self) | 53 unittest.TestCase.setUp(self) |
| 52 # Set these up as they are used often. | 54 # Set these up as they are used often. |
| 53 self.crosutils = os.path.join(os.path.dirname(__file__), '..') | 55 self.crosutils = os.path.join(os.path.dirname(__file__), '..') |
| 54 self.crosutilsbin = os.path.join(os.path.dirname(__file__)) | 56 self.crosutilsbin = os.path.join(os.path.dirname(__file__)) |
| 55 self.download_folder = os.path.join(self.crosutils, 'latest_download') | 57 self.download_folder = os.path.join(self.crosutils, 'latest_download') |
| 58 self.vm_image_path = None | |
| 56 if not os.path.exists(self.download_folder): | 59 if not os.path.exists(self.download_folder): |
| 57 os.makedirs(self.download_folder) | 60 os.makedirs(self.download_folder) |
| 58 | 61 |
| 59 # -------- Helper functions --------- | 62 # -------- Helper functions --------- |
| 60 | 63 |
| 64 def _PrepareRealBase(self, image_path): | |
| 65 self.PerformUpdate(image_path) | |
| 66 | |
| 67 def _PrepareVMBase(self, image_path): | |
| 68 # VM Constants. | |
| 69 FULL_VDISK_SIZE = 6072 | |
| 70 FULL_STATEFULFS_SIZE = 3074 | |
| 71 # Needed for VM delta updates. We need to use the qemu image rather | |
| 72 # than the base image on a first update. By tracking the first_update | |
| 73 # we can set src_image to the qemu form of the base image when | |
| 74 # performing generating the delta payload. | |
| 75 self._first_update = True | |
| 76 self.vm_image_path = '%s/chromiumos_qemu_image.bin' % os.path.dirname( | |
| 77 image_path) | |
| 78 if not os.path.exists(self.vm_image_path): | |
| 79 Info('Creating %s' % self.vm_image_path) | |
| 80 RunCommand(['%s/image_to_vm.sh' % self.crosutils, | |
| 81 '--full', | |
| 82 '--from=%s' % ReinterpretPathForChroot( | |
| 83 os.path.dirname(image_path)), | |
| 84 '--vdisk_size=%s' % FULL_VDISK_SIZE, | |
| 85 '--statefulfs_size=%s' % FULL_STATEFULFS_SIZE, | |
| 86 '--board=%s' % self.board, | |
| 87 '--test_image'], enter_chroot=True) | |
| 88 | |
| 89 Info('Using %s as base' % self.vm_image_path) | |
| 90 self.assertTrue(os.path.exists(self.vm_image_path)) | |
| 91 | |
| 92 def AppendUpdateFlags(self, cmd, image_path, src_image_path, proxy_port, | |
| 93 private_key_path): | |
| 94 """Appends common args to an update cmd defined by an array. | |
| 95 | |
| 96 Modifies cmd in places by appending appropriate items given args. | |
| 97 """ | |
| 98 if proxy_port: cmd.append('--proxy_port=%s' % proxy_port) | |
| 99 | |
| 100 # Get pregenerated update if we have one. | |
| 101 update_id = _GenerateUpdateId(target=image_path, src=src_image_path, | |
| 102 key=private_key_path) | |
| 103 cache_path = dev_server_cache[update_id] | |
| 104 if cache_path: | |
| 105 update_url = DevServerWrapper.GetDevServerURL(proxy_port, cache_path) | |
| 106 cmd.append('--update_url=%s' % update_url) | |
| 107 else: | |
| 108 cmd.append('--image=%s' % image_path) | |
| 109 if src_image_path: cmd.append('--src_image=%s' % src_image_path) | |
| 110 | |
| 111 def RunUpdateCmd(self, cmd): | |
| 112 """Runs the given update cmd given verbose options. | |
| 113 | |
| 114 Raises an UpdateException if the update fails. | |
| 115 """ | |
| 116 if self.verbose: | |
| 117 try: | |
| 118 RunCommand(cmd) | |
| 119 except Exception, e: | |
| 120 raise UpdateException(1, e.message) | |
| 121 else: | |
| 122 (code, stdout, stderr) = RunCommandCaptureOutput(cmd) | |
| 123 if code != 0: | |
| 124 raise UpdateException(code, stdout) | |
| 125 | |
| 61 def GetStatefulChangeFlag(self, stateful_change): | 126 def GetStatefulChangeFlag(self, stateful_change): |
| 62 """Returns the flag to pass to image_to_vm for the stateful change.""" | 127 """Returns the flag to pass to image_to_vm for the stateful change.""" |
| 63 stateful_change_flag = '' | 128 stateful_change_flag = '' |
| 64 if stateful_change: | 129 if stateful_change: |
| 65 stateful_change_flag = '--stateful_update_flag=%s' % stateful_change | 130 stateful_change_flag = '--stateful_update_flag=%s' % stateful_change |
| 66 | 131 |
| 67 return stateful_change_flag | 132 return stateful_change_flag |
| 68 | 133 |
| 69 def _ParseGenerateTestReportOutput(self, output): | 134 def _ParseGenerateTestReportOutput(self, output): |
| 70 """Returns the percentage of tests that passed based on output.""" | 135 """Returns the percentage of tests that passed based on output.""" |
| (...skipping 23 matching lines...) Expand all Loading... | |
| 94 Info('Output from VerifyImage():') | 159 Info('Output from VerifyImage():') |
| 95 print >> sys.stderr, output | 160 print >> sys.stderr, output |
| 96 sys.stderr.flush() | 161 sys.stderr.flush() |
| 97 percent_passed = self._ParseGenerateTestReportOutput(output) | 162 percent_passed = self._ParseGenerateTestReportOutput(output) |
| 98 Info('Percent passed: %d vs. Percent required: %d' % ( | 163 Info('Percent passed: %d vs. Percent required: %d' % ( |
| 99 percent_passed, percent_required_to_pass)) | 164 percent_passed, percent_required_to_pass)) |
| 100 unittest.assertTrue(percent_passed >= percent_required_to_pass) | 165 unittest.assertTrue(percent_passed >= percent_required_to_pass) |
| 101 return percent_passed | 166 return percent_passed |
| 102 | 167 |
| 103 def PerformUpdate(self, image_path, src_image_path='', stateful_change='old', | 168 def PerformUpdate(self, image_path, src_image_path='', stateful_change='old', |
| 104 proxy_port=None): | 169 proxy_port=None, private_key_path=None): |
| 105 """Performs an update using _UpdateImage and reports any error. | 170 """Performs an update using _UpdateImage and reports any error. |
| 106 | 171 |
| 107 Subclasses should not override this method but override _UpdateImage | 172 Subclasses should not override this method but override _UpdateImage |
| 108 instead. | 173 instead. |
| 109 | 174 |
| 110 Args: | 175 Args: |
| 111 image_path: Path to the image to update with. This image must be a test | 176 image_path: Path to the image to update with. This image must be a test |
| 112 image. | 177 image. |
| 113 src_image_path: Optional. If set, perform a delta update using the | 178 src_image_path: Optional. If set, perform a delta update using the |
| 114 image specified by the path as the source image. | 179 image specified by the path as the source image. |
| 115 stateful_change: How to modify the stateful partition. Values are: | 180 stateful_change: How to modify the stateful partition. Values are: |
| 116 'old': Don't modify stateful partition. Just update normally. | 181 'old': Don't modify stateful partition. Just update normally. |
| 117 'clean': Uses clobber-state to wipe the stateful partition with the | 182 'clean': Uses clobber-state to wipe the stateful partition with the |
| 118 exception of code needed for ssh. | 183 exception of code needed for ssh. |
| 119 proxy_port: Port to have the client connect to. For use with | 184 proxy_port: Port to have the client connect to. For use with |
| 120 CrosTestProxy. | 185 CrosTestProxy. |
| 121 Raises an UpdateException if _UpdateImage returns an error. | 186 Raises an UpdateException if _UpdateImage returns an error. |
| 122 """ | 187 """ |
| 123 try: | 188 try: |
| 124 if not self.use_delta_updates: | 189 if not self.use_delta_updates: src_image_path = '' |
| 125 src_image_path = '' | 190 if private_key_path: |
| 191 key_to_use = private_key_path | |
| 192 else: | |
| 193 key_to_use = self.private_key | |
| 126 | 194 |
| 127 self._UpdateImage(image_path, src_image_path, stateful_change, proxy_port) | 195 self._UpdateImage(image_path, src_image_path, stateful_change, proxy_port, |
| 196 key_to_use) | |
| 128 except UpdateException as err: | 197 except UpdateException as err: |
| 129 # If the update fails, print it out | 198 # If the update fails, print it out |
| 130 Warning(err.stdout) | 199 Warning(err.stdout) |
| 131 raise | 200 raise |
| 132 | 201 |
| 133 def AttemptUpdateWithPayloadExpectedFailure(self, payload, expected_msg): | 202 def AttemptUpdateWithPayloadExpectedFailure(self, payload, expected_msg): |
| 134 """Attempt a payload update, expect it to fail with expected log""" | 203 """Attempt a payload update, expect it to fail with expected log""" |
| 135 try: | 204 try: |
| 136 self._UpdateUsingPayload(payload) | 205 self._UpdateUsingPayload(payload) |
| 137 except UpdateException as err: | 206 except UpdateException as err: |
| (...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 171 def ProcessOptions(cls, parser, options): | 240 def ProcessOptions(cls, parser, options): |
| 172 """Processes options. | 241 """Processes options. |
| 173 | 242 |
| 174 Static method that should be called from main. Subclasses should also | 243 Static method that should be called from main. Subclasses should also |
| 175 call their parent method if they override it. | 244 call their parent method if they override it. |
| 176 """ | 245 """ |
| 177 cls.verbose = options.verbose | 246 cls.verbose = options.verbose |
| 178 cls.base_image_path = options.base_image | 247 cls.base_image_path = options.base_image |
| 179 cls.target_image_path = options.target_image | 248 cls.target_image_path = options.target_image |
| 180 cls.use_delta_updates = options.delta | 249 cls.use_delta_updates = options.delta |
| 250 cls.board = options.board | |
| 251 cls.private_key = options.private_key | |
| 252 cls.clean = options.clean | |
| 181 if options.quick_test: | 253 if options.quick_test: |
| 182 cls.verify_suite = 'build_RootFilesystemSize' | 254 cls.verify_suite = 'build_RootFilesystemSize' |
| 183 else: | 255 else: |
| 184 cls.verify_suite = 'suite_Smoke' | 256 cls.verify_suite = 'suite_Smoke' |
| 185 | 257 |
| 186 # Sanity checks. | 258 # Sanity checks. |
| 187 if not cls.base_image_path: | 259 if not cls.base_image_path: |
| 188 parser.error('Need path to base image for vm.') | 260 parser.error('Need path to base image for vm.') |
| 189 elif not os.path.exists(cls.base_image_path): | 261 elif not os.path.exists(cls.base_image_path): |
| 190 Die('%s does not exist' % cls.base_image_path) | 262 Die('%s does not exist' % cls.base_image_path) |
| 191 | 263 |
| 192 if not cls.target_image_path: | 264 if not cls.target_image_path: |
| 193 parser.error('Need path to target image to update with.') | 265 parser.error('Need path to target image to update with.') |
| 194 elif not os.path.exists(cls.target_image_path): | 266 elif not os.path.exists(cls.target_image_path): |
| 195 Die('%s does not exist' % cls.target_image_path) | 267 Die('%s does not exist' % cls.target_image_path) |
| 196 | 268 |
| 197 def PrepareBase(self, image_path): | 269 def PrepareBase(self, image_path): |
| 198 """Prepares target with base_image_path.""" | 270 """Prepares target with base_image_path.""" |
| 199 pass | 271 pass |
| 200 | 272 |
| 201 def _UpdateImage(self, image_path, src_image_path='', stateful_change='old', | 273 def _UpdateImage(self, image_path, src_image_path='', stateful_change='old', |
| 202 proxy_port=None): | 274 proxy_port=None, private_key_path=None): |
| 203 """Implementation of an actual update. | 275 """Implementation of an actual update. |
| 204 | 276 |
| 205 See PerformUpdate for description of args. Subclasses must override this | 277 See PerformUpdate for description of args. Subclasses must override this |
| 206 method with the correct update procedure for the class. | 278 method with the correct update procedure for the class. |
| 207 """ | 279 """ |
| 208 pass | 280 pass |
| 209 | 281 |
| 210 def _UpdateUsingPayload(self, update_path, stateful_change='old', | 282 def _UpdateUsingPayload(self, update_path, stateful_change='old', |
| 211 proxy_port=None): | 283 proxy_port=None): |
| 212 """Updates target with the pre-generated update stored in update_path. | 284 """Updates target with the pre-generated update stored in update_path. |
| (...skipping 190 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 403 def ProcessOptions(cls, parser, options): | 475 def ProcessOptions(cls, parser, options): |
| 404 """Processes non-vm-specific options.""" | 476 """Processes non-vm-specific options.""" |
| 405 AUTest.ProcessOptions(parser, options) | 477 AUTest.ProcessOptions(parser, options) |
| 406 cls.remote = options.remote | 478 cls.remote = options.remote |
| 407 | 479 |
| 408 if not cls.remote: | 480 if not cls.remote: |
| 409 parser.error('We require a remote address for real tests.') | 481 parser.error('We require a remote address for real tests.') |
| 410 | 482 |
| 411 def PrepareBase(self, image_path): | 483 def PrepareBase(self, image_path): |
| 412 """Auto-update to base image to prepare for test.""" | 484 """Auto-update to base image to prepare for test.""" |
| 413 self.PerformUpdate(image_path) | 485 _PrepareRealBase(image_path) |
| 414 | 486 |
| 415 def _UpdateImage(self, image_path, src_image_path='', stateful_change='old', | 487 def _UpdateImage(self, image_path, src_image_path='', stateful_change='old', |
| 416 proxy_port=None): | 488 proxy_port=None, private_key_path=None): |
| 417 """Updates a remote image using image_to_live.sh.""" | 489 """Updates a remote image using image_to_live.sh.""" |
| 418 stateful_change_flag = self.GetStatefulChangeFlag(stateful_change) | 490 stateful_change_flag = self.GetStatefulChangeFlag(stateful_change) |
| 419 cmd = ['%s/image_to_live.sh' % self.crosutils, | 491 cmd = ['%s/image_to_live.sh' % self.crosutils, |
| 420 '--image=%s' % image_path, | |
| 421 '--remote=%s' % self.remote, | 492 '--remote=%s' % self.remote, |
| 422 stateful_change_flag, | 493 stateful_change_flag, |
| 423 '--verify', | 494 '--verify', |
| 424 '--src_image=%s' % src_image_path | |
| 425 ] | 495 ] |
| 426 | 496 self.AppendUpdateFlags(cmd, image_path, src_image_path, proxy_port, |
| 427 if proxy_port: | 497 private_key_path) |
| 428 cmd.append('--proxy_port=%s' % proxy_port) | 498 self.RunUpdateCmd(cmd) |
| 429 | |
| 430 if self.verbose: | |
| 431 try: | |
| 432 RunCommand(cmd) | |
| 433 except Exception, e: | |
| 434 raise UpdateException(1, e.message) | |
| 435 else: | |
| 436 (code, stdout, stderr) = RunCommandCaptureOutput(cmd) | |
| 437 if code != 0: | |
| 438 raise UpdateException(code, stdout) | |
| 439 | 499 |
| 440 def _UpdateUsingPayload(self, update_path, stateful_change='old', | 500 def _UpdateUsingPayload(self, update_path, stateful_change='old', |
| 441 proxy_port=None): | 501 proxy_port=None): |
| 442 """Updates a remote image using image_to_live.sh.""" | 502 """Updates a remote image using image_to_live.sh.""" |
| 443 stateful_change_flag = self.GetStatefulChangeFlag(stateful_change) | 503 stateful_change_flag = self.GetStatefulChangeFlag(stateful_change) |
| 444 cmd = ['%s/image_to_live.sh' % self.crosutils, | 504 cmd = ['%s/image_to_live.sh' % self.crosutils, |
| 445 '--payload=%s' % update_path, | 505 '--payload=%s' % update_path, |
| 446 '--remote=%s' % self.remote, | 506 '--remote=%s' % self.remote, |
| 447 stateful_change_flag, | 507 stateful_change_flag, |
| 448 '--verify', | 508 '--verify', |
| 449 ] | 509 ] |
| 450 | 510 if proxy_port: cmd.append('--proxy_port=%s' % proxy_port) |
| 451 if proxy_port: | 511 self.RunUpdateCmd(cmd) |
| 452 cmd.append('--proxy_port=%s' % proxy_port) | |
| 453 | |
| 454 if self.verbose: | |
| 455 try: | |
| 456 RunCommand(cmd) | |
| 457 except Exception, e: | |
| 458 raise UpdateException(1, e.message) | |
| 459 else: | |
| 460 (code, stdout, stderr) = RunCommandCaptureOutput(cmd) | |
| 461 if code != 0: | |
| 462 raise UpdateException(code, stdout) | |
| 463 | 512 |
| 464 def VerifyImage(self, percent_required_to_pass): | 513 def VerifyImage(self, percent_required_to_pass): |
| 465 """Verifies an image using run_remote_tests.sh with verification suite.""" | 514 """Verifies an image using run_remote_tests.sh with verification suite.""" |
| 466 output = RunCommand([ | 515 output = RunCommand([ |
| 467 '%s/run_remote_tests.sh' % self.crosutils, | 516 '%s/run_remote_tests.sh' % self.crosutils, |
| 468 '--remote=%s' % self.remote, | 517 '--remote=%s' % self.remote, |
| 469 self.verify_suite, | 518 self.verify_suite, |
| 470 ], error_ok=True, enter_chroot=False, redirect_stdout=True) | 519 ], error_ok=True, enter_chroot=False, redirect_stdout=True) |
| 471 return self.AssertEnoughTestsPassed(self, output, percent_required_to_pass) | 520 return self.AssertEnoughTestsPassed(self, output, percent_required_to_pass) |
| 472 | 521 |
| 473 | 522 |
| 474 class VirtualAUTest(unittest.TestCase, AUTest): | 523 class VirtualAUTest(unittest.TestCase, AUTest): |
| 475 """Test harness for updating virtual machines.""" | 524 """Test harness for updating virtual machines.""" |
| 476 | 525 |
| 477 # VM Constants. | |
| 478 _FULL_VDISK_SIZE = 6072 | |
| 479 _FULL_STATEFULFS_SIZE = 3074 | |
| 480 | |
| 481 # Class variables used to acquire individual VM variables per test. | 526 # Class variables used to acquire individual VM variables per test. |
| 482 _vm_lock = threading.Lock() | 527 _vm_lock = threading.Lock() |
| 483 _next_port = 9222 | 528 _next_port = 9222 |
| 484 | 529 |
| 485 def _KillExistingVM(self, pid_file): | 530 def _KillExistingVM(self, pid_file): |
| 486 if os.path.exists(pid_file): | 531 if os.path.exists(pid_file): |
| 487 Warning('Existing %s found. Deleting and killing process' % | 532 Warning('Existing %s found. Deleting and killing process' % |
| 488 pid_file) | 533 pid_file) |
| 489 RunCommand(['./cros_stop_vm', '--kvm_pid=%s' % pid_file], | 534 RunCommand(['./cros_stop_vm', '--kvm_pid=%s' % pid_file], |
| 490 cwd=self.crosutilsbin) | 535 cwd=self.crosutilsbin) |
| 491 | 536 |
| 492 assert not os.path.exists(pid_file) | 537 assert not os.path.exists(pid_file) |
| 493 | 538 |
| 494 def _AcquireUniquePortAndPidFile(self): | 539 def _AcquireUniquePortAndPidFile(self): |
| 495 """Acquires unique ssh port and pid file for VM.""" | 540 """Acquires unique ssh port and pid file for VM.""" |
| 496 with VirtualAUTest._vm_lock: | 541 with VirtualAUTest._vm_lock: |
| 497 self._ssh_port = VirtualAUTest._next_port | 542 self._ssh_port = VirtualAUTest._next_port |
| 498 self._kvm_pid_file = '/tmp/kvm.%d' % self._ssh_port | 543 self._kvm_pid_file = '/tmp/kvm.%d' % self._ssh_port |
| 499 VirtualAUTest._next_port += 1 | 544 VirtualAUTest._next_port += 1 |
| 500 | 545 |
| 501 def setUp(self): | 546 def setUp(self): |
| 502 """Unit test overriden method. Is called before every test.""" | 547 """Unit test overriden method. Is called before every test.""" |
| 503 AUTest.setUp(self) | 548 AUTest.setUp(self) |
| 504 self.vm_image_path = None | |
| 505 self._AcquireUniquePortAndPidFile() | 549 self._AcquireUniquePortAndPidFile() |
| 506 self._KillExistingVM(self._kvm_pid_file) | 550 self._KillExistingVM(self._kvm_pid_file) |
| 507 | 551 |
| 508 def tearDown(self): | 552 def tearDown(self): |
| 509 self._KillExistingVM(self._kvm_pid_file) | 553 self._KillExistingVM(self._kvm_pid_file) |
| 510 | 554 |
| 511 @classmethod | 555 @classmethod |
| 512 def ProcessOptions(cls, parser, options): | 556 def ProcessOptions(cls, parser, options): |
| 513 """Processes vm-specific options.""" | 557 """Processes vm-specific options.""" |
| 514 AUTest.ProcessOptions(parser, options) | 558 AUTest.ProcessOptions(parser, options) |
| 515 cls.board = options.board | |
| 516 | 559 |
| 517 # Communicate flags to tests. | 560 # Communicate flags to tests. |
| 518 cls.graphics_flag = '' | 561 cls.graphics_flag = '' |
| 519 if options.no_graphics: cls.graphics_flag = '--no_graphics' | 562 if options.no_graphics: cls.graphics_flag = '--no_graphics' |
| 520 | 563 if not cls.board: parser.error('Need board to convert base image to vm.') |
| 521 if not cls.board: | |
| 522 parser.error('Need board to convert base image to vm.') | |
| 523 | 564 |
| 524 def PrepareBase(self, image_path): | 565 def PrepareBase(self, image_path): |
| 525 """Creates an update-able VM based on base image.""" | 566 """Creates an update-able VM based on base image.""" |
| 526 # Needed for VM delta updates. We need to use the qemu image rather | 567 self._PrepareVMBase(image_path) |
| 527 # than the base image on a first update. By tracking the first_update | |
| 528 # we can set src_image to the qemu form of the base image when | |
| 529 # performing generating the delta payload. | |
| 530 self._first_update = True | |
| 531 self.vm_image_path = '%s/chromiumos_qemu_image.bin' % os.path.dirname( | |
| 532 image_path) | |
| 533 if not os.path.exists(self.vm_image_path): | |
| 534 Info('Creating %s' % vm_image_path) | |
| 535 RunCommand(['%s/image_to_vm.sh' % self.crosutils, | |
| 536 '--full', | |
| 537 '--from=%s' % ReinterpretPathForChroot( | |
| 538 os.path.dirname(image_path)), | |
| 539 '--vdisk_size=%s' % self._FULL_VDISK_SIZE, | |
| 540 '--statefulfs_size=%s' % self._FULL_STATEFULFS_SIZE, | |
| 541 '--board=%s' % self.board, | |
| 542 '--test_image'], enter_chroot=True) | |
| 543 | |
| 544 Info('Using %s as base' % self.vm_image_path) | |
| 545 self.assertTrue(os.path.exists(self.vm_image_path)) | |
| 546 | 568 |
| 547 def _UpdateImage(self, image_path, src_image_path='', stateful_change='old', | 569 def _UpdateImage(self, image_path, src_image_path='', stateful_change='old', |
| 548 proxy_port=''): | 570 proxy_port='', private_key_path=None): |
| 549 """Updates VM image with image_path.""" | 571 """Updates VM image with image_path.""" |
| 550 stateful_change_flag = self.GetStatefulChangeFlag(stateful_change) | 572 stateful_change_flag = self.GetStatefulChangeFlag(stateful_change) |
| 551 if src_image_path and self._first_update: | 573 if src_image_path and self._first_update: |
| 552 src_image_path = self.vm_image_path | 574 src_image_path = self.vm_image_path |
| 553 self._first_update = False | 575 self._first_update = False |
| 554 | 576 |
| 555 # Check image payload cache first. | 577 cmd = ['%s/cros_run_vm_update' % self.crosutilsbin, |
| 556 update_id = _GenerateUpdateId(target=image_path, src=src_image_path) | 578 '--vm_image_path=%s' % self.vm_image_path, |
| 557 cache_path = dev_server_cache[update_id] | 579 '--snapshot', |
| 558 if cache_path: | 580 self.graphics_flag, |
| 559 Info('Using cache %s' % cache_path) | 581 '--persist', |
| 560 update_url = DevServerWrapper.GetDevServerURL(proxy_port, cache_path) | 582 '--kvm_pid=%s' % self._kvm_pid_file, |
| 561 cmd = ['%s/cros_run_vm_update' % self.crosutilsbin, | 583 '--ssh_port=%s' % self._ssh_port, |
| 562 '--vm_image_path=%s' % self.vm_image_path, | 584 stateful_change_flag, |
| 563 '--snapshot', | 585 ] |
| 564 self.graphics_flag, | |
| 565 '--persist', | |
| 566 '--kvm_pid=%s' % self._kvm_pid_file, | |
| 567 '--ssh_port=%s' % self._ssh_port, | |
| 568 stateful_change_flag, | |
| 569 '--update_url=%s' % update_url, | |
| 570 ] | |
| 571 else: | |
| 572 cmd = ['%s/cros_run_vm_update' % self.crosutilsbin, | |
| 573 '--update_image_path=%s' % image_path, | |
| 574 '--vm_image_path=%s' % self.vm_image_path, | |
| 575 '--snapshot', | |
| 576 self.graphics_flag, | |
| 577 '--persist', | |
| 578 '--kvm_pid=%s' % self._kvm_pid_file, | |
| 579 '--ssh_port=%s' % self._ssh_port, | |
| 580 stateful_change_flag, | |
| 581 '--src_image=%s' % src_image_path, | |
| 582 '--proxy_port=%s' % proxy_port | |
| 583 ] | |
| 584 | 586 |
| 585 if self.verbose: | 587 self.AppendUpdateFlags(cmd, image_path, src_image_path, proxy_port, |
| 586 try: | 588 private_key_path) |
| 587 RunCommand(cmd) | 589 self.RunUpdateCmd(cmd) |
| 588 except Exception, e: | |
| 589 raise UpdateException(1, e.message) | |
| 590 else: | |
| 591 (code, stdout, stderr) = RunCommandCaptureOutput(cmd) | |
| 592 if code != 0: | |
| 593 raise UpdateException(code, stdout) | |
| 594 | 590 |
| 595 def _UpdateUsingPayload(self, update_path, stateful_change='old', | 591 def _UpdateUsingPayload(self, update_path, stateful_change='old', |
| 596 proxy_port=None): | 592 proxy_port=None): |
| 597 """Updates a vm image using cros_run_vm_update.""" | 593 """Updates a vm image using cros_run_vm_update.""" |
| 598 stateful_change_flag = self.GetStatefulChangeFlag(stateful_change) | 594 stateful_change_flag = self.GetStatefulChangeFlag(stateful_change) |
| 599 cmd = ['%s/cros_run_vm_update' % self.crosutilsbin, | 595 cmd = ['%s/cros_run_vm_update' % self.crosutilsbin, |
| 600 '--payload=%s' % update_path, | 596 '--payload=%s' % update_path, |
| 601 '--vm_image_path=%s' % self.vm_image_path, | 597 '--vm_image_path=%s' % self.vm_image_path, |
| 602 '--snapshot', | 598 '--snapshot', |
| 603 self.graphics_flag, | 599 self.graphics_flag, |
| 604 '--persist', | 600 '--persist', |
| 605 '--kvm_pid=%s' % self._kvm_pid_file, | 601 '--kvm_pid=%s' % self._kvm_pid_file, |
| 606 '--ssh_port=%s' % self._ssh_port, | 602 '--ssh_port=%s' % self._ssh_port, |
| 607 stateful_change_flag, | 603 stateful_change_flag, |
| 608 ] | 604 ] |
| 609 | 605 if proxy_port: cmd.append('--proxy_port=%s' % proxy_port) |
| 610 if proxy_port: | 606 self.RunUpdateCmd(cmd) |
| 611 cmd.append('--proxy_port=%s' % proxy_port) | |
| 612 | |
| 613 if self.verbose: | |
| 614 try: | |
| 615 RunCommand(cmd) | |
| 616 except Exception, e: | |
| 617 raise UpdateException(1, e.message) | |
| 618 else: | |
| 619 (code, stdout, stderr) = RunCommandCaptureOutput(cmd) | |
| 620 if code != 0: | |
| 621 raise UpdateException(code, stdout) | |
| 622 | 607 |
| 623 def VerifyImage(self, percent_required_to_pass): | 608 def VerifyImage(self, percent_required_to_pass): |
| 624 """Runs vm smoke suite to verify image.""" | 609 """Runs vm smoke suite to verify image.""" |
| 625 # image_to_live already verifies lsb-release matching. This is just | 610 # image_to_live already verifies lsb-release matching. This is just |
| 626 # for additional steps. | 611 # for additional steps. |
| 627 | 612 |
| 628 commandWithArgs = ['%s/cros_run_vm_test' % self.crosutilsbin, | 613 commandWithArgs = ['%s/cros_run_vm_test' % self.crosutilsbin, |
| 629 '--image_path=%s' % self.vm_image_path, | 614 '--image_path=%s' % self.vm_image_path, |
| 630 '--snapshot', | 615 '--snapshot', |
| 631 '--persist', | 616 '--persist', |
| 632 '--kvm_pid=%s' % self._kvm_pid_file, | 617 '--kvm_pid=%s' % self._kvm_pid_file, |
| 633 '--ssh_port=%s' % self._ssh_port, | 618 '--ssh_port=%s' % self._ssh_port, |
| 634 self.verify_suite, | 619 self.verify_suite, |
| 635 ] | 620 ] |
| 636 | 621 |
| 637 if self.graphics_flag: | 622 if self.graphics_flag: |
| 638 commandWithArgs.append(self.graphics_flag) | 623 commandWithArgs.append(self.graphics_flag) |
| 639 | 624 |
| 640 output = RunCommand(commandWithArgs, error_ok=True, enter_chroot=False, | 625 output = RunCommand(commandWithArgs, error_ok=True, enter_chroot=False, |
| 641 redirect_stdout=True) | 626 redirect_stdout=True) |
| 642 return self.AssertEnoughTestsPassed(self, output, percent_required_to_pass) | 627 return self.AssertEnoughTestsPassed(self, output, percent_required_to_pass) |
| 643 | 628 |
| 644 | 629 |
| 645 class GenerateVirtualAUDeltasTest(VirtualAUTest): | 630 class PregenerateAUDeltas(unittest.TestCase, AUTest): |
| 646 """Class the overrides VirtualAUTest and stores deltas we will generate.""" | 631 """Magical class that emulates an AUTest to store deltas we will generate. |
| 632 | |
| 633 This class emulates an AUTest such that when it runs as a TestCase it runs | |
| 634 through the exact up | |
| 635 """ | |
| 647 delta_list = {} | 636 delta_list = {} |
| 648 | 637 |
| 649 def setUp(self): | 638 def setUp(self): |
| 650 AUTest.setUp(self) | 639 AUTest.setUp(self) |
| 651 | 640 |
| 652 def tearDown(self): | 641 def tearDown(self): |
| 653 pass | 642 pass |
| 654 | 643 |
| 644 @classmethod | |
| 645 def ProcessOptions(cls, parser, options): | |
| 646 AUTest.ProcessOptions(parser, options) | |
| 647 cls.au_type = options.type | |
| 648 | |
| 649 def PrepareBase(self, image_path): | |
| 650 if self.au_type == 'vm': | |
| 651 self._PrepareVMBase(image_path) | |
| 652 else: | |
| 653 self._PrepareRealBase(image_path) | |
| 654 | |
| 655 def _UpdateImage(self, image_path, src_image_path='', stateful_change='old', | 655 def _UpdateImage(self, image_path, src_image_path='', stateful_change='old', |
| 656 proxy_port=None): | 656 proxy_port=None, private_key_path=None): |
| 657 if src_image_path and self._first_update: | 657 if self.au_type == 'vm' and src_image_path and self._first_update: |
| 658 src_image_path = self.vm_image_path | 658 src_image_path = self.vm_image_path |
| 659 self._first_update = False | 659 self._first_update = False |
| 660 | 660 |
| 661 # Generate a value that combines delta with private key path. | |
| 662 val = '%s+%s' % (src_image_path, private_key_path) | |
| 661 if not self.delta_list.has_key(image_path): | 663 if not self.delta_list.has_key(image_path): |
| 662 self.delta_list[image_path] = set([src_image_path]) | 664 self.delta_list[image_path] = set([val]) |
| 663 else: | 665 else: |
| 664 self.delta_list[image_path].add(src_image_path) | 666 self.delta_list[image_path].add(val) |
| 665 | 667 |
| 666 def AttemptUpdateWithPayloadExpectedFailure(self, payload, expected_msg): | 668 def AttemptUpdateWithPayloadExpectedFailure(self, payload, expected_msg): |
| 667 pass | 669 pass |
| 668 | 670 |
| 669 def VerifyImage(self, percent_required_to_pass): | 671 def VerifyImage(self, percent_required_to_pass): |
| 670 pass | 672 pass |
| 671 | 673 |
| 672 | 674 |
| 673 class ParallelJob(threading.Thread): | 675 class ParallelJob(threading.Thread): |
| 674 """Small wrapper for threading. Thread that releases a semaphores on exit.""" | 676 """Small wrapper for threading. Thread that releases a semaphores on exit.""" |
| (...skipping 74 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 749 def GetDevServerURL(cls, port, sub_dir): | 751 def GetDevServerURL(cls, port, sub_dir): |
| 750 """Returns the dev server url for a given port and sub directory.""" | 752 """Returns the dev server url for a given port and sub directory.""" |
| 751 ip_addr = GetIPAddress() | 753 ip_addr = GetIPAddress() |
| 752 if not port: port = 8080 | 754 if not port: port = 8080 |
| 753 url = 'http://%(ip)s:%(port)s/%(dir)s' % {'ip': ip_addr, | 755 url = 'http://%(ip)s:%(port)s/%(dir)s' % {'ip': ip_addr, |
| 754 'port': str(port), | 756 'port': str(port), |
| 755 'dir': sub_dir} | 757 'dir': sub_dir} |
| 756 return url | 758 return url |
| 757 | 759 |
| 758 | 760 |
| 759 def _GenerateUpdateId(target, src): | 761 def _GenerateUpdateId(target, src, key): |
| 760 """Returns a simple representation id of target and src paths.""" | 762 """Returns a simple representation id of target and src paths.""" |
| 761 if src: | 763 update_id = target |
| 762 return '%s->%s' % (target, src) | 764 if src: update_id = '->'.join([update_id, src]) |
| 763 else: | 765 if key: update_id = '+'.join([update_id, key]) |
| 764 return target | 766 return update_id |
| 765 | 767 |
| 766 | 768 |
| 767 def _RunParallelJobs(number_of_sumultaneous_jobs, jobs, jobs_args, print_status) : | 769 def _RunParallelJobs(number_of_sumultaneous_jobs, jobs, jobs_args, |
| 770 print_status): | |
| 768 | 771 |
| 769 """Runs set number of specified jobs in parallel. | 772 """Runs set number of specified jobs in parallel. |
| 770 | 773 |
| 771 Args: | 774 Args: |
| 772 number_of_simultaneous_jobs: Max number of threads to be run in parallel. | 775 number_of_simultaneous_jobs: Max number of threads to be run in parallel. |
| 773 jobs: Array of methods to run. | 776 jobs: Array of methods to run. |
| 774 jobs_args: Array of args associated with method calls. | 777 jobs_args: Array of args associated with method calls. |
| 775 print_status: True if you'd like this to print out .'s as it runs jobs. | 778 print_status: True if you'd like this to print out .'s as it runs jobs. |
| 776 Returns: | 779 Returns: |
| 777 Returns an array of results corresponding to each thread. | 780 Returns an array of results corresponding to each thread. |
| (...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 827 This method effectively pre-generates the dev server cache for all tests. | 830 This method effectively pre-generates the dev server cache for all tests. |
| 828 | 831 |
| 829 Args: | 832 Args: |
| 830 parser: parser from main. | 833 parser: parser from main. |
| 831 options: options from parsed parser. | 834 options: options from parsed parser. |
| 832 Returns: | 835 Returns: |
| 833 Dictionary of Update Identifiers->Relative cache locations. | 836 Dictionary of Update Identifiers->Relative cache locations. |
| 834 Raises: | 837 Raises: |
| 835 UpdateException if we fail to generate an update. | 838 UpdateException if we fail to generate an update. |
| 836 """ | 839 """ |
| 837 def _GenerateVMUpdate(target, src): | 840 def _GenerateVMUpdate(target, src, private_key_path): |
| 838 """Generates an update using the devserver.""" | 841 """Generates an update using the devserver.""" |
| 839 target = ReinterpretPathForChroot(target) | 842 command = ['./enter_chroot.sh', |
| 840 if src: | 843 '--nogit_config', |
| 841 src = ReinterpretPathForChroot(src) | 844 '--', |
| 845 'sudo', | |
| 846 './start_devserver', | |
| 847 '--pregenerate_update', | |
| 848 '--exit', | |
| 849 ] | |
| 850 # Add actual args to command. | |
| 851 command.append('--image=%s' % ReinterpretPathForChroot(target)) | |
| 852 if src: command.append('--src_image=%s' % ReinterpretPathForChroot(src)) | |
| 853 if options.type == 'vm': command.append('--for_vm') | |
| 854 if private_key_path: | |
| 855 command.append('--private_key=%s' % | |
| 856 ReinterpretPathForChroot(private_key_path)) | |
| 842 | 857 |
| 843 return RunCommandCaptureOutput(['./enter_chroot.sh', | 858 return RunCommandCaptureOutput(command, combine_stdout_stderr=True, |
| 844 '--nogit_config', | 859 print_cmd=True) |
| 845 '--', | |
| 846 'sudo', | |
| 847 './start_devserver', | |
| 848 '--pregenerate_update', | |
| 849 '--exit', | |
| 850 '--image=%s' % target, | |
| 851 '--src_image=%s' % src, | |
| 852 '--for_vm', | |
| 853 ], combine_stdout_stderr=True, | |
| 854 print_cmd=False) | |
| 855 | 860 |
| 856 # Get the list of deltas by mocking out update method in test class. | 861 # Get the list of deltas by mocking out update method in test class. |
| 857 test_suite = _PrepareTestSuite(parser, options, GenerateVirtualAUDeltasTest) | 862 test_suite = _PrepareTestSuite(parser, options, PregenerateAUDeltas) |
| 858 test_result = unittest.TextTestRunner(verbosity=0).run(test_suite) | 863 test_result = unittest.TextTestRunner(verbosity=0).run(test_suite) |
| 864 if not test_result.wasSuccessful(): | |
| 865 raise UpdateException(1, 'Error finding updates to generate.') | |
| 859 | 866 |
| 860 Info('The following delta updates are required.') | 867 Info('The following delta updates are required.') |
| 861 update_ids = [] | 868 update_ids = [] |
| 862 jobs = [] | 869 jobs = [] |
| 863 args = [] | 870 args = [] |
| 864 for target, srcs in GenerateVirtualAUDeltasTest.delta_list.items(): | 871 for target, srcs in PregenerateAUDeltas.delta_list.items(): |
| 865 for src in srcs: | 872 for src_key in srcs: |
| 866 update_id = _GenerateUpdateId(target=target, src=src) | 873 (src, key) = src_key.split('+') |
| 874 # TODO(sosa): Add private key as part of caching name once devserver can | |
| 875 # handle it its own cache. | |
| 876 update_id = _GenerateUpdateId(target=target, src=src, key=key) | |
| 867 print >> sys.stderr, 'AU: %s' % update_id | 877 print >> sys.stderr, 'AU: %s' % update_id |
| 868 update_ids.append(update_id) | 878 update_ids.append(update_id) |
| 869 jobs.append(_GenerateVMUpdate) | 879 jobs.append(_GenerateVMUpdate) |
| 870 args.append((target, src)) | 880 args.append((target, src, key)) |
| 871 | 881 |
| 872 raw_results = _RunParallelJobs(options.jobs, jobs, args, print_status=True) | 882 raw_results = _RunParallelJobs(options.jobs, jobs, args, print_status=True) |
| 873 results = [] | 883 results = [] |
| 874 | 884 |
| 875 # Looking for this line in the output. | 885 # Looking for this line in the output. |
| 876 key_line_re = re.compile('^PREGENERATED_UPDATE=([\w/.]+)') | 886 key_line_re = re.compile('^PREGENERATED_UPDATE=([\w/.]+)') |
| 877 for result in raw_results: | 887 for result in raw_results: |
| 878 (return_code, output, _) = result | 888 (return_code, output, _) = result |
| 879 if return_code != 0: | 889 if return_code != 0: |
| 880 Warning(output) | 890 Warning(output) |
| (...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 912 test_case = unittest.TestLoader().loadTestsFromName(test_name) | 922 test_case = unittest.TestLoader().loadTestsFromName(test_name) |
| 913 threads.append(unittest.TextTestRunner().run) | 923 threads.append(unittest.TextTestRunner().run) |
| 914 args.append(test_case) | 924 args.append(test_case) |
| 915 | 925 |
| 916 results = _RunParallelJobs(options.jobs, threads, args, print_status=False) | 926 results = _RunParallelJobs(options.jobs, threads, args, print_status=False) |
| 917 for test_result in results: | 927 for test_result in results: |
| 918 if not test_result.wasSuccessful(): | 928 if not test_result.wasSuccessful(): |
| 919 Die('Test harness was not successful') | 929 Die('Test harness was not successful') |
| 920 | 930 |
| 921 | 931 |
| 932 def InsertPublicKeyIntoImage(image_path, key_path): | |
| 933 """Inserts public key into image @ static update_engine location.""" | |
| 934 from_dir = os.path.dirname(image_path) | |
| 935 image = os.path.basename(image_path) | |
| 936 crosutils_dir = os.path.abspath(__file__).rsplit('/', 2)[0] | |
| 937 target_key_path = 'usr/share/update_engine/update-payload-key.pub.pem' | |
| 938 | |
| 939 # Temporary directories for this function. | |
| 940 rootfs_dir = tempfile.mkdtemp(suffix='rootfs', prefix='tmp') | |
| 941 stateful_dir = tempfile.mkdtemp(suffix='stateful', prefix='tmp') | |
| 942 | |
| 943 Info('Copying %s into %s' % (key_path, image_path)) | |
| 944 try: | |
| 945 RunCommand(['./mount_gpt_image.sh', | |
| 946 '--from=%s' % from_dir, | |
| 947 '--image=%s' % image, | |
| 948 '--rootfs_mountpt=%s' % rootfs_dir, | |
| 949 '--stateful_mountpt=%s' % stateful_dir, | |
| 950 ], print_cmd=False, redirect_stdout=True, | |
| 951 redirect_stderr=True, cwd=crosutils_dir) | |
| 952 path = os.path.join(rootfs_dir, target_key_path) | |
| 953 dir_path = os.path.dirname(path) | |
| 954 RunCommand(['sudo', 'mkdir', '--parents', dir_path], print_cmd=False) | |
| 955 RunCommand(['sudo', 'cp', '--force', '-p', key_path, path], | |
| 956 print_cmd=False) | |
| 957 finally: | |
| 958 # Unmount best effort regardless. | |
| 959 RunCommand(['./mount_gpt_image.sh', | |
| 960 '--unmount', | |
| 961 '--rootfs_mountpt=%s' % rootfs_dir, | |
| 962 '--stateful_mountpt=%s' % stateful_dir, | |
| 963 ], print_cmd=False, redirect_stdout=True, redirect_stderr=True, | |
| 964 cwd=crosutils_dir) | |
| 965 RunCommand(['bin/cros_make_image_bootable', from_dir, image, ], | |
|
dgarrett
2011/02/11 01:25:55
Should this be in the finally clause, or unindente
sosa
2011/02/11 21:20:02
Done.
| |
| 966 print_cmd=False, redirect_stdout=True, redirect_stderr=True, | |
| 967 enter_chroot=True, cwd=crosutils_dir) | |
| 968 # Clean up our directories. | |
| 969 os.rmdir(rootfs_dir) | |
| 970 os.rmdir(stateful_dir) | |
| 971 | |
| 972 | |
| 973 def CleanPreviousWork(options): | |
| 974 """Cleans up previous work from the devserver cache and local image cache.""" | |
| 975 Info('Cleaning up previous work.') | |
| 976 # Wipe devserver cache. | |
| 977 RunCommandCaptureOutput( | |
| 978 ['sudo', './start_devserver', '--clear_cache', '--exit', ], | |
| 979 enter_chroot=True, print_cmd=False, combine_stdout_stderr=True) | |
| 980 | |
| 981 # Clean previous vm images if they exist. | |
| 982 if options.type == 'vm': | |
| 983 target_vm_image_path = '%s/chromiumos_qemu_image.bin' % os.path.dirname( | |
| 984 options.target_image) | |
| 985 base_vm_image_path = '%s/chromiumos_qemu_image.bin' % os.path.dirname( | |
| 986 options.base_image) | |
| 987 if os.path.exists(target_vm_image_path): os.remove(target_vm_image_path) | |
| 988 if os.path.exists(base_vm_image_path): os.remove(base_vm_image_path) | |
| 989 | |
| 990 | |
| 922 def main(): | 991 def main(): |
| 923 parser = optparse.OptionParser() | 992 parser = optparse.OptionParser() |
| 924 parser.add_option('-b', '--base_image', | 993 parser.add_option('-b', '--base_image', |
| 925 help='path to the base image.') | 994 help='path to the base image.') |
| 926 parser.add_option('-r', '--board', | 995 parser.add_option('-r', '--board', |
| 927 help='board for the images.') | 996 help='board for the images.') |
| 997 parser.add_option('--clean', default=False, dest='clean', action='store_true', | |
| 998 help='Clean all previous state') | |
| 928 parser.add_option('--no_delta', action='store_false', default=True, | 999 parser.add_option('--no_delta', action='store_false', default=True, |
| 929 dest='delta', | 1000 dest='delta', |
| 930 help='Disable using delta updates.') | 1001 help='Disable using delta updates.') |
| 931 parser.add_option('--no_graphics', action='store_true', | 1002 parser.add_option('--no_graphics', action='store_true', |
| 932 help='Disable graphics for the vm test.') | 1003 help='Disable graphics for the vm test.') |
| 933 parser.add_option('-j', '--jobs', default=8, type=int, | 1004 parser.add_option('-j', '--jobs', default=8, type=int, |
| 934 help='Number of simultaneous jobs') | 1005 help='Number of simultaneous jobs') |
| 1006 parser.add_option('--public_key', default=None, | |
| 1007 help='Public key to use on images and updates.') | |
| 1008 parser.add_option('--private_key', default=None, | |
| 1009 help='Private key to use on images and updates.') | |
| 935 parser.add_option('-q', '--quick_test', default=False, action='store_true', | 1010 parser.add_option('-q', '--quick_test', default=False, action='store_true', |
| 936 help='Use a basic test to verify image.') | 1011 help='Use a basic test to verify image.') |
| 937 parser.add_option('-m', '--remote', | 1012 parser.add_option('-m', '--remote', |
| 938 help='Remote address for real test.') | 1013 help='Remote address for real test.') |
| 939 parser.add_option('-t', '--target_image', | 1014 parser.add_option('-t', '--target_image', |
| 940 help='path to the target image.') | 1015 help='path to the target image.') |
| 941 parser.add_option('--test_prefix', default='test', | 1016 parser.add_option('--test_prefix', default='test', |
| 942 help='Only runs tests with specific prefix i.e. ' | 1017 help='Only runs tests with specific prefix i.e. ' |
| 943 'testFullUpdateWipeStateful.') | 1018 'testFullUpdateWipeStateful.') |
| 944 parser.add_option('-p', '--type', default='vm', | 1019 parser.add_option('-p', '--type', default='vm', |
| 945 help='type of test to run: [vm, real]. Default: vm.') | 1020 help='type of test to run: [vm, real]. Default: vm.') |
| 946 parser.add_option('--verbose', default=True, action='store_true', | 1021 parser.add_option('--verbose', default=True, action='store_true', |
| 947 help='Print out rather than capture output as much as ' | 1022 help='Print out rather than capture output as much as ' |
| 948 'possible.') | 1023 'possible.') |
| 949 (options, leftover_args) = parser.parse_args() | 1024 (options, leftover_args) = parser.parse_args() |
| 950 | 1025 |
| 951 if leftover_args: | 1026 if leftover_args: parser.error('Found unsupported flags: %s' % leftover_args) |
| 952 parser.error('Found extra options we do not support: %s' % leftover_args) | 1027 |
| 1028 assert options.target_image and os.path.exists(options.target_image), \ | |
| 1029 'Target image path does not exist' | |
| 1030 if not options.base_image: | |
| 1031 Info('Base image not specified. Using target image as base image.') | |
| 1032 options.base_image = options.target_image | |
| 1033 | |
| 1034 # Sanity checks on keys and insert them onto the image. The caches must be | |
| 1035 # cleaned so we know that the vm images and payloads match the possibly new | |
| 1036 # key. | |
| 1037 if options.private_key or options.public_key: | |
|
dgarrett
2011/02/11 01:25:55
If either is specified, you verify that both exist
sosa
2011/02/11 21:20:02
Done.
| |
| 1038 assert os.path.exists(options.private_key), 'Could not find private key.' | |
| 1039 assert os.path.exists(options.public_key), 'Could not find public key.' | |
| 1040 InsertPublicKeyIntoImage(options.target_image, options.public_key) | |
| 1041 InsertPublicKeyIntoImage(options.base_image, options.public_key) | |
| 1042 options.clean = True | |
| 1043 | |
| 1044 # Clean up previous work if were requested to. | |
|
dgarrett
2011/02/11 01:25:55
s/were/we're/
sosa
2011/02/11 21:20:02
Done.
| |
| 1045 if options.clean: CleanPreviousWork(options) | |
| 953 | 1046 |
| 954 # Figure out the test_class. | 1047 # Figure out the test_class. |
| 955 if options.type == 'vm': test_class = VirtualAUTest | 1048 if options.type == 'vm': test_class = VirtualAUTest |
| 956 elif options.type == 'real': test_class = RealAUTest | 1049 elif options.type == 'real': test_class = RealAUTest |
| 957 else: parser.error('Could not parse harness type %s.' % options.type) | 1050 else: parser.error('Could not parse harness type %s.' % options.type) |
| 958 | 1051 |
| 959 # TODO(sosa): Caching doesn't really make sense on non-vm images (yet). | 1052 # Generate cache of updates to use during test harness. |
| 960 global dev_server_cache | 1053 global dev_server_cache |
| 961 if options.type == 'vm' and options.jobs > 1: | 1054 dev_server_cache = _PregenerateUpdates(parser, options) |
| 962 dev_server_cache = _PregenerateUpdates(parser, options) | 1055 my_server = DevServerWrapper() |
| 963 my_server = DevServerWrapper() | 1056 my_server.start() |
| 964 my_server.start() | 1057 try: |
| 965 try: | 1058 if options.type == 'vm': |
| 966 _RunTestsInParallel(parser, options, test_class) | 1059 _RunTestsInParallel(parser, options, test_class) |
| 967 finally: | 1060 else: |
| 968 my_server.Stop() | 1061 # TODO(sosa) - Take in a machine pool for a real test. |
| 969 | 1062 # Can't run in parallel with only one remote device. |
| 970 else: | 1063 test_suite = _PrepareTestSuite(parser, options, test_class) |
| 971 dev_server_cache = None | 1064 test_result = unittest.TextTestRunner(verbosity=2).run(test_suite) |
| 972 test_suite = _PrepareTestSuite(parser, options, test_class) | 1065 if not test_result.wasSuccessful(): Die('Test harness failed.') |
| 973 test_result = unittest.TextTestRunner(verbosity=2).run(test_suite) | 1066 finally: |
| 974 if not test_result.wasSuccessful(): | 1067 my_server.Stop() |
| 975 Die('Test harness was not successful.') | |
| 976 | 1068 |
| 977 | 1069 |
| 978 if __name__ == '__main__': | 1070 if __name__ == '__main__': |
| 979 main() | 1071 main() |
| OLD | NEW |