[cros_vm]: --output in cros_run_vm_test

* --output to write output to a file from cros_run_vm_test
* cros_vm --cmd implemention consistent with other cros scripts
* Function ProcessResult() to deal with return code.
* Pass through args from cros_run_vm_test to cros_vm.
* Don't wait for chrome with KVM enabled.

BUG=chromium:775485
TEST=manual

Change-Id: I691dd40e6427b0a1e230844b554b9399305c2efb
Reviewed-on: https://chromium-review.googlesource.com/743364
Commit-Ready: Achuith Bhandarkar <achuith@chromium.org>
Tested-by: Achuith Bhandarkar <achuith@chromium.org>
Reviewed-by: Steven Bennetts <stevenjb@chromium.org>
diff --git a/scripts/cros_vm.py b/scripts/cros_vm.py
index 60a9bcf..7c4e9b1 100644
--- a/scripts/cros_vm.py
+++ b/scripts/cros_vm.py
@@ -7,6 +7,7 @@
 
 from __future__ import print_function
 
+import argparse
 import os
 
 from chromite.lib import commandline
@@ -30,28 +31,28 @@
 
   SSH_PORT = 9222
 
-  def __init__(self, image_path=None, qemu_path=None, enable_kvm=True,
-               display=True, ssh_port=SSH_PORT, dry_run=False):
+  def __init__(self, argv):
     """Initialize VM.
 
     Args:
-      image_path: path of vm image.
-      qemu_path: path to qemu binary.
-      enable_kvm: enable kvm (kernel support for virtualization).
-      display: display video output.
-      ssh_port: ssh port to use.
-      dry_run: disable VM commands.
+      argv: command line args.
     """
+    opts = self._ParseArgs(argv)
+    opts.Freeze()
 
-    self.qemu_path = qemu_path
-    self.enable_kvm = enable_kvm
+    self.qemu_path = opts.qemu_path
+    self.enable_kvm = opts.enable_kvm
     # We don't need sudo access for software emulation or if /dev/kvm is
     # writeable.
-    self.use_sudo = enable_kvm and not os.access('/dev/kvm', os.W_OK)
-    self.display = display
-    self.image_path = image_path
-    self.ssh_port = ssh_port
-    self.dry_run = dry_run
+    self.use_sudo = self.enable_kvm and not os.access('/dev/kvm', os.W_OK)
+    self.display = opts.display
+    self.image_path = opts.image_path
+    self.ssh_port = opts.ssh_port
+    self.dry_run = opts.dry_run
+
+    self.start = opts.start
+    self.stop = opts.stop
+    self.cmd = opts.args[1:] if opts.cmd else None
 
     self.vm_dir = os.path.join(osutils.GetGlobalTempDir(), 'cros_vm')
     if os.path.exists(self.vm_dir):
@@ -70,12 +71,11 @@
     self.kvm_serial = '%s.serial' % self.kvm_monitor
 
     self.remote = remote_access.RemoteDevice(remote_access.LOCALHOST,
-                                             port=ssh_port)
+                                             port=self.ssh_port)
 
     # TODO(achuith): support nographics, snapshot, mem_path, usb_passthrough,
     # moblab, etc.
 
-
   def _RunCommand(self, *args, **kwargs):
     """Use SudoRunCommand or RunCommand as necessary."""
     if self.use_sudo:
@@ -93,26 +93,21 @@
     if recreate:
       osutils.SafeMakedirs(self.vm_dir)
 
-  def PerformAction(self, start=False, stop=False, cmd=None):
+  def Run(self):
     """Performs an action, one of start, stop, or run a command in the VM.
 
-    Args:
-      start: start the VM.
-      stop: stop the VM.
-      cmd: list or scalar command to run in the VM.
-
     Returns:
       cmd output.
     """
 
-    if not start and not stop and not cmd:
+    if not self.start and not self.stop and not self.cmd:
       raise VMError('Must specify one of start, stop, or cmd.')
-    if start:
+    if self.start:
       self.Start()
-    if stop:
+    if self.cmd:
+      return self.RemoteCommand(self.cmd)
+    if self.stop:
       self.Stop()
-    if cmd:
-      return self.RemoteCommand(cmd.split())
 
   def Start(self):
     """Start the VM."""
@@ -254,58 +249,59 @@
     if result.returncode != 0:
       raise VMError('WaitForBoot failed: %s.' % result.error)
 
-    self._WaitForProcs()
+    # Chrome can take a while to start with software emulation.
+    if not self.enable_kvm:
+      self._WaitForProcs()
 
-  def RemoteCommand(self, cmd):
+  def RemoteCommand(self, cmd, **kwargs):
     """Run a remote command in the VM.
 
     Args:
-      cmd: command to run, of list type.
+      cmd: command to run.
+      kwargs: additional args (see documentation for RemoteDevice.RunCommand).
     """
-    if not isinstance(cmd, list):
-      raise VMError('cmd must be a list.')
-
     if not self.dry_run:
       return self.remote.RunCommand(cmd, debug_level=logging.INFO,
                                     combine_stdout_stderr=True,
                                     log_output=True,
-                                    error_code_ok=True)
+                                    error_code_ok=True,
+                                    **kwargs)
 
-def ParseCommandLine(argv):
-  """Parse the command line.
+  @staticmethod
+  def _ParseArgs(argv):
+    """Parse a list of args.
 
-  Args:
-    argv: Command arguments.
+    Args:
+      argv: list of command line arguments.
 
-  Returns:
-    List of parsed args.
-  """
-  parser = commandline.ArgumentParser(description=__doc__)
-  parser.add_argument('--start', action='store_true', default=False,
-                      help='Start the VM.')
-  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('--qemu-path', type='path',
-                      help='Path of qemu binary to launch with --start.')
-  parser.add_argument('--disable-kvm', dest='enable_kvm',
-                      action='store_false', default=True,
-                      help='Disable KVM, use software emulation.')
-  parser.add_argument('--no-display', dest='display',
-                      action='store_false', default=True,
-                      help='Do not display video output.')
-  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', default=False,
-                      help='dry run for debugging.')
-  return parser.parse_args(argv)
-
+    Returns:
+      List of parsed opts.
+    """
+    parser = commandline.ArgumentParser(description=__doc__)
+    parser.add_argument('--start', action='store_true', default=False,
+                        help='Start the VM.')
+    parser.add_argument('--stop', action='store_true', default=False,
+                        help='Stop the VM.')
+    parser.add_argument('--image-path', type='path',
+                        help='Path to VM image 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', dest='enable_kvm',
+                        action='store_false', default=True,
+                        help='Disable KVM, use software emulation.')
+    parser.add_argument('--no-display', dest='display',
+                        action='store_false', default=True,
+                        help='Do not display video output.')
+    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', default=False,
+                        help='dry run for debugging.')
+    parser.add_argument('--cmd', action='store_true', default=False,
+                        help='Run a command in the VM.')
+    parser.add_argument('args', nargs=argparse.REMAINDER,
+                        help='Command to run in the VM.')
+    return parser.parse_args(argv)
 
 def main(argv):
-  args = ParseCommandLine(argv)
-  vm = VM(image_path=args.image_path, qemu_path=args.qemu_path,
-          enable_kvm=args.enable_kvm, display=args.display,
-          ssh_port=args.ssh_port, dry_run=args.dry_run)
-  vm.PerformAction(start=args.start, stop=args.stop, cmd=args.cmd)
+  vm = VM(argv)
+  vm.Run()