blob: 47be404a741a7f4f5ec833348b5ae54f09118dbd [file] [log] [blame]
Kevin Chengeb85e862018-10-09 15:35:13 -07001# Copyright 2018 - The Android Open Source Project
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7# http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14r"""Delete entry point.
15
16Delete will handle all the logic related to deleting a local/remote instance
17of an Android Virtual Device.
18"""
19
20from __future__ import print_function
chojoyce6e30cc52019-11-04 16:18:09 +080021
Kevin Chengeb85e862018-10-09 15:35:13 -070022import logging
herbertxue07293a32018-11-05 20:40:11 +080023import re
24import subprocess
Kevin Chengeb85e862018-10-09 15:35:13 -070025
herbertxue07293a32018-11-05 20:40:11 +080026from acloud import errors
27from acloud.internal import constants
cyland315c6d2019-12-06 17:06:56 +000028from acloud.internal.lib import auth
cyland315c6d2019-12-06 17:06:56 +000029from acloud.internal.lib import cvd_compute_client_multi_stage
Hsin-Yi Chen7d59edb2021-08-20 15:19:14 +080030from acloud.internal.lib import emulator_console
31from acloud.internal.lib import goldfish_remote_host_client
herbertxueeadbba02021-07-15 14:39:26 +080032from acloud.internal.lib import oxygen_client
cyland315c6d2019-12-06 17:06:56 +000033from acloud.internal.lib import ssh as ssh_object
herbertxueeadbba02021-07-15 14:39:26 +080034from acloud.internal.lib import utils
Sam Chiu99dfee32018-11-20 10:19:17 +080035from acloud.list import list as list_instances
Kevin Chengeb85e862018-10-09 15:35:13 -070036from acloud.public import config
37from acloud.public import device_driver
herbertxue07293a32018-11-05 20:40:11 +080038from acloud.public import report
Kevin Chengeb85e862018-10-09 15:35:13 -070039
herbertxue1512f8a2019-06-27 13:56:23 +080040
Kevin Chengeb85e862018-10-09 15:35:13 -070041logger = logging.getLogger(__name__)
42
chojoyce5535cfb2019-10-09 18:28:03 +080043_COMMAND_GET_PROCESS_ID = ["pgrep", "run_cvd"]
herbertxue07293a32018-11-05 20:40:11 +080044_COMMAND_GET_PROCESS_COMMAND = ["ps", "-o", "command", "-p"]
chojoyce5535cfb2019-10-09 18:28:03 +080045_RE_RUN_CVD = re.compile(r"^(?P<run_cvd>.+run_cvd)")
herbertxue2aa26d12019-12-05 11:03:05 +080046_LOCAL_INSTANCE_PREFIX = "local-"
Dan Shie8b7e432021-08-18 11:01:31 -070047_RE_OXYGEN_RELEASE_ERROR = re.compile(
48 r".*Error received while trying to release device: (?P<error>.*)$", re.DOTALL)
herbertxue07293a32018-11-05 20:40:11 +080049
Kevin Chengeb85e862018-10-09 15:35:13 -070050
herbertxue07293a32018-11-05 20:40:11 +080051def DeleteInstances(cfg, instances_to_delete):
52 """Delete instances according to instances_to_delete.
53
herbertxue07293a32018-11-05 20:40:11 +080054 Args:
55 cfg: AcloudConfig object.
Sam Chiu99dfee32018-11-20 10:19:17 +080056 instances_to_delete: List of list.Instance() object.
herbertxue07293a32018-11-05 20:40:11 +080057
58 Returns:
Hsin-Yi Chen39474792020-10-21 18:16:44 +080059 Report object.
herbertxue07293a32018-11-05 20:40:11 +080060 """
Hsin-Yi Chen9037ee42019-11-01 16:33:44 +080061 delete_report = report.Report(command="delete")
Sam Chiu99dfee32018-11-20 10:19:17 +080062 remote_instance_list = []
63 for instance in instances_to_delete:
64 if instance.islocal:
Hsin-Yi Chen9037ee42019-11-01 16:33:44 +080065 if instance.avd_type == constants.TYPE_GF:
66 DeleteLocalGoldfishInstance(instance, delete_report)
67 elif instance.avd_type == constants.TYPE_CF:
68 DeleteLocalCuttlefishInstance(instance, delete_report)
69 else:
70 delete_report.AddError("Deleting %s is not supported." %
71 instance.avd_type)
72 delete_report.SetStatus(report.Status.FAIL)
Sam Chiu99dfee32018-11-20 10:19:17 +080073 else:
74 remote_instance_list.append(instance.name)
herbertxue9e1e27a2018-12-12 16:25:27 +080075 # Delete ssvnc viewer
Sam Chiudd6ce362020-01-23 09:35:52 +080076 if instance.vnc_port:
77 utils.CleanupSSVncviewer(instance.vnc_port)
herbertxue07293a32018-11-05 20:40:11 +080078
Sam Chiu99dfee32018-11-20 10:19:17 +080079 if remote_instance_list:
herbertxue07293a32018-11-05 20:40:11 +080080 # TODO(119283708): We should move DeleteAndroidVirtualDevices into
81 # delete.py after gce is deprecated.
82 # Stop remote instances.
Sam Chiu99dfee32018-11-20 10:19:17 +080083 return DeleteRemoteInstances(cfg, remote_instance_list, delete_report)
herbertxue07293a32018-11-05 20:40:11 +080084
herbertxue07293a32018-11-05 20:40:11 +080085 return delete_report
86
87
Sam Chiu99dfee32018-11-20 10:19:17 +080088@utils.TimeExecute(function_description="Deleting remote instances",
89 result_evaluator=utils.ReportEvaluator,
90 display_waiting_dots=False)
91def DeleteRemoteInstances(cfg, instances_to_delete, delete_report=None):
92 """Delete remote instances.
93
94 Args:
95 cfg: AcloudConfig object.
96 instances_to_delete: List of instance names(string).
97 delete_report: Report object.
98
99 Returns:
100 Report instance if there are instances to delete, None otherwise.
Sam Chiu43d81c52020-02-04 10:49:47 +0800101
102 Raises:
103 error.ConfigError: when config doesn't support remote instances.
Sam Chiu99dfee32018-11-20 10:19:17 +0800104 """
Sam Chiu43d81c52020-02-04 10:49:47 +0800105 if not cfg.SupportRemoteInstance():
106 raise errors.ConfigError("No gcp project info found in config! "
107 "The execution of deleting remote instances "
108 "has been aborted.")
Sam Chiu99dfee32018-11-20 10:19:17 +0800109 utils.PrintColorString("")
110 for instance in instances_to_delete:
111 utils.PrintColorString(" - %s" % instance, utils.TextColors.WARNING)
112 utils.PrintColorString("")
113 utils.PrintColorString("status: waiting...", end="")
114
115 # TODO(119283708): We should move DeleteAndroidVirtualDevices into
116 # delete.py after gce is deprecated.
117 # Stop remote instances.
118 delete_report = device_driver.DeleteAndroidVirtualDevices(
119 cfg, instances_to_delete, delete_report)
120
121 return delete_report
122
123
Hsin-Yi Chen9037ee42019-11-01 16:33:44 +0800124@utils.TimeExecute(function_description="Deleting local cuttlefish instances",
Sam Chiu99dfee32018-11-20 10:19:17 +0800125 result_evaluator=utils.ReportEvaluator)
Hsin-Yi Chen9037ee42019-11-01 16:33:44 +0800126def DeleteLocalCuttlefishInstance(instance, delete_report):
127 """Delete a local cuttlefish instance.
herbertxue07293a32018-11-05 20:40:11 +0800128
Sam Chiudd6ce362020-01-23 09:35:52 +0800129 Delete local instance and write delete instance
herbertxue07293a32018-11-05 20:40:11 +0800130 information to report.
131
Sam Chiu34e752e2019-08-16 18:15:36 +0800132 Args:
133 instance: instance.LocalInstance object.
134 delete_report: Report object.
135
herbertxue07293a32018-11-05 20:40:11 +0800136 Returns:
Hsin-Yi Chen9037ee42019-11-01 16:33:44 +0800137 delete_report.
herbertxue07293a32018-11-05 20:40:11 +0800138 """
Hsin-Yi Chenf3663ca2020-07-17 14:10:27 +0800139 ins_lock = instance.GetLock()
140 if not ins_lock.Lock():
Hsin-Yi Chencaec52f2020-07-16 14:59:26 +0800141 delete_report.AddError("%s is locked by another process." %
142 instance.name)
143 delete_report.SetStatus(report.Status.FAIL)
144 return delete_report
145
herbertxue07293a32018-11-05 20:40:11 +0800146 try:
Hsin-Yi Chen39474792020-10-21 18:16:44 +0800147 ins_lock.SetInUse(False)
Sam Chiudd6ce362020-01-23 09:35:52 +0800148 instance.Delete()
149 delete_report.SetStatus(report.Status.SUCCESS)
150 device_driver.AddDeletionResultToReport(
151 delete_report, [instance.name], failed=[],
152 error_msgs=[],
153 resource_name="instance")
herbertxue07293a32018-11-05 20:40:11 +0800154 except subprocess.CalledProcessError as e:
155 delete_report.AddError(str(e))
156 delete_report.SetStatus(report.Status.FAIL)
Hsin-Yi Chencaec52f2020-07-16 14:59:26 +0800157 finally:
Hsin-Yi Chenf3663ca2020-07-17 14:10:27 +0800158 ins_lock.Unlock()
Sam Chiu34e752e2019-08-16 18:15:36 +0800159
herbertxue07293a32018-11-05 20:40:11 +0800160 return delete_report
Kevin Chengeb85e862018-10-09 15:35:13 -0700161
162
Hsin-Yi Chen9037ee42019-11-01 16:33:44 +0800163@utils.TimeExecute(function_description="Deleting local goldfish instances",
164 result_evaluator=utils.ReportEvaluator)
165def DeleteLocalGoldfishInstance(instance, delete_report):
166 """Delete a local goldfish instance.
167
168 Args:
169 instance: LocalGoldfishInstance object.
170 delete_report: Report object.
171
172 Returns:
173 delete_report.
174 """
Hsin-Yi Chenf3663ca2020-07-17 14:10:27 +0800175 lock = instance.GetLock()
176 if not lock.Lock():
177 delete_report.AddError("%s is locked by another process." %
178 instance.name)
Hsin-Yi Chen9037ee42019-11-01 16:33:44 +0800179 delete_report.SetStatus(report.Status.FAIL)
Hsin-Yi Chenf3663ca2020-07-17 14:10:27 +0800180 return delete_report
181
182 try:
Hsin-Yi Chen39474792020-10-21 18:16:44 +0800183 lock.SetInUse(False)
Hsin-Yi Chenf3663ca2020-07-17 14:10:27 +0800184 if instance.adb.EmuCommand("kill") == 0:
185 delete_report.SetStatus(report.Status.SUCCESS)
186 device_driver.AddDeletionResultToReport(
187 delete_report, [instance.name], failed=[],
188 error_msgs=[],
189 resource_name="instance")
190 else:
191 delete_report.AddError("Cannot kill %s." % instance.device_serial)
192 delete_report.SetStatus(report.Status.FAIL)
193 finally:
Hsin-Yi Chenf3663ca2020-07-17 14:10:27 +0800194 lock.Unlock()
Hsin-Yi Chen9037ee42019-11-01 16:33:44 +0800195
Hsin-Yi Chen9037ee42019-11-01 16:33:44 +0800196 return delete_report
197
198
Hsin-Yi Chen39474792020-10-21 18:16:44 +0800199def ResetLocalInstanceLockByName(name, delete_report):
200 """Set the lock state of a local instance to be not in use.
201
202 Args:
203 name: The instance name.
204 delete_report: Report object.
205 """
206 ins_lock = list_instances.GetLocalInstanceLockByName(name)
207 if not ins_lock:
208 delete_report.AddError("%s is not a valid local instance name." % name)
209 delete_report.SetStatus(report.Status.FAIL)
210 return
211
212 if not ins_lock.Lock():
213 delete_report.AddError("%s is locked by another process." % name)
214 delete_report.SetStatus(report.Status.FAIL)
215 return
216
217 try:
218 ins_lock.SetInUse(False)
219 delete_report.SetStatus(report.Status.SUCCESS)
220 device_driver.AddDeletionResultToReport(
221 delete_report, [name], failed=[], error_msgs=[],
222 resource_name="instance")
223 finally:
224 ins_lock.Unlock()
225
226
Hsin-Yi Chen7d59edb2021-08-20 15:19:14 +0800227@utils.TimeExecute(function_description=("Deleting remote host goldfish "
228 "instance"),
229 result_evaluator=utils.ReportEvaluator)
230def DeleteHostGoldfishInstance(cfg, name, ssh_user,
231 ssh_private_key_path, delete_report):
232 """Delete a goldfish instance on a remote host by console command.
233
234 Args:
235 cfg: An AcloudConfig object.
236 name: String, the instance name.
237 remote_host : String, the IP address of the host.
238 ssh_user: String or None, the ssh user for the host.
239 ssh_private_key_path: String or None, the ssh private key for the host.
240 delete_report: A Report object.
241
242 Returns:
243 delete_report.
244 """
245 ip_addr, port = goldfish_remote_host_client.ParseEmulatorConsoleAddress(
246 name)
247 try:
248 with emulator_console.RemoteEmulatorConsole(
249 ip_addr, port,
250 (ssh_user or constants.GCE_USER),
251 (ssh_private_key_path or cfg.ssh_private_key_path),
252 cfg.extra_args_ssh_tunnel) as console:
253 console.Kill()
254 delete_report.SetStatus(report.Status.SUCCESS)
255 device_driver.AddDeletionResultToReport(
256 delete_report, [name], failed=[], error_msgs=[],
257 resource_name="instance")
258 except errors.DeviceConnectionError as e:
259 delete_report.AddError("%s is not deleted: %s" % (name, str(e)))
260 delete_report.SetStatus(report.Status.FAIL)
261 return delete_report
262
263
cylan0f61af62019-12-30 22:31:10 +0800264def CleanUpRemoteHost(cfg, remote_host, host_user,
cyland315c6d2019-12-06 17:06:56 +0000265 host_ssh_private_key_path=None):
266 """Clean up the remote host.
267
268 Args:
269 cfg: An AcloudConfig instance.
270 remote_host : String, ip address or host name of the remote host.
271 host_user: String of user login into the instance.
272 host_ssh_private_key_path: String of host key for logging in to the
273 host.
274
275 Returns:
276 A Report instance.
277 """
278 delete_report = report.Report(command="delete")
279 credentials = auth.CreateCredentials(cfg)
280 compute_client = cvd_compute_client_multi_stage.CvdComputeClient(
281 acloud_config=cfg,
282 oauth2_credentials=credentials)
283 ssh = ssh_object.Ssh(
284 ip=ssh_object.IP(ip=remote_host),
cylan0f61af62019-12-30 22:31:10 +0800285 user=host_user,
cyland315c6d2019-12-06 17:06:56 +0000286 ssh_private_key_path=(
287 host_ssh_private_key_path or cfg.ssh_private_key_path))
288 try:
cylan0f61af62019-12-30 22:31:10 +0800289 compute_client.InitRemoteHost(ssh, remote_host, host_user)
cyland315c6d2019-12-06 17:06:56 +0000290 delete_report.SetStatus(report.Status.SUCCESS)
291 device_driver.AddDeletionResultToReport(
292 delete_report, [remote_host], failed=[],
293 error_msgs=[],
294 resource_name="remote host")
295 except subprocess.CalledProcessError as e:
296 delete_report.AddError(str(e))
297 delete_report.SetStatus(report.Status.FAIL)
298
299 return delete_report
300
herbertxue2aa26d12019-12-05 11:03:05 +0800301
Hsin-Yi Chen7d59edb2021-08-20 15:19:14 +0800302def DeleteInstanceByNames(cfg, instances, host_user,
303 host_ssh_private_key_path):
304 """Delete instances by the given instance names.
305
306 This method can identify the following types of instance names:
307 local cuttlefish instance: local-instance-<id>
308 local goldfish instance: local-goldfish-instance-<id>
Hsin-Yi Chendf972042021-09-13 18:21:30 +0800309 remote host goldfish instance: host-goldfish-<ip_addr>-<port>-<build_info>
Hsin-Yi Chen7d59edb2021-08-20 15:19:14 +0800310 remote instance: ins-<uuid>-<build_info>
herbertxue2aa26d12019-12-05 11:03:05 +0800311
312 Args:
313 cfg: AcloudConfig object.
314 instances: List of instance name.
Hsin-Yi Chen7d59edb2021-08-20 15:19:14 +0800315 host_user: String or None, the ssh user for remote hosts.
316 host_ssh_private_key_path: String or None, the ssh private key for
317 remote hosts.
herbertxue2aa26d12019-12-05 11:03:05 +0800318
319 Returns:
320 A Report instance.
321 """
322 delete_report = report.Report(command="delete")
Hsin-Yi Chen39474792020-10-21 18:16:44 +0800323 local_names = set(name for name in instances if
324 name.startswith(_LOCAL_INSTANCE_PREFIX))
Hsin-Yi Chen7d59edb2021-08-20 15:19:14 +0800325 remote_host_gf_names = set(
326 name for name in instances if
327 goldfish_remote_host_client.ParseEmulatorConsoleAddress(name))
328 remote_names = list(set(instances) - local_names - remote_host_gf_names)
329
Hsin-Yi Chen39474792020-10-21 18:16:44 +0800330 if local_names:
331 active_instances = list_instances.GetLocalInstancesByNames(local_names)
332 inactive_names = local_names.difference(ins.name for ins in
333 active_instances)
334 if active_instances:
335 utils.PrintColorString("Deleting local instances")
336 delete_report = DeleteInstances(cfg, active_instances)
337 if inactive_names:
338 utils.PrintColorString("Unlocking local instances")
339 for name in inactive_names:
340 ResetLocalInstanceLockByName(name, delete_report)
Hsin-Yi Chen7d59edb2021-08-20 15:19:14 +0800341
342 if remote_host_gf_names:
343 for name in remote_host_gf_names:
344 DeleteHostGoldfishInstance(
345 cfg, name, host_user, host_ssh_private_key_path, delete_report)
346
Hsin-Yi Chen39474792020-10-21 18:16:44 +0800347 if remote_names:
348 delete_report = DeleteRemoteInstances(cfg, remote_names, delete_report)
herbertxue2aa26d12019-12-05 11:03:05 +0800349 return delete_report
350
351
herbertxueeadbba02021-07-15 14:39:26 +0800352def _ReleaseOxygenDevice(cfg, instances, ip):
353 """ Release one Oxygen device.
354
355 Args:
356 cfg: AcloudConfig object.
357 instances: List of instance name.
358 ip: String of device ip.
359
360 Returns:
361 A Report instance.
362 """
363 if len(instances) != 1:
364 raise errors.CommandArgError(
365 "The release device function doesn't support multiple instances. "
366 "Please check the specified instance names: %s" % instances)
367 instance_name = instances[0]
368 delete_report = report.Report(command="delete")
369 try:
370 oxygen_client.OxygenClient.ReleaseDevice(instance_name, ip,
371 cfg.oxygen_client)
372 delete_report.SetStatus(report.Status.SUCCESS)
373 device_driver.AddDeletionResultToReport(
374 delete_report, [instance_name], failed=[],
375 error_msgs=[],
376 resource_name="instance")
377 except subprocess.CalledProcessError as e:
Dan Shie8b7e432021-08-18 11:01:31 -0700378 logger.error("Failed to release device from Oxygen, error: %s",
379 e.output)
380 error = str(e)
381 match = _RE_OXYGEN_RELEASE_ERROR.match(e.output)
382 if match:
383 error = match.group("error").strip()
384 delete_report.AddError(error)
385 delete_report.SetErrorType(constants.ACLOUD_OXYGEN_RELEASE_ERROR)
herbertxueeadbba02021-07-15 14:39:26 +0800386 delete_report.SetStatus(report.Status.FAIL)
387 return delete_report
388
389
Kevin Chengeb85e862018-10-09 15:35:13 -0700390def Run(args):
391 """Run delete.
392
herbertxue7bd20a22019-02-22 14:08:54 +0800393 After delete command executed, tool will return one Report instance.
394 If there is no instance to delete, just reutrn empty Report.
395
Kevin Chengeb85e862018-10-09 15:35:13 -0700396 Args:
397 args: Namespace object from argparse.parse_args.
Sam Chiu99dfee32018-11-20 10:19:17 +0800398
399 Returns:
400 A Report instance.
Kevin Chengeb85e862018-10-09 15:35:13 -0700401 """
herbertxue2aa26d12019-12-05 11:03:05 +0800402 # Prioritize delete instances by names without query all instance info from
403 # GCP project.
Sam Chiu43d81c52020-02-04 10:49:47 +0800404 cfg = config.GetAcloudConfig(args)
herbertxueeadbba02021-07-15 14:39:26 +0800405 if args.oxygen:
406 return _ReleaseOxygenDevice(cfg, args.instance_names, args.ip)
herbertxue2aa26d12019-12-05 11:03:05 +0800407 if args.instance_names:
Sam Chiu43d81c52020-02-04 10:49:47 +0800408 return DeleteInstanceByNames(cfg,
Hsin-Yi Chen7d59edb2021-08-20 15:19:14 +0800409 args.instance_names,
410 args.host_user,
411 args.host_ssh_private_key_path)
cyland315c6d2019-12-06 17:06:56 +0000412 if args.remote_host:
cyland315c6d2019-12-06 17:06:56 +0000413 return CleanUpRemoteHost(cfg, args.remote_host, args.host_user,
414 args.host_ssh_private_key_path)
herbertxue2aa26d12019-12-05 11:03:05 +0800415
Hsin-Yi Chen9037ee42019-11-01 16:33:44 +0800416 instances = list_instances.GetLocalInstances()
Sam Chiu43d81c52020-02-04 10:49:47 +0800417 if not args.local_only and cfg.SupportRemoteInstance():
Hsin-Yi Chen9037ee42019-11-01 16:33:44 +0800418 instances.extend(list_instances.GetRemoteInstances(cfg))
Sam Chiu99dfee32018-11-20 10:19:17 +0800419
herbertxue2aa26d12019-12-05 11:03:05 +0800420 if args.adb_port:
Hsin-Yi Chen9037ee42019-11-01 16:33:44 +0800421 instances = list_instances.FilterInstancesByAdbPort(instances,
422 args.adb_port)
423 elif not args.all:
424 # Provide instances list to user and let user choose what to delete if
425 # user didn't specify instances in args.
426 instances = list_instances.ChooseInstancesFromList(instances)
herbertxue7bd20a22019-02-22 14:08:54 +0800427
Hsin-Yi Chen39474792020-10-21 18:16:44 +0800428 if not instances:
429 utils.PrintColorString("No instances to delete")
Hsin-Yi Chen9037ee42019-11-01 16:33:44 +0800430 return DeleteInstances(cfg, instances)