xref: /aosp_15_r20/tools/acloud/public/actions/create_goldfish_action.py (revision 800a58d989c669b8eb8a71d8df53b1ba3d411444)
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"""Action to create goldfish device instances.
17
18A Goldfish device is an emulated android device based on the android
19emulator.
20"""
21import logging
22import os
23import re
24
25from acloud import errors
26from acloud.public.actions import base_device_factory
27from acloud.public.actions import common_operations
28from acloud.internal import constants
29from acloud.internal.lib import android_build_client
30from acloud.internal.lib import auth
31from acloud.internal.lib import goldfish_compute_client
32from acloud.internal.lib import utils
33
34
35logger = logging.getLogger(__name__)
36
37_EMULATOR_INFO_FILENAME = "emulator-info.txt"
38_SYSIMAGE_INFO_FILENAME = "android-info.txt"
39_VERSION_PATTERN = r"version-.*=(\d+)"
40
41
42class GoldfishDeviceFactory(base_device_factory.BaseDeviceFactory):
43    """A class that can produce a goldfish device.
44
45    Attributes:
46        _cfg: An AcloudConfig instance.
47        _build_target: String, the build target, e.g. aosp_x86-eng.
48        _build_id: String, Build id, e.g. "2263051", "P2804227"
49        _emulator_build_target: String, the emulator build target, e.g. aosp_x86-eng.
50        _emulator_build_id: String, emulator build id.
51        _gpu: String, GPU to attach to the device or None. e.g. "nvidia-tesla-k80"
52        _blank_data_disk_size_gb: Integer, extra disk size
53        _build_client: An AndroidBuildClient instance
54        _branch: String, android branch name, e.g. git_master
55        _emulator_branch: String, emulator branch name, e.g. "aosp-emu-master-dev"
56    """
57    LOG_FILES = ["/home/vsoc-01/emulator.log",
58                 "/home/vsoc-01/log/logcat.log",
59                 "/home/vsoc-01/log/adb.log",
60                 "/var/log/daemon.log"]
61
62    def __init__(self,
63                 cfg,
64                 build_target,
65                 build_id,
66                 emulator_build_target,
67                 emulator_build_id,
68                 kernel_build_id=None,
69                 kernel_branch=None,
70                 kernel_build_target=None,
71                 gpu=None,
72                 avd_spec=None,
73                 tags=None,
74                 branch=None,
75                 emulator_branch=None):
76
77        """Initialize.
78
79        Args:
80            cfg: An AcloudConfig instance.
81            build_target: String, the build target, e.g. aosp_x86-eng.
82            build_id: String, Build id, e.g. "2263051", "P2804227"
83            emulator_build_target: String, the emulator build target, e.g. aosp_x86-eng.
84            emulator_build_id: String, emulator build id.
85            gpu: String, GPU to attach to the device or None. e.g. "nvidia-tesla-k80"
86            avd_spec: An AVDSpec instance.
87            tags: A list of tags to associate with the instance. e.g.
88                  ["http-server", "https-server"]
89            branch: String, branch of the emulator build target.
90            emulator_branch: String, branch of the emulator.
91        """
92
93        self.credentials = auth.CreateCredentials(cfg)
94
95        compute_client = goldfish_compute_client.GoldfishComputeClient(
96            cfg, self.credentials)
97        super().__init__(compute_client)
98
99        # Private creation parameters
100        self._cfg = cfg
101        self._gpu = gpu
102        self._avd_spec = avd_spec
103        self._blank_data_disk_size_gb = cfg.extra_data_disk_size_gb
104        self._extra_scopes = cfg.extra_scopes
105        self._tags = tags
106
107        # Configure clients
108        self._build_client = android_build_client.AndroidBuildClient(
109            self.credentials)
110
111        # Get build info
112        self.build_info = self._build_client.GetBuildInfo(
113            build_target, build_id, branch)
114        self.emulator_build_info = self._build_client.GetBuildInfo(
115            emulator_build_target, emulator_build_id, emulator_branch)
116        self.kernel_build_info = self._build_client.GetBuildInfo(
117            kernel_build_target or cfg.kernel_build_target, kernel_build_id,
118            kernel_branch)
119
120    def GetBuildInfoDict(self):
121        """Get build info dictionary.
122
123        Returns:
124          A build info dictionary
125        """
126        build_info_dict = {
127            key: val for key, val in utils.GetDictItems(self.build_info) if val}
128
129        build_info_dict.update(
130            {"emulator_%s" % key: val
131             for key, val in utils.GetDictItems(self.emulator_build_info) if val}
132            )
133
134        build_info_dict.update(
135            {"kernel_%s" % key: val
136             for key, val in utils.GetDictItems(self.kernel_build_info) if val}
137            )
138
139        return build_info_dict
140
141    def CreateInstance(self):
142        """Creates single configured goldfish device.
143
144        Override method from parent class.
145
146        Returns:
147            String, the name of the created instance.
148        """
149        instance = self._compute_client.GenerateInstanceName(
150            build_id=self.build_info.build_id,
151            build_target=self.build_info.build_target)
152
153        self._compute_client.CreateInstance(
154            instance=instance,
155            image_name=self._cfg.stable_goldfish_host_image_name,
156            image_project=self._cfg.stable_goldfish_host_image_project,
157            build_target=self.build_info.build_target,
158            branch=self.build_info.branch,
159            build_id=self.build_info.build_id,
160            emulator_branch=self.emulator_build_info.branch,
161            emulator_build_id=self.emulator_build_info.build_id,
162            emulator_build_target=self.emulator_build_info.build_target,
163            kernel_branch=self.kernel_build_info.branch,
164            kernel_build_id=self.kernel_build_info.build_id,
165            kernel_build_target=self.kernel_build_info.build_target,
166            gpu=self._gpu,
167            blank_data_disk_size_gb=self._blank_data_disk_size_gb,
168            avd_spec=self._avd_spec,
169            tags=self._tags,
170            extra_scopes=self._extra_scopes,
171            launch_args=self._cfg.launch_args)
172
173        return instance
174
175
176def ParseBuildInfo(filename):
177    """Parse build id based on a substring.
178
179    This will parse a file which contains build information to be used. For an
180    emulator build, the file will contain the information about the
181    corresponding stable system image build id. In emulator-info.txt, the file
182    will contains the information about the corresponding stable emulator
183    build id, for example "require version-emulator=5292001". In
184    android-info.txt, the file will contains the information about a stable
185    system image build id, for example
186    "version-sysimage-git_pi-dev-sdk_gphone_x86_64-userdebug=4833817"
187
188    Args:
189        filename: Name of file to parse.
190
191    Returns:
192        Build id parsed from the file based on pattern
193        Returns None if pattern not found in file
194    """
195    with open(filename) as build_info_file:
196        for line in build_info_file:
197            match = re.search(_VERSION_PATTERN, line)
198            if match:
199                return match.group(1)
200    return None
201
202
203def _FetchBuildIdFromFile(cfg, build_target, build_id, filename):
204    """Parse and fetch build id from a file based on a pattern.
205
206    Verify if one of the system image or emulator binary build id is missing.
207    If found missing, then update according to the resource file.
208
209    Args:
210        cfg: An AcloudConfig instance.
211        build_target: Target name.
212        build_id: Build id, a string, e.g. "2263051", "P2804227"
213        filename: Name of file containing the build info.
214
215    Returns:
216        A build id or None
217    """
218    build_client = android_build_client.AndroidBuildClient(
219        auth.CreateCredentials(cfg))
220
221    with utils.TempDir() as tempdir:
222        temp_filename = os.path.join(tempdir, filename)
223        build_client.DownloadArtifact(build_target,
224                                      build_id,
225                                      filename,
226                                      temp_filename)
227
228        return ParseBuildInfo(temp_filename)
229
230
231#pylint: disable=too-many-locals
232def CreateDevices(avd_spec=None,
233                  cfg=None,
234                  build_target=None,
235                  build_id=None,
236                  emulator_build_id=None,
237                  emulator_branch=None,
238                  emulator_build_target=None,
239                  kernel_build_id=None,
240                  kernel_branch=None,
241                  kernel_build_target=None,
242                  gpu=None,
243                  num=1,
244                  serial_log_file=None,
245                  autoconnect=False,
246                  branch=None,
247                  tags=None,
248                  report_internal_ip=False,
249                  boot_timeout_secs=None):
250    """Create one or multiple Goldfish devices.
251
252    Args:
253        avd_spec: An AVDSpec instance.
254        cfg: An AcloudConfig instance.
255        build_target: String, the build target, e.g. aosp_x86-eng.
256        build_id: String, Build id, e.g. "2263051", "P2804227"
257        branch: String, Branch name for system image.
258        emulator_build_id: String, emulator build id.
259        emulator_branch: String, emulator branch name.
260        emulator_build_target: String, emulator build target.
261        gpu: String, GPU to attach to the device or None. e.g. "nvidia-k80"
262        kernel_build_id: Kernel build id, a string.
263        kernel_branch: Kernel branch name, a string.
264        kernel_build_target: Kernel build artifact, a string.
265        num: Integer, Number of devices to create.
266        serial_log_file: String, A path to a file where serial output should
267                        be saved to.
268        autoconnect: Boolean, Create ssh tunnel(s) and adb connect after device
269                     creation.
270        branch: String, Branch name for system image.
271        tags: A list of tags to associate with the instance. e.g.
272              ["http-server", "https-server"]
273        report_internal_ip: Boolean to report the internal ip instead of
274                            external ip.
275        boot_timeout_secs: Integer, the maximum time in seconds used to
276                           wait for the AVD to boot.
277
278    Returns:
279        A Report instance.
280    """
281    client_adb_port = None
282    if avd_spec:
283        cfg = avd_spec.cfg
284        build_target = avd_spec.remote_image[constants.BUILD_TARGET]
285        build_id = avd_spec.remote_image[constants.BUILD_ID]
286        branch = avd_spec.remote_image[constants.BUILD_BRANCH]
287        num = avd_spec.num
288        emulator_build_id = avd_spec.emulator_build_id
289        emulator_build_target = avd_spec.emulator_build_target
290        gpu = avd_spec.gpu
291        serial_log_file = avd_spec.serial_log_file
292        autoconnect = avd_spec.autoconnect
293        report_internal_ip = avd_spec.report_internal_ip
294        client_adb_port = avd_spec.client_adb_port
295        boot_timeout_secs = avd_spec.boot_timeout_secs
296
297    if not emulator_build_target:
298        emulator_build_target = cfg.emulator_build_target
299
300    # If emulator_build_id and emulator_branch is None, retrieve emulator
301    # build id from platform build emulator-info.txt artifact
302    # Example: require version-emulator=5292001
303    if not emulator_build_id and not emulator_branch:
304        logger.info("emulator_build_id not provided. "
305                    "Attempting to get %s from build %s/%s.", _EMULATOR_INFO_FILENAME,
306                    build_id, build_target)
307        emulator_build_id = _FetchBuildIdFromFile(cfg,
308                                                  build_target,
309                                                  build_id,
310                                                  _EMULATOR_INFO_FILENAME)
311
312    if not emulator_build_id:
313        raise errors.CommandArgError("Emulator build id not found "
314                                     "in %s" % _EMULATOR_INFO_FILENAME)
315
316    # If build_id and branch is None, retrieve build_id from
317    # emulator build android-info.txt artifact
318    # Example: version-sysimage-git_pi-dev-sdk_gphone_x86_64-userdebug=4833817
319    if not build_id and not branch:
320        build_id = _FetchBuildIdFromFile(cfg,
321                                         emulator_build_target,
322                                         emulator_build_id,
323                                         _SYSIMAGE_INFO_FILENAME)
324
325    if not build_id:
326        raise errors.CommandArgError("Emulator system image build id not found "
327                                     "in %s" % _SYSIMAGE_INFO_FILENAME)
328    logger.info(
329        "Creating a goldfish device in project %s, build_target: %s, "
330        "build_id: %s, emulator_bid: %s, emulator_branch: %s, kernel_build_id: %s, "
331        "kernel_branch: %s, kernel_build_target: %s, GPU: %s, num: %s, "
332        "serial_log_file: %s, "
333        "autoconnect: %s", cfg.project, build_target, build_id,
334        emulator_build_id, emulator_branch, kernel_build_id, kernel_branch,
335        kernel_build_target, gpu, num, serial_log_file, autoconnect)
336
337    device_factory = GoldfishDeviceFactory(
338        cfg, build_target, build_id,
339        emulator_build_target,
340        emulator_build_id, gpu=gpu,
341        avd_spec=avd_spec, tags=tags,
342        branch=branch,
343        emulator_branch=emulator_branch,
344        kernel_build_id=kernel_build_id,
345        kernel_branch=kernel_branch,
346        kernel_build_target=kernel_build_target)
347
348    return common_operations.CreateDevices("create_gf", cfg, device_factory,
349                                           num, constants.TYPE_GF,
350                                           report_internal_ip, autoconnect,
351                                           serial_log_file, client_adb_port,
352                                           boot_timeout_secs)
353