blob: 7eecd11f7dcfe15bbd65058ff9e70c72da3c566f [file] [log] [blame]
Keun Soo Yimb293fdb2016-09-21 16:03:44 -07001#!/usr/bin/env python
2#
3# Copyright 2016 - 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
17"""Public Device Driver APIs.
18
19This module provides public device driver APIs that can be called
20as a Python library.
21
Kevin Chengb5963882018-05-09 00:06:27 -070022TODO: The following APIs have not been implemented
Keun Soo Yimb293fdb2016-09-21 16:03:44 -070023 - RebootAVD(ip):
24 - RegisterSshPubKey(username, key):
25 - UnregisterSshPubKey(username, key):
26 - CleanupStaleImages():
27 - CleanupStaleDevices():
28"""
29
Kevin Chengd9d5f0f2018-06-19 14:54:17 -070030from __future__ import print_function
Keun Soo Yimb293fdb2016-09-21 16:03:44 -070031import datetime
32import logging
33import os
34
Kevin Chengd9d5f0f2018-06-19 14:54:17 -070035# pylint: disable=import-error
Keun Soo Yimb293fdb2016-09-21 16:03:44 -070036import dateutil.parser
37import dateutil.tz
38
Sam Chiu7de3b232018-12-06 19:45:52 +080039from acloud import errors
Keun Soo Yimb293fdb2016-09-21 16:03:44 -070040from acloud.public import avd
Keun Soo Yimb293fdb2016-09-21 16:03:44 -070041from acloud.public import report
Kevin Chengb5963882018-05-09 00:06:27 -070042from acloud.public.actions import common_operations
Keun Soo Yimb293fdb2016-09-21 16:03:44 -070043from acloud.internal import constants
44from acloud.internal.lib import auth
45from acloud.internal.lib import android_build_client
46from acloud.internal.lib import android_compute_client
47from acloud.internal.lib import gstorage_client
48from acloud.internal.lib import utils
49
50logger = logging.getLogger(__name__)
51
Keun Soo Yimb293fdb2016-09-21 16:03:44 -070052MAX_BATCH_CLEANUP_COUNT = 100
53
cylan66713722018-10-06 01:38:26 +080054_SSH_USER = "root"
Kevin Chengb5963882018-05-09 00:06:27 -070055
Keun Soo Yimb293fdb2016-09-21 16:03:44 -070056
Kevin Cheng5c124ec2018-05-16 13:28:51 -070057# pylint: disable=invalid-name
Keun Soo Yimb293fdb2016-09-21 16:03:44 -070058class AndroidVirtualDevicePool(object):
59 """A class that manages a pool of devices."""
60
61 def __init__(self, cfg, devices=None):
62 self._devices = devices or []
63 self._cfg = cfg
Sam Chiu4d9bb4b2018-10-26 11:38:23 +080064 credentials = auth.CreateCredentials(cfg)
Keun Soo Yimb293fdb2016-09-21 16:03:44 -070065 self._build_client = android_build_client.AndroidBuildClient(
66 credentials)
67 self._storage_client = gstorage_client.StorageClient(credentials)
68 self._compute_client = android_compute_client.AndroidComputeClient(
69 cfg, credentials)
70
chojoyce7a361732018-11-26 16:26:13 +080071 @utils.TimeExecute("Creating GCE image")
Keun Soo Yimb293fdb2016-09-21 16:03:44 -070072 def _CreateGceImageWithBuildInfo(self, build_target, build_id):
73 """Creates a Gce image using build from Launch Control.
74
75 Clone avd-system.tar.gz of a build to a cache storage bucket
76 using launch control api. And then create a Gce image.
77
78 Args:
Kevin Chengb5963882018-05-09 00:06:27 -070079 build_target: Target name, e.g. "aosp_cf_x86_phone-userdebug"
Keun Soo Yimb293fdb2016-09-21 16:03:44 -070080 build_id: Build id, a string, e.g. "2263051", "P2804227"
81
82 Returns:
83 String, name of the Gce image that has been created.
84 """
85 logger.info("Creating a new gce image using build: build_id %s, "
86 "build_target %s", build_id, build_target)
87 disk_image_id = utils.GenerateUniqueName(
88 suffix=self._cfg.disk_image_name)
89 self._build_client.CopyTo(
90 build_target,
91 build_id,
92 artifact_name=self._cfg.disk_image_name,
93 destination_bucket=self._cfg.storage_bucket_name,
94 destination_path=disk_image_id)
95 disk_image_url = self._storage_client.GetUrl(
96 self._cfg.storage_bucket_name, disk_image_id)
97 try:
98 image_name = self._compute_client.GenerateImageName(build_target,
99 build_id)
100 self._compute_client.CreateImage(image_name=image_name,
101 source_uri=disk_image_url)
102 finally:
103 self._storage_client.Delete(self._cfg.storage_bucket_name,
104 disk_image_id)
105 return image_name
106
chojoyce7a361732018-11-26 16:26:13 +0800107 @utils.TimeExecute("Creating GCE image")
Keun Soo Yimb293fdb2016-09-21 16:03:44 -0700108 def _CreateGceImageWithLocalFile(self, local_disk_image):
109 """Create a Gce image with a local image file.
110
111 The local disk image can be either a tar.gz file or a
112 raw vmlinux image.
113 e.g. /tmp/avd-system.tar.gz or /tmp/android_system_disk_syslinux.img
114 If a raw vmlinux image is provided, it will be archived into a tar.gz file.
115
116 The final tar.gz file will be uploaded to a cache bucket in storage.
117
118 Args:
119 local_disk_image: string, path to a local disk image,
120
121 Returns:
122 String, name of the Gce image that has been created.
123
124 Raises:
125 DriverError: if a file with an unexpected extension is given.
126 """
127 logger.info("Creating a new gce image from a local file %s",
128 local_disk_image)
129 with utils.TempDir() as tempdir:
130 if local_disk_image.endswith(self._cfg.disk_raw_image_extension):
131 dest_tar_file = os.path.join(tempdir,
132 self._cfg.disk_image_name)
133 utils.MakeTarFile(
134 src_dict={local_disk_image: self._cfg.disk_raw_image_name},
135 dest=dest_tar_file)
136 local_disk_image = dest_tar_file
137 elif not local_disk_image.endswith(self._cfg.disk_image_extension):
138 raise errors.DriverError(
139 "Wrong local_disk_image type, must be a *%s file or *%s file"
140 % (self._cfg.disk_raw_image_extension,
141 self._cfg.disk_image_extension))
142
143 disk_image_id = utils.GenerateUniqueName(
144 suffix=self._cfg.disk_image_name)
145 self._storage_client.Upload(
146 local_src=local_disk_image,
147 bucket_name=self._cfg.storage_bucket_name,
148 object_name=disk_image_id,
149 mime_type=self._cfg.disk_image_mime_type)
150 disk_image_url = self._storage_client.GetUrl(
151 self._cfg.storage_bucket_name, disk_image_id)
152 try:
153 image_name = self._compute_client.GenerateImageName()
154 self._compute_client.CreateImage(image_name=image_name,
155 source_uri=disk_image_url)
156 finally:
157 self._storage_client.Delete(self._cfg.storage_bucket_name,
158 disk_image_id)
159 return image_name
160
herbertxue6ef54a52019-05-02 11:38:58 +0800161 # pylint: disable=too-many-locals
Keun Soo Yimb293fdb2016-09-21 16:03:44 -0700162 def CreateDevices(self,
163 num,
164 build_target=None,
165 build_id=None,
166 gce_image=None,
167 local_disk_image=None,
168 cleanup=True,
169 extra_data_disk_size_gb=None,
chojoyce7a361732018-11-26 16:26:13 +0800170 precreated_data_image=None,
Kevin Chengc330f6f2019-05-13 09:32:42 -0700171 avd_spec=None,
172 extra_scopes=None):
Keun Soo Yimb293fdb2016-09-21 16:03:44 -0700173 """Creates |num| devices for given build_target and build_id.
174
175 - If gce_image is provided, will use it to create an instance.
176 - If local_disk_image is provided, will upload it to a temporary
177 caching storage bucket which is defined by user as |storage_bucket_name|
178 And then create an gce image with it; and then create an instance.
179 - If build_target and build_id are provided, will clone the disk image
180 via launch control to the temporary caching storage bucket.
181 And then create an gce image with it; and then create an instance.
182
183 Args:
184 num: Number of devices to create.
Kevin Chengb5963882018-05-09 00:06:27 -0700185 build_target: Target name, e.g. "aosp_cf_x86_phone-userdebug"
Keun Soo Yimb293fdb2016-09-21 16:03:44 -0700186 build_id: Build id, a string, e.g. "2263051", "P2804227"
187 gce_image: string, if given, will use this image
188 instead of creating a new one.
189 implies cleanup=False.
190 local_disk_image: string, path to a local disk image, e.g.
191 /tmp/avd-system.tar.gz
192 cleanup: boolean, if True clean up compute engine image after creating
193 the instance.
194 extra_data_disk_size_gb: Integer, size of extra disk, or None.
195 precreated_data_image: A string, the image to use for the extra disk.
chojoyce7a361732018-11-26 16:26:13 +0800196 avd_spec: AVDSpec object for pass hw_property.
Kevin Chengc330f6f2019-05-13 09:32:42 -0700197 extra_scopes: A list of extra scopes given to the new instance.
Keun Soo Yimb293fdb2016-09-21 16:03:44 -0700198
199 Raises:
200 errors.DriverError: If no source is specified for image creation.
201 """
202 if gce_image:
203 # GCE image is provided, we can directly move to instance creation.
204 logger.info("Using existing gce image %s", gce_image)
205 image_name = gce_image
206 cleanup = False
207 elif local_disk_image:
208 image_name = self._CreateGceImageWithLocalFile(local_disk_image)
209 elif build_target and build_id:
210 image_name = self._CreateGceImageWithBuildInfo(build_target,
211 build_id)
212 else:
213 raise errors.DriverError(
214 "Invalid image source, must specify one of the following: gce_image, "
215 "local_disk_image, or build_target and build id.")
216
217 # Create GCE instances.
218 try:
219 for _ in range(num):
220 instance = self._compute_client.GenerateInstanceName(
221 build_target, build_id)
222 extra_disk_name = None
223 if extra_data_disk_size_gb > 0:
224 extra_disk_name = self._compute_client.GetDataDiskName(
225 instance)
226 self._compute_client.CreateDisk(extra_disk_name,
227 precreated_data_image,
228 extra_data_disk_size_gb)
Kevin Chengb5963882018-05-09 00:06:27 -0700229 self._compute_client.CreateInstance(
230 instance=instance,
231 image_name=image_name,
chojoyce7a361732018-11-26 16:26:13 +0800232 extra_disk_name=extra_disk_name,
Kevin Chengc330f6f2019-05-13 09:32:42 -0700233 avd_spec=avd_spec,
234 extra_scopes=extra_scopes)
Keun Soo Yimb293fdb2016-09-21 16:03:44 -0700235 ip = self._compute_client.GetInstanceIP(instance)
236 self.devices.append(avd.AndroidVirtualDevice(
237 ip=ip, instance_name=instance))
238 finally:
239 if cleanup:
240 self._compute_client.DeleteImage(image_name)
241
242 def DeleteDevices(self):
243 """Deletes devices.
244
245 Returns:
246 A tuple, (deleted, failed, error_msgs)
247 deleted: A list of names of instances that have been deleted.
248 faild: A list of names of instances that we fail to delete.
249 error_msgs: A list of failure messages.
250 """
251 instance_names = [device.instance_name for device in self._devices]
252 return self._compute_client.DeleteInstances(instance_names,
253 self._cfg.zone)
254
chojoyce7a361732018-11-26 16:26:13 +0800255 @utils.TimeExecute("Waiting for AVD to boot")
Keun Soo Yimb293fdb2016-09-21 16:03:44 -0700256 def WaitForBoot(self):
257 """Waits for all devices to boot up.
258
259 Returns:
260 A dictionary that contains all the failures.
261 The key is the name of the instance that fails to boot,
Kevin Chengb5963882018-05-09 00:06:27 -0700262 the value is an errors.DeviceBoottError object.
Keun Soo Yimb293fdb2016-09-21 16:03:44 -0700263 """
264 failures = {}
265 for device in self._devices:
266 try:
267 self._compute_client.WaitForBoot(device.instance_name)
Kevin Chengb5963882018-05-09 00:06:27 -0700268 except errors.DeviceBootError as e:
Keun Soo Yimb293fdb2016-09-21 16:03:44 -0700269 failures[device.instance_name] = e
270 return failures
271
272 @property
273 def devices(self):
274 """Returns a list of devices in the pool.
275
276 Returns:
277 A list of devices in the pool.
278 """
279 return self._devices
280
281
herbertxue07293a32018-11-05 20:40:11 +0800282def AddDeletionResultToReport(report_obj, deleted, failed, error_msgs,
283 resource_name):
Keun Soo Yimb293fdb2016-09-21 16:03:44 -0700284 """Adds deletion result to a Report object.
285
286 This function will add the following to report.data.
287 "deleted": [
288 {"name": "resource_name", "type": "resource_name"},
289 ],
290 "failed": [
291 {"name": "resource_name", "type": "resource_name"},
292 ],
293 This function will append error_msgs to report.errors.
294
295 Args:
296 report_obj: A Report object.
297 deleted: A list of names of the resources that have been deleted.
298 failed: A list of names of the resources that we fail to delete.
299 error_msgs: A list of error message strings to be added to the report.
300 resource_name: A string, representing the name of the resource.
301 """
302 for name in deleted:
303 report_obj.AddData(key="deleted",
304 value={"name": name,
305 "type": resource_name})
306 for name in failed:
307 report_obj.AddData(key="failed",
308 value={"name": name,
309 "type": resource_name})
310 report_obj.AddErrors(error_msgs)
311 if failed or error_msgs:
312 report_obj.SetStatus(report.Status.FAIL)
313
314
315def _FetchSerialLogsFromDevices(compute_client, instance_names, output_file,
316 port):
317 """Fetch serial logs from a port for a list of devices to a local file.
318
319 Args:
320 compute_client: An object of android_compute_client.AndroidComputeClient
321 instance_names: A list of instance names.
322 output_file: A path to a file ending with "tar.gz"
323 port: The number of serial port to read from, 0 for serial output, 1 for
324 logcat.
325 """
326 with utils.TempDir() as tempdir:
327 src_dict = {}
328 for instance_name in instance_names:
329 serial_log = compute_client.GetSerialPortOutput(
330 instance=instance_name, port=port)
331 file_name = "%s.log" % instance_name
332 file_path = os.path.join(tempdir, file_name)
333 src_dict[file_path] = file_name
334 with open(file_path, "w") as f:
335 f.write(serial_log.encode("utf-8"))
336 utils.MakeTarFile(src_dict, output_file)
337
338
Kevin Cheng5c124ec2018-05-16 13:28:51 -0700339# pylint: disable=too-many-locals
Keun Soo Yimb293fdb2016-09-21 16:03:44 -0700340def CreateAndroidVirtualDevices(cfg,
341 build_target=None,
342 build_id=None,
343 num=1,
344 gce_image=None,
345 local_disk_image=None,
346 cleanup=True,
347 serial_log_file=None,
Kevin Chengb5963882018-05-09 00:06:27 -0700348 logcat_file=None,
Kevin Cheng86d43c72018-08-30 10:59:14 -0700349 autoconnect=False,
chojoyce7a361732018-11-26 16:26:13 +0800350 report_internal_ip=False,
351 avd_spec=None):
Keun Soo Yimb293fdb2016-09-21 16:03:44 -0700352 """Creates one or multiple android devices.
353
354 Args:
355 cfg: An AcloudConfig instance.
Kevin Chengb5963882018-05-09 00:06:27 -0700356 build_target: Target name, e.g. "aosp_cf_x86_phone-userdebug"
Keun Soo Yimb293fdb2016-09-21 16:03:44 -0700357 build_id: Build id, a string, e.g. "2263051", "P2804227"
358 num: Number of devices to create.
359 gce_image: string, if given, will use this gce image
360 instead of creating a new one.
361 implies cleanup=False.
362 local_disk_image: string, path to a local disk image, e.g.
363 /tmp/avd-system.tar.gz
364 cleanup: boolean, if True clean up compute engine image and
365 disk image in storage after creating the instance.
366 serial_log_file: A path to a file where serial output should
Fang Dengfbef7c92017-02-08 14:09:34 -0800367 be saved to.
Keun Soo Yimb293fdb2016-09-21 16:03:44 -0700368 logcat_file: A path to a file where logcat logs should be saved.
Kevin Chengb5963882018-05-09 00:06:27 -0700369 autoconnect: Create ssh tunnel(s) and adb connect after device creation.
Kevin Cheng86d43c72018-08-30 10:59:14 -0700370 report_internal_ip: Boolean to report the internal ip instead of
371 external ip.
chojoyce7a361732018-11-26 16:26:13 +0800372 avd_spec: AVDSpec object for pass hw_property.
Keun Soo Yimb293fdb2016-09-21 16:03:44 -0700373
374 Returns:
375 A Report instance.
376 """
377 r = report.Report(command="create")
Sam Chiu4d9bb4b2018-10-26 11:38:23 +0800378 credentials = auth.CreateCredentials(cfg)
Keun Soo Yimb293fdb2016-09-21 16:03:44 -0700379 compute_client = android_compute_client.AndroidComputeClient(cfg,
380 credentials)
381 try:
Kevin Chengb5963882018-05-09 00:06:27 -0700382 common_operations.CreateSshKeyPairIfNecessary(cfg)
Keun Soo Yimb293fdb2016-09-21 16:03:44 -0700383 device_pool = AndroidVirtualDevicePool(cfg)
384 device_pool.CreateDevices(
385 num,
386 build_target,
387 build_id,
388 gce_image,
389 local_disk_image,
390 cleanup,
391 extra_data_disk_size_gb=cfg.extra_data_disk_size_gb,
392 precreated_data_image=cfg.precreated_data_image_map.get(
chojoyce7a361732018-11-26 16:26:13 +0800393 cfg.extra_data_disk_size_gb),
Kevin Chengc330f6f2019-05-13 09:32:42 -0700394 avd_spec=avd_spec,
395 extra_scopes=cfg.extra_scopes)
Keun Soo Yimb293fdb2016-09-21 16:03:44 -0700396 failures = device_pool.WaitForBoot()
397 # Write result to report.
398 for device in device_pool.devices:
cylan66713722018-10-06 01:38:26 +0800399 ip = (device.ip.internal if report_internal_ip
400 else device.ip.external)
401 device_dict = {
402 "ip": ip,
403 "instance_name": device.instance_name
404 }
Kevin Chengb5963882018-05-09 00:06:27 -0700405 if autoconnect:
chojoyce7a361732018-11-26 16:26:13 +0800406 forwarded_ports = utils.AutoConnect(
herbertxue6ef54a52019-05-02 11:38:58 +0800407 ip, cfg.ssh_private_key_path, constants.GCE_VNC_PORT,
408 constants.GCE_ADB_PORT, _SSH_USER, avd_spec.adb_port)
cylan66713722018-10-06 01:38:26 +0800409 device_dict[constants.VNC_PORT] = forwarded_ports.vnc_port
410 device_dict[constants.ADB_PORT] = forwarded_ports.adb_port
Keun Soo Yimb293fdb2016-09-21 16:03:44 -0700411 if device.instance_name in failures:
412 r.AddData(key="devices_failing_boot", value=device_dict)
413 r.AddError(str(failures[device.instance_name]))
414 else:
415 r.AddData(key="devices", value=device_dict)
416 if failures:
417 r.SetStatus(report.Status.BOOT_FAIL)
418 else:
419 r.SetStatus(report.Status.SUCCESS)
420
421 # Dump serial and logcat logs.
422 if serial_log_file:
Fang Dengfbef7c92017-02-08 14:09:34 -0800423 _FetchSerialLogsFromDevices(
424 compute_client,
425 instance_names=[d.instance_name for d in device_pool.devices],
426 port=constants.DEFAULT_SERIAL_PORT,
427 output_file=serial_log_file)
Keun Soo Yimb293fdb2016-09-21 16:03:44 -0700428 if logcat_file:
Fang Dengfbef7c92017-02-08 14:09:34 -0800429 _FetchSerialLogsFromDevices(
430 compute_client,
431 instance_names=[d.instance_name for d in device_pool.devices],
432 port=constants.LOGCAT_SERIAL_PORT,
433 output_file=logcat_file)
Keun Soo Yimb293fdb2016-09-21 16:03:44 -0700434 except errors.DriverError as e:
435 r.AddError(str(e))
436 r.SetStatus(report.Status.FAIL)
437 return r
438
439
herbertxue07293a32018-11-05 20:40:11 +0800440def DeleteAndroidVirtualDevices(cfg, instance_names, default_report=None):
Keun Soo Yimb293fdb2016-09-21 16:03:44 -0700441 """Deletes android devices.
442
443 Args:
444 cfg: An AcloudConfig instance.
445 instance_names: A list of names of the instances to delete.
herbertxue07293a32018-11-05 20:40:11 +0800446 default_report: A initialized Report instance.
Keun Soo Yimb293fdb2016-09-21 16:03:44 -0700447
448 Returns:
449 A Report instance.
450 """
herbertxue07293a32018-11-05 20:40:11 +0800451 r = default_report if default_report else report.Report(command="delete")
Sam Chiu4d9bb4b2018-10-26 11:38:23 +0800452 credentials = auth.CreateCredentials(cfg)
Keun Soo Yimb293fdb2016-09-21 16:03:44 -0700453 compute_client = android_compute_client.AndroidComputeClient(cfg,
454 credentials)
455 try:
456 deleted, failed, error_msgs = compute_client.DeleteInstances(
457 instance_names, cfg.zone)
herbertxue07293a32018-11-05 20:40:11 +0800458 AddDeletionResultToReport(
Keun Soo Yimb293fdb2016-09-21 16:03:44 -0700459 r, deleted,
460 failed, error_msgs,
461 resource_name="instance")
462 if r.status == report.Status.UNKNOWN:
463 r.SetStatus(report.Status.SUCCESS)
464 except errors.DriverError as e:
465 r.AddError(str(e))
466 r.SetStatus(report.Status.FAIL)
467 return r
468
469
470def _FindOldItems(items, cut_time, time_key):
471 """Finds items from |items| whose timestamp is earlier than |cut_time|.
472
473 Args:
474 items: A list of items. Each item is a dictionary represent
475 the properties of the item. It should has a key as noted
476 by time_key.
477 cut_time: A datetime.datatime object.
478 time_key: String, key for the timestamp.
479
480 Returns:
481 A list of those from |items| whose timestamp is earlier than cut_time.
482 """
483 cleanup_list = []
484 for item in items:
485 t = dateutil.parser.parse(item[time_key])
486 if t < cut_time:
487 cleanup_list.append(item)
488 return cleanup_list
489
490
491def Cleanup(cfg, expiration_mins):
492 """Cleans up stale gce images, gce instances, and disk images in storage.
493
494 Args:
495 cfg: An AcloudConfig instance.
496 expiration_mins: Integer, resources older than |expiration_mins| will
497 be cleaned up.
498
499 Returns:
500 A Report instance.
501 """
502 r = report.Report(command="cleanup")
503 try:
504 cut_time = (datetime.datetime.now(dateutil.tz.tzlocal()) -
505 datetime.timedelta(minutes=expiration_mins))
506 logger.info(
507 "Cleaning up any gce images/instances and cached build artifacts."
508 "in google storage that are older than %s", cut_time)
Sam Chiu4d9bb4b2018-10-26 11:38:23 +0800509 credentials = auth.CreateCredentials(cfg)
Keun Soo Yimb293fdb2016-09-21 16:03:44 -0700510 compute_client = android_compute_client.AndroidComputeClient(
511 cfg, credentials)
512 storage_client = gstorage_client.StorageClient(credentials)
513
514 # Cleanup expired instances
515 items = compute_client.ListInstances(zone=cfg.zone)
516 cleanup_list = [
517 item["name"]
518 for item in _FindOldItems(items, cut_time, "creationTimestamp")
519 ]
520 logger.info("Found expired instances: %s", cleanup_list)
521 for i in range(0, len(cleanup_list), MAX_BATCH_CLEANUP_COUNT):
522 result = compute_client.DeleteInstances(
523 instances=cleanup_list[i:i + MAX_BATCH_CLEANUP_COUNT],
524 zone=cfg.zone)
herbertxue07293a32018-11-05 20:40:11 +0800525 AddDeletionResultToReport(r, *result, resource_name="instance")
Keun Soo Yimb293fdb2016-09-21 16:03:44 -0700526
527 # Cleanup expired images
528 items = compute_client.ListImages()
529 skip_list = cfg.precreated_data_image_map.viewvalues()
530 cleanup_list = [
531 item["name"]
532 for item in _FindOldItems(items, cut_time, "creationTimestamp")
533 if item["name"] not in skip_list
534 ]
535 logger.info("Found expired images: %s", cleanup_list)
536 for i in range(0, len(cleanup_list), MAX_BATCH_CLEANUP_COUNT):
537 result = compute_client.DeleteImages(
538 image_names=cleanup_list[i:i + MAX_BATCH_CLEANUP_COUNT])
herbertxue07293a32018-11-05 20:40:11 +0800539 AddDeletionResultToReport(r, *result, resource_name="image")
Keun Soo Yimb293fdb2016-09-21 16:03:44 -0700540
541 # Cleanup expired disks
542 # Disks should have been attached to instances with autoDelete=True.
543 # However, sometimes disks may not be auto deleted successfully.
544 items = compute_client.ListDisks(zone=cfg.zone)
545 cleanup_list = [
546 item["name"]
547 for item in _FindOldItems(items, cut_time, "creationTimestamp")
548 if not item.get("users")
549 ]
550 logger.info("Found expired disks: %s", cleanup_list)
551 for i in range(0, len(cleanup_list), MAX_BATCH_CLEANUP_COUNT):
552 result = compute_client.DeleteDisks(
553 disk_names=cleanup_list[i:i + MAX_BATCH_CLEANUP_COUNT],
554 zone=cfg.zone)
herbertxue07293a32018-11-05 20:40:11 +0800555 AddDeletionResultToReport(r, *result, resource_name="disk")
Keun Soo Yimb293fdb2016-09-21 16:03:44 -0700556
557 # Cleanup expired google storage
558 items = storage_client.List(bucket_name=cfg.storage_bucket_name)
559 cleanup_list = [
560 item["name"]
561 for item in _FindOldItems(items, cut_time, "timeCreated")
562 ]
563 logger.info("Found expired cached artifacts: %s", cleanup_list)
564 for i in range(0, len(cleanup_list), MAX_BATCH_CLEANUP_COUNT):
565 result = storage_client.DeleteFiles(
566 bucket_name=cfg.storage_bucket_name,
567 object_names=cleanup_list[i:i + MAX_BATCH_CLEANUP_COUNT])
herbertxue07293a32018-11-05 20:40:11 +0800568 AddDeletionResultToReport(
Keun Soo Yimb293fdb2016-09-21 16:03:44 -0700569 r, *result, resource_name="cached_build_artifact")
570
571 # Everything succeeded, write status to report.
572 if r.status == report.Status.UNKNOWN:
573 r.SetStatus(report.Status.SUCCESS)
574 except errors.DriverError as e:
575 r.AddError(str(e))
576 r.SetStatus(report.Status.FAIL)
577 return r
578
579
Fang Dengcef4b112017-03-02 11:20:17 -0800580def CheckAccess(cfg):
581 """Check if user has access.
582
583 Args:
584 cfg: An AcloudConfig instance.
585 """
Sam Chiu4d9bb4b2018-10-26 11:38:23 +0800586 credentials = auth.CreateCredentials(cfg)
Fang Dengcef4b112017-03-02 11:20:17 -0800587 compute_client = android_compute_client.AndroidComputeClient(
Kevin Cheng5c124ec2018-05-16 13:28:51 -0700588 cfg, credentials)
Fang Dengcef4b112017-03-02 11:20:17 -0800589 logger.info("Checking if user has access to project %s", cfg.project)
590 if not compute_client.CheckAccess():
591 logger.error("User does not have access to project %s", cfg.project)
592 # Print here so that command line user can see it.
Kevin Chengd9d5f0f2018-06-19 14:54:17 -0700593 print("Looks like you do not have access to %s. " % cfg.project)
Fang Dengcef4b112017-03-02 11:20:17 -0800594 if cfg.project in cfg.no_project_access_msg_map:
Kevin Chengd9d5f0f2018-06-19 14:54:17 -0700595 print(cfg.no_project_access_msg_map[cfg.project])