[cros_vm]: Download qemu binary from chromeos prebuilt.

1. Whenever we fetch the vm image, also fetch the qemu binary.

The location of the latest qemu binary, built by the SDK builder,
can be found at:
https://chromium.googlesource.com/chromiumos/overlays/board-overlays.git/+/master/overlay-amd64-host/prebuilt.conf

2. Improve the logic to locate a qemu binary:
* Use the downloaded binary in the cache of the chrome sdk shell.
* Use the binary in the chroot if we're in the cros source tree.
* Fallback to system qemu binary which probably won't pass the version
test.

3. We're using constants.SOURCE_ROOT in two places - to get us to the chroot
to find the qemu binary, and also to get to the build directory to find the
latest locally built VM image. This will not work if we're using chromite
from the chrome tree, eg, src/third_party/chromite/bin/cros_vm. So add
additional checks to protect against broken links.

BUG=chromium:733875
TEST=cros_vm --start works in both chroot and chrome-sdk shell.

Change-Id: Idd544ba8eb40500e8504957532ca0fe7e6fd67ad
Reviewed-on: https://chromium-review.googlesource.com/860903
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 52185c8..8139953 100644
--- a/scripts/cros_vm.py
+++ b/scripts/cros_vm.py
@@ -101,6 +101,24 @@
     if recreate:
       osutils.SafeMakedirs(self.vm_dir)
 
+  def _GetCachePath(self, key):
+    """Get cache path for key.
+
+    Args:
+      key: cache key.
+    """
+    tarball_cache = cache.TarballCache(os.path.join(
+        path_util.GetCacheDir(),
+        cros_chrome_sdk.COMMAND_NAME,
+        cros_chrome_sdk.SDKFetcher.TARBALL_CACHE))
+    lkgm = cros_chrome_sdk.SDKFetcher.GetChromeLKGM()
+    if lkgm:
+      cache_key = (self.board, lkgm, key)
+      with tarball_cache.Lookup(cache_key) as ref:
+        if ref.Exists():
+          return ref.path
+    return None
+
   @cros_build_lib.MemoizedSingleCall
   def QemuVersion(self):
     """Determine QEMU version."""
@@ -130,38 +148,53 @@
 
   def _SetQemuPath(self):
     """Find a suitable Qemu executable."""
+    qemu_exe = 'qemu-system-x86_64'
+    qemu_exe_path = os.path.join('usr/bin', qemu_exe)
+
+    # Check SDK cache.
     if not self.qemu_path:
-      self.qemu_path = osutils.Which('qemu-system-x86_64')
+      qemu_dir = self._GetCachePath(cros_chrome_sdk.SDKFetcher.QEMU_BIN_KEY)
+      if qemu_dir:
+        qemu_path = os.path.join(qemu_dir, qemu_exe_path)
+        if os.path.isfile(qemu_path):
+          self.qemu_path = qemu_path
+
+    # Check chroot.
     if not self.qemu_path:
-      raise VMError('Qemu not found.')
-    logging.debug('Qemu path: %s', self.qemu_path)
+      qemu_path = os.path.join(
+          constants.SOURCE_ROOT, constants.DEFAULT_CHROOT_DIR, qemu_exe_path)
+      if os.path.isfile(qemu_path):
+        self.qemu_path = qemu_path
+
+    # Check system.
+    if not self.qemu_path:
+      self.qemu_path = osutils.Which(qemu_exe)
+
+    if not self.qemu_path or not os.path.isfile(self.qemu_path):
+      raise VMError('QEMU not found.')
+    logging.debug('QEMU path: %s', self.qemu_path)
     self._CheckQemuMinVersion()
 
   def _GetBuiltVMImagePath(self):
     """Get path of a locally built VM image."""
-    return os.path.join(constants.SOURCE_ROOT, 'src/build/images',
-                        cros_build_lib.GetBoard(self.board),
-                        'latest', constants.VM_IMAGE_BIN)
+    vm_image_path = os.path.join(constants.SOURCE_ROOT, 'src/build/images',
+                                 cros_build_lib.GetBoard(self.board),
+                                 'latest', constants.VM_IMAGE_BIN)
+    return vm_image_path if os.path.isfile(vm_image_path) else None
 
-  def _GetDownloadedVMImagePath(self):
-    """Get path of a downloaded VM image."""
-    tarball_cache = cache.TarballCache(os.path.join(
-        path_util.GetCacheDir(),
-        cros_chrome_sdk.COMMAND_NAME,
-        cros_chrome_sdk.SDKFetcher.TARBALL_CACHE))
-    lkgm = cros_chrome_sdk.SDKFetcher.GetChromeLKGM()
-    if not lkgm:
-      return None
-    cache_key = (self.board, lkgm, constants.VM_IMAGE_TAR)
-    with tarball_cache.Lookup(cache_key) as ref:
-      if ref.Exists():
-        return os.path.join(ref.path, constants.VM_IMAGE_BIN)
+  def _GetCacheVMImagePath(self):
+    """Get path of a cached VM image."""
+    cache_path = self._GetCachePath(constants.VM_IMAGE_TAR)
+    if cache_path:
+      vm_image = os.path.join(cache_path, constants.VM_IMAGE_BIN)
+      if os.path.isfile(vm_image):
+        return vm_image
     return None
 
   def _SetVMImagePath(self):
     """Detect VM image path in SDK and chroot."""
     if not self.image_path:
-      self.image_path = (self._GetDownloadedVMImagePath() or
+      self.image_path = (self._GetCacheVMImagePath() or
                          self._GetBuiltVMImagePath())
     if not self.image_path:
       raise VMError('No VM image found. Use cros chrome-sdk --download-vm.')