[autotest] Add code in verify and cleanup to remove cros-version label if it doesn't match

Make sure the dut's cros-version label matches the actual version stored in
lsb-release under attribute CHROMEOS_RELEASE_VERSION.

It's added to both cleanup and verify because:
1. If a test failed by dut issue or autoserv crash, cleanup will be queued.
the lab gets a chance to verify the cros-version label before the dut being
assigned to another test.
2. verify is called before each test, so confirming the cros-version label
there is necessary.

The check takes only one ssh call to the dut. Making a duplicated check (reset
calls both cleanup and verify) should not lead too much overhead.

BUG=chromium:469895
TEST=unittest, run local test
1. Add a cros-version label that matches lsb-release, run dummy test, confirm
the label is preserved.
2. Add a cros-version label doesn't match lsb-release, run dummy test, confirm
the label is removed by verify task, and verify job failed.

Change-Id: Ia4760967118f2aacfe5c7f378bb482df7995cef3
Reviewed-on: https://chromium-review.googlesource.com/262390
Trybot-Ready: Dan Shi <dshi@chromium.org>
Tested-by: Dan Shi <dshi@chromium.org>
Reviewed-by: Fang Deng <fdeng@chromium.org>
Commit-Queue: Dan Shi <dshi@chromium.org>
Reviewed-by: Chris Sosa <sosa@chromium.org>
diff --git a/client/common_lib/site_utils.py b/client/common_lib/site_utils.py
index b5e739c..4e21cb2 100644
--- a/client/common_lib/site_utils.py
+++ b/client/common_lib/site_utils.py
@@ -376,3 +376,103 @@
             raise KeyError('Argument %s is not given a value. argspec: %s, '
                            'args:%s, kwargs:%s' %
                            (arg_name, argspec, args, kwargs))
+
+
+def version_match(build_version, release_version, update_url=''):
+    """Compare release versino from lsb-release with cros-version label.
+
+    build_version is a string based on build name. It is prefixed with builder
+    info and branch ID, e.g., lumpy-release/R43-6809.0.0. It may not include
+    builder info, e.g., lumpy-release, in which case, update_url shall be passed
+    in to determine if the build is a trybot or pgo-generate build.
+    release_version is retrieved from lsb-release.
+    These two values might not match exactly.
+
+    The method is designed to compare version for following 6 scenarios with
+    samples of build version and expected release version:
+    1. trybot paladin build.
+    build version:   trybot-lumpy-paladin/R27-3837.0.0-b123
+    release version: 3837.0.2013_03_21_1340
+
+    2. trybot release build.
+    build version:   trybot-lumpy-release/R27-3837.0.0-b456
+    release version: 3837.0.0
+
+    3. buildbot official release build.
+    build version:   lumpy-release/R27-3837.0.0
+    release version: 3837.0.0
+
+    4. non-official paladin rc build.
+    build version:   lumpy-paladin/R27-3878.0.0-rc7
+    release version: 3837.0.0-rc7
+
+    5. chrome-perf build.
+    build version:   lumpy-chrome-perf/R28-3837.0.0-b2996
+    release version: 3837.0.0
+
+    6. pgo-generate build.
+    build version:   lumpy-release-pgo-generate/R28-3837.0.0-b2996
+    release version: 3837.0.0-pgo-generate
+
+    TODO: This logic has a bug if a trybot paladin build failed to be
+    installed in a DUT running an older trybot paladin build with same
+    platform number, but different build number (-b###). So to conclusively
+    determine if a tryjob paladin build is imaged successfully, we may need
+    to find out the date string from update url.
+
+    @param build_version: Build name for cros version, e.g.
+                          peppy-release/R43-6809.0.0 or R43-6809.0.0
+    @param release_version: Release version retrieved from lsb-release,
+                            e.g., 6809.0.0
+    @param update_url: Update url which include the full builder information.
+                       Default is set to empty string.
+
+    @return: True if the values match, otherwise returns False.
+    """
+    # If the build is from release, CQ or PFQ builder, cros-version label must
+    # be ended with release version in lsb-release.
+    if build_version.endswith(release_version):
+        return True
+
+    # Remove R#- and -b# at the end of build version
+    stripped_version = re.sub(r'(R\d+-|-b\d+)', '', build_version)
+    # Trim the builder info, e.g., trybot-lumpy-paladin/
+    stripped_version = stripped_version.split('/')[-1]
+
+    is_trybot_paladin_build = (
+            re.match(r'.*trybot-.+-paladin', build_version) or
+            re.match(r'.*trybot-.+-paladin', update_url))
+
+    # Replace date string with 0 in release_version
+    release_version_no_date = re.sub(r'\d{4}_\d{2}_\d{2}_\d+', '0',
+                                    release_version)
+    has_date_string = release_version != release_version_no_date
+
+    is_pgo_generate_build = (
+            re.match(r'.+-pgo-generate', build_version) or
+            re.match(r'.+-pgo-generate', update_url))
+
+    # Remove |-pgo-generate| in release_version
+    release_version_no_pgo = release_version.replace('-pgo-generate', '')
+    has_pgo_generate = release_version != release_version_no_pgo
+
+    if is_trybot_paladin_build:
+        if not has_date_string:
+            logging.error('A trybot paladin build is expected. Version '
+                          '"%s" is not a paladin build.', release_version)
+            return False
+        return stripped_version == release_version_no_date
+    elif is_pgo_generate_build:
+        if not has_pgo_generate:
+            logging.error('A pgo-generate build is expected. Version '
+                          '"%s" is not a pgo-generate build.',
+                          release_version)
+            return False
+        return stripped_version == release_version_no_pgo
+    else:
+        if has_date_string:
+            logging.error('Unexpected date found in a non trybot paladin '
+                          'build.')
+            return False
+        # Versioned build, i.e., rc or release build.
+        return stripped_version == release_version