Add --boot-timeout option to extend boot timeout

Bug: 136192404
Test: atest acloud_test --host&
      acloud create -v (check timeout is 300s)
      acloud create -v --boot-timeout 600 (check timeout is 600s)

Change-Id: Idbb32209c76b32a7e302556e72c508f74c92148d
diff --git a/create/avd_spec.py b/create/avd_spec.py
index b1ba69e..b57b3e0 100644
--- a/create/avd_spec.py
+++ b/create/avd_spec.py
@@ -126,6 +126,9 @@
         self._username = None
         self._password = None
 
+        # The maximum time in seconds used to wait for the AVD to boot.
+        self._boot_timeout_secs = None
+
         self._ProcessArgs(args)
 
     def __repr__(self):
@@ -272,6 +275,8 @@
         self._username = args.username
         self._password = args.password
 
+        self._boot_timeout_secs = args.boot_timeout_secs
+
     @staticmethod
     def _GetFlavorFromString(flavor_string):
         """Get flavor name from flavor string.
@@ -624,3 +629,8 @@
     def password(self):
         """Return password."""
         return self._password
+
+    @property
+    def boot_timeout_secs(self):
+        """Return boot_timeout_secs."""
+        return self._boot_timeout_secs
diff --git a/create/cheeps_remote_image_remote_instance.py b/create/cheeps_remote_image_remote_instance.py
index 8bdf7f9..5376a9f 100644
--- a/create/cheeps_remote_image_remote_instance.py
+++ b/create/cheeps_remote_image_remote_instance.py
@@ -58,7 +58,8 @@
             report_internal_ip=avd_spec.report_internal_ip,
             autoconnect=avd_spec.autoconnect,
             avd_type=constants.TYPE_CHEEPS,
-            client_adb_port=avd_spec.client_adb_port)
+            client_adb_port=avd_spec.client_adb_port,
+            boot_timeout_secs=avd_spec.boot_timeout_secs)
 
         # Launch vnc client if we're auto-connecting.
         if avd_spec.autoconnect:
diff --git a/create/create_args.py b/create/create_args.py
index 31d949f..4e8e9f0 100644
--- a/create/create_args.py
+++ b/create/create_args.py
@@ -92,6 +92,12 @@
         dest="skip_pre_run_check",
         required=False,
         help="Skip the pre-run check.")
+    parser.add_argument(
+        "--boot-timeout",
+        dest="boot_timeout_secs",
+        type=int,
+        required=False,
+        help="The maximum time in seconds used to wait for the AVD to boot.")
 
     # TODO(b/118439885): Old arg formats to support transition, delete when
     # transistion is done.
diff --git a/create/local_image_remote_instance.py b/create/local_image_remote_instance.py
index 7e428ef..ac78cd7 100644
--- a/create/local_image_remote_instance.py
+++ b/create/local_image_remote_instance.py
@@ -304,7 +304,8 @@
             "create_cf", avd_spec.cfg, device_factory, avd_spec.num,
             report_internal_ip=avd_spec.report_internal_ip,
             autoconnect=avd_spec.autoconnect,
-            avd_type=constants.TYPE_CF)
+            avd_type=constants.TYPE_CF,
+            boot_timeout_secs=avd_spec.boot_timeout_secs)
         # Launch vnc client if we're auto-connecting.
         if avd_spec.autoconnect:
             utils.LaunchVNCFromReport(report, avd_spec, no_prompts)
diff --git a/internal/lib/android_compute_client.py b/internal/lib/android_compute_client.py
index 293a57c..03f6759 100755
--- a/internal/lib/android_compute_client.py
+++ b/internal/lib/android_compute_client.py
@@ -338,21 +338,25 @@
                 return False
             raise
 
-    def WaitForBoot(self, instance):
+    def WaitForBoot(self, instance, boot_timeout_secs=None):
         """Wait for boot to completes or hit timeout.
 
         Args:
             instance: string, instance name.
+            boot_timeout_secs: Integer, the maximum time in seconds used to
+                               wait for the AVD to boot.
         """
-        logger.info("Waiting for instance to boot up: %s", instance)
+        boot_timeout_secs = boot_timeout_secs or self.BOOT_TIMEOUT_SECS
+        logger.info("Waiting for instance to boot up %s for %s secs",
+                    instance, boot_timeout_secs)
         timeout_exception = errors.DeviceBootTimeoutError(
             "Device %s did not finish on boot within timeout (%s secs)" %
-            (instance, self.BOOT_TIMEOUT_SECS)),
+            (instance, boot_timeout_secs)),
         utils.PollAndWait(
             func=self.CheckBoot,
             expected_return=True,
             timeout_exception=timeout_exception,
-            timeout_secs=self.BOOT_TIMEOUT_SECS,
+            timeout_secs=boot_timeout_secs,
             sleep_interval_secs=self.BOOT_CHECK_INTERVAL_SECS,
             instance=instance)
         logger.info("Instance boot completed: %s", instance)
diff --git a/public/actions/common_operations.py b/public/actions/common_operations.py
index f65f35c..ad78471 100644
--- a/public/actions/common_operations.py
+++ b/public/actions/common_operations.py
@@ -134,9 +134,13 @@
 
     @utils.TimeExecute(function_description="Waiting for AVD(s) to boot up",
                        result_evaluator=utils.BootEvaluator)
-    def WaitForBoot(self):
+    def WaitForBoot(self, boot_timeout_secs):
         """Waits for all devices to boot up.
 
+        Args:
+            boot_timeout_secs: Integer, the maximum time in seconds used to
+                               wait for the AVD to boot.
+
         Returns:
             A dictionary that contains all the failures.
             The key is the name of the instance that fails to boot,
@@ -145,7 +149,7 @@
         failures = {}
         for device in self._devices:
             try:
-                self._compute_client.WaitForBoot(device.instance_name)
+                self._compute_client.WaitForBoot(device.instance_name, boot_timeout_secs)
             except errors.DeviceBootError as e:
                 failures[device.instance_name] = e
         return failures
@@ -246,7 +250,7 @@
 def CreateDevices(command, cfg, device_factory, num, avd_type,
                   report_internal_ip=False, autoconnect=False,
                   serial_log_file=None, logcat_file=None,
-                  client_adb_port=None):
+                  client_adb_port=None, boot_timeout_secs=None):
     """Create a set of devices using the given factory.
 
     Main jobs in create devices.
@@ -265,6 +269,7 @@
         logcat_file: String, the file path to tar the logcats.
         autoconnect: Boolean, whether to auto connect to device.
         client_adb_port: Integer, Specify port for adb forwarding.
+        boot_timeout_secs: Integer, boot timeout secs.
 
     Raises:
         errors: Create instance fail.
@@ -278,7 +283,7 @@
         device_pool = DevicePool(device_factory)
         device_pool.CreateDevices(num)
         device_pool.SetDeviceBuildInfo()
-        failures = device_pool.WaitForBoot()
+        failures = device_pool.WaitForBoot(boot_timeout_secs)
         if failures:
             reporter.SetStatus(report.Status.BOOT_FAIL)
         else:
diff --git a/public/actions/create_cuttlefish_action.py b/public/actions/create_cuttlefish_action.py
index ecc6a1d..1fb2af2 100644
--- a/public/actions/create_cuttlefish_action.py
+++ b/public/actions/create_cuttlefish_action.py
@@ -198,6 +198,7 @@
         A Report instance.
     """
     client_adb_port = None
+    boot_timeout_secs = None
     if avd_spec:
         cfg = avd_spec.cfg
         build_target = avd_spec.remote_image[constants.BUILD_TARGET]
@@ -209,6 +210,7 @@
         serial_log_file = avd_spec.serial_log_file
         logcat_file = avd_spec.logcat_file
         client_adb_port = avd_spec.client_adb_port
+        boot_timeout_secs = avd_spec.boot_timeout_secs
     logger.info(
         "Creating a cuttlefish device in project %s, "
         "build_target: %s, "
@@ -238,4 +240,4 @@
                                            num, constants.TYPE_CF,
                                            report_internal_ip, autoconnect,
                                            serial_log_file, logcat_file,
-                                           client_adb_port)
+                                           client_adb_port, boot_timeout_secs)
diff --git a/public/actions/create_goldfish_action.py b/public/actions/create_goldfish_action.py
index cfb57a2..1cc28cf 100644
--- a/public/actions/create_goldfish_action.py
+++ b/public/actions/create_goldfish_action.py
@@ -268,6 +268,7 @@
         A Report instance.
     """
     client_adb_port = None
+    boot_timeout_secs = None
     if avd_spec:
         cfg = avd_spec.cfg
         build_target = avd_spec.remote_image[constants.BUILD_TARGET]
@@ -281,6 +282,7 @@
         autoconnect = avd_spec.autoconnect
         report_internal_ip = avd_spec.report_internal_ip
         client_adb_port = avd_spec.client_adb_port
+        boot_timeout_secs = avd_spec.boot_timeout_secs
 
     # If emulator_build_id and emulator_branch is None, retrieve emulator
     # build id from platform build emulator-info.txt artifact
@@ -332,4 +334,4 @@
                                            num, constants.TYPE_GF,
                                            report_internal_ip, autoconnect,
                                            serial_log_file, logcat_file,
-                                           client_adb_port)
+                                           client_adb_port, boot_timeout_secs)