gooftool: add a waiver list to 'gooftool finalize' and 'gooftool verify'

For early builds, we may need to disable some functions in gooftool when
the underlying software/hardware is not ready. A workround to this is
commented out those functions in gooftool and maybe creates a 'DO NOT
COMMIT' CL like CL:363670.

To solve this problem, add a --waive_list that accept a list of items, and
for phase <= DVT, functions listed in the waive_list are skipped,
for phase >= PVT_DOGFOOD, this list should be empty.

Usage example:
  gooftool finalize --waive_list verify_tpm generate_stable_device_secret

Also make this option available in 'finalize' test.

BUG=None
TEST=Run finalize test.

Change-Id: I748a13ebcc04b20f00cf4969a42ee1668aa8c159
Reviewed-on: https://chromium-review.googlesource.com/377579
Commit-Ready: Ting Shen <phoenixshen@chromium.org>
Tested-by: Ting Shen <phoenixshen@chromium.org>
Reviewed-by: Hung-Te Lin <hungte@chromium.org>
diff --git a/py/gooftool/commands.py b/py/gooftool/commands.py
index 258f5ed..aadcee7 100755
--- a/py/gooftool/commands.py
+++ b/py/gooftool/commands.py
@@ -46,9 +46,9 @@
 from cros.factory.test.rules import phase
 from cros.factory.test.rules.privacy import FilterDict
 from cros.factory.utils.argparse_utils import CmdArg
-from cros.factory.utils.argparse_utils import Command
 from cros.factory.utils.argparse_utils import ParseCmdline
 from cros.factory.utils.argparse_utils import verbosity_cmd_arg
+from cros.factory.utils import argparse_utils
 from cros.factory.utils import file_utils
 from cros.factory.utils import sys_utils
 from cros.factory.utils import time_utils
@@ -88,6 +88,33 @@
   return _global_gooftool
 
 
+def Command(cmd_name, *args, **kwargs):
+  """ Decorator for commands in gooftool.
+
+  This is similar to argparse_utils.Command, but all gooftool commands
+  can be waived during `gooftool finalize` or `gooftool verify` using
+  --waive_list option.
+  """
+  def Decorate(fun):
+    def CommandWithWaiveCheck(options):
+      waive_list = vars(options).get('waive_list', [])
+      if phase.GetPhase() >= phase.PVT_DOGFOOD and waive_list != []:
+        raise Error(
+            'waive_list should be empty for phase %s' % phase.GetPhase())
+
+      try:
+        fun(options)
+      except Exception as e:
+        if cmd_name in waive_list:
+          logging.exception(e)
+        else:
+          raise
+
+    return argparse_utils.Command(cmd_name, *args, **kwargs)(
+        CommandWithWaiveCheck)
+  return Decorate
+
+
 @Command('write_hwid',
          CmdArg('hwid', metavar='HWID', help='HWID string'))
 def WriteHWID(options):
@@ -179,6 +206,12 @@
     '--wipe_finish_token',
     help='Required token when notifying station after wipe finished')
 
+_waive_list_cmd_arg = CmdArg(
+    '--waive_list', nargs='*', default=[], metavar='SUBCMD',
+    help='A list of waived checks, serperated by whitespace.'
+         'Each item should be a sub-command of gooftool.'
+         'e.g. "gooftool verify --waive_list verify_tpm clear_gbb_flags".')
+
 @Command('best_match_hwids',
          _hwdb_path_cmd_arg,
          CmdArg('-b', '--board', metavar='BOARD',
@@ -776,7 +809,8 @@
          _cros_core_cmd_arg,
          _release_rootfs_cmd_arg,
          _firmware_path_cmd_arg,
-         _enforced_release_channels_cmd_arg)
+         _enforced_release_channels_cmd_arg,
+         _waive_list_cmd_arg)
 def Verify(options):
   """Verifies if whole factory process is ready for finalization.
 
@@ -971,7 +1005,8 @@
          _enforced_release_channels_cmd_arg,
          _station_ip_cmd_arg,
          _station_port_cmd_arg,
-         _wipe_finish_token_cmd_arg)
+         _wipe_finish_token_cmd_arg,
+         _waive_list_cmd_arg)
 def Finalize(options):
   """Verify system readiness and trigger transition into release state.