autotest: get the latest FW releave version from Cros image archive

This patch adds _get_latest_release() function to CrosHost class,
which gets the latest FW release version from Chromeos image archive,
and uses it in firmware installation if AFE query fails.

If 'board' is in the format of 'baseboard_model', then 'baseboard'
only shall be used.

BUG=b:123717818
TEST=ran provision_FirmwareUpdate on local octopus fleex.

Change-Id: I895c275cc22aa63677499c2bbb037aa44a69d459
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/third_party/autotest/+/1919644
Commit-Queue: Namyoon Woo <namyoon@chromium.org>
Tested-by: Namyoon Woo <namyoon@chromium.org>
Reviewed-by: Mary Ruthven <mruthven@chromium.org>
diff --git a/server/cros/provision.py b/server/cros/provision.py
index a0dc7ef..46d0b1f 100644
--- a/server/cros/provision.py
+++ b/server/cros/provision.py
@@ -30,6 +30,11 @@
 # Postfix -cheetsth to distinguish ChromeOS build during Cheets provisioning.
 CHEETS_SUFFIX = '-cheetsth'
 
+# ChromeOS image archive server address
+CROS_IMAGE_ARCHIVE = 'gs://chromeos-image-archive'
+
+# ChromeOS firmware branch directory name. %s is for a (base)board name.
+FW_BRANCH_GLOB = 'firmware-%s-[0-9]*.B-firmwarebranch'
 
 _Action = collections.namedtuple('_Action', 'name, value')
 
diff --git a/server/hosts/cros_host.py b/server/hosts/cros_host.py
index 13a9cd8..6b06f07 100644
--- a/server/hosts/cros_host.py
+++ b/server/hosts/cros_host.py
@@ -599,6 +599,44 @@
         self.host_info_store.commit(info)
 
 
+    def get_latest_release_version(self, board):
+        """Search for the latest package release version from the image archive,
+            and return it.
+
+        @param board: board name
+
+        @return 'firmware-{board}-{branch}-firmwarebranch/{release-version}'
+                or None if LATEST release file does not exist.
+        """
+
+        # This might be in the format of 'baseboard_model',
+        # e.g. octopus_fleex. In that case, board should be just
+        # 'baseboard' to use in search for image package, e.g. octopus.
+        board = board.split('_')[0]
+
+        # Read 'LATEST-1.0.0' file
+        branch_dir = provision.FW_BRANCH_GLOB % board
+        latest_file = os.path.join(provision.CROS_IMAGE_ARCHIVE, branch_dir,
+                                'LATEST-1.0.0')
+        try:
+            result = utils.system_output('gsutil cat ' +  latest_file)
+
+            candidates = re.findall('RNone.+?b[0-9]+', result)
+        except error.CmdError:
+            logging.error('No LATEST release info is available.')
+            return None
+
+        release_path = os.path.join(provision.CROS_IMAGE_ARCHIVE, branch_dir,
+                                 candidates[0], board)
+        release = utils.system_output('gsutil ls -d ' + release_path)
+        # Now 'release_ver' has a full directory path: e.g.
+        #  gs://chromeos-image-archive/firmware-octopus-11297.B-firmwarebranch/
+        #       RNone-1.0.0-b4395530/octopus/
+        #
+        # Remove CROS_IMAGE_ARCHIVE and any surrounding '/'s.
+        return release.replace(provision.CROS_IMAGE_ARCHIVE,'').strip('/')
+
+
     def firmware_install(self, build=None, rw_only=False, dest=None):
         """Install firmware to the DUT.
 
@@ -645,7 +683,7 @@
                 raise error.TestError(
                         'Failed to find stable firmware build for %s.',
                         self.hostname)
-            logging.info('Will install firmware from build %s.', build)
+        logging.info('Will install firmware from build %s.', build)
 
         ds = dev_server.ImageServer.resolve(build, self.hostname)
         ds.stage_artifacts(build, ['firmware'])