• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/usr/bin/env python
2#
3# Copyright 2018 - The Android Open Source Project
4#
5# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9#     http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16"""A client that manages Cuttlefish Virtual Device on compute engine.
17
18** CvdComputeClient **
19
20CvdComputeClient derives from AndroidComputeClient. It manges a google
21compute engine project that is setup for running Cuttlefish Virtual Devices.
22It knows how to create a host instance from Cuttlefish Stable Host Image, fetch
23Android build, and start Android within the host instance.
24
25** Class hierarchy **
26
27  base_cloud_client.BaseCloudApiClient
28                ^
29                |
30       gcompute_client.ComputeClient
31                ^
32                |
33       android_compute_client.AndroidComputeClient
34                ^
35                |
36       cvd_compute_client.CvdComputeClient
37
38"""
39
40import getpass
41import logging
42
43from acloud.internal import constants
44from acloud.internal.lib import android_compute_client
45from acloud.internal.lib import gcompute_client
46from acloud.internal.lib import utils
47
48
49logger = logging.getLogger(__name__)
50
51_METADATA_TO_UNSET = ["cvd_01_launch",
52                      "cvd_01_fetch_android_build_target",
53                      "cvd_01_fetch_android_bid",
54                      "cvd_01_fetch_system_bid",
55                      "cvd_01_fetch_system_build_target",
56                      "cvd_01_fetch_kernel_bid",
57                      "cvd_01_fetch_kernel_build_target"]
58
59# TODO(228405515): Delete CvdComputeClient class.
60class CvdComputeClient(android_compute_client.AndroidComputeClient):
61    """Client that manages Anadroid Virtual Device."""
62
63    DATA_POLICY_CREATE_IF_MISSING = "create_if_missing"
64
65    # TODO: refactor CreateInstance to take in an object that contains these
66    # args, this method differs too and holds way too cf-specific args to put in
67    # the parent method.
68    # pylint: disable=arguments-differ,too-many-locals
69    @utils.TimeExecute(function_description="Creating GCE instance")
70    def CreateInstance(self, instance, image_name, image_project,
71                       build_target=None, branch=None, build_id=None,
72                       kernel_branch=None, kernel_build_id=None,
73                       kernel_build_target=None, blank_data_disk_size_gb=None,
74                       avd_spec=None, extra_scopes=None,
75                       system_build_target=None, system_branch=None,
76                       system_build_id=None):
77        """Create a cuttlefish instance given stable host image and build id.
78
79        Args:
80            instance: instance name.
81            image_name: A string, the name of the GCE image.
82            image_project: A string, name of the project where the image lives.
83                           Assume the default project if None.
84            build_target: Target name, e.g. "aosp_cf_x86_64_phone-userdebug"
85            branch: Branch name, e.g. "aosp-master"
86            build_id: Build id, a string, e.g. "2263051", "P2804227"
87            kernel_branch: Kernel branch name, e.g. "kernel-common-android-4.14"
88            kernel_build_id: Kernel build id, a string, e.g. "223051", "P280427"
89            kernel_build_target: String, Kernel build target name.
90            blank_data_disk_size_gb: Size of the blank data disk in GB.
91            avd_spec: An AVDSpec instance.
92            extra_scopes: A list of extra scopes to be passed to the instance.
93            system_build_target: Target name for the system image,
94                           e.g. "cf_x86_phone-userdebug"
95            system_branch: A String, branch name for the system image.
96            system_build_id: A string, build id for the system image.
97        """
98        self._CheckMachineSize()
99
100        # A blank data disk would be created on the host. Make sure the size of
101        # the boot disk is large enough to hold it.
102        boot_disk_size_gb = (
103            int(self.GetImage(image_name, image_project)["diskSizeGb"]) +
104            blank_data_disk_size_gb)
105        disk_args = self._GetDiskArgs(
106            instance, image_name, image_project, boot_disk_size_gb)
107
108        # Transitional metadata variable as outlined in go/cuttlefish-deployment
109        # These metadata tell the host instance to fetch and launch one
110        # cuttlefish device (cvd-01). Ideally we should use a separate tool to
111        # manage CVD devices on the host instance and not through metadata.
112        # TODO(b/77626419): Remove these metadata once the
113        # cuttlefish-google.service is turned off on the host instance.
114        metadata = self._metadata.copy()
115        metadata["cvd_01_fetch_android_build_target"] = build_target
116        metadata["cvd_01_fetch_android_bid"] = "{branch}/{build_id}".format(
117            branch=branch, build_id=build_id)
118        if kernel_branch and kernel_build_id:
119            metadata["cvd_01_fetch_kernel_bid"] = "{branch}/{build_id}".format(
120                branch=kernel_branch, build_id=kernel_build_id)
121        if kernel_build_target:
122            metadata["cvd_01_fetch_kernel_build_target"] = kernel_build_target
123        if system_build_target:
124            metadata["cvd_01_fetch_system_build_target"] = system_build_target
125        if system_branch and system_build_id:
126            metadata["cvd_01_fetch_system_bid"] = "{branch}/{build_id}".format(
127                branch=system_branch, build_id=system_build_id)
128        metadata["cvd_01_launch"] = self._GetLaunchCvdArgs(avd_spec)
129
130        # For the local image, we unset the _METADATA_TO_UNSET from
131        # metadata to tell server not to launch cvd and not to fetch image
132        # while instance is booted up.
133        if avd_spec and avd_spec.image_source == constants.IMAGE_SRC_LOCAL:
134            for meta in _METADATA_TO_UNSET:
135                metadata.pop(meta, None)
136
137        if blank_data_disk_size_gb > 0:
138            # Policy 'create_if_missing' would create a blank userdata disk if
139            # missing. If already exist, reuse the disk.
140            metadata["cvd_01_data_policy"] = self.DATA_POLICY_CREATE_IF_MISSING
141            metadata["cvd_01_blank_data_disk_size"] = str(
142                blank_data_disk_size_gb * 1024)
143        metadata["user"] = getpass.getuser()
144        # Update metadata by avd_spec
145        # for legacy create_cf cmd, we will keep using resolution.
146        # And always use avd_spec for acloud create cmd.
147        # TODO(b/118406018): deprecate resolution config and use hw_proprty for
148        # all create cmds.
149        if avd_spec:
150            metadata[constants.INS_KEY_AVD_TYPE] = avd_spec.avd_type
151            metadata[constants.INS_KEY_AVD_FLAVOR] = avd_spec.flavor
152            metadata["cvd_01_x_res"] = avd_spec.hw_property[constants.HW_X_RES]
153            metadata["cvd_01_y_res"] = avd_spec.hw_property[constants.HW_Y_RES]
154            metadata["cvd_01_dpi"] = avd_spec.hw_property[constants.HW_ALIAS_DPI]
155            if constants.HW_ALIAS_DISK in avd_spec.hw_property:
156                metadata["cvd_01_blank_data_disk_size"] = avd_spec.hw_property[
157                    constants.HW_ALIAS_DISK]
158            # Use another METADATA_DISPLAY to record resolution which will be
159            # retrieved in acloud list cmd. We try not to use cvd_01_x_res
160            # since cvd_01_xxx metadata is going to deprecated by cuttlefish.
161            metadata[constants.INS_KEY_DISPLAY] = ("%sx%s (%s)" % (
162                avd_spec.hw_property[constants.HW_X_RES],
163                avd_spec.hw_property[constants.HW_Y_RES],
164                avd_spec.hw_property[constants.HW_ALIAS_DPI]))
165        else:
166            resolution = self._resolution.split("x")
167            metadata["cvd_01_dpi"] = resolution[3]
168            metadata["cvd_01_x_res"] = resolution[0]
169            metadata["cvd_01_y_res"] = resolution[1]
170
171        gcompute_client.ComputeClient.CreateInstance(
172            self,
173            instance=instance,
174            image_name=image_name,
175            image_project=image_project,
176            disk_args=disk_args,
177            metadata=metadata,
178            machine_type=self._machine_type,
179            network=self._network,
180            zone=self._zone,
181            disk_type=avd_spec.disk_type if avd_spec else None,
182            extra_scopes=extra_scopes)
183
184    def _GetLaunchCvdArgs(self, avd_spec):
185        """Define the launch_cvd args.
186
187        Set launch_cvd args with following priority.
188        -First: Set args from config.
189        -Second: Set args from cpu and memory settings.
190        -Third: Set args as "1" to don't pass any args.
191
192        Args:
193            avd_spec: An AVDSpec instance.
194
195        Returns:
196            String of launch_cvd args.
197        """
198        if self._launch_args:
199            return self._launch_args
200
201        if avd_spec:
202            cpu_arg = ""
203            mem_arg = ""
204            if constants.HW_ALIAS_CPUS in avd_spec.hw_property:
205                cpu_arg = ("-cpus=%s" %
206                           avd_spec.hw_property[constants.HW_ALIAS_CPUS])
207            if constants.HW_ALIAS_MEMORY in avd_spec.hw_property:
208                mem_arg = ("-memory_mb=%s" %
209                           avd_spec.hw_property[constants.HW_ALIAS_MEMORY])
210            if cpu_arg or mem_arg:
211                return cpu_arg + " " + mem_arg
212
213        return "1"
214