blob: 09f6495b21ade101f3c4f2c608dde0e3cf3d1173 [file] [log] [blame]
Kevin Chengb5963882018-05-09 00:06:27 -07001#!/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.
Kevin Chengb5963882018-05-09 00:06:27 -070016"""Common operations between managing GCE and Cuttlefish devices.
17
18This module provides the common operations between managing GCE (device_driver)
19and Cuttlefish (create_cuttlefish_action) devices. Should not be called
20directly.
21"""
22
herbertxuedf01c422018-09-06 19:52:52 +080023from __future__ import print_function
Kevin Chengb5963882018-05-09 00:06:27 -070024import logging
herbertxuedf01c422018-09-06 19:52:52 +080025import time
Kevin Chengb5963882018-05-09 00:06:27 -070026
27from acloud.public import avd
28from acloud.public import errors
29from acloud.public import report
30from acloud.internal.lib import utils
31
32logger = logging.getLogger(__name__)
33
34
35def CreateSshKeyPairIfNecessary(cfg):
Kevin Cheng3031f8a2018-05-16 13:21:51 -070036 """Create ssh key pair if necessary.
Kevin Chengb5963882018-05-09 00:06:27 -070037
Kevin Cheng3031f8a2018-05-16 13:21:51 -070038 Args:
39 cfg: An Acloudconfig instance.
Kevin Chengb5963882018-05-09 00:06:27 -070040
Kevin Cheng3031f8a2018-05-16 13:21:51 -070041 Raises:
42 error.DriverError: If it falls into an unexpected condition.
43 """
44 if not cfg.ssh_public_key_path:
45 logger.warning(
46 "ssh_public_key_path is not specified in acloud config. "
47 "Project-wide public key will "
48 "be used when creating AVD instances. "
49 "Please ensure you have the correct private half of "
50 "a project-wide public key if you want to ssh into the "
51 "instances after creation.")
52 elif cfg.ssh_public_key_path and not cfg.ssh_private_key_path:
53 logger.warning(
54 "Only ssh_public_key_path is specified in acloud config, "
55 "but ssh_private_key_path is missing. "
56 "Please ensure you have the correct private half "
57 "if you want to ssh into the instances after creation.")
58 elif cfg.ssh_public_key_path and cfg.ssh_private_key_path:
59 utils.CreateSshKeyPairIfNotExist(cfg.ssh_private_key_path,
60 cfg.ssh_public_key_path)
61 else:
62 # Should never reach here.
63 raise errors.DriverError(
64 "Unexpected error in CreateSshKeyPairIfNecessary")
Kevin Chengb5963882018-05-09 00:06:27 -070065
66
67class DevicePool(object):
Kevin Cheng3031f8a2018-05-16 13:21:51 -070068 """A class that manages a pool of virtual devices.
Kevin Chengb5963882018-05-09 00:06:27 -070069
Kevin Cheng3031f8a2018-05-16 13:21:51 -070070 Attributes:
71 devices: A list of devices in the pool.
Kevin Chengb5963882018-05-09 00:06:27 -070072 """
73
Kevin Cheng3031f8a2018-05-16 13:21:51 -070074 def __init__(self, device_factory, devices=None):
75 """Constructs a new DevicePool.
Kevin Chengb5963882018-05-09 00:06:27 -070076
Kevin Cheng3031f8a2018-05-16 13:21:51 -070077 Args:
78 device_factory: A device factory capable of producing a goldfish or
79 cuttlefish device. The device factory must expose an attribute with
80 the credentials that can be used to retrieve information from the
81 constructed device.
82 devices: List of devices managed by this pool.
83 """
84 self._devices = devices or []
85 self._device_factory = device_factory
86 self._compute_client = device_factory.GetComputeClient()
Kevin Chengb5963882018-05-09 00:06:27 -070087
Kevin Cheng3031f8a2018-05-16 13:21:51 -070088 def CreateDevices(self, num):
89 """Creates |num| devices for given build_target and build_id.
Kevin Chengb5963882018-05-09 00:06:27 -070090
Kevin Cheng3031f8a2018-05-16 13:21:51 -070091 Args:
92 num: Number of devices to create.
93 """
Kevin Chengb5963882018-05-09 00:06:27 -070094
Kevin Cheng3031f8a2018-05-16 13:21:51 -070095 # Create host instances for cuttlefish/goldfish device.
96 # Currently one instance supports only 1 device.
97 for _ in range(num):
98 instance = self._device_factory.CreateInstance()
99 ip = self._compute_client.GetInstanceIP(instance)
100 self.devices.append(
101 avd.AndroidVirtualDevice(ip=ip, instance_name=instance))
102
103 def WaitForBoot(self):
104 """Waits for all devices to boot up.
105
106 Returns:
107 A dictionary that contains all the failures.
108 The key is the name of the instance that fails to boot,
109 and the value is an errors.DeviceBootError object.
110 """
111 failures = {}
112 for device in self._devices:
113 try:
114 self._compute_client.WaitForBoot(device.instance_name)
115 except errors.DeviceBootError as e:
116 failures[device.instance_name] = e
117 return failures
118
119 @property
120 def devices(self):
121 """Returns a list of devices in the pool.
122
123 Returns:
124 A list of devices in the pool.
125 """
126 return self._devices
Kevin Chengb5963882018-05-09 00:06:27 -0700127
128
Kevin Cheng86d43c72018-08-30 10:59:14 -0700129def CreateDevices(command, cfg, device_factory, num, report_internal_ip=False):
Kevin Cheng3031f8a2018-05-16 13:21:51 -0700130 """Create a set of devices using the given factory.
Kevin Chengb5963882018-05-09 00:06:27 -0700131
herbertxuedf01c422018-09-06 19:52:52 +0800132 Main jobs in create devices.
133 1. Create GCE instance: Launch instance in GCP(Google Cloud Platform).
134 2. Starting up AVD: Wait device boot up.
135
Kevin Cheng3031f8a2018-05-16 13:21:51 -0700136 Args:
137 command: The name of the command, used for reporting.
138 cfg: An AcloudConfig instance.
139 device_factory: A factory capable of producing a single device.
140 num: The number of devices to create.
Kevin Cheng86d43c72018-08-30 10:59:14 -0700141 report_internal_ip: Boolean to report the internal ip instead of
142 external ip.
Kevin Chengb5963882018-05-09 00:06:27 -0700143
herbertxuedf01c422018-09-06 19:52:52 +0800144 Raises:
145 errors: Create instance fail.
146
Kevin Cheng3031f8a2018-05-16 13:21:51 -0700147 Returns:
148 A Report instance.
149 """
150 reporter = report.Report(command=command)
151 try:
herbertxuedf01c422018-09-06 19:52:52 +0800152 gce_start_time = time.time()
153 utils.PrintColorString("Creating GCE instance%s..." %
154 ("s" if num > 1 else ""), end="")
Kevin Cheng3031f8a2018-05-16 13:21:51 -0700155 CreateSshKeyPairIfNecessary(cfg)
156 device_pool = DevicePool(device_factory)
herbertxuedf01c422018-09-06 19:52:52 +0800157 try:
158 device_pool.CreateDevices(num)
159 except:
160 utils.PrintColorString("Fail (%ds)" % (time.time() - gce_start_time),
161 utils.TextColors.FAIL)
162 raise
163 utils.PrintColorString("OK (%ds)" % (time.time() - gce_start_time),
164 utils.TextColors.OKGREEN)
165
166 utils.PrintColorString("Starting up AVD%s..." %
167 ("s" if num > 1 else ""), end="")
168 start_boot_time = time.time()
Kevin Cheng3031f8a2018-05-16 13:21:51 -0700169 failures = device_pool.WaitForBoot()
herbertxuedf01c422018-09-06 19:52:52 +0800170 if failures:
171 reporter.SetStatus(report.Status.BOOT_FAIL)
172 utils.PrintColorString("Fail (%ds)" % (time.time() - start_boot_time),
173 utils.TextColors.FAIL)
174 else:
175 reporter.SetStatus(report.Status.SUCCESS)
176 utils.PrintColorString("OK (%ds)" % (time.time() - start_boot_time),
177 utils.TextColors.OKGREEN)
Kevin Cheng3031f8a2018-05-16 13:21:51 -0700178 # Write result to report.
179 for device in device_pool.devices:
180 device_dict = {
Kevin Cheng86d43c72018-08-30 10:59:14 -0700181 "ip": (device.ip.internal if report_internal_ip
182 else device.ip.external),
Kevin Cheng3031f8a2018-05-16 13:21:51 -0700183 "instance_name": device.instance_name
184 }
185 if device.instance_name in failures:
186 reporter.AddData(key="devices_failing_boot", value=device_dict)
187 reporter.AddError(str(failures[device.instance_name]))
188 else:
189 reporter.AddData(key="devices", value=device_dict)
Kevin Cheng3031f8a2018-05-16 13:21:51 -0700190 except errors.DriverError as e:
191 reporter.AddError(str(e))
192 reporter.SetStatus(report.Status.FAIL)
193 return reporter