cros_flash: Use the new paygen library instead of devserver

We should not be generating update payload using devserver anymore. This
functionality is being deprecated. So let's deprecate it from cros flash
too. Currently, cros flash starts a devserver to generate update
payloads. However, we can use the newer paygen library in
chromite.lib.paygen to tackle this issue and this CL does that. There
are still some corner cases with caching since we don't use the
devserver cache anymore, but it should suffice the use cases we now have
for the cros flash.

BUG=chromium:872441
TEST=unittest
Ran two times to account for caching and both inside and outside chroot:
    `cros flash --debug <IP> local/reef/latest/test`
Ran two times for caching and both inside and outside chroot:
    `cros flash --debug <IP> xbuddy://remote/reef/latest-canary/test`
Ran two times to account for caching and both inside and outside chroot:
    `cros flash --debug <IP> chromiumos_test_image.bin
Ran inside cros chrome-sdk (simple chrome workflow) after patching the
chrome's chromite with these changes.

Change-Id: I73b27d5945757a9cf20de3bb8ac2aa2ff158a3d0
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/chromite/+/1441772
Reviewed-by: Mike Frysinger <vapier@chromium.org>
Commit-Queue: Amin Hassani <ahassani@chromium.org>
Tested-by: Amin Hassani <ahassani@chromium.org>
diff --git a/cli/flash.py b/cli/flash.py
index 409e9b2..99eab41 100644
--- a/cli/flash.py
+++ b/cli/flash.py
@@ -23,6 +23,9 @@
 from chromite.lib import path_util
 from chromite.lib import remote_access
 
+from chromite.lib.paygen import paygen_payload_lib
+from chromite.lib.paygen import paygen_stateful_payload_lib
+
 
 DEVSERVER_STATIC_DIR = path_util.FromChrootPath(
     os.path.join(constants.CHROOT_SOURCE_ROOT, 'devserver', 'static'))
@@ -386,10 +389,19 @@
     If self.image is a directory, we directly use the provided update payload(s)
     in this directory.
 
-    If self.image is an image, let devserver access it and generate payloads.
+    If self.image is an image, we will generate payloads for it and put them in
+    our temporary directory. The reason is that people may modify a local image
+    or override it (on the same path) with a different image, so in order to be
+    safe each time we need to generate the payloads and not cache them.
 
-    If not in the above cases, let devserver first obtain the image path. Then
-    devserver will access the image and generate payloads.
+    If non of the above cases, we use the xbuddy to first obtain the image path
+    (and possibly download it). Then we will generate the payloads in the same
+    directory the image is located. The reason is that this is what devserver
+    used to do. The path to the image generated by the devserver (or xbuddy) is
+    unique and normally nobody override its image with a different one. That is
+    why I think it is safe to put the payloads next to the image. This is a poor
+    man's version of caching but it makes cros flash faster for users who flash
+    the same image multiple times (without doing any change to the image).
 
     Args:
       device: A ChromiumOSDevice object.
@@ -397,53 +409,47 @@
     Returns:
       A string payload_dir, that represents the payload directory.
     """
-    payload_dir = self.tempdir
-
     if os.path.isdir(self.image):
       # The given path is a directory.
-      payload_dir = self.image
-      logging.info('Using provided payloads in %s', payload_dir)
-    elif os.path.isfile(self.image):
-      # The given path is an image.
-      logging.info('Using image %s', self.image)
-      try:
-        ds_wrapper.GetUpdatePayloadsFromLocalPath(
-            self.image, payload_dir,
-            src_image_to_delta=self.src_image_to_delta,
-            static_dir=DEVSERVER_STATIC_DIR)
-      except:
-        logging.error('Unable to get payloads from local path: %s', payload_dir)
-        raise
+      logging.info('Using provided payloads in %s', self.image)
+      return self.image
 
+    if os.path.isfile(self.image):
+      # The given path is an image.
+      image_path = self.image
+      payload_dir = self.tempdir
     else:
+      # Assuming it is an xbuddy path.
       self.board = cros_build_lib.GetBoard(device_board=device.board,
                                            override_board=self.board,
                                            force=self.yes)
       if not self.board:
-        raise FlashError('No board identified')
-
+        raise FlashError('No board identified.')
       if not self.force and self.board != device.board:
         # If a board was specified, it must be compatible with the device.
         raise FlashError('Device (%s) is incompatible with board %s' %
                          (device.board, self.board))
-
       logging.info('Board is %s', self.board)
 
       # Translate the xbuddy path to get the exact image to use.
-      translated_path, resolved_path = ds_wrapper.GetImagePathWithXbuddy(
-          self.image, self.board, static_dir=DEVSERVER_STATIC_DIR,
-          lookup_only=True)
-      logging.notice('Using image %s', translated_path)
-      # Convert the translated path to be used in the update request.
-      image_path = ds_wrapper.ConvertTranslatedPath(resolved_path,
-                                                    translated_path)
+      translated_path, _ = ds_wrapper.GetImagePathWithXbuddy(
+          self.image, self.board, static_dir=DEVSERVER_STATIC_DIR)
+      image_path = ds_wrapper.TranslatedPathToLocalPath(
+          translated_path, DEVSERVER_STATIC_DIR)
+      payload_dir = os.path.join(os.path.dirname(image_path), 'payloads')
 
-      # Launch a local devserver to generate/serve update payloads.
-      ds_wrapper.GetUpdatePayloads(
-          image_path, payload_dir, board=self.board,
-          src_image_to_delta=self.src_image_to_delta,
-          static_dir=DEVSERVER_STATIC_DIR)
+    logging.notice('Using image path %s and payload directory %s',
+                   image_path, payload_dir)
 
+    # Generate rootfs and stateful update payloads if they do not exist.
+    payload_path = os.path.join(payload_dir, ds_wrapper.ROOTFS_FILENAME)
+    if not os.path.exists(payload_path):
+      paygen_payload_lib.GenerateUpdatePayload(
+          image_path, payload_path, src_image=self.src_image_to_delta)
+    if not os.path.exists(os.path.join(payload_dir,
+                                       ds_wrapper.STATEFUL_FILENAME)):
+      paygen_stateful_payload_lib.GenerateStatefulPayload(image_path,
+                                                          payload_dir)
     return payload_dir
 
   def Run(self):