xref: /aosp_15_r20/tools/acloud/delete/delete.py (revision 800a58d989c669b8eb8a71d8df53b1ba3d411444)
1*800a58d9SAndroid Build Coastguard Worker# Copyright 2018 - The Android Open Source Project
2*800a58d9SAndroid Build Coastguard Worker#
3*800a58d9SAndroid Build Coastguard Worker# Licensed under the Apache License, Version 2.0 (the "License");
4*800a58d9SAndroid Build Coastguard Worker# you may not use this file except in compliance with the License.
5*800a58d9SAndroid Build Coastguard Worker# You may obtain a copy of the License at
6*800a58d9SAndroid Build Coastguard Worker#
7*800a58d9SAndroid Build Coastguard Worker#     http://www.apache.org/licenses/LICENSE-2.0
8*800a58d9SAndroid Build Coastguard Worker#
9*800a58d9SAndroid Build Coastguard Worker# Unless required by applicable law or agreed to in writing, software
10*800a58d9SAndroid Build Coastguard Worker# distributed under the License is distributed on an "AS IS" BASIS,
11*800a58d9SAndroid Build Coastguard Worker# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12*800a58d9SAndroid Build Coastguard Worker# See the License for the specific language governing permissions and
13*800a58d9SAndroid Build Coastguard Worker# limitations under the License.
14*800a58d9SAndroid Build Coastguard Workerr"""Delete entry point.
15*800a58d9SAndroid Build Coastguard Worker
16*800a58d9SAndroid Build Coastguard WorkerDelete will handle all the logic related to deleting a local/remote instance
17*800a58d9SAndroid Build Coastguard Workerof an Android Virtual Device.
18*800a58d9SAndroid Build Coastguard Worker"""
19*800a58d9SAndroid Build Coastguard Worker
20*800a58d9SAndroid Build Coastguard Workerfrom __future__ import print_function
21*800a58d9SAndroid Build Coastguard Worker
22*800a58d9SAndroid Build Coastguard Workerimport logging
23*800a58d9SAndroid Build Coastguard Workerimport re
24*800a58d9SAndroid Build Coastguard Workerimport subprocess
25*800a58d9SAndroid Build Coastguard Worker
26*800a58d9SAndroid Build Coastguard Workerfrom acloud import errors
27*800a58d9SAndroid Build Coastguard Workerfrom acloud.internal import constants
28*800a58d9SAndroid Build Coastguard Workerfrom acloud.internal.lib import cvd_utils
29*800a58d9SAndroid Build Coastguard Workerfrom acloud.internal.lib import emulator_console
30*800a58d9SAndroid Build Coastguard Workerfrom acloud.internal.lib import goldfish_utils
31*800a58d9SAndroid Build Coastguard Workerfrom acloud.internal.lib import oxygen_client
32*800a58d9SAndroid Build Coastguard Workerfrom acloud.internal.lib import ssh
33*800a58d9SAndroid Build Coastguard Workerfrom acloud.internal.lib import utils
34*800a58d9SAndroid Build Coastguard Workerfrom acloud.list import list as list_instances
35*800a58d9SAndroid Build Coastguard Workerfrom acloud.public import config
36*800a58d9SAndroid Build Coastguard Workerfrom acloud.public import device_driver
37*800a58d9SAndroid Build Coastguard Workerfrom acloud.public import report
38*800a58d9SAndroid Build Coastguard Worker
39*800a58d9SAndroid Build Coastguard Worker
40*800a58d9SAndroid Build Coastguard Workerlogger = logging.getLogger(__name__)
41*800a58d9SAndroid Build Coastguard Worker
42*800a58d9SAndroid Build Coastguard Worker_COMMAND_GET_PROCESS_ID = ["pgrep", "run_cvd"]
43*800a58d9SAndroid Build Coastguard Worker_COMMAND_GET_PROCESS_COMMAND = ["ps", "-o", "command", "-p"]
44*800a58d9SAndroid Build Coastguard Worker_RE_RUN_CVD = re.compile(r"^(?P<run_cvd>.+run_cvd)")
45*800a58d9SAndroid Build Coastguard Worker_LOCAL_INSTANCE_PREFIX = "local-"
46*800a58d9SAndroid Build Coastguard Worker_RE_OXYGEN_RELEASE_ERROR = re.compile(
47*800a58d9SAndroid Build Coastguard Worker    r".*Error received while trying to release device: (?P<error>.*)$", re.DOTALL)
48*800a58d9SAndroid Build Coastguard Worker
49*800a58d9SAndroid Build Coastguard Worker
50*800a58d9SAndroid Build Coastguard Workerdef DeleteInstances(cfg, instances_to_delete):
51*800a58d9SAndroid Build Coastguard Worker    """Delete instances according to instances_to_delete.
52*800a58d9SAndroid Build Coastguard Worker
53*800a58d9SAndroid Build Coastguard Worker    Args:
54*800a58d9SAndroid Build Coastguard Worker        cfg: AcloudConfig object.
55*800a58d9SAndroid Build Coastguard Worker        instances_to_delete: List of list.Instance() object.
56*800a58d9SAndroid Build Coastguard Worker
57*800a58d9SAndroid Build Coastguard Worker    Returns:
58*800a58d9SAndroid Build Coastguard Worker        Report object.
59*800a58d9SAndroid Build Coastguard Worker    """
60*800a58d9SAndroid Build Coastguard Worker    delete_report = report.Report(command="delete")
61*800a58d9SAndroid Build Coastguard Worker    remote_instance_list = []
62*800a58d9SAndroid Build Coastguard Worker    for instance in instances_to_delete:
63*800a58d9SAndroid Build Coastguard Worker        if instance.islocal:
64*800a58d9SAndroid Build Coastguard Worker            if instance.avd_type == constants.TYPE_GF:
65*800a58d9SAndroid Build Coastguard Worker                DeleteLocalGoldfishInstance(instance, delete_report)
66*800a58d9SAndroid Build Coastguard Worker            elif instance.avd_type == constants.TYPE_CF:
67*800a58d9SAndroid Build Coastguard Worker                DeleteLocalCuttlefishInstance(instance, delete_report)
68*800a58d9SAndroid Build Coastguard Worker            else:
69*800a58d9SAndroid Build Coastguard Worker                delete_report.AddError("Deleting %s is not supported." %
70*800a58d9SAndroid Build Coastguard Worker                                       instance.avd_type)
71*800a58d9SAndroid Build Coastguard Worker                delete_report.SetStatus(report.Status.FAIL)
72*800a58d9SAndroid Build Coastguard Worker        else:
73*800a58d9SAndroid Build Coastguard Worker            remote_instance_list.append(instance.name)
74*800a58d9SAndroid Build Coastguard Worker        # Delete ssvnc viewer
75*800a58d9SAndroid Build Coastguard Worker        if instance.vnc_port:
76*800a58d9SAndroid Build Coastguard Worker            utils.CleanupSSVncviewer(instance.vnc_port)
77*800a58d9SAndroid Build Coastguard Worker
78*800a58d9SAndroid Build Coastguard Worker    if remote_instance_list:
79*800a58d9SAndroid Build Coastguard Worker        # TODO(119283708): We should move DeleteAndroidVirtualDevices into
80*800a58d9SAndroid Build Coastguard Worker        # delete.py after gce is deprecated.
81*800a58d9SAndroid Build Coastguard Worker        # Stop remote instances.
82*800a58d9SAndroid Build Coastguard Worker        return DeleteRemoteInstances(cfg, remote_instance_list, delete_report)
83*800a58d9SAndroid Build Coastguard Worker
84*800a58d9SAndroid Build Coastguard Worker    return delete_report
85*800a58d9SAndroid Build Coastguard Worker
86*800a58d9SAndroid Build Coastguard Worker
87*800a58d9SAndroid Build Coastguard Worker@utils.TimeExecute(function_description="Deleting remote instances",
88*800a58d9SAndroid Build Coastguard Worker                   result_evaluator=utils.ReportEvaluator,
89*800a58d9SAndroid Build Coastguard Worker                   display_waiting_dots=False)
90*800a58d9SAndroid Build Coastguard Workerdef DeleteRemoteInstances(cfg, instances_to_delete, delete_report=None):
91*800a58d9SAndroid Build Coastguard Worker    """Delete remote instances.
92*800a58d9SAndroid Build Coastguard Worker
93*800a58d9SAndroid Build Coastguard Worker    Args:
94*800a58d9SAndroid Build Coastguard Worker        cfg: AcloudConfig object.
95*800a58d9SAndroid Build Coastguard Worker        instances_to_delete: List of instance names(string).
96*800a58d9SAndroid Build Coastguard Worker        delete_report: Report object.
97*800a58d9SAndroid Build Coastguard Worker
98*800a58d9SAndroid Build Coastguard Worker    Returns:
99*800a58d9SAndroid Build Coastguard Worker        Report instance if there are instances to delete, None otherwise.
100*800a58d9SAndroid Build Coastguard Worker
101*800a58d9SAndroid Build Coastguard Worker    Raises:
102*800a58d9SAndroid Build Coastguard Worker        error.ConfigError: when config doesn't support remote instances.
103*800a58d9SAndroid Build Coastguard Worker    """
104*800a58d9SAndroid Build Coastguard Worker    if not cfg.SupportRemoteInstance():
105*800a58d9SAndroid Build Coastguard Worker        raise errors.ConfigError("No gcp project info found in config! "
106*800a58d9SAndroid Build Coastguard Worker                                 "The execution of deleting remote instances "
107*800a58d9SAndroid Build Coastguard Worker                                 "has been aborted.")
108*800a58d9SAndroid Build Coastguard Worker    utils.PrintColorString("")
109*800a58d9SAndroid Build Coastguard Worker    for instance in instances_to_delete:
110*800a58d9SAndroid Build Coastguard Worker        utils.PrintColorString(" - %s" % instance, utils.TextColors.WARNING)
111*800a58d9SAndroid Build Coastguard Worker    utils.PrintColorString("")
112*800a58d9SAndroid Build Coastguard Worker    utils.PrintColorString("status: waiting...", end="")
113*800a58d9SAndroid Build Coastguard Worker
114*800a58d9SAndroid Build Coastguard Worker    # TODO(119283708): We should move DeleteAndroidVirtualDevices into
115*800a58d9SAndroid Build Coastguard Worker    # delete.py after gce is deprecated.
116*800a58d9SAndroid Build Coastguard Worker    # Stop remote instances.
117*800a58d9SAndroid Build Coastguard Worker    delete_report = device_driver.DeleteAndroidVirtualDevices(
118*800a58d9SAndroid Build Coastguard Worker        cfg, instances_to_delete, delete_report)
119*800a58d9SAndroid Build Coastguard Worker
120*800a58d9SAndroid Build Coastguard Worker    return delete_report
121*800a58d9SAndroid Build Coastguard Worker
122*800a58d9SAndroid Build Coastguard Worker
123*800a58d9SAndroid Build Coastguard Worker@utils.TimeExecute(function_description="Deleting local cuttlefish instances",
124*800a58d9SAndroid Build Coastguard Worker                   result_evaluator=utils.ReportEvaluator)
125*800a58d9SAndroid Build Coastguard Workerdef DeleteLocalCuttlefishInstance(instance, delete_report):
126*800a58d9SAndroid Build Coastguard Worker    """Delete a local cuttlefish instance.
127*800a58d9SAndroid Build Coastguard Worker
128*800a58d9SAndroid Build Coastguard Worker    Delete local instance and write delete instance
129*800a58d9SAndroid Build Coastguard Worker    information to report.
130*800a58d9SAndroid Build Coastguard Worker
131*800a58d9SAndroid Build Coastguard Worker    Args:
132*800a58d9SAndroid Build Coastguard Worker        instance: instance.LocalInstance object.
133*800a58d9SAndroid Build Coastguard Worker        delete_report: Report object.
134*800a58d9SAndroid Build Coastguard Worker
135*800a58d9SAndroid Build Coastguard Worker    Returns:
136*800a58d9SAndroid Build Coastguard Worker        delete_report.
137*800a58d9SAndroid Build Coastguard Worker    """
138*800a58d9SAndroid Build Coastguard Worker    ins_lock = instance.GetLock()
139*800a58d9SAndroid Build Coastguard Worker    if not ins_lock.Lock():
140*800a58d9SAndroid Build Coastguard Worker        delete_report.AddError("%s is locked by another process." %
141*800a58d9SAndroid Build Coastguard Worker                               instance.name)
142*800a58d9SAndroid Build Coastguard Worker        delete_report.SetStatus(report.Status.FAIL)
143*800a58d9SAndroid Build Coastguard Worker        return delete_report
144*800a58d9SAndroid Build Coastguard Worker
145*800a58d9SAndroid Build Coastguard Worker    try:
146*800a58d9SAndroid Build Coastguard Worker        ins_lock.SetInUse(False)
147*800a58d9SAndroid Build Coastguard Worker        instance.Delete()
148*800a58d9SAndroid Build Coastguard Worker        delete_report.SetStatus(report.Status.SUCCESS)
149*800a58d9SAndroid Build Coastguard Worker        device_driver.AddDeletionResultToReport(
150*800a58d9SAndroid Build Coastguard Worker            delete_report, [instance.name], failed=[],
151*800a58d9SAndroid Build Coastguard Worker            error_msgs=[],
152*800a58d9SAndroid Build Coastguard Worker            resource_name="instance")
153*800a58d9SAndroid Build Coastguard Worker    except subprocess.CalledProcessError as e:
154*800a58d9SAndroid Build Coastguard Worker        delete_report.AddError(str(e))
155*800a58d9SAndroid Build Coastguard Worker        delete_report.SetStatus(report.Status.FAIL)
156*800a58d9SAndroid Build Coastguard Worker    finally:
157*800a58d9SAndroid Build Coastguard Worker        ins_lock.Unlock()
158*800a58d9SAndroid Build Coastguard Worker
159*800a58d9SAndroid Build Coastguard Worker    return delete_report
160*800a58d9SAndroid Build Coastguard Worker
161*800a58d9SAndroid Build Coastguard Worker
162*800a58d9SAndroid Build Coastguard Worker@utils.TimeExecute(function_description="Deleting local goldfish instances",
163*800a58d9SAndroid Build Coastguard Worker                   result_evaluator=utils.ReportEvaluator)
164*800a58d9SAndroid Build Coastguard Workerdef DeleteLocalGoldfishInstance(instance, delete_report):
165*800a58d9SAndroid Build Coastguard Worker    """Delete a local goldfish instance.
166*800a58d9SAndroid Build Coastguard Worker
167*800a58d9SAndroid Build Coastguard Worker    Args:
168*800a58d9SAndroid Build Coastguard Worker        instance: LocalGoldfishInstance object.
169*800a58d9SAndroid Build Coastguard Worker        delete_report: Report object.
170*800a58d9SAndroid Build Coastguard Worker
171*800a58d9SAndroid Build Coastguard Worker    Returns:
172*800a58d9SAndroid Build Coastguard Worker        delete_report.
173*800a58d9SAndroid Build Coastguard Worker    """
174*800a58d9SAndroid Build Coastguard Worker    lock = instance.GetLock()
175*800a58d9SAndroid Build Coastguard Worker    if not lock.Lock():
176*800a58d9SAndroid Build Coastguard Worker        delete_report.AddError("%s is locked by another process." %
177*800a58d9SAndroid Build Coastguard Worker                               instance.name)
178*800a58d9SAndroid Build Coastguard Worker        delete_report.SetStatus(report.Status.FAIL)
179*800a58d9SAndroid Build Coastguard Worker        return delete_report
180*800a58d9SAndroid Build Coastguard Worker
181*800a58d9SAndroid Build Coastguard Worker    try:
182*800a58d9SAndroid Build Coastguard Worker        lock.SetInUse(False)
183*800a58d9SAndroid Build Coastguard Worker        if instance.adb.EmuCommand("kill") == 0:
184*800a58d9SAndroid Build Coastguard Worker            delete_report.SetStatus(report.Status.SUCCESS)
185*800a58d9SAndroid Build Coastguard Worker            device_driver.AddDeletionResultToReport(
186*800a58d9SAndroid Build Coastguard Worker                delete_report, [instance.name], failed=[],
187*800a58d9SAndroid Build Coastguard Worker                error_msgs=[],
188*800a58d9SAndroid Build Coastguard Worker                resource_name="instance")
189*800a58d9SAndroid Build Coastguard Worker        else:
190*800a58d9SAndroid Build Coastguard Worker            delete_report.AddError("Cannot kill %s." % instance.device_serial)
191*800a58d9SAndroid Build Coastguard Worker            delete_report.SetStatus(report.Status.FAIL)
192*800a58d9SAndroid Build Coastguard Worker    finally:
193*800a58d9SAndroid Build Coastguard Worker        lock.Unlock()
194*800a58d9SAndroid Build Coastguard Worker
195*800a58d9SAndroid Build Coastguard Worker    return delete_report
196*800a58d9SAndroid Build Coastguard Worker
197*800a58d9SAndroid Build Coastguard Worker
198*800a58d9SAndroid Build Coastguard Workerdef ResetLocalInstanceLockByName(name, delete_report):
199*800a58d9SAndroid Build Coastguard Worker    """Set the lock state of a local instance to be not in use.
200*800a58d9SAndroid Build Coastguard Worker
201*800a58d9SAndroid Build Coastguard Worker    Args:
202*800a58d9SAndroid Build Coastguard Worker        name: The instance name.
203*800a58d9SAndroid Build Coastguard Worker        delete_report: Report object.
204*800a58d9SAndroid Build Coastguard Worker    """
205*800a58d9SAndroid Build Coastguard Worker    ins_lock = list_instances.GetLocalInstanceLockByName(name)
206*800a58d9SAndroid Build Coastguard Worker    if not ins_lock:
207*800a58d9SAndroid Build Coastguard Worker        delete_report.AddError("%s is not a valid local instance name." % name)
208*800a58d9SAndroid Build Coastguard Worker        delete_report.SetStatus(report.Status.FAIL)
209*800a58d9SAndroid Build Coastguard Worker        return
210*800a58d9SAndroid Build Coastguard Worker
211*800a58d9SAndroid Build Coastguard Worker    if not ins_lock.Lock():
212*800a58d9SAndroid Build Coastguard Worker        delete_report.AddError("%s is locked by another process." % name)
213*800a58d9SAndroid Build Coastguard Worker        delete_report.SetStatus(report.Status.FAIL)
214*800a58d9SAndroid Build Coastguard Worker        return
215*800a58d9SAndroid Build Coastguard Worker
216*800a58d9SAndroid Build Coastguard Worker    try:
217*800a58d9SAndroid Build Coastguard Worker        ins_lock.SetInUse(False)
218*800a58d9SAndroid Build Coastguard Worker        delete_report.SetStatus(report.Status.SUCCESS)
219*800a58d9SAndroid Build Coastguard Worker        device_driver.AddDeletionResultToReport(
220*800a58d9SAndroid Build Coastguard Worker            delete_report, [name], failed=[], error_msgs=[],
221*800a58d9SAndroid Build Coastguard Worker            resource_name="instance")
222*800a58d9SAndroid Build Coastguard Worker    finally:
223*800a58d9SAndroid Build Coastguard Worker        ins_lock.Unlock()
224*800a58d9SAndroid Build Coastguard Worker
225*800a58d9SAndroid Build Coastguard Worker
226*800a58d9SAndroid Build Coastguard Worker@utils.TimeExecute(function_description=("Deleting remote host goldfish "
227*800a58d9SAndroid Build Coastguard Worker                                         "instance"),
228*800a58d9SAndroid Build Coastguard Worker                   result_evaluator=utils.ReportEvaluator)
229*800a58d9SAndroid Build Coastguard Workerdef DeleteHostGoldfishInstance(cfg, name, ssh_user,
230*800a58d9SAndroid Build Coastguard Worker                               ssh_private_key_path, delete_report):
231*800a58d9SAndroid Build Coastguard Worker    """Delete a goldfish instance on a remote host by console command.
232*800a58d9SAndroid Build Coastguard Worker
233*800a58d9SAndroid Build Coastguard Worker    Args:
234*800a58d9SAndroid Build Coastguard Worker        cfg: An AcloudConfig object.
235*800a58d9SAndroid Build Coastguard Worker        name: String, the instance name.
236*800a58d9SAndroid Build Coastguard Worker        remote_host : String, the IP address of the host.
237*800a58d9SAndroid Build Coastguard Worker        ssh_user: String or None, the ssh user for the host.
238*800a58d9SAndroid Build Coastguard Worker        ssh_private_key_path: String or None, the ssh private key for the host.
239*800a58d9SAndroid Build Coastguard Worker        delete_report: A Report object.
240*800a58d9SAndroid Build Coastguard Worker
241*800a58d9SAndroid Build Coastguard Worker    Returns:
242*800a58d9SAndroid Build Coastguard Worker        delete_report.
243*800a58d9SAndroid Build Coastguard Worker    """
244*800a58d9SAndroid Build Coastguard Worker    ip_addr, port = goldfish_utils.ParseRemoteHostConsoleAddress(name)
245*800a58d9SAndroid Build Coastguard Worker    try:
246*800a58d9SAndroid Build Coastguard Worker        with emulator_console.RemoteEmulatorConsole(
247*800a58d9SAndroid Build Coastguard Worker                ip_addr, port,
248*800a58d9SAndroid Build Coastguard Worker                (ssh_user or constants.GCE_USER),
249*800a58d9SAndroid Build Coastguard Worker                (ssh_private_key_path or cfg.ssh_private_key_path),
250*800a58d9SAndroid Build Coastguard Worker                cfg.extra_args_ssh_tunnel) as console:
251*800a58d9SAndroid Build Coastguard Worker            console.Kill()
252*800a58d9SAndroid Build Coastguard Worker        delete_report.SetStatus(report.Status.SUCCESS)
253*800a58d9SAndroid Build Coastguard Worker        device_driver.AddDeletionResultToReport(
254*800a58d9SAndroid Build Coastguard Worker            delete_report, [name], failed=[], error_msgs=[],
255*800a58d9SAndroid Build Coastguard Worker            resource_name="instance")
256*800a58d9SAndroid Build Coastguard Worker    except errors.DeviceConnectionError as e:
257*800a58d9SAndroid Build Coastguard Worker        delete_report.AddError("%s is not deleted: %s" % (name, str(e)))
258*800a58d9SAndroid Build Coastguard Worker        delete_report.SetStatus(report.Status.FAIL)
259*800a58d9SAndroid Build Coastguard Worker    return delete_report
260*800a58d9SAndroid Build Coastguard Worker
261*800a58d9SAndroid Build Coastguard Worker
262*800a58d9SAndroid Build Coastguard Worker@utils.TimeExecute(function_description=("Deleting remote host cuttlefish "
263*800a58d9SAndroid Build Coastguard Worker                                         "instance"),
264*800a58d9SAndroid Build Coastguard Worker                   result_evaluator=utils.ReportEvaluator)
265*800a58d9SAndroid Build Coastguard Workerdef CleanUpRemoteHost(cfg, remote_host, host_user, host_ssh_private_key_path,
266*800a58d9SAndroid Build Coastguard Worker                      base_dir, delete_report):
267*800a58d9SAndroid Build Coastguard Worker    """Clean up the remote host.
268*800a58d9SAndroid Build Coastguard Worker
269*800a58d9SAndroid Build Coastguard Worker    Args:
270*800a58d9SAndroid Build Coastguard Worker        cfg: An AcloudConfig instance.
271*800a58d9SAndroid Build Coastguard Worker        remote_host : String, ip address or host name of the remote host.
272*800a58d9SAndroid Build Coastguard Worker        host_user: String of user login into the instance.
273*800a58d9SAndroid Build Coastguard Worker        host_ssh_private_key_path: String of host key for logging in to the
274*800a58d9SAndroid Build Coastguard Worker                                   host.
275*800a58d9SAndroid Build Coastguard Worker        base_dir: String, the base directory on the remote host.
276*800a58d9SAndroid Build Coastguard Worker        delete_report: A Report object.
277*800a58d9SAndroid Build Coastguard Worker
278*800a58d9SAndroid Build Coastguard Worker    Returns:
279*800a58d9SAndroid Build Coastguard Worker        delete_report.
280*800a58d9SAndroid Build Coastguard Worker    """
281*800a58d9SAndroid Build Coastguard Worker    ssh_obj = ssh.Ssh(
282*800a58d9SAndroid Build Coastguard Worker        ip=ssh.IP(ip=remote_host),
283*800a58d9SAndroid Build Coastguard Worker        user=host_user,
284*800a58d9SAndroid Build Coastguard Worker        ssh_private_key_path=(
285*800a58d9SAndroid Build Coastguard Worker            host_ssh_private_key_path or cfg.ssh_private_key_path))
286*800a58d9SAndroid Build Coastguard Worker    try:
287*800a58d9SAndroid Build Coastguard Worker        cvd_utils.CleanUpRemoteCvd(ssh_obj, base_dir, raise_error=True)
288*800a58d9SAndroid Build Coastguard Worker        delete_report.SetStatus(report.Status.SUCCESS)
289*800a58d9SAndroid Build Coastguard Worker        device_driver.AddDeletionResultToReport(
290*800a58d9SAndroid Build Coastguard Worker            delete_report, [remote_host], failed=[],
291*800a58d9SAndroid Build Coastguard Worker            error_msgs=[],
292*800a58d9SAndroid Build Coastguard Worker            resource_name="remote host")
293*800a58d9SAndroid Build Coastguard Worker    except subprocess.CalledProcessError as e:
294*800a58d9SAndroid Build Coastguard Worker        delete_report.AddError(str(e))
295*800a58d9SAndroid Build Coastguard Worker        delete_report.SetStatus(report.Status.FAIL)
296*800a58d9SAndroid Build Coastguard Worker    return delete_report
297*800a58d9SAndroid Build Coastguard Worker
298*800a58d9SAndroid Build Coastguard Worker
299*800a58d9SAndroid Build Coastguard Workerdef DeleteInstanceByNames(cfg, instances, host_user,
300*800a58d9SAndroid Build Coastguard Worker                          host_ssh_private_key_path):
301*800a58d9SAndroid Build Coastguard Worker    """Delete instances by the given instance names.
302*800a58d9SAndroid Build Coastguard Worker
303*800a58d9SAndroid Build Coastguard Worker    This method can identify the following types of instance names:
304*800a58d9SAndroid Build Coastguard Worker    local cuttlefish instance: local-instance-<id>
305*800a58d9SAndroid Build Coastguard Worker    local goldfish instance: local-goldfish-instance-<id>
306*800a58d9SAndroid Build Coastguard Worker    remote host cuttlefish instance: host-<ip_addr>-<build_info>
307*800a58d9SAndroid Build Coastguard Worker    remote host goldfish instance: host-goldfish-<ip_addr>-<port>-<build_info>
308*800a58d9SAndroid Build Coastguard Worker    remote instance: ins-<uuid>-<build_info>
309*800a58d9SAndroid Build Coastguard Worker
310*800a58d9SAndroid Build Coastguard Worker    Args:
311*800a58d9SAndroid Build Coastguard Worker        cfg: AcloudConfig object.
312*800a58d9SAndroid Build Coastguard Worker        instances: List of instance name.
313*800a58d9SAndroid Build Coastguard Worker        host_user: String or None, the ssh user for remote hosts.
314*800a58d9SAndroid Build Coastguard Worker        host_ssh_private_key_path: String or None, the ssh private key for
315*800a58d9SAndroid Build Coastguard Worker                                   remote hosts.
316*800a58d9SAndroid Build Coastguard Worker
317*800a58d9SAndroid Build Coastguard Worker    Returns:
318*800a58d9SAndroid Build Coastguard Worker        A Report instance.
319*800a58d9SAndroid Build Coastguard Worker    """
320*800a58d9SAndroid Build Coastguard Worker    delete_report = report.Report(command="delete")
321*800a58d9SAndroid Build Coastguard Worker    local_names = set(name for name in instances if
322*800a58d9SAndroid Build Coastguard Worker                      name.startswith(_LOCAL_INSTANCE_PREFIX))
323*800a58d9SAndroid Build Coastguard Worker    remote_host_cf_names = set(
324*800a58d9SAndroid Build Coastguard Worker        name for name in instances if cvd_utils.ParseRemoteHostAddress(name))
325*800a58d9SAndroid Build Coastguard Worker    remote_host_gf_names = set(
326*800a58d9SAndroid Build Coastguard Worker        name for name in instances if
327*800a58d9SAndroid Build Coastguard Worker        goldfish_utils.ParseRemoteHostConsoleAddress(name))
328*800a58d9SAndroid Build Coastguard Worker    remote_names = list(set(instances) - local_names - remote_host_cf_names -
329*800a58d9SAndroid Build Coastguard Worker                        remote_host_gf_names)
330*800a58d9SAndroid Build Coastguard Worker
331*800a58d9SAndroid Build Coastguard Worker    if local_names:
332*800a58d9SAndroid Build Coastguard Worker        active_instances = list_instances.GetLocalInstancesByNames(local_names)
333*800a58d9SAndroid Build Coastguard Worker        inactive_names = local_names.difference(ins.name for ins in
334*800a58d9SAndroid Build Coastguard Worker                                                active_instances)
335*800a58d9SAndroid Build Coastguard Worker        if active_instances:
336*800a58d9SAndroid Build Coastguard Worker            utils.PrintColorString("Deleting local instances")
337*800a58d9SAndroid Build Coastguard Worker            delete_report = DeleteInstances(cfg, active_instances)
338*800a58d9SAndroid Build Coastguard Worker        if inactive_names:
339*800a58d9SAndroid Build Coastguard Worker            utils.PrintColorString("Unlocking local instances")
340*800a58d9SAndroid Build Coastguard Worker            for name in inactive_names:
341*800a58d9SAndroid Build Coastguard Worker                ResetLocalInstanceLockByName(name, delete_report)
342*800a58d9SAndroid Build Coastguard Worker
343*800a58d9SAndroid Build Coastguard Worker    if remote_host_cf_names:
344*800a58d9SAndroid Build Coastguard Worker        for name in remote_host_cf_names:
345*800a58d9SAndroid Build Coastguard Worker            ip_addr, base_dir = cvd_utils.ParseRemoteHostAddress(name)
346*800a58d9SAndroid Build Coastguard Worker            CleanUpRemoteHost(cfg, ip_addr, host_user,
347*800a58d9SAndroid Build Coastguard Worker                              host_ssh_private_key_path, base_dir,
348*800a58d9SAndroid Build Coastguard Worker                              delete_report)
349*800a58d9SAndroid Build Coastguard Worker
350*800a58d9SAndroid Build Coastguard Worker    if remote_host_gf_names:
351*800a58d9SAndroid Build Coastguard Worker        for name in remote_host_gf_names:
352*800a58d9SAndroid Build Coastguard Worker            DeleteHostGoldfishInstance(
353*800a58d9SAndroid Build Coastguard Worker                cfg, name, host_user, host_ssh_private_key_path, delete_report)
354*800a58d9SAndroid Build Coastguard Worker
355*800a58d9SAndroid Build Coastguard Worker    if remote_names:
356*800a58d9SAndroid Build Coastguard Worker        delete_report = DeleteRemoteInstances(cfg, remote_names, delete_report)
357*800a58d9SAndroid Build Coastguard Worker    return delete_report
358*800a58d9SAndroid Build Coastguard Worker
359*800a58d9SAndroid Build Coastguard Worker
360*800a58d9SAndroid Build Coastguard Workerdef _ReleaseOxygenDevice(cfg, instances, ip):
361*800a58d9SAndroid Build Coastguard Worker    """ Release one Oxygen device.
362*800a58d9SAndroid Build Coastguard Worker
363*800a58d9SAndroid Build Coastguard Worker    Args:
364*800a58d9SAndroid Build Coastguard Worker        cfg: AcloudConfig object.
365*800a58d9SAndroid Build Coastguard Worker        instances: List of instance name.
366*800a58d9SAndroid Build Coastguard Worker        ip: String of device ip.
367*800a58d9SAndroid Build Coastguard Worker
368*800a58d9SAndroid Build Coastguard Worker    Returns:
369*800a58d9SAndroid Build Coastguard Worker        A Report instance.
370*800a58d9SAndroid Build Coastguard Worker    """
371*800a58d9SAndroid Build Coastguard Worker    if len(instances) != 1:
372*800a58d9SAndroid Build Coastguard Worker        raise errors.CommandArgError(
373*800a58d9SAndroid Build Coastguard Worker            "The release device function doesn't support multiple instances. "
374*800a58d9SAndroid Build Coastguard Worker            "Please check the specified instance names: %s" % instances)
375*800a58d9SAndroid Build Coastguard Worker    instance_name = instances[0]
376*800a58d9SAndroid Build Coastguard Worker    delete_report = report.Report(command="delete")
377*800a58d9SAndroid Build Coastguard Worker    try:
378*800a58d9SAndroid Build Coastguard Worker        oxygen_client.OxygenClient.ReleaseDevice(instance_name, ip,
379*800a58d9SAndroid Build Coastguard Worker                                                 cfg.oxygen_client)
380*800a58d9SAndroid Build Coastguard Worker        delete_report.SetStatus(report.Status.SUCCESS)
381*800a58d9SAndroid Build Coastguard Worker        device_driver.AddDeletionResultToReport(
382*800a58d9SAndroid Build Coastguard Worker            delete_report, [instance_name], failed=[],
383*800a58d9SAndroid Build Coastguard Worker            error_msgs=[],
384*800a58d9SAndroid Build Coastguard Worker            resource_name="instance")
385*800a58d9SAndroid Build Coastguard Worker    except subprocess.CalledProcessError as e:
386*800a58d9SAndroid Build Coastguard Worker        logger.error("Failed to release device from Oxygen, error: %s",
387*800a58d9SAndroid Build Coastguard Worker            e.output)
388*800a58d9SAndroid Build Coastguard Worker        error = str(e)
389*800a58d9SAndroid Build Coastguard Worker        match = _RE_OXYGEN_RELEASE_ERROR.match(e.output)
390*800a58d9SAndroid Build Coastguard Worker        if match:
391*800a58d9SAndroid Build Coastguard Worker            error = match.group("error").strip()
392*800a58d9SAndroid Build Coastguard Worker        delete_report.AddError(error)
393*800a58d9SAndroid Build Coastguard Worker        delete_report.SetErrorType(constants.ACLOUD_OXYGEN_RELEASE_ERROR)
394*800a58d9SAndroid Build Coastguard Worker        delete_report.SetStatus(report.Status.FAIL)
395*800a58d9SAndroid Build Coastguard Worker    return delete_report
396*800a58d9SAndroid Build Coastguard Worker
397*800a58d9SAndroid Build Coastguard Worker
398*800a58d9SAndroid Build Coastguard Workerdef Run(args):
399*800a58d9SAndroid Build Coastguard Worker    """Run delete.
400*800a58d9SAndroid Build Coastguard Worker
401*800a58d9SAndroid Build Coastguard Worker    After delete command executed, tool will return one Report instance.
402*800a58d9SAndroid Build Coastguard Worker    If there is no instance to delete, just reutrn empty Report.
403*800a58d9SAndroid Build Coastguard Worker
404*800a58d9SAndroid Build Coastguard Worker    Args:
405*800a58d9SAndroid Build Coastguard Worker        args: Namespace object from argparse.parse_args.
406*800a58d9SAndroid Build Coastguard Worker
407*800a58d9SAndroid Build Coastguard Worker    Returns:
408*800a58d9SAndroid Build Coastguard Worker        A Report instance.
409*800a58d9SAndroid Build Coastguard Worker    """
410*800a58d9SAndroid Build Coastguard Worker    # Prioritize delete instances by names without query all instance info from
411*800a58d9SAndroid Build Coastguard Worker    # GCP project.
412*800a58d9SAndroid Build Coastguard Worker    cfg = config.GetAcloudConfig(args)
413*800a58d9SAndroid Build Coastguard Worker    if args.oxygen:
414*800a58d9SAndroid Build Coastguard Worker        return _ReleaseOxygenDevice(cfg, args.instance_names, args.ip)
415*800a58d9SAndroid Build Coastguard Worker    if args.instance_names:
416*800a58d9SAndroid Build Coastguard Worker        return DeleteInstanceByNames(cfg,
417*800a58d9SAndroid Build Coastguard Worker                                     args.instance_names,
418*800a58d9SAndroid Build Coastguard Worker                                     args.host_user,
419*800a58d9SAndroid Build Coastguard Worker                                     args.host_ssh_private_key_path)
420*800a58d9SAndroid Build Coastguard Worker    if args.remote_host:
421*800a58d9SAndroid Build Coastguard Worker        delete_report = report.Report(command="delete")
422*800a58d9SAndroid Build Coastguard Worker        CleanUpRemoteHost(cfg, args.remote_host, args.host_user,
423*800a58d9SAndroid Build Coastguard Worker                          args.host_ssh_private_key_path,
424*800a58d9SAndroid Build Coastguard Worker                          cvd_utils.GetRemoteHostBaseDir(1),
425*800a58d9SAndroid Build Coastguard Worker                          delete_report)
426*800a58d9SAndroid Build Coastguard Worker        return delete_report
427*800a58d9SAndroid Build Coastguard Worker
428*800a58d9SAndroid Build Coastguard Worker    instances = list_instances.GetLocalInstances()
429*800a58d9SAndroid Build Coastguard Worker    if not args.local_only and cfg.SupportRemoteInstance():
430*800a58d9SAndroid Build Coastguard Worker        instances.extend(list_instances.GetRemoteInstances(cfg))
431*800a58d9SAndroid Build Coastguard Worker
432*800a58d9SAndroid Build Coastguard Worker    if args.adb_port:
433*800a58d9SAndroid Build Coastguard Worker        instances = list_instances.FilterInstancesByAdbPort(instances,
434*800a58d9SAndroid Build Coastguard Worker                                                            args.adb_port)
435*800a58d9SAndroid Build Coastguard Worker    elif not args.all:
436*800a58d9SAndroid Build Coastguard Worker        # Provide instances list to user and let user choose what to delete if
437*800a58d9SAndroid Build Coastguard Worker        # user didn't specify instances in args.
438*800a58d9SAndroid Build Coastguard Worker        instances = list_instances.ChooseInstancesFromList(instances)
439*800a58d9SAndroid Build Coastguard Worker
440*800a58d9SAndroid Build Coastguard Worker    if not instances:
441*800a58d9SAndroid Build Coastguard Worker        utils.PrintColorString("No instances to delete")
442*800a58d9SAndroid Build Coastguard Worker    return DeleteInstances(cfg, instances)
443