xref: /aosp_15_r20/tools/acloud/internal/lib/android_compute_client.py (revision 800a58d989c669b8eb8a71d8df53b1ba3d411444)
1*800a58d9SAndroid Build Coastguard Worker#!/usr/bin/env python
2*800a58d9SAndroid Build Coastguard Worker#
3*800a58d9SAndroid Build Coastguard Worker# Copyright 2016 - The Android Open Source Project
4*800a58d9SAndroid Build Coastguard Worker#
5*800a58d9SAndroid Build Coastguard Worker# Licensed under the Apache License, Version 2.0 (the "License");
6*800a58d9SAndroid Build Coastguard Worker# you may not use this file except in compliance with the License.
7*800a58d9SAndroid Build Coastguard Worker# You may obtain a copy of the License at
8*800a58d9SAndroid Build Coastguard Worker#
9*800a58d9SAndroid Build Coastguard Worker#     http://www.apache.org/licenses/LICENSE-2.0
10*800a58d9SAndroid Build Coastguard Worker#
11*800a58d9SAndroid Build Coastguard Worker# Unless required by applicable law or agreed to in writing, software
12*800a58d9SAndroid Build Coastguard Worker# distributed under the License is distributed on an "AS IS" BASIS,
13*800a58d9SAndroid Build Coastguard Worker# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14*800a58d9SAndroid Build Coastguard Worker# See the License for the specific language governing permissions and
15*800a58d9SAndroid Build Coastguard Worker# limitations under the License.
16*800a58d9SAndroid Build Coastguard Worker"""A client that manages Android compute engine instances.
17*800a58d9SAndroid Build Coastguard Worker
18*800a58d9SAndroid Build Coastguard Worker** AndroidComputeClient **
19*800a58d9SAndroid Build Coastguard Worker
20*800a58d9SAndroid Build Coastguard WorkerAndroidComputeClient derives from ComputeClient. It manges a google
21*800a58d9SAndroid Build Coastguard Workercompute engine project that is setup for running Android instances.
22*800a58d9SAndroid Build Coastguard WorkerIt knows how to create android GCE images and instances.
23*800a58d9SAndroid Build Coastguard Worker
24*800a58d9SAndroid Build Coastguard Worker** Class hierarchy **
25*800a58d9SAndroid Build Coastguard Worker
26*800a58d9SAndroid Build Coastguard Worker  base_cloud_client.BaseCloudApiClient
27*800a58d9SAndroid Build Coastguard Worker                ^
28*800a58d9SAndroid Build Coastguard Worker                |
29*800a58d9SAndroid Build Coastguard Worker       gcompute_client.ComputeClient
30*800a58d9SAndroid Build Coastguard Worker                ^
31*800a58d9SAndroid Build Coastguard Worker                |
32*800a58d9SAndroid Build Coastguard Worker    gcompute_client.AndroidComputeClient
33*800a58d9SAndroid Build Coastguard Worker"""
34*800a58d9SAndroid Build Coastguard Worker
35*800a58d9SAndroid Build Coastguard Workerimport getpass
36*800a58d9SAndroid Build Coastguard Workerimport logging
37*800a58d9SAndroid Build Coastguard Workerimport os
38*800a58d9SAndroid Build Coastguard Workerimport uuid
39*800a58d9SAndroid Build Coastguard Worker
40*800a58d9SAndroid Build Coastguard Workerfrom acloud import errors
41*800a58d9SAndroid Build Coastguard Workerfrom acloud.internal import constants
42*800a58d9SAndroid Build Coastguard Workerfrom acloud.internal.lib import gcompute_client
43*800a58d9SAndroid Build Coastguard Workerfrom acloud.internal.lib import utils
44*800a58d9SAndroid Build Coastguard Workerfrom acloud.public import config
45*800a58d9SAndroid Build Coastguard Worker
46*800a58d9SAndroid Build Coastguard Worker
47*800a58d9SAndroid Build Coastguard Workerlogger = logging.getLogger(__name__)
48*800a58d9SAndroid Build Coastguard Worker_ZONE = "zone"
49*800a58d9SAndroid Build Coastguard Worker_VERSION = "version"
50*800a58d9SAndroid Build Coastguard Worker
51*800a58d9SAndroid Build Coastguard Worker
52*800a58d9SAndroid Build Coastguard Workerclass AndroidComputeClient(gcompute_client.ComputeClient):
53*800a58d9SAndroid Build Coastguard Worker    """Client that manages Anadroid Virtual Device."""
54*800a58d9SAndroid Build Coastguard Worker    IMAGE_NAME_FMT = "img-{uuid}-{build_id}-{build_target}"
55*800a58d9SAndroid Build Coastguard Worker    DATA_DISK_NAME_FMT = "data-{instance}"
56*800a58d9SAndroid Build Coastguard Worker    BOOT_COMPLETED_MSG = "VIRTUAL_DEVICE_BOOT_COMPLETED"
57*800a58d9SAndroid Build Coastguard Worker    BOOT_STARTED_MSG = "VIRTUAL_DEVICE_BOOT_STARTED"
58*800a58d9SAndroid Build Coastguard Worker    BOOT_CHECK_INTERVAL_SECS = 10
59*800a58d9SAndroid Build Coastguard Worker    OPERATION_TIMEOUT_SECS = 20 * 60  # Override parent value, 20 mins
60*800a58d9SAndroid Build Coastguard Worker
61*800a58d9SAndroid Build Coastguard Worker    NAME_LENGTH_LIMIT = 63
62*800a58d9SAndroid Build Coastguard Worker    # If the generated name ends with '-', replace it with REPLACER.
63*800a58d9SAndroid Build Coastguard Worker    REPLACER = "e"
64*800a58d9SAndroid Build Coastguard Worker
65*800a58d9SAndroid Build Coastguard Worker    def __init__(self, acloud_config, oauth2_credentials):
66*800a58d9SAndroid Build Coastguard Worker        """Initialize.
67*800a58d9SAndroid Build Coastguard Worker
68*800a58d9SAndroid Build Coastguard Worker        Args:
69*800a58d9SAndroid Build Coastguard Worker            acloud_config: An AcloudConfig object.
70*800a58d9SAndroid Build Coastguard Worker            oauth2_credentials: An oauth2client.OAuth2Credentials instance.
71*800a58d9SAndroid Build Coastguard Worker        """
72*800a58d9SAndroid Build Coastguard Worker        super().__init__(acloud_config, oauth2_credentials)
73*800a58d9SAndroid Build Coastguard Worker        self._zone = acloud_config.zone
74*800a58d9SAndroid Build Coastguard Worker        self._machine_type = acloud_config.machine_type
75*800a58d9SAndroid Build Coastguard Worker        self._min_machine_size = acloud_config.min_machine_size
76*800a58d9SAndroid Build Coastguard Worker        self._network = acloud_config.network
77*800a58d9SAndroid Build Coastguard Worker        self._orientation = acloud_config.orientation
78*800a58d9SAndroid Build Coastguard Worker        self._resolution = acloud_config.resolution
79*800a58d9SAndroid Build Coastguard Worker        self._metadata = acloud_config.metadata_variable.copy()
80*800a58d9SAndroid Build Coastguard Worker        self._ssh_public_key_path = acloud_config.ssh_public_key_path
81*800a58d9SAndroid Build Coastguard Worker        self._launch_args = acloud_config.launch_args
82*800a58d9SAndroid Build Coastguard Worker        self._instance_name_pattern = acloud_config.instance_name_pattern
83*800a58d9SAndroid Build Coastguard Worker        self._gce_hostname = None
84*800a58d9SAndroid Build Coastguard Worker        self._AddPerInstanceSshkey()
85*800a58d9SAndroid Build Coastguard Worker        self._dict_report = {_ZONE: self._zone,
86*800a58d9SAndroid Build Coastguard Worker                             _VERSION: config.GetVersion()}
87*800a58d9SAndroid Build Coastguard Worker
88*800a58d9SAndroid Build Coastguard Worker    # TODO(147047953): New args to contorl zone metrics check.
89*800a58d9SAndroid Build Coastguard Worker    def _VerifyZoneByQuota(self):
90*800a58d9SAndroid Build Coastguard Worker        """Verify the zone must have enough quota to create instance.
91*800a58d9SAndroid Build Coastguard Worker
92*800a58d9SAndroid Build Coastguard Worker        Returns:
93*800a58d9SAndroid Build Coastguard Worker            Boolean, True if zone have enough quota to create instance.
94*800a58d9SAndroid Build Coastguard Worker
95*800a58d9SAndroid Build Coastguard Worker        Raises:
96*800a58d9SAndroid Build Coastguard Worker            errors.CheckGCEZonesQuotaError: the zone doesn't have enough quota.
97*800a58d9SAndroid Build Coastguard Worker        """
98*800a58d9SAndroid Build Coastguard Worker        if self.EnoughMetricsInZone(self._zone):
99*800a58d9SAndroid Build Coastguard Worker            return True
100*800a58d9SAndroid Build Coastguard Worker        raise errors.CheckGCEZonesQuotaError(
101*800a58d9SAndroid Build Coastguard Worker            "There is no enough quota in zone: %s" % self._zone)
102*800a58d9SAndroid Build Coastguard Worker
103*800a58d9SAndroid Build Coastguard Worker    def _AddPerInstanceSshkey(self):
104*800a58d9SAndroid Build Coastguard Worker        """Add per-instance ssh key.
105*800a58d9SAndroid Build Coastguard Worker
106*800a58d9SAndroid Build Coastguard Worker        Assign the ssh publick key to instacne then use ssh command to
107*800a58d9SAndroid Build Coastguard Worker        control remote instance via the ssh publick key. Added sshkey for two
108*800a58d9SAndroid Build Coastguard Worker        users. One is vsoc01, another is current user.
109*800a58d9SAndroid Build Coastguard Worker
110*800a58d9SAndroid Build Coastguard Worker        """
111*800a58d9SAndroid Build Coastguard Worker        if self._ssh_public_key_path:
112*800a58d9SAndroid Build Coastguard Worker            rsa = self._LoadSshPublicKey(self._ssh_public_key_path)
113*800a58d9SAndroid Build Coastguard Worker            logger.info("ssh_public_key_path is specified in config: %s, "
114*800a58d9SAndroid Build Coastguard Worker                        "will add the key to the instance.",
115*800a58d9SAndroid Build Coastguard Worker                        self._ssh_public_key_path)
116*800a58d9SAndroid Build Coastguard Worker            self._metadata["sshKeys"] = "{0}:{2}\n{1}:{2}".format(getpass.getuser(),
117*800a58d9SAndroid Build Coastguard Worker                                                                  constants.GCE_USER,
118*800a58d9SAndroid Build Coastguard Worker                                                                  rsa)
119*800a58d9SAndroid Build Coastguard Worker        else:
120*800a58d9SAndroid Build Coastguard Worker            logger.warning(
121*800a58d9SAndroid Build Coastguard Worker                "ssh_public_key_path is not specified in config, "
122*800a58d9SAndroid Build Coastguard Worker                "only project-wide key will be effective.")
123*800a58d9SAndroid Build Coastguard Worker
124*800a58d9SAndroid Build Coastguard Worker    @classmethod
125*800a58d9SAndroid Build Coastguard Worker    def _FormalizeName(cls, name):
126*800a58d9SAndroid Build Coastguard Worker        """Formalize the name to comply with RFC1035.
127*800a58d9SAndroid Build Coastguard Worker
128*800a58d9SAndroid Build Coastguard Worker        The name must be 1-63 characters long and match the regular expression
129*800a58d9SAndroid Build Coastguard Worker        [a-z]([-a-z0-9]*[a-z0-9])? which means the first character must be a
130*800a58d9SAndroid Build Coastguard Worker        lowercase letter, and all following characters must be a dash,
131*800a58d9SAndroid Build Coastguard Worker        lowercase letter, or digit, except the last character, which cannot be
132*800a58d9SAndroid Build Coastguard Worker        a dash.
133*800a58d9SAndroid Build Coastguard Worker
134*800a58d9SAndroid Build Coastguard Worker        Args:
135*800a58d9SAndroid Build Coastguard Worker          name: A string.
136*800a58d9SAndroid Build Coastguard Worker
137*800a58d9SAndroid Build Coastguard Worker        Returns:
138*800a58d9SAndroid Build Coastguard Worker          name: A string that complies with RFC1035.
139*800a58d9SAndroid Build Coastguard Worker        """
140*800a58d9SAndroid Build Coastguard Worker        name = name.replace("_", "-").replace(".", "-").lower()
141*800a58d9SAndroid Build Coastguard Worker        name = name[:cls.NAME_LENGTH_LIMIT]
142*800a58d9SAndroid Build Coastguard Worker        if name[-1] == "-":
143*800a58d9SAndroid Build Coastguard Worker            name = name[:-1] + cls.REPLACER
144*800a58d9SAndroid Build Coastguard Worker        return name
145*800a58d9SAndroid Build Coastguard Worker
146*800a58d9SAndroid Build Coastguard Worker    def _CheckMachineSize(self):
147*800a58d9SAndroid Build Coastguard Worker        """Check machine size.
148*800a58d9SAndroid Build Coastguard Worker
149*800a58d9SAndroid Build Coastguard Worker        Check if the desired machine type |self._machine_type| meets
150*800a58d9SAndroid Build Coastguard Worker        the requirement of minimum machine size specified as
151*800a58d9SAndroid Build Coastguard Worker        |self._min_machine_size|.
152*800a58d9SAndroid Build Coastguard Worker
153*800a58d9SAndroid Build Coastguard Worker        Raises:
154*800a58d9SAndroid Build Coastguard Worker            errors.DriverError: if check fails.
155*800a58d9SAndroid Build Coastguard Worker        """
156*800a58d9SAndroid Build Coastguard Worker        if self.CompareMachineSize(self._machine_type, self._min_machine_size,
157*800a58d9SAndroid Build Coastguard Worker                                   self._zone) < 0:
158*800a58d9SAndroid Build Coastguard Worker            raise errors.DriverError(
159*800a58d9SAndroid Build Coastguard Worker                "%s does not meet the minimum required machine size %s" %
160*800a58d9SAndroid Build Coastguard Worker                (self._machine_type, self._min_machine_size))
161*800a58d9SAndroid Build Coastguard Worker
162*800a58d9SAndroid Build Coastguard Worker    @classmethod
163*800a58d9SAndroid Build Coastguard Worker    def GenerateImageName(cls, build_target=None, build_id=None):
164*800a58d9SAndroid Build Coastguard Worker        """Generate an image name given build_target, build_id.
165*800a58d9SAndroid Build Coastguard Worker
166*800a58d9SAndroid Build Coastguard Worker        Args:
167*800a58d9SAndroid Build Coastguard Worker            build_target: Target name, e.g. "aosp_cf_x86_64_phone-userdebug"
168*800a58d9SAndroid Build Coastguard Worker            build_id: Build id, a string, e.g. "2263051", "P2804227"
169*800a58d9SAndroid Build Coastguard Worker
170*800a58d9SAndroid Build Coastguard Worker        Returns:
171*800a58d9SAndroid Build Coastguard Worker            A string, representing image name.
172*800a58d9SAndroid Build Coastguard Worker        """
173*800a58d9SAndroid Build Coastguard Worker        if not build_target and not build_id:
174*800a58d9SAndroid Build Coastguard Worker            return "image-" + uuid.uuid4().hex
175*800a58d9SAndroid Build Coastguard Worker        name = cls.IMAGE_NAME_FMT.format(
176*800a58d9SAndroid Build Coastguard Worker            build_target=build_target,
177*800a58d9SAndroid Build Coastguard Worker            build_id=build_id,
178*800a58d9SAndroid Build Coastguard Worker            uuid=uuid.uuid4().hex[:8])
179*800a58d9SAndroid Build Coastguard Worker        return cls._FormalizeName(name)
180*800a58d9SAndroid Build Coastguard Worker
181*800a58d9SAndroid Build Coastguard Worker    @classmethod
182*800a58d9SAndroid Build Coastguard Worker    def GetDataDiskName(cls, instance):
183*800a58d9SAndroid Build Coastguard Worker        """Get data disk name for an instance.
184*800a58d9SAndroid Build Coastguard Worker
185*800a58d9SAndroid Build Coastguard Worker        Args:
186*800a58d9SAndroid Build Coastguard Worker            instance: An instance_name.
187*800a58d9SAndroid Build Coastguard Worker
188*800a58d9SAndroid Build Coastguard Worker        Returns:
189*800a58d9SAndroid Build Coastguard Worker            The corresponding data disk name.
190*800a58d9SAndroid Build Coastguard Worker        """
191*800a58d9SAndroid Build Coastguard Worker        name = cls.DATA_DISK_NAME_FMT.format(instance=instance)
192*800a58d9SAndroid Build Coastguard Worker        return cls._FormalizeName(name)
193*800a58d9SAndroid Build Coastguard Worker
194*800a58d9SAndroid Build Coastguard Worker    def GenerateInstanceName(self, build_target=None, build_id=None):
195*800a58d9SAndroid Build Coastguard Worker        """Generate an instance name given build_target, build_id.
196*800a58d9SAndroid Build Coastguard Worker
197*800a58d9SAndroid Build Coastguard Worker        Target is not used as instance name has a length limit.
198*800a58d9SAndroid Build Coastguard Worker
199*800a58d9SAndroid Build Coastguard Worker        Args:
200*800a58d9SAndroid Build Coastguard Worker            build_target: Target name, e.g. "aosp_cf_x86_64_phone-userdebug"
201*800a58d9SAndroid Build Coastguard Worker            build_id: Build id, a string, e.g. "2263051", "P2804227"
202*800a58d9SAndroid Build Coastguard Worker
203*800a58d9SAndroid Build Coastguard Worker        Returns:
204*800a58d9SAndroid Build Coastguard Worker            A string, representing instance name.
205*800a58d9SAndroid Build Coastguard Worker        """
206*800a58d9SAndroid Build Coastguard Worker        name = self._instance_name_pattern.format(build_target=build_target,
207*800a58d9SAndroid Build Coastguard Worker                                                  build_id=build_id,
208*800a58d9SAndroid Build Coastguard Worker                                                  uuid=uuid.uuid4().hex[:8])
209*800a58d9SAndroid Build Coastguard Worker        return self._FormalizeName(name)
210*800a58d9SAndroid Build Coastguard Worker
211*800a58d9SAndroid Build Coastguard Worker    def CreateDisk(self,
212*800a58d9SAndroid Build Coastguard Worker                   disk_name,
213*800a58d9SAndroid Build Coastguard Worker                   source_image,
214*800a58d9SAndroid Build Coastguard Worker                   size_gb,
215*800a58d9SAndroid Build Coastguard Worker                   zone=None,
216*800a58d9SAndroid Build Coastguard Worker                   source_project=None,
217*800a58d9SAndroid Build Coastguard Worker                   disk_type=gcompute_client.PersistentDiskType.STANDARD):
218*800a58d9SAndroid Build Coastguard Worker        """Create a gce disk.
219*800a58d9SAndroid Build Coastguard Worker
220*800a58d9SAndroid Build Coastguard Worker        Args:
221*800a58d9SAndroid Build Coastguard Worker            disk_name: String, name of disk.
222*800a58d9SAndroid Build Coastguard Worker            source_image: String, name to the image name.
223*800a58d9SAndroid Build Coastguard Worker            size_gb: Integer, size in gigabytes.
224*800a58d9SAndroid Build Coastguard Worker            zone: String, name of the zone, e.g. us-central1-b.
225*800a58d9SAndroid Build Coastguard Worker            source_project: String, required if the image is located in a different
226*800a58d9SAndroid Build Coastguard Worker                            project.
227*800a58d9SAndroid Build Coastguard Worker            disk_type: String, a value from PersistentDiskType, STANDARD
228*800a58d9SAndroid Build Coastguard Worker                       for regular hard disk or SSD for solid state disk.
229*800a58d9SAndroid Build Coastguard Worker        """
230*800a58d9SAndroid Build Coastguard Worker        if self.CheckDiskExists(disk_name, self._zone):
231*800a58d9SAndroid Build Coastguard Worker            raise errors.DriverError(
232*800a58d9SAndroid Build Coastguard Worker                "Failed to create disk %s, already exists." % disk_name)
233*800a58d9SAndroid Build Coastguard Worker        if source_image and not self.CheckImageExists(source_image):
234*800a58d9SAndroid Build Coastguard Worker            raise errors.DriverError(
235*800a58d9SAndroid Build Coastguard Worker                "Failed to create disk %s, source image %s does not exist." %
236*800a58d9SAndroid Build Coastguard Worker                (disk_name, source_image))
237*800a58d9SAndroid Build Coastguard Worker        super().CreateDisk(
238*800a58d9SAndroid Build Coastguard Worker            disk_name,
239*800a58d9SAndroid Build Coastguard Worker            source_image=source_image,
240*800a58d9SAndroid Build Coastguard Worker            size_gb=size_gb,
241*800a58d9SAndroid Build Coastguard Worker            zone=zone or self._zone)
242*800a58d9SAndroid Build Coastguard Worker
243*800a58d9SAndroid Build Coastguard Worker    @staticmethod
244*800a58d9SAndroid Build Coastguard Worker    def _LoadSshPublicKey(ssh_public_key_path):
245*800a58d9SAndroid Build Coastguard Worker        """Load the content of ssh public key from a file.
246*800a58d9SAndroid Build Coastguard Worker
247*800a58d9SAndroid Build Coastguard Worker        Args:
248*800a58d9SAndroid Build Coastguard Worker            ssh_public_key_path: String, path to the public key file.
249*800a58d9SAndroid Build Coastguard Worker                               E.g. ~/.ssh/acloud_rsa.pub
250*800a58d9SAndroid Build Coastguard Worker        Returns:
251*800a58d9SAndroid Build Coastguard Worker            String, content of the file.
252*800a58d9SAndroid Build Coastguard Worker
253*800a58d9SAndroid Build Coastguard Worker        Raises:
254*800a58d9SAndroid Build Coastguard Worker            errors.DriverError if the public key file does not exist
255*800a58d9SAndroid Build Coastguard Worker            or the content is not valid.
256*800a58d9SAndroid Build Coastguard Worker        """
257*800a58d9SAndroid Build Coastguard Worker        key_path = os.path.expanduser(ssh_public_key_path)
258*800a58d9SAndroid Build Coastguard Worker        if not os.path.exists(key_path):
259*800a58d9SAndroid Build Coastguard Worker            raise errors.DriverError(
260*800a58d9SAndroid Build Coastguard Worker                "SSH public key file %s does not exist." % key_path)
261*800a58d9SAndroid Build Coastguard Worker
262*800a58d9SAndroid Build Coastguard Worker        with open(key_path) as f:
263*800a58d9SAndroid Build Coastguard Worker            rsa = f.read()
264*800a58d9SAndroid Build Coastguard Worker            rsa = rsa.strip() if rsa else rsa
265*800a58d9SAndroid Build Coastguard Worker            utils.VerifyRsaPubKey(rsa)
266*800a58d9SAndroid Build Coastguard Worker        return rsa
267*800a58d9SAndroid Build Coastguard Worker
268*800a58d9SAndroid Build Coastguard Worker    # pylint: disable=too-many-locals, arguments-differ
269*800a58d9SAndroid Build Coastguard Worker    @utils.TimeExecute("Creating GCE Instance")
270*800a58d9SAndroid Build Coastguard Worker    def CreateInstance(self,
271*800a58d9SAndroid Build Coastguard Worker                       instance,
272*800a58d9SAndroid Build Coastguard Worker                       image_name,
273*800a58d9SAndroid Build Coastguard Worker                       machine_type=None,
274*800a58d9SAndroid Build Coastguard Worker                       metadata=None,
275*800a58d9SAndroid Build Coastguard Worker                       network=None,
276*800a58d9SAndroid Build Coastguard Worker                       zone=None,
277*800a58d9SAndroid Build Coastguard Worker                       disk_args=None,
278*800a58d9SAndroid Build Coastguard Worker                       image_project=None,
279*800a58d9SAndroid Build Coastguard Worker                       gpu=None,
280*800a58d9SAndroid Build Coastguard Worker                       extra_disk_name=None,
281*800a58d9SAndroid Build Coastguard Worker                       avd_spec=None,
282*800a58d9SAndroid Build Coastguard Worker                       extra_scopes=None,
283*800a58d9SAndroid Build Coastguard Worker                       tags=None):
284*800a58d9SAndroid Build Coastguard Worker        """Create a gce instance with a gce image.
285*800a58d9SAndroid Build Coastguard Worker
286*800a58d9SAndroid Build Coastguard Worker        Args:
287*800a58d9SAndroid Build Coastguard Worker            instance: String, instance name.
288*800a58d9SAndroid Build Coastguard Worker            image_name: String, source image used to create this disk.
289*800a58d9SAndroid Build Coastguard Worker            machine_type: String, representing machine_type,
290*800a58d9SAndroid Build Coastguard Worker                          e.g. "n1-standard-1"
291*800a58d9SAndroid Build Coastguard Worker            metadata: Dict, maps a metadata name to its value.
292*800a58d9SAndroid Build Coastguard Worker            network: String, representing network name, e.g. "default"
293*800a58d9SAndroid Build Coastguard Worker            zone: String, representing zone name, e.g. "us-central1-f"
294*800a58d9SAndroid Build Coastguard Worker            disk_args: A list of extra disk args (strings), see _GetDiskArgs
295*800a58d9SAndroid Build Coastguard Worker                       for example, if None, will create a disk using the given
296*800a58d9SAndroid Build Coastguard Worker                       image.
297*800a58d9SAndroid Build Coastguard Worker            image_project: String, name of the project where the image
298*800a58d9SAndroid Build Coastguard Worker                           belongs. Assume the default project if None.
299*800a58d9SAndroid Build Coastguard Worker            gpu: String, type of gpu to attach. e.g. "nvidia-tesla-k80", if
300*800a58d9SAndroid Build Coastguard Worker                 None no gpus will be attached. For more details see:
301*800a58d9SAndroid Build Coastguard Worker                 https://cloud.google.com/compute/docs/gpus/add-gpus
302*800a58d9SAndroid Build Coastguard Worker            extra_disk_name: String,the name of the extra disk to attach.
303*800a58d9SAndroid Build Coastguard Worker            avd_spec: AVDSpec object that tells us what we're going to create.
304*800a58d9SAndroid Build Coastguard Worker            extra_scopes: List, extra scopes (strings) to be passed to the
305*800a58d9SAndroid Build Coastguard Worker                          instance.
306*800a58d9SAndroid Build Coastguard Worker            tags: A list of tags to associate with the instance. e.g.
307*800a58d9SAndroid Build Coastguard Worker                 ["http-server", "https-server"]
308*800a58d9SAndroid Build Coastguard Worker        """
309*800a58d9SAndroid Build Coastguard Worker        self._CheckMachineSize()
310*800a58d9SAndroid Build Coastguard Worker        disk_args = self._GetDiskArgs(instance, image_name)
311*800a58d9SAndroid Build Coastguard Worker        metadata = self._metadata.copy()
312*800a58d9SAndroid Build Coastguard Worker        metadata["cfg_sta_display_resolution"] = self._resolution
313*800a58d9SAndroid Build Coastguard Worker        metadata["t_force_orientation"] = self._orientation
314*800a58d9SAndroid Build Coastguard Worker        metadata[constants.INS_KEY_AVD_TYPE] = avd_spec.avd_type
315*800a58d9SAndroid Build Coastguard Worker
316*800a58d9SAndroid Build Coastguard Worker        # Use another METADATA_DISPLAY to record resolution which will be
317*800a58d9SAndroid Build Coastguard Worker        # retrieved in acloud list cmd. We try not to use cvd_01_x_res
318*800a58d9SAndroid Build Coastguard Worker        # since cvd_01_xxx metadata is going to deprecated by cuttlefish.
319*800a58d9SAndroid Build Coastguard Worker        metadata[constants.INS_KEY_DISPLAY] = ("%sx%s (%s)" % (
320*800a58d9SAndroid Build Coastguard Worker            avd_spec.hw_property[constants.HW_X_RES],
321*800a58d9SAndroid Build Coastguard Worker            avd_spec.hw_property[constants.HW_Y_RES],
322*800a58d9SAndroid Build Coastguard Worker            avd_spec.hw_property[constants.HW_ALIAS_DPI]))
323*800a58d9SAndroid Build Coastguard Worker
324*800a58d9SAndroid Build Coastguard Worker        super().CreateInstance(
325*800a58d9SAndroid Build Coastguard Worker            instance, image_name, self._machine_type, metadata, self._network,
326*800a58d9SAndroid Build Coastguard Worker            self._zone, disk_args, image_project, gpu, extra_disk_name,
327*800a58d9SAndroid Build Coastguard Worker            extra_scopes=extra_scopes, tags=tags)
328*800a58d9SAndroid Build Coastguard Worker
329*800a58d9SAndroid Build Coastguard Worker    def CheckBootFailure(self, serial_out, instance):
330*800a58d9SAndroid Build Coastguard Worker        """Determine if serial output has indicated any boot failure.
331*800a58d9SAndroid Build Coastguard Worker
332*800a58d9SAndroid Build Coastguard Worker        Subclass has to define this function to detect failures
333*800a58d9SAndroid Build Coastguard Worker        in the boot process
334*800a58d9SAndroid Build Coastguard Worker
335*800a58d9SAndroid Build Coastguard Worker        Args:
336*800a58d9SAndroid Build Coastguard Worker            serial_out: string
337*800a58d9SAndroid Build Coastguard Worker            instance: string, instance name.
338*800a58d9SAndroid Build Coastguard Worker
339*800a58d9SAndroid Build Coastguard Worker        Raises:
340*800a58d9SAndroid Build Coastguard Worker            Raises errors.DeviceBootError exception if a failure is detected.
341*800a58d9SAndroid Build Coastguard Worker        """
342*800a58d9SAndroid Build Coastguard Worker        pass
343*800a58d9SAndroid Build Coastguard Worker
344*800a58d9SAndroid Build Coastguard Worker    def CheckBoot(self, instance):
345*800a58d9SAndroid Build Coastguard Worker        """Check once to see if boot completes.
346*800a58d9SAndroid Build Coastguard Worker
347*800a58d9SAndroid Build Coastguard Worker        Args:
348*800a58d9SAndroid Build Coastguard Worker            instance: string, instance name.
349*800a58d9SAndroid Build Coastguard Worker
350*800a58d9SAndroid Build Coastguard Worker        Returns:
351*800a58d9SAndroid Build Coastguard Worker            True if the BOOT_COMPLETED_MSG or BOOT_STARTED_MSG appears in serial
352*800a58d9SAndroid Build Coastguard Worker            port output, otherwise False.
353*800a58d9SAndroid Build Coastguard Worker        """
354*800a58d9SAndroid Build Coastguard Worker        try:
355*800a58d9SAndroid Build Coastguard Worker            serial_out = self.GetSerialPortOutput(instance=instance, port=1)
356*800a58d9SAndroid Build Coastguard Worker            self.CheckBootFailure(serial_out, instance)
357*800a58d9SAndroid Build Coastguard Worker            return ((self.BOOT_COMPLETED_MSG in serial_out)
358*800a58d9SAndroid Build Coastguard Worker                    or (self.BOOT_STARTED_MSG in serial_out))
359*800a58d9SAndroid Build Coastguard Worker        except errors.HttpError as e:
360*800a58d9SAndroid Build Coastguard Worker            if e.code == 400:
361*800a58d9SAndroid Build Coastguard Worker                logger.debug("CheckBoot: Instance is not ready yet %s", str(e))
362*800a58d9SAndroid Build Coastguard Worker                return False
363*800a58d9SAndroid Build Coastguard Worker            logger.error("Unexpected http status: %d, %s", e.code, e.message)
364*800a58d9SAndroid Build Coastguard Worker            raise
365*800a58d9SAndroid Build Coastguard Worker
366*800a58d9SAndroid Build Coastguard Worker    def WaitForBoot(self, instance, boot_timeout_secs=None):
367*800a58d9SAndroid Build Coastguard Worker        """Wait for boot to completes or hit timeout.
368*800a58d9SAndroid Build Coastguard Worker
369*800a58d9SAndroid Build Coastguard Worker        Args:
370*800a58d9SAndroid Build Coastguard Worker            instance: string, instance name.
371*800a58d9SAndroid Build Coastguard Worker            boot_timeout_secs: Integer, the maximum time in seconds used to
372*800a58d9SAndroid Build Coastguard Worker                               wait for the AVD to boot.
373*800a58d9SAndroid Build Coastguard Worker        """
374*800a58d9SAndroid Build Coastguard Worker        boot_timeout_secs = boot_timeout_secs or constants.DEFAULT_CF_BOOT_TIMEOUT
375*800a58d9SAndroid Build Coastguard Worker        logger.info("Waiting for instance to boot up %s for %s secs",
376*800a58d9SAndroid Build Coastguard Worker                    instance, boot_timeout_secs)
377*800a58d9SAndroid Build Coastguard Worker        timeout_exception = errors.DeviceBootTimeoutError(
378*800a58d9SAndroid Build Coastguard Worker            "Device %s did not finish on boot within timeout (%s secs)" %
379*800a58d9SAndroid Build Coastguard Worker            (instance, boot_timeout_secs))
380*800a58d9SAndroid Build Coastguard Worker        utils.PollAndWait(
381*800a58d9SAndroid Build Coastguard Worker            func=self.CheckBoot,
382*800a58d9SAndroid Build Coastguard Worker            expected_return=True,
383*800a58d9SAndroid Build Coastguard Worker            timeout_exception=timeout_exception,
384*800a58d9SAndroid Build Coastguard Worker            timeout_secs=boot_timeout_secs,
385*800a58d9SAndroid Build Coastguard Worker            sleep_interval_secs=self.BOOT_CHECK_INTERVAL_SECS,
386*800a58d9SAndroid Build Coastguard Worker            instance=instance)
387*800a58d9SAndroid Build Coastguard Worker        logger.info("Instance boot completed: %s", instance)
388*800a58d9SAndroid Build Coastguard Worker
389*800a58d9SAndroid Build Coastguard Worker    def GetInstanceIP(self, instance, zone=None):
390*800a58d9SAndroid Build Coastguard Worker        """Get Instance IP given instance name.
391*800a58d9SAndroid Build Coastguard Worker
392*800a58d9SAndroid Build Coastguard Worker        Args:
393*800a58d9SAndroid Build Coastguard Worker            instance: String, representing instance name.
394*800a58d9SAndroid Build Coastguard Worker            zone: String, representing zone name, e.g. "us-central1-f"
395*800a58d9SAndroid Build Coastguard Worker
396*800a58d9SAndroid Build Coastguard Worker        Returns:
397*800a58d9SAndroid Build Coastguard Worker            ssh.IP object, that stores internal and external ip of the instance.
398*800a58d9SAndroid Build Coastguard Worker        """
399*800a58d9SAndroid Build Coastguard Worker        return super().GetInstanceIP(instance, zone or self._zone)
400*800a58d9SAndroid Build Coastguard Worker
401*800a58d9SAndroid Build Coastguard Worker    def GetSerialPortOutput(self, instance, zone=None, port=1):
402*800a58d9SAndroid Build Coastguard Worker        """Get serial port output.
403*800a58d9SAndroid Build Coastguard Worker
404*800a58d9SAndroid Build Coastguard Worker        Args:
405*800a58d9SAndroid Build Coastguard Worker            instance: string, instance name.
406*800a58d9SAndroid Build Coastguard Worker            zone: String, representing zone name, e.g. "us-central1-f"
407*800a58d9SAndroid Build Coastguard Worker            port: int, which COM port to read from, 1-4, default to 1.
408*800a58d9SAndroid Build Coastguard Worker
409*800a58d9SAndroid Build Coastguard Worker        Returns:
410*800a58d9SAndroid Build Coastguard Worker            String, contents of the output.
411*800a58d9SAndroid Build Coastguard Worker
412*800a58d9SAndroid Build Coastguard Worker        Raises:
413*800a58d9SAndroid Build Coastguard Worker            errors.DriverError: For malformed response.
414*800a58d9SAndroid Build Coastguard Worker        """
415*800a58d9SAndroid Build Coastguard Worker        return super().GetSerialPortOutput(
416*800a58d9SAndroid Build Coastguard Worker            instance, zone or self._zone, port)
417*800a58d9SAndroid Build Coastguard Worker
418*800a58d9SAndroid Build Coastguard Worker    def ExtendReportData(self, key, value):
419*800a58d9SAndroid Build Coastguard Worker        """Extend the report data.
420*800a58d9SAndroid Build Coastguard Worker
421*800a58d9SAndroid Build Coastguard Worker        Args:
422*800a58d9SAndroid Build Coastguard Worker            key: string of key name.
423*800a58d9SAndroid Build Coastguard Worker            value: string of data value.
424*800a58d9SAndroid Build Coastguard Worker        """
425*800a58d9SAndroid Build Coastguard Worker        self._dict_report.update({key: value})
426*800a58d9SAndroid Build Coastguard Worker
427*800a58d9SAndroid Build Coastguard Worker    @property
428*800a58d9SAndroid Build Coastguard Worker    def dict_report(self):
429*800a58d9SAndroid Build Coastguard Worker        """Return dict_report"""
430*800a58d9SAndroid Build Coastguard Worker        return self._dict_report
431*800a58d9SAndroid Build Coastguard Worker
432*800a58d9SAndroid Build Coastguard Worker    @property
433*800a58d9SAndroid Build Coastguard Worker    def gce_hostname(self):
434*800a58d9SAndroid Build Coastguard Worker        """Return gce_hostname"""
435*800a58d9SAndroid Build Coastguard Worker        return self._gce_hostname
436