1 # Copyright (c) 2011 The Chromium OS Authors. All rights reserved. 2 # Use of this source code is governed by a BSD-style license that can be 3 # found in the LICENSE file. 4 5 """ 6 Factory install VM tests. 7 8 This test supports the flags documented in FactoryInstallTest, plus: 9 10 debug_vnc: whether to run VNC on the KVM (for debugging only) 11 debug_save_hda: if specified, path to save the hda.bin to after 12 running the factory install shim (for debugging only) 13 debug_reuse_hda: if specified, path to an existing hda.bin image 14 to reuse (for debugging only) 15 """ 16 17 18 import os, re 19 20 from autotest_lib.client.bin import utils as client_utils 21 from autotest_lib.server.cros.factory_install_test import FactoryInstallTest 22 from autotest_lib.server.hosts import ssh_host 23 24 25 # How long to wait after killing KVMs. 26 _KILL_KVM_WAIT_SEC = 5 27 28 # The size of the "SSD" in the KVM. 29 _HDA_SIZE_MB = 8192 30 31 # The name of the image used for hda. This should be unique to this test, 32 # since we will kill all stray KVM processes using this disk image. 33 _HDA_FILENAME = "factory_InstallVM_hda.bin" 34 35 36 class factory_InstallVM(FactoryInstallTest): 37 """ 38 Factory install VM tests. 39 40 See file-level docstring for more information. 41 """ 42 43 def _get_kvm_command(self, kvm_args=[]): 44 """ 45 Returns the command to run KVM. 46 47 @param kvm_args: A list of extra args to pass to KVM. 48 """ 49 kvm_base_args = [ 50 "kvm", 51 "-m", "2048", 52 "-net", "nic,model=virtio", 53 "-net", "user,hostfwd=tcp::%d-:22" % self.ssh_tunnel_port, 54 "-vga", "vmware", # Because -vga std is slow 55 ] 56 57 if self.vnc: 58 # Without nographic, we need to explicitly add "-serial stdio" 59 # (or output will go to vc). Use 127.0.0.1 to ensure that kvm 60 # listens with IPv4. 61 kvm_base_args.extend(["-serial", "stdio", "-vnc", "127.0.0.1:1"]) 62 else: 63 kvm_base_args.append("-nographic") 64 65 return " ".join(kvm_base_args + kvm_args) 66 67 def _kill_kvm(self): 68 """ 69 Kills the KVM on the client machine. 70 71 This will kill any KVM whose command line contains _HDA_FILENAME 72 (which is specific to this test). 73 """ 74 def try_kill_kvm(): 75 pattern = "^kvm.*%s" % _HDA_FILENAME, 76 if (self.client.run("pgrep -f '%s'" % pattern, ignore_status=True) 77 .exit_status == 1): 78 return True 79 self.client.run("pkill -f '%s'" % (pattern)) 80 return False 81 82 client_utils.poll_for_condition( 83 try_kill_kvm, timeout=_KILL_KVM_WAIT_SEC, desc="Kill KVM") 84 85 def get_hwid_cfg(self): 86 """ 87 Overridden from superclass. 88 """ 89 return "vm" 90 91 def get_dut_client(self): 92 """ 93 Overridden from superclass. 94 """ 95 return ssh_host.SSHHost("localhost", port=self.ssh_tunnel_port) 96 97 def run_factory_install(self, local_hdb): 98 """ 99 Overridden from superclass. 100 """ 101 self.hda = os.path.join(self.client.get_tmp_dir(), _HDA_FILENAME) 102 103 if self.reuse_hda is not None: 104 self.client.run("cp %s %s" % (self.reuse_hda, self.hda)) 105 else: 106 # Mount partition 12 the image and modify it to enable serial 107 # logging. 108 mount = self._mount_partition(local_hdb, 12) 109 self._modify_file( 110 os.path.join(mount, "syslinux/usb.A.cfg"), 111 lambda contents: re.sub(r"console=\w+", "console=ttyS0", 112 contents)) 113 self._umount_partition(mount) 114 115 # On the client, create a nice big sparse file for hda 116 # (a.k.a. the SSD). 117 self.client.run("truncate -s %dM %s" % (_HDA_SIZE_MB, self.hda)) 118 hdb = os.path.join(self.client.get_tmp_dir(), "hdb.bin") 119 self.client.send_file(local_hdb, hdb) 120 121 # Fire up the KVM and wait for the factory install to complete. 122 self._kill_kvm() # Just in case 123 self.client.run_grep( 124 self._get_kvm_command( 125 ["-drive", "file=%s,boot=off" % self.hda, 126 "-drive", "file=%s,boot=on" % hdb, 127 "-no-reboot"]), 128 timeout=FactoryInstallTest.FACTORY_INSTALL_TIMEOUT_SEC, 129 stdout_ok_regexp="Factory Installer Complete") 130 self._kill_kvm() 131 132 if self.save_hda is not None: 133 self.client.run("cp %s %s" % (self.hda, self.save_hda)) 134 135 # Run KVM again (the factory tests should now come up). 136 kvm = self.client.run(self._get_kvm_command([ 137 "-hda", self.hda, 138 "-daemonize", 139 "-no-reboot"])) 140 141 def reboot_for_wipe(self): 142 """ 143 Overridden from superclass. 144 """ 145 # Use halt instead of reboot; reboot doesn't work consistently in KVM. 146 self.get_dut_client().halt() 147 self._kill_kvm() 148 149 # Start KVM again. The ChromeOS test image should now come up. 150 kvm = self.client.run( 151 self._get_kvm_command(["-hda", self.hda, "-daemonize"])) 152 153 def run_once(self, host, debug_reuse_hda=None, debug_save_hda=None, 154 debug_vnc=False, **args): 155 self.client = host 156 self.reuse_hda = debug_reuse_hda 157 self.save_hda = debug_save_hda 158 self.vnc = self.parse_boolean(debug_vnc) 159 160 self.cleanup_tasks.append(self._kill_kvm) 161 162 super(factory_InstallVM, self).run_once(**args) 163