• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# 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
21
22import logging
23import re
24import subprocess
25
26from acloud import errors
27from acloud.internal import constants
28from acloud.internal.lib import auth
29from acloud.internal.lib import cvd_compute_client_multi_stage
30from acloud.internal.lib import utils
31from acloud.internal.lib import ssh as ssh_object
32from acloud.list import list as list_instances
33from acloud.public import config
34from acloud.public import device_driver
35from acloud.public import report
36
37
38logger = logging.getLogger(__name__)
39
40_COMMAND_GET_PROCESS_ID = ["pgrep", "run_cvd"]
41_COMMAND_GET_PROCESS_COMMAND = ["ps", "-o", "command", "-p"]
42_RE_RUN_CVD = re.compile(r"^(?P<run_cvd>.+run_cvd)")
43_LOCAL_INSTANCE_PREFIX = "local-"
44
45
46def DeleteInstances(cfg, instances_to_delete):
47    """Delete instances according to instances_to_delete.
48
49    Args:
50        cfg: AcloudConfig object.
51        instances_to_delete: List of list.Instance() object.
52
53    Returns:
54        Report object.
55    """
56    delete_report = report.Report(command="delete")
57    remote_instance_list = []
58    for instance in instances_to_delete:
59        if instance.islocal:
60            if instance.avd_type == constants.TYPE_GF:
61                DeleteLocalGoldfishInstance(instance, delete_report)
62            elif instance.avd_type == constants.TYPE_CF:
63                DeleteLocalCuttlefishInstance(instance, delete_report)
64            else:
65                delete_report.AddError("Deleting %s is not supported." %
66                                       instance.avd_type)
67                delete_report.SetStatus(report.Status.FAIL)
68        else:
69            remote_instance_list.append(instance.name)
70        # Delete ssvnc viewer
71        if instance.vnc_port:
72            utils.CleanupSSVncviewer(instance.vnc_port)
73
74    if remote_instance_list:
75        # TODO(119283708): We should move DeleteAndroidVirtualDevices into
76        # delete.py after gce is deprecated.
77        # Stop remote instances.
78        return DeleteRemoteInstances(cfg, remote_instance_list, delete_report)
79
80    return delete_report
81
82
83@utils.TimeExecute(function_description="Deleting remote instances",
84                   result_evaluator=utils.ReportEvaluator,
85                   display_waiting_dots=False)
86def DeleteRemoteInstances(cfg, instances_to_delete, delete_report=None):
87    """Delete remote instances.
88
89    Args:
90        cfg: AcloudConfig object.
91        instances_to_delete: List of instance names(string).
92        delete_report: Report object.
93
94    Returns:
95        Report instance if there are instances to delete, None otherwise.
96
97    Raises:
98        error.ConfigError: when config doesn't support remote instances.
99    """
100    if not cfg.SupportRemoteInstance():
101        raise errors.ConfigError("No gcp project info found in config! "
102                                 "The execution of deleting remote instances "
103                                 "has been aborted.")
104    utils.PrintColorString("")
105    for instance in instances_to_delete:
106        utils.PrintColorString(" - %s" % instance, utils.TextColors.WARNING)
107    utils.PrintColorString("")
108    utils.PrintColorString("status: waiting...", end="")
109
110    # TODO(119283708): We should move DeleteAndroidVirtualDevices into
111    # delete.py after gce is deprecated.
112    # Stop remote instances.
113    delete_report = device_driver.DeleteAndroidVirtualDevices(
114        cfg, instances_to_delete, delete_report)
115
116    return delete_report
117
118
119@utils.TimeExecute(function_description="Deleting local cuttlefish instances",
120                   result_evaluator=utils.ReportEvaluator)
121def DeleteLocalCuttlefishInstance(instance, delete_report):
122    """Delete a local cuttlefish instance.
123
124    Delete local instance and write delete instance
125    information to report.
126
127    Args:
128        instance: instance.LocalInstance object.
129        delete_report: Report object.
130
131    Returns:
132        delete_report.
133    """
134    ins_lock = instance.GetLock()
135    if not ins_lock.Lock():
136        delete_report.AddError("%s is locked by another process." %
137                               instance.name)
138        delete_report.SetStatus(report.Status.FAIL)
139        return delete_report
140
141    try:
142        ins_lock.SetInUse(False)
143        instance.Delete()
144        delete_report.SetStatus(report.Status.SUCCESS)
145        device_driver.AddDeletionResultToReport(
146            delete_report, [instance.name], failed=[],
147            error_msgs=[],
148            resource_name="instance")
149    except subprocess.CalledProcessError as e:
150        delete_report.AddError(str(e))
151        delete_report.SetStatus(report.Status.FAIL)
152    finally:
153        ins_lock.Unlock()
154
155    return delete_report
156
157
158@utils.TimeExecute(function_description="Deleting local goldfish instances",
159                   result_evaluator=utils.ReportEvaluator)
160def DeleteLocalGoldfishInstance(instance, delete_report):
161    """Delete a local goldfish instance.
162
163    Args:
164        instance: LocalGoldfishInstance object.
165        delete_report: Report object.
166
167    Returns:
168        delete_report.
169    """
170    lock = instance.GetLock()
171    if not lock.Lock():
172        delete_report.AddError("%s is locked by another process." %
173                               instance.name)
174        delete_report.SetStatus(report.Status.FAIL)
175        return delete_report
176
177    try:
178        lock.SetInUse(False)
179        if instance.adb.EmuCommand("kill") == 0:
180            delete_report.SetStatus(report.Status.SUCCESS)
181            device_driver.AddDeletionResultToReport(
182                delete_report, [instance.name], failed=[],
183                error_msgs=[],
184                resource_name="instance")
185        else:
186            delete_report.AddError("Cannot kill %s." % instance.device_serial)
187            delete_report.SetStatus(report.Status.FAIL)
188    finally:
189        lock.Unlock()
190
191    return delete_report
192
193
194def ResetLocalInstanceLockByName(name, delete_report):
195    """Set the lock state of a local instance to be not in use.
196
197    Args:
198        name: The instance name.
199        delete_report: Report object.
200    """
201    ins_lock = list_instances.GetLocalInstanceLockByName(name)
202    if not ins_lock:
203        delete_report.AddError("%s is not a valid local instance name." % name)
204        delete_report.SetStatus(report.Status.FAIL)
205        return
206
207    if not ins_lock.Lock():
208        delete_report.AddError("%s is locked by another process." % name)
209        delete_report.SetStatus(report.Status.FAIL)
210        return
211
212    try:
213        ins_lock.SetInUse(False)
214        delete_report.SetStatus(report.Status.SUCCESS)
215        device_driver.AddDeletionResultToReport(
216            delete_report, [name], failed=[], error_msgs=[],
217            resource_name="instance")
218    finally:
219        ins_lock.Unlock()
220
221
222def CleanUpRemoteHost(cfg, remote_host, host_user,
223                      host_ssh_private_key_path=None):
224    """Clean up the remote host.
225
226    Args:
227        cfg: An AcloudConfig instance.
228        remote_host : String, ip address or host name of the remote host.
229        host_user: String of user login into the instance.
230        host_ssh_private_key_path: String of host key for logging in to the
231                                   host.
232
233    Returns:
234        A Report instance.
235    """
236    delete_report = report.Report(command="delete")
237    credentials = auth.CreateCredentials(cfg)
238    compute_client = cvd_compute_client_multi_stage.CvdComputeClient(
239        acloud_config=cfg,
240        oauth2_credentials=credentials)
241    ssh = ssh_object.Ssh(
242        ip=ssh_object.IP(ip=remote_host),
243        user=host_user,
244        ssh_private_key_path=(
245            host_ssh_private_key_path or cfg.ssh_private_key_path))
246    try:
247        compute_client.InitRemoteHost(ssh, remote_host, host_user)
248        delete_report.SetStatus(report.Status.SUCCESS)
249        device_driver.AddDeletionResultToReport(
250            delete_report, [remote_host], failed=[],
251            error_msgs=[],
252            resource_name="remote host")
253    except subprocess.CalledProcessError as e:
254        delete_report.AddError(str(e))
255        delete_report.SetStatus(report.Status.FAIL)
256
257    return delete_report
258
259
260def DeleteInstanceByNames(cfg, instances):
261    """Delete instances by the names of these instances.
262
263    Args:
264        cfg: AcloudConfig object.
265        instances: List of instance name.
266
267    Returns:
268        A Report instance.
269    """
270    delete_report = report.Report(command="delete")
271    local_names = set(name for name in instances if
272                      name.startswith(_LOCAL_INSTANCE_PREFIX))
273    remote_names = list(set(instances) - set(local_names))
274    if local_names:
275        active_instances = list_instances.GetLocalInstancesByNames(local_names)
276        inactive_names = local_names.difference(ins.name for ins in
277                                                active_instances)
278        if active_instances:
279            utils.PrintColorString("Deleting local instances")
280            delete_report = DeleteInstances(cfg, active_instances)
281        if inactive_names:
282            utils.PrintColorString("Unlocking local instances")
283            for name in inactive_names:
284                ResetLocalInstanceLockByName(name, delete_report)
285    if remote_names:
286        delete_report = DeleteRemoteInstances(cfg, remote_names, delete_report)
287    return delete_report
288
289
290def Run(args):
291    """Run delete.
292
293    After delete command executed, tool will return one Report instance.
294    If there is no instance to delete, just reutrn empty Report.
295
296    Args:
297        args: Namespace object from argparse.parse_args.
298
299    Returns:
300        A Report instance.
301    """
302    # Prioritize delete instances by names without query all instance info from
303    # GCP project.
304    cfg = config.GetAcloudConfig(args)
305    if args.instance_names:
306        return DeleteInstanceByNames(cfg,
307                                     args.instance_names)
308    if args.remote_host:
309        return CleanUpRemoteHost(cfg, args.remote_host, args.host_user,
310                                 args.host_ssh_private_key_path)
311
312    instances = list_instances.GetLocalInstances()
313    if not args.local_only and cfg.SupportRemoteInstance():
314        instances.extend(list_instances.GetRemoteInstances(cfg))
315
316    if args.adb_port:
317        instances = list_instances.FilterInstancesByAdbPort(instances,
318                                                            args.adb_port)
319    elif not args.all:
320        # Provide instances list to user and let user choose what to delete if
321        # user didn't specify instances in args.
322        instances = list_instances.ChooseInstancesFromList(instances)
323
324    if not instances:
325        utils.PrintColorString("No instances to delete")
326    return DeleteInstances(cfg, instances)
327