cros_vm: Support for --disable-kvm

* --disable-kvm allows for testing with qemu without kvm.
By default, use -enable-kvm.
* _GetVMPid no longer throws, returns 0 for all failures.
* Add default for boolean args
* Replace kvm_path with qemu_path

BUG=chromium:591624
TEST=manual

Change-Id: I9acf7e75aac51909b89602e9122224824bff1a86
Reviewed-on: https://chromium-review.googlesource.com/397698
Commit-Ready: Achuith Bhandarkar <achuith@chromium.org>
Tested-by: Achuith Bhandarkar <achuith@chromium.org>
Reviewed-by: Mike Frysinger <vapier@chromium.org>
diff --git a/scripts/cros_vm.py b/scripts/cros_vm.py
index 2f1bc4a..8178ea8 100644
--- a/scripts/cros_vm.py
+++ b/scripts/cros_vm.py
@@ -12,6 +12,7 @@
 from chromite.lib import commandline
 from chromite.lib import cros_build_lib
 from chromite.lib import cros_logging as logging
+from chromite.lib import osutils
 from chromite.lib import remote_access
 
 
@@ -30,18 +31,20 @@
   VM_DIR = '/var/run/cros_vm'
 
 
-  def __init__(self, kvm_path=None, image_path=None, ssh_port=SSH_PORT,
-               dry_run=False):
+  def __init__(self, image_path=None, qemu_path=None, enable_kvm=True,
+               ssh_port=SSH_PORT, dry_run=False):
     """Initialize VM.
 
     Args:
-      kvm_path: path to kvm binary.
       image_path: path of vm image.
+      qemu_path: path to qemu binary.
+      enable_kvm: enable kvm (kernel support for virtualization).
       ssh_port: ssh port to use.
       dry_run: disable VM commands.
     """
 
-    self.kvm_path = kvm_path
+    self.qemu_path = qemu_path
+    self.enable_kvm = enable_kvm
     self.image_path = image_path
     self.ssh_port = ssh_port
     self.dry_run = dry_run
@@ -67,21 +70,6 @@
       cros_build_lib.SudoRunCommand(['mkdir', VM.VM_DIR])
       cros_build_lib.SudoRunCommand(['chmod', '777', VM.VM_DIR])
 
-  @staticmethod
-  def _FindKVMBinary():
-    """Returns path to KVM binary.
-
-    Returns:
-      KVM binary path.
-    """
-
-    for exe in ['kvm', 'qemu-kvm', 'qemu-system-x86_64']:
-      try:
-        return cros_build_lib.RunCommand(['which', exe],
-                                         redirect_stdout=True).output.rstrip()
-      except cros_build_lib.RunCommandError:
-        raise VMError('KVM path not found.')
-
   def PerformAction(self, start=False, stop=False, cmd=None):
     """Performs an action, one of start, stop, or run a command in the VM.
 
@@ -109,9 +97,11 @@
     self.Stop()
 
     logging.debug('Start VM')
-    if not self.kvm_path:
-      self.kvm_path = self._FindKVMBinary()
-    logging.debug('kvm path=%s', self.kvm_path)
+    if not self.qemu_path:
+      self.qemu_path = osutils.Which('qemu-system-x86_64')
+    if not self.qemu_path:
+      raise VMError('qemu not found.')
+    logging.debug('qemu path=%s', self.qemu_path)
 
     if not self.image_path:
       self.image_path = os.environ.get('VM_IMAGE_PATH', '')
@@ -124,15 +114,18 @@
     for pipe in [self.kvm_pipe_in, self.kvm_pipe_out]:
       os.mkfifo(pipe, 0600)
 
-    args = [self.kvm_path, '-m', '2G', '-smp', '4', '-vga', 'cirrus',
+    args = [self.qemu_path, '-m', '2G', '-smp', '4', '-vga', 'cirrus',
+            '-daemonize',
             '-pidfile', self.pidfile,
             '-chardev', 'pipe,id=control_pipe,path=%s' % self.kvm_monitor,
             '-serial', 'file:%s' % self.kvm_serial,
-            '-mon', 'chardev=control_pipe', '-daemonize',
+            '-mon', 'chardev=control_pipe',
             '-net', 'nic,model=virtio',
             '-net', 'user,hostfwd=tcp::%d-:22' % self.ssh_port,
             '-drive', 'file=%s,index=0,media=disk,cache=unsafe'
             % self.image_path]
+    if self.enable_kvm:
+      args.append('-enable-kvm')
     logging.info(' '.join(args))
     logging.info('Pid file: %s', self.pidfile)
     if not self.dry_run:
@@ -145,16 +138,19 @@
       pid of the VM.
     """
     if not os.path.exists(self.VM_DIR):
-      logging.info('No VM running.')
+      logging.debug('%s not present.', self.VM_DIR)
       return 0
 
     if not os.path.exists(self.pidfile):
-      raise VMError('%s does not exist.' % self.pidfile)
+      logging.info('%s does not exist.', self.pidfile)
+      return 0
 
     pid = cros_build_lib.SudoRunCommand(['cat', self.pidfile],
                                         redirect_stdout=True).output.rstrip()
     if not pid.isdigit():
-      raise VMError('%s in %s is not a pid.' % (pid, self.pidfile))
+      logging.error('%s in %s is not a pid.', pid, self.pidfile)
+      return 0
+
     return int(pid)
 
   def IsRunning(self):
@@ -163,11 +159,7 @@
     Returns:
       True if there's a running VM.
     """
-    try:
-      pid = self._GetVMPid()
-    except VMError:
-      return False
-
+    pid = self._GetVMPid()
     if not pid:
       return False
 
@@ -181,16 +173,15 @@
     logging.debug('Stop VM')
 
     pid = self._GetVMPid()
-    if not pid:
-      return
+    if pid:
+      logging.info('Killing %d.', pid)
+      if not self.dry_run:
+        cros_build_lib.SudoRunCommand(['kill', '-9', str(pid)],
+                                      error_code_ok=True)
 
-    logging.info('Killing %d.', pid)
-    if not self.dry_run:
-      cros_build_lib.SudoRunCommand(['kill', '-9', str(pid)],
-                                    error_code_ok=True)
     self._CleanupFiles(recreate=False)
 
-  def WaitForBoot(self, timeout=10, poll_interval=0.1):
+  def WaitForBoot(self, timeout=120, poll_interval=0.1):
     """Wait for the VM to boot up.
 
     If there is no VM running, start one.
@@ -246,24 +237,27 @@
     List of parsed args.
   """
   parser = commandline.ArgumentParser(description=__doc__)
-  parser.add_argument('--start', action='store_true',
+  parser.add_argument('--start', action='store_true', default=False,
                       help='Start the VM.')
-  parser.add_argument('--stop', action='store_true',
+  parser.add_argument('--stop', action='store_true', default=False,
                       help='Stop the VM.')
   parser.add_argument('--cmd', help='Run this command in the VM.')
   parser.add_argument('--image-path', type='path',
                       help='Path to VM image to launch with --start.')
-  parser.add_argument('--kvm-path', type='path',
-                      help='Path of kvm binary to launch with --start.')
+  parser.add_argument('--qemu-path', type='path',
+                      help='Path of qemu binary to launch with --start.')
+  parser.add_argument('--disable-kvm', action='store_true', default=False,
+                      help='Disable KVM, use software emulation.')
   parser.add_argument('--ssh-port', type=int, default=VM.SSH_PORT,
                       help='ssh port to communicate with VM.')
-  parser.add_argument('--dry-run', action='store_true',
+  parser.add_argument('--dry-run', action='store_true', default=False,
                       help='dry run for debugging.')
   return parser.parse_args(argv)
 
 
 def main(argv):
   args = ParseCommandLine(argv)
-  vm = VM(kvm_path=args.kvm_path, image_path=args.image_path,
+  vm = VM(image_path=args.image_path,
+          qemu_path=args.qemu_path, enable_kvm=not args.disable_kvm,
           ssh_port=args.ssh_port, dry_run=args.dry_run)
   vm.PerformAction(start=args.start, stop=args.stop, cmd=args.cmd)