blob: 08f6bf3996d6ec9cef791d6de27cae47ff7b8c5f [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
39from acloud.public import avd
40from acloud.public import errors
41from 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#For gce_x86_phones remote instances: adb port is 5555 and vnc is 6444.
55_TARGET_VNC_PORT = 6444
56_TARGET_ADB_PORT = 5555
57_SSH_USER = "root"
Kevin Chengb5963882018-05-09 00:06:27 -070058
Keun Soo Yimb293fdb2016-09-21 16:03:44 -070059
Kevin Cheng5c124ec2018-05-16 13:28:51 -070060# pylint: disable=invalid-name
Keun Soo Yimb293fdb2016-09-21 16:03:44 -070061class AndroidVirtualDevicePool(object):
62 """A class that manages a pool of devices."""
63
64 def __init__(self, cfg, devices=None):
65 self._devices = devices or []
66 self._cfg = cfg
Sam Chiu4d9bb4b2018-10-26 11:38:23 +080067 credentials = auth.CreateCredentials(cfg)
Keun Soo Yimb293fdb2016-09-21 16:03:44 -070068 self._build_client = android_build_client.AndroidBuildClient(
69 credentials)
70 self._storage_client = gstorage_client.StorageClient(credentials)
71 self._compute_client = android_compute_client.AndroidComputeClient(
72 cfg, credentials)
73
74 def _CreateGceImageWithBuildInfo(self, build_target, build_id):
75 """Creates a Gce image using build from Launch Control.
76
77 Clone avd-system.tar.gz of a build to a cache storage bucket
78 using launch control api. And then create a Gce image.
79
80 Args:
Kevin Chengb5963882018-05-09 00:06:27 -070081 build_target: Target name, e.g. "aosp_cf_x86_phone-userdebug"
Keun Soo Yimb293fdb2016-09-21 16:03:44 -070082 build_id: Build id, a string, e.g. "2263051", "P2804227"
83
84 Returns:
85 String, name of the Gce image that has been created.
86 """
87 logger.info("Creating a new gce image using build: build_id %s, "
88 "build_target %s", build_id, build_target)
89 disk_image_id = utils.GenerateUniqueName(
90 suffix=self._cfg.disk_image_name)
91 self._build_client.CopyTo(
92 build_target,
93 build_id,
94 artifact_name=self._cfg.disk_image_name,
95 destination_bucket=self._cfg.storage_bucket_name,
96 destination_path=disk_image_id)
97 disk_image_url = self._storage_client.GetUrl(
98 self._cfg.storage_bucket_name, disk_image_id)
99 try:
100 image_name = self._compute_client.GenerateImageName(build_target,
101 build_id)
102 self._compute_client.CreateImage(image_name=image_name,
103 source_uri=disk_image_url)
104 finally:
105 self._storage_client.Delete(self._cfg.storage_bucket_name,
106 disk_image_id)
107 return image_name
108
109 def _CreateGceImageWithLocalFile(self, local_disk_image):
110 """Create a Gce image with a local image file.
111
112 The local disk image can be either a tar.gz file or a
113 raw vmlinux image.
114 e.g. /tmp/avd-system.tar.gz or /tmp/android_system_disk_syslinux.img
115 If a raw vmlinux image is provided, it will be archived into a tar.gz file.
116
117 The final tar.gz file will be uploaded to a cache bucket in storage.
118
119 Args:
120 local_disk_image: string, path to a local disk image,
121
122 Returns:
123 String, name of the Gce image that has been created.
124
125 Raises:
126 DriverError: if a file with an unexpected extension is given.
127 """
128 logger.info("Creating a new gce image from a local file %s",
129 local_disk_image)
130 with utils.TempDir() as tempdir:
131 if local_disk_image.endswith(self._cfg.disk_raw_image_extension):
132 dest_tar_file = os.path.join(tempdir,
133 self._cfg.disk_image_name)
134 utils.MakeTarFile(
135 src_dict={local_disk_image: self._cfg.disk_raw_image_name},
136 dest=dest_tar_file)
137 local_disk_image = dest_tar_file
138 elif not local_disk_image.endswith(self._cfg.disk_image_extension):
139 raise errors.DriverError(
140 "Wrong local_disk_image type, must be a *%s file or *%s file"
141 % (self._cfg.disk_raw_image_extension,
142 self._cfg.disk_image_extension))
143
144 disk_image_id = utils.GenerateUniqueName(
145 suffix=self._cfg.disk_image_name)
146 self._storage_client.Upload(
147 local_src=local_disk_image,
148 bucket_name=self._cfg.storage_bucket_name,
149 object_name=disk_image_id,
150 mime_type=self._cfg.disk_image_mime_type)
151 disk_image_url = self._storage_client.GetUrl(
152 self._cfg.storage_bucket_name, disk_image_id)
153 try:
154 image_name = self._compute_client.GenerateImageName()
155 self._compute_client.CreateImage(image_name=image_name,
156 source_uri=disk_image_url)
157 finally:
158 self._storage_client.Delete(self._cfg.storage_bucket_name,
159 disk_image_id)
160 return image_name
161
162 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,
170 precreated_data_image=None):
171 """Creates |num| devices for given build_target and build_id.
172
173 - If gce_image is provided, will use it to create an instance.
174 - If local_disk_image is provided, will upload it to a temporary
175 caching storage bucket which is defined by user as |storage_bucket_name|
176 And then create an gce image with it; and then create an instance.
177 - If build_target and build_id are provided, will clone the disk image
178 via launch control to the temporary caching storage bucket.
179 And then create an gce image with it; and then create an instance.
180
181 Args:
182 num: Number of devices to create.
Kevin Chengb5963882018-05-09 00:06:27 -0700183 build_target: Target name, e.g. "aosp_cf_x86_phone-userdebug"
Keun Soo Yimb293fdb2016-09-21 16:03:44 -0700184 build_id: Build id, a string, e.g. "2263051", "P2804227"
185 gce_image: string, if given, will use this image
186 instead of creating a new one.
187 implies cleanup=False.
188 local_disk_image: string, path to a local disk image, e.g.
189 /tmp/avd-system.tar.gz
190 cleanup: boolean, if True clean up compute engine image after creating
191 the instance.
192 extra_data_disk_size_gb: Integer, size of extra disk, or None.
193 precreated_data_image: A string, the image to use for the extra disk.
194
195 Raises:
196 errors.DriverError: If no source is specified for image creation.
197 """
198 if gce_image:
199 # GCE image is provided, we can directly move to instance creation.
200 logger.info("Using existing gce image %s", gce_image)
201 image_name = gce_image
202 cleanup = False
203 elif local_disk_image:
204 image_name = self._CreateGceImageWithLocalFile(local_disk_image)
205 elif build_target and build_id:
206 image_name = self._CreateGceImageWithBuildInfo(build_target,
207 build_id)
208 else:
209 raise errors.DriverError(
210 "Invalid image source, must specify one of the following: gce_image, "
211 "local_disk_image, or build_target and build id.")
212
213 # Create GCE instances.
214 try:
215 for _ in range(num):
216 instance = self._compute_client.GenerateInstanceName(
217 build_target, build_id)
218 extra_disk_name = None
219 if extra_data_disk_size_gb > 0:
220 extra_disk_name = self._compute_client.GetDataDiskName(
221 instance)
222 self._compute_client.CreateDisk(extra_disk_name,
223 precreated_data_image,
224 extra_data_disk_size_gb)
Kevin Chengb5963882018-05-09 00:06:27 -0700225 self._compute_client.CreateInstance(
226 instance=instance,
227 image_name=image_name,
228 extra_disk_name=extra_disk_name)
Keun Soo Yimb293fdb2016-09-21 16:03:44 -0700229 ip = self._compute_client.GetInstanceIP(instance)
230 self.devices.append(avd.AndroidVirtualDevice(
231 ip=ip, instance_name=instance))
232 finally:
233 if cleanup:
234 self._compute_client.DeleteImage(image_name)
235
236 def DeleteDevices(self):
237 """Deletes devices.
238
239 Returns:
240 A tuple, (deleted, failed, error_msgs)
241 deleted: A list of names of instances that have been deleted.
242 faild: A list of names of instances that we fail to delete.
243 error_msgs: A list of failure messages.
244 """
245 instance_names = [device.instance_name for device in self._devices]
246 return self._compute_client.DeleteInstances(instance_names,
247 self._cfg.zone)
248
249 def WaitForBoot(self):
250 """Waits for all devices to boot up.
251
252 Returns:
253 A dictionary that contains all the failures.
254 The key is the name of the instance that fails to boot,
Kevin Chengb5963882018-05-09 00:06:27 -0700255 the value is an errors.DeviceBoottError object.
Keun Soo Yimb293fdb2016-09-21 16:03:44 -0700256 """
257 failures = {}
258 for device in self._devices:
259 try:
260 self._compute_client.WaitForBoot(device.instance_name)
Kevin Chengb5963882018-05-09 00:06:27 -0700261 except errors.DeviceBootError as e:
Keun Soo Yimb293fdb2016-09-21 16:03:44 -0700262 failures[device.instance_name] = e
263 return failures
264
265 @property
266 def devices(self):
267 """Returns a list of devices in the pool.
268
269 Returns:
270 A list of devices in the pool.
271 """
272 return self._devices
273
274
275def _AddDeletionResultToReport(report_obj, deleted, failed, error_msgs,
276 resource_name):
277 """Adds deletion result to a Report object.
278
279 This function will add the following to report.data.
280 "deleted": [
281 {"name": "resource_name", "type": "resource_name"},
282 ],
283 "failed": [
284 {"name": "resource_name", "type": "resource_name"},
285 ],
286 This function will append error_msgs to report.errors.
287
288 Args:
289 report_obj: A Report object.
290 deleted: A list of names of the resources that have been deleted.
291 failed: A list of names of the resources that we fail to delete.
292 error_msgs: A list of error message strings to be added to the report.
293 resource_name: A string, representing the name of the resource.
294 """
295 for name in deleted:
296 report_obj.AddData(key="deleted",
297 value={"name": name,
298 "type": resource_name})
299 for name in failed:
300 report_obj.AddData(key="failed",
301 value={"name": name,
302 "type": resource_name})
303 report_obj.AddErrors(error_msgs)
304 if failed or error_msgs:
305 report_obj.SetStatus(report.Status.FAIL)
306
307
308def _FetchSerialLogsFromDevices(compute_client, instance_names, output_file,
309 port):
310 """Fetch serial logs from a port for a list of devices to a local file.
311
312 Args:
313 compute_client: An object of android_compute_client.AndroidComputeClient
314 instance_names: A list of instance names.
315 output_file: A path to a file ending with "tar.gz"
316 port: The number of serial port to read from, 0 for serial output, 1 for
317 logcat.
318 """
319 with utils.TempDir() as tempdir:
320 src_dict = {}
321 for instance_name in instance_names:
322 serial_log = compute_client.GetSerialPortOutput(
323 instance=instance_name, port=port)
324 file_name = "%s.log" % instance_name
325 file_path = os.path.join(tempdir, file_name)
326 src_dict[file_path] = file_name
327 with open(file_path, "w") as f:
328 f.write(serial_log.encode("utf-8"))
329 utils.MakeTarFile(src_dict, output_file)
330
331
Kevin Cheng5c124ec2018-05-16 13:28:51 -0700332# pylint: disable=too-many-locals
Keun Soo Yimb293fdb2016-09-21 16:03:44 -0700333def CreateAndroidVirtualDevices(cfg,
334 build_target=None,
335 build_id=None,
336 num=1,
337 gce_image=None,
338 local_disk_image=None,
339 cleanup=True,
340 serial_log_file=None,
Kevin Chengb5963882018-05-09 00:06:27 -0700341 logcat_file=None,
Kevin Cheng86d43c72018-08-30 10:59:14 -0700342 autoconnect=False,
343 report_internal_ip=False):
Keun Soo Yimb293fdb2016-09-21 16:03:44 -0700344 """Creates one or multiple android devices.
345
346 Args:
347 cfg: An AcloudConfig instance.
Kevin Chengb5963882018-05-09 00:06:27 -0700348 build_target: Target name, e.g. "aosp_cf_x86_phone-userdebug"
Keun Soo Yimb293fdb2016-09-21 16:03:44 -0700349 build_id: Build id, a string, e.g. "2263051", "P2804227"
350 num: Number of devices to create.
351 gce_image: string, if given, will use this gce image
352 instead of creating a new one.
353 implies cleanup=False.
354 local_disk_image: string, path to a local disk image, e.g.
355 /tmp/avd-system.tar.gz
356 cleanup: boolean, if True clean up compute engine image and
357 disk image in storage after creating the instance.
358 serial_log_file: A path to a file where serial output should
Fang Dengfbef7c92017-02-08 14:09:34 -0800359 be saved to.
Keun Soo Yimb293fdb2016-09-21 16:03:44 -0700360 logcat_file: A path to a file where logcat logs should be saved.
Kevin Chengb5963882018-05-09 00:06:27 -0700361 autoconnect: Create ssh tunnel(s) and adb connect after device creation.
Kevin Cheng86d43c72018-08-30 10:59:14 -0700362 report_internal_ip: Boolean to report the internal ip instead of
363 external ip.
Keun Soo Yimb293fdb2016-09-21 16:03:44 -0700364
365 Returns:
366 A Report instance.
367 """
368 r = report.Report(command="create")
Sam Chiu4d9bb4b2018-10-26 11:38:23 +0800369 credentials = auth.CreateCredentials(cfg)
Keun Soo Yimb293fdb2016-09-21 16:03:44 -0700370 compute_client = android_compute_client.AndroidComputeClient(cfg,
371 credentials)
372 try:
Kevin Chengb5963882018-05-09 00:06:27 -0700373 common_operations.CreateSshKeyPairIfNecessary(cfg)
Keun Soo Yimb293fdb2016-09-21 16:03:44 -0700374 device_pool = AndroidVirtualDevicePool(cfg)
375 device_pool.CreateDevices(
376 num,
377 build_target,
378 build_id,
379 gce_image,
380 local_disk_image,
381 cleanup,
382 extra_data_disk_size_gb=cfg.extra_data_disk_size_gb,
383 precreated_data_image=cfg.precreated_data_image_map.get(
384 cfg.extra_data_disk_size_gb))
385 failures = device_pool.WaitForBoot()
386 # Write result to report.
387 for device in device_pool.devices:
cylan66713722018-10-06 01:38:26 +0800388 ip = (device.ip.internal if report_internal_ip
389 else device.ip.external)
390 device_dict = {
391 "ip": ip,
392 "instance_name": device.instance_name
393 }
Kevin Chengb5963882018-05-09 00:06:27 -0700394 if autoconnect:
cylan66713722018-10-06 01:38:26 +0800395 forwarded_ports = utils.AutoConnect(ip,
396 cfg.ssh_private_key_path,
397 _TARGET_VNC_PORT,
398 _TARGET_ADB_PORT,
399 _SSH_USER)
400 device_dict[constants.VNC_PORT] = forwarded_ports.vnc_port
401 device_dict[constants.ADB_PORT] = forwarded_ports.adb_port
Keun Soo Yimb293fdb2016-09-21 16:03:44 -0700402 if device.instance_name in failures:
403 r.AddData(key="devices_failing_boot", value=device_dict)
404 r.AddError(str(failures[device.instance_name]))
405 else:
406 r.AddData(key="devices", value=device_dict)
407 if failures:
408 r.SetStatus(report.Status.BOOT_FAIL)
409 else:
410 r.SetStatus(report.Status.SUCCESS)
411
412 # Dump serial and logcat logs.
413 if serial_log_file:
Fang Dengfbef7c92017-02-08 14:09:34 -0800414 _FetchSerialLogsFromDevices(
415 compute_client,
416 instance_names=[d.instance_name for d in device_pool.devices],
417 port=constants.DEFAULT_SERIAL_PORT,
418 output_file=serial_log_file)
Keun Soo Yimb293fdb2016-09-21 16:03:44 -0700419 if logcat_file:
Fang Dengfbef7c92017-02-08 14:09:34 -0800420 _FetchSerialLogsFromDevices(
421 compute_client,
422 instance_names=[d.instance_name for d in device_pool.devices],
423 port=constants.LOGCAT_SERIAL_PORT,
424 output_file=logcat_file)
Keun Soo Yimb293fdb2016-09-21 16:03:44 -0700425 except errors.DriverError as e:
426 r.AddError(str(e))
427 r.SetStatus(report.Status.FAIL)
428 return r
429
430
431def DeleteAndroidVirtualDevices(cfg, instance_names):
432 """Deletes android devices.
433
434 Args:
435 cfg: An AcloudConfig instance.
436 instance_names: A list of names of the instances to delete.
437
438 Returns:
439 A Report instance.
440 """
441 r = report.Report(command="delete")
Sam Chiu4d9bb4b2018-10-26 11:38:23 +0800442 credentials = auth.CreateCredentials(cfg)
Keun Soo Yimb293fdb2016-09-21 16:03:44 -0700443 compute_client = android_compute_client.AndroidComputeClient(cfg,
444 credentials)
445 try:
446 deleted, failed, error_msgs = compute_client.DeleteInstances(
447 instance_names, cfg.zone)
448 _AddDeletionResultToReport(
449 r, deleted,
450 failed, error_msgs,
451 resource_name="instance")
452 if r.status == report.Status.UNKNOWN:
453 r.SetStatus(report.Status.SUCCESS)
454 except errors.DriverError as e:
455 r.AddError(str(e))
456 r.SetStatus(report.Status.FAIL)
457 return r
458
459
460def _FindOldItems(items, cut_time, time_key):
461 """Finds items from |items| whose timestamp is earlier than |cut_time|.
462
463 Args:
464 items: A list of items. Each item is a dictionary represent
465 the properties of the item. It should has a key as noted
466 by time_key.
467 cut_time: A datetime.datatime object.
468 time_key: String, key for the timestamp.
469
470 Returns:
471 A list of those from |items| whose timestamp is earlier than cut_time.
472 """
473 cleanup_list = []
474 for item in items:
475 t = dateutil.parser.parse(item[time_key])
476 if t < cut_time:
477 cleanup_list.append(item)
478 return cleanup_list
479
480
481def Cleanup(cfg, expiration_mins):
482 """Cleans up stale gce images, gce instances, and disk images in storage.
483
484 Args:
485 cfg: An AcloudConfig instance.
486 expiration_mins: Integer, resources older than |expiration_mins| will
487 be cleaned up.
488
489 Returns:
490 A Report instance.
491 """
492 r = report.Report(command="cleanup")
493 try:
494 cut_time = (datetime.datetime.now(dateutil.tz.tzlocal()) -
495 datetime.timedelta(minutes=expiration_mins))
496 logger.info(
497 "Cleaning up any gce images/instances and cached build artifacts."
498 "in google storage that are older than %s", cut_time)
Sam Chiu4d9bb4b2018-10-26 11:38:23 +0800499 credentials = auth.CreateCredentials(cfg)
Keun Soo Yimb293fdb2016-09-21 16:03:44 -0700500 compute_client = android_compute_client.AndroidComputeClient(
501 cfg, credentials)
502 storage_client = gstorage_client.StorageClient(credentials)
503
504 # Cleanup expired instances
505 items = compute_client.ListInstances(zone=cfg.zone)
506 cleanup_list = [
507 item["name"]
508 for item in _FindOldItems(items, cut_time, "creationTimestamp")
509 ]
510 logger.info("Found expired instances: %s", cleanup_list)
511 for i in range(0, len(cleanup_list), MAX_BATCH_CLEANUP_COUNT):
512 result = compute_client.DeleteInstances(
513 instances=cleanup_list[i:i + MAX_BATCH_CLEANUP_COUNT],
514 zone=cfg.zone)
515 _AddDeletionResultToReport(r, *result, resource_name="instance")
516
517 # Cleanup expired images
518 items = compute_client.ListImages()
519 skip_list = cfg.precreated_data_image_map.viewvalues()
520 cleanup_list = [
521 item["name"]
522 for item in _FindOldItems(items, cut_time, "creationTimestamp")
523 if item["name"] not in skip_list
524 ]
525 logger.info("Found expired images: %s", cleanup_list)
526 for i in range(0, len(cleanup_list), MAX_BATCH_CLEANUP_COUNT):
527 result = compute_client.DeleteImages(
528 image_names=cleanup_list[i:i + MAX_BATCH_CLEANUP_COUNT])
529 _AddDeletionResultToReport(r, *result, resource_name="image")
530
531 # Cleanup expired disks
532 # Disks should have been attached to instances with autoDelete=True.
533 # However, sometimes disks may not be auto deleted successfully.
534 items = compute_client.ListDisks(zone=cfg.zone)
535 cleanup_list = [
536 item["name"]
537 for item in _FindOldItems(items, cut_time, "creationTimestamp")
538 if not item.get("users")
539 ]
540 logger.info("Found expired disks: %s", cleanup_list)
541 for i in range(0, len(cleanup_list), MAX_BATCH_CLEANUP_COUNT):
542 result = compute_client.DeleteDisks(
543 disk_names=cleanup_list[i:i + MAX_BATCH_CLEANUP_COUNT],
544 zone=cfg.zone)
545 _AddDeletionResultToReport(r, *result, resource_name="disk")
546
547 # Cleanup expired google storage
548 items = storage_client.List(bucket_name=cfg.storage_bucket_name)
549 cleanup_list = [
550 item["name"]
551 for item in _FindOldItems(items, cut_time, "timeCreated")
552 ]
553 logger.info("Found expired cached artifacts: %s", cleanup_list)
554 for i in range(0, len(cleanup_list), MAX_BATCH_CLEANUP_COUNT):
555 result = storage_client.DeleteFiles(
556 bucket_name=cfg.storage_bucket_name,
557 object_names=cleanup_list[i:i + MAX_BATCH_CLEANUP_COUNT])
558 _AddDeletionResultToReport(
559 r, *result, resource_name="cached_build_artifact")
560
561 # Everything succeeded, write status to report.
562 if r.status == report.Status.UNKNOWN:
563 r.SetStatus(report.Status.SUCCESS)
564 except errors.DriverError as e:
565 r.AddError(str(e))
566 r.SetStatus(report.Status.FAIL)
567 return r
568
569
570def AddSshRsa(cfg, user, ssh_rsa_path):
571 """Add public ssh rsa key to the project.
572
573 Args:
574 cfg: An AcloudConfig instance.
575 user: the name of the user which the key belongs to.
576 ssh_rsa_path: The absolute path to public rsa key.
577
578 Returns:
579 A Report instance.
580 """
581 r = report.Report(command="sshkey")
582 try:
Sam Chiu4d9bb4b2018-10-26 11:38:23 +0800583 credentials = auth.CreateCredentials(cfg)
Keun Soo Yimb293fdb2016-09-21 16:03:44 -0700584 compute_client = android_compute_client.AndroidComputeClient(
585 cfg, credentials)
586 compute_client.AddSshRsa(user, ssh_rsa_path)
587 r.SetStatus(report.Status.SUCCESS)
588 except errors.DriverError as e:
589 r.AddError(str(e))
590 r.SetStatus(report.Status.FAIL)
591 return r
Fang Dengcef4b112017-03-02 11:20:17 -0800592
593
594def CheckAccess(cfg):
595 """Check if user has access.
596
597 Args:
598 cfg: An AcloudConfig instance.
599 """
Sam Chiu4d9bb4b2018-10-26 11:38:23 +0800600 credentials = auth.CreateCredentials(cfg)
Fang Dengcef4b112017-03-02 11:20:17 -0800601 compute_client = android_compute_client.AndroidComputeClient(
Kevin Cheng5c124ec2018-05-16 13:28:51 -0700602 cfg, credentials)
Fang Dengcef4b112017-03-02 11:20:17 -0800603 logger.info("Checking if user has access to project %s", cfg.project)
604 if not compute_client.CheckAccess():
605 logger.error("User does not have access to project %s", cfg.project)
606 # Print here so that command line user can see it.
Kevin Chengd9d5f0f2018-06-19 14:54:17 -0700607 print("Looks like you do not have access to %s. " % cfg.project)
Fang Dengcef4b112017-03-02 11:20:17 -0800608 if cfg.project in cfg.no_project_access_msg_map:
Kevin Chengd9d5f0f2018-06-19 14:54:17 -0700609 print(cfg.no_project_access_msg_map[cfg.project])