| # Copyright (c) 2011 The Chromium OS Authors. All rights reserved. |
| # Use of this source code is governed by a BSD-style license that can be |
| # found in the LICENSE file. |
| |
| """ |
| Factory install VM tests. |
| |
| This test supports the flags documented in FactoryInstallTest, plus: |
| |
| debug_vnc: whether to run VNC on the KVM (for debugging only) |
| debug_save_hda: if specified, path to save the hda.bin to after |
| running the factory install shim (for debugging only) |
| debug_reuse_hda: if specified, path to an existing hda.bin image |
| to reuse (for debugging only) |
| """ |
| |
| |
| import os, re |
| |
| from autotest_lib.client.bin import utils as client_utils |
| from autotest_lib.server.cros.factory_install_test import FactoryInstallTest |
| from autotest_lib.server.hosts import ssh_host |
| |
| |
| # How long to wait after killing KVMs. |
| _KILL_KVM_WAIT_SEC = 5 |
| |
| # The size of the "SSD" in the KVM. |
| _HDA_SIZE_MB = 8192 |
| |
| # The name of the image used for hda. This should be unique to this test, |
| # since we will kill all stray KVM processes using this disk image. |
| _HDA_FILENAME = "factory_InstallVM_hda.bin" |
| |
| |
| class factory_InstallVM(FactoryInstallTest): |
| """ |
| Factory install VM tests. |
| |
| See file-level docstring for more information. |
| """ |
| |
| def _get_kvm_command(self, kvm_args=[]): |
| """ |
| Returns the command to run KVM. |
| |
| @param kvm_args: A list of extra args to pass to KVM. |
| """ |
| kvm_base_args = [ |
| "kvm", |
| "-m", "2048", |
| "-net", "nic,model=virtio", |
| "-net", "user,hostfwd=tcp::%d-:22" % self.ssh_tunnel_port, |
| "-vga", "vmware", # Because -vga std is slow |
| ] |
| |
| if self.vnc: |
| # Without nographic, we need to explicitly add "-serial stdio" |
| # (or output will go to vc). Use 127.0.0.1 to ensure that kvm |
| # listens with IPv4. |
| kvm_base_args.extend(["-serial", "stdio", "-vnc", "127.0.0.1:1"]) |
| else: |
| kvm_base_args.append("-nographic") |
| |
| return " ".join(kvm_base_args + kvm_args) |
| |
| def _kill_kvm(self): |
| """ |
| Kills the KVM on the client machine. |
| |
| This will kill any KVM whose command line contains _HDA_FILENAME |
| (which is specific to this test). |
| """ |
| def try_kill_kvm(): |
| pattern = "^kvm.*%s" % _HDA_FILENAME, |
| if (self.client.run("pgrep -f '%s'" % pattern, ignore_status=True) |
| .exit_status == 1): |
| return True |
| self.client.run("pkill -f '%s'" % (pattern)) |
| return False |
| |
| client_utils.poll_for_condition( |
| try_kill_kvm, timeout=_KILL_KVM_WAIT_SEC, desc="Kill KVM") |
| |
| def get_hwid_cfg(self): |
| """ |
| Overridden from superclass. |
| """ |
| return "vm" |
| |
| def get_dut_client(self): |
| """ |
| Overridden from superclass. |
| """ |
| return ssh_host.SSHHost("localhost", port=self.ssh_tunnel_port) |
| |
| def run_factory_install(self, local_hdb): |
| """ |
| Overridden from superclass. |
| """ |
| self.hda = os.path.join(self.client.get_tmp_dir(), _HDA_FILENAME) |
| |
| if self.reuse_hda is not None: |
| self.client.run("cp %s %s" % (self.reuse_hda, self.hda)) |
| else: |
| # Mount partition 12 the image and modify it to enable serial |
| # logging. |
| mount = self._mount_partition(local_hdb, 12) |
| self._modify_file( |
| os.path.join(mount, "syslinux/usb.A.cfg"), |
| lambda contents: re.sub(r"console=\w+", "console=ttyS0", |
| contents)) |
| self._umount_partition(mount) |
| |
| # On the client, create a nice big sparse file for hda |
| # (a.k.a. the SSD). |
| self.client.run("truncate -s %dM %s" % (_HDA_SIZE_MB, self.hda)) |
| hdb = os.path.join(self.client.get_tmp_dir(), "hdb.bin") |
| self.client.send_file(local_hdb, hdb) |
| |
| # Fire up the KVM and wait for the factory install to complete. |
| self._kill_kvm() # Just in case |
| self.client.run_grep( |
| self._get_kvm_command( |
| ["-drive", "file=%s,boot=off" % self.hda, |
| "-drive", "file=%s,boot=on" % hdb, |
| "-no-reboot"]), |
| timeout=FactoryInstallTest.FACTORY_INSTALL_TIMEOUT_SEC, |
| stdout_ok_regexp="Factory Installer Complete") |
| self._kill_kvm() |
| |
| if self.save_hda is not None: |
| self.client.run("cp %s %s" % (self.hda, self.save_hda)) |
| |
| # Run KVM again (the factory tests should now come up). |
| kvm = self.client.run(self._get_kvm_command([ |
| "-hda", self.hda, |
| "-daemonize", |
| "-no-reboot"])) |
| |
| def reboot_for_wipe(self): |
| """ |
| Overridden from superclass. |
| """ |
| # Use halt instead of reboot; reboot doesn't work consistently in KVM. |
| self.get_dut_client().halt() |
| self._kill_kvm() |
| |
| # Start KVM again. The ChromeOS test image should now come up. |
| kvm = self.client.run( |
| self._get_kvm_command(["-hda", self.hda, "-daemonize"])) |
| |
| def run_once(self, host, debug_reuse_hda=None, debug_save_hda=None, |
| debug_vnc=False, **args): |
| self.client = host |
| self.reuse_hda = debug_reuse_hda |
| self.save_hda = debug_save_hda |
| self.vnc = self.parse_boolean(debug_vnc) |
| |
| self.cleanup_tasks.append(self._kill_kvm) |
| |
| super(factory_InstallVM, self).run_once(**args) |