Add dcae-cli and component-json-schemas projects
[dcaegen2/platform/cli.git] / dcae-cli / dcae_cli / util / undeploy.py
1 # ============LICENSE_START=======================================================
2 # org.onap.dcae
3 # ================================================================================
4 # Copyright (c) 2017 AT&T Intellectual Property. All rights reserved.
5 # ================================================================================
6 # Licensed under the Apache License, Version 2.0 (the "License");
7 # you may not use this file except in compliance with the License.
8 # You may obtain a copy of the License at
9 #
10 #      http://www.apache.org/licenses/LICENSE-2.0
11 #
12 # Unless required by applicable law or agreed to in writing, software
13 # distributed under the License is distributed on an "AS IS" BASIS,
14 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 # See the License for the specific language governing permissions and
16 # limitations under the License.
17 # ============LICENSE_END=========================================================
18 #
19 # ECOMP is a trademark and service mark of AT&T Intellectual Property.
20
21 # -*- coding: utf-8 -*-
22 """
23 Provides utilities for undeploying components
24 """
25 from functools import partial
26 from dcae_cli.util.exc import DcaeException
27 import dcae_cli.util.profiles as profiles
28 from dcae_cli.util.cdap_util import undeploy_component as undeploy_cdap_component
29 from dcae_cli.util.discovery import get_healthy_instances, get_defective_instances, \
30         remove_config
31 from dcae_cli.util import docker_util as du
32 from dcae_cli.util.logger import get_logger
33
34
35 log = get_logger('Undeploy')
36
37
38 def _handler(undeploy_funcs, instances):
39     """Handles the undeployment
40
41     Executes all undeployment functions for all instances and gathers up the
42     results. No short circuiting.
43
44     Args
45     ----
46     undeploy_funcs: List of functions that have the following signature `fn: string->boolean`
47         the input is a fully qualified instance name and the return is True upon
48         success and False for failures
49     instances: List of fully qualified instance names
50
51     Returns
52     -------
53     (failures, results) where each are a list of tuples.  Each tuple has the
54     structure: `(<instance name>, result of func 1, result of func 2, ..)`.
55     """
56     if not instances:
57         return [], []
58
59     # Invoke all undeploy funcs for all instances
60     def invoke_undeploys(instance):
61         return tuple([ undeploy_func(instance) for undeploy_func in undeploy_funcs ])
62
63     results = [ (instance, ) + invoke_undeploys(instance) for instance in instances ]
64
65     # Determine failures
66     filter_failures_func = partial(filter, lambda result: not all(result[1:]))
67     failures = list(filter_failures_func(results))
68
69     return failures, results
70
71
72 def _handler_report(failures, results):
73     """Reports the result of handling"""
74     if len(failures) > 0:
75         failed_names = [ result[0] for result in failures ]
76         log.warn("Could not completely undeploy: {0}".format(", ".join(failed_names)))
77
78         # This message captures a case where you are seeing a false negative. If
79         # you attempted to undeploy a component instance and it partially failed
80         # the first time but "succeeded" the second time, the second undeploy
81         # would get reported as a failure. The second undeploy would probably
82         # also be partial undeploy because the undeploy operation that succeeded
83         # the first time will fail the second time.
84         log.warn("NOTE: This could be expected since we are attempting to undeploy a component in a bad partial state")
85     elif len(results) == 0:
86         log.warn("No components found to undeploy")
87     else:
88         # This seems like important info so set it to warning so that it shows up
89         log.warn("Undeployed components: {0}".format(len(results)))
90
91
92 def undeploy_component(user, cname, cver, catalog):
93     '''Undeploys a component based on the component type'''
94     cname, cver = catalog.verify_component(cname, cver)
95     ctype = catalog.get_component_type(cname, cver)
96     profile = profiles.get_profile()
97     # Get *all* instances of the component whether running healthy or in a bad partial
98     # deployed state
99     instances = get_healthy_instances(user, cname, cver) + get_defective_instances(user, cname, cver)
100
101     if ctype == 'docker':
102         client = du.get_docker_client(profile)
103         image = catalog.get_docker_image(cname, cver)
104         undeploy_func = partial(du.undeploy_component, client, image)
105     elif ctype == 'cdap':
106         undeploy_func = partial(undeploy_cdap_component, profile)
107     else:
108         raise DcaeException("Unsupported component type for undeploy")
109
110     log.warn("Undeploying components: {0}".format(len(instances)))
111     _handler_report(*_handler([undeploy_func, remove_config], instances))