Fix nfdeployment uninstalling issue
[pti/o2.git] / o2dms / service / nfdeployment_handler.py
1 # Copyright (C) 2021 Wind River Systems, Inc.
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.
14
15 # pylint: disable=unused-argument
16 from __future__ import annotations
17 from o2dms.domain.states import NfDeploymentState
18 # from o2common.service import messagebus
19 from o2dms.domain.dms import NfDeployment, NfDeploymentDesc
20 from o2dms.domain import commands
21 from typing import Callable
22
23 from o2dms.domain.exceptions import NfdeploymentNotFoundError
24 from o2dms.domain import events
25 from o2common.service.unit_of_work import AbstractUnitOfWork
26 from helm_sdk import Helm
27 from ruamel import yaml
28 import json
29 from o2common.config import config
30 from retry import retry
31 # if TYPE_CHECKING:
32 #     from . import unit_of_work
33
34 from o2common.helper import o2logging
35 logger = o2logging.get_logger(__name__)
36 LOCAL_HELM_BIN = config.get_helm_cli()
37 K8S_KUBECONFIG, K8S_APISERVER, K8S_TOKEN = \
38     config.get_k8s_api_endpoint()
39
40
41 def publish_nfdeployment_state_change(
42     event: events.NfDeploymentStateChanged,
43     publish: Callable,
44 ):
45     publish("NfDeploymentStateChanged", event)
46     logger.debug(
47         "published NfDeploymentStateChanged: {}, state from {} to {}".format(
48             event.NfDeploymentId, event.FromState, event.ToState))
49
50
51 def handle_nfdeployment_statechanged(
52     cmd: commands.HandleNfDeploymentStateChanged,
53     uow: AbstractUnitOfWork
54 ):
55     if cmd.FromState == NfDeploymentState.Initial:
56         if cmd.ToState == NfDeploymentState.Installing:
57             cmd2 = commands.InstallNfDeployment(cmd.NfDeploymentId)
58             install_nfdeployment(cmd2, uow)
59         else:
60             logger.debug("Not insterested state change: {}".format(cmd))
61     elif cmd.FromState == NfDeploymentState.Installed \
62             or cmd.FromState == NfDeploymentState.Installing \
63             or cmd.FromState == NfDeploymentState.Updating \
64             or cmd.FromState == NfDeploymentState.Abnormal:
65
66         if cmd.ToState == NfDeploymentState.Uninstalling:
67             cmd2 = commands.UninstallNfDeployment(cmd.NfDeploymentId)
68             uninstall_nfdeployment(cmd2, uow)
69         else:
70             logger.debug("Not insterested state change: {}".format(cmd))
71     elif cmd.FromState == NfDeploymentState.Initial \
72             or cmd.FromState == NfDeploymentState.Abnormal:
73
74         if cmd.ToState == NfDeploymentState.Deleting:
75             # cmd2 = commands.UninstallNfDeployment(cmd.NfDeploymentId)
76             # uninstall_nfdeployment(cmd2, uow)
77             cmd2 = commands.DeleteNfDeployment(cmd.NfDeploymentId)
78             delete_nfdeployment(cmd2, uow)
79         else:
80             logger.debug("Not insterested state change: {}".format(cmd))
81     else:
82         logger.debug("Not insterested state change: {}".format(cmd))
83
84
85 # retry 10 seconds
86 @retry(
87     (NfdeploymentNotFoundError),
88     tries=100,
89     delay=2, max_delay=10000, backoff=1)
90 def _retry_get_nfdeployment(
91         cmd: commands.InstallNfDeployment,
92         uow: AbstractUnitOfWork):
93     nfdeployment: NfDeployment = uow.nfdeployments.get(
94         cmd.NfDeploymentId)
95     if nfdeployment is None:
96         raise NfdeploymentNotFoundError(
97             "Cannot find NfDeployment: {}".format(
98                 cmd.NfDeploymentId))
99     return nfdeployment
100
101
102 def install_nfdeployment(
103     cmd: commands.InstallNfDeployment,
104     uow: AbstractUnitOfWork
105 ):
106     logger.info("install with NfDeploymentId: {}".format(
107         cmd.NfDeploymentId))
108     nfdeployment: NfDeployment = _retry_get_nfdeployment(cmd, uow)
109     if nfdeployment is None:
110         raise Exception("Cannot find NfDeployment: {}".format(
111             cmd.NfDeploymentId))
112     # get nfdeploymentdescriptor by descriptorId
113     desc: NfDeploymentDesc = uow.nfdeployment_descs.get(
114         nfdeployment.descriptorId)
115     if desc is None:
116         raise Exception(
117             "Cannot find NfDeploymentDescriptor:{} for NfDeployment:{}".format(
118                 nfdeployment.descriptorId, nfdeployment.id
119             ))
120
121     nfdeployment.set_state(NfDeploymentState.Installing)
122
123     # helm repo add
124     repourl = desc.artifactRepoUrl
125     helm = Helm(logger, LOCAL_HELM_BIN, environment_variables={})
126     repoName = None
127     try:
128         repolist = helm.repo_list()
129         for repo in repolist:
130             if repo['url'] == repourl:
131                 repoName = repo['name']
132                 break
133     except Exception:
134         # repoExisted
135         repoName = None
136
137     if not repoName:
138         repoName = "repo4{}".format(nfdeployment.name)
139         logger.debug("Trying to add repo:{}".format(repourl))
140         helm.repo_add(repoName, repourl)
141     helm.repo_update(None)
142
143     repolist = helm.repo_list()
144     logger.debug('repo list:{}'.format(repolist))
145
146     # helm install name chart
147     values_file_path = '/tmp/override_{}.yaml'.format(nfdeployment.name)
148     if len(desc.inputParams) > 0:
149         logger.info("dump override yaml:{}".format(values_file_path))
150         values = json.loads(desc.inputParams)
151         _create_values_file(values_file_path, values)
152     else:
153         values_file_path = None
154
155     logger.debug('Try to helm install {}/{} {} -f {}'.format(
156         repoName, nfdeployment.name, desc.artifactName, values_file_path))
157     tokens = desc.artifactName.split(':')
158     chartname = tokens[0]
159     myflags = None
160     # if (len(tokens) > 1):
161     #     myflags = {"name": "version", "value": tokens[1]}
162     result = helm.install(
163         nfdeployment.name, "{}/{}".format(repoName, chartname), flags=myflags,
164         values_file=values_file_path, kubeconfig=K8S_KUBECONFIG,
165         token=K8S_TOKEN, apiserver=K8S_APISERVER)
166     logger.debug('result: {}'.format(result))
167
168     # in case success
169     with uow:
170         entity: NfDeployment = uow.nfdeployments.get(cmd.NfDeploymentId)
171         if entity:
172             entity.set_state(NfDeploymentState.Installed)
173             entity.transit_state(NfDeploymentState.Installed)
174         uow.commit()
175
176
177 def _create_values_file(filePath: str, content: dict):
178     with open(filePath, "w", encoding="utf-8") as f:
179         yaml.dump(content, f, Dumper=yaml.RoundTripDumper)
180
181
182 def uninstall_nfdeployment(
183     cmd: commands.UninstallNfDeployment,
184     uow: AbstractUnitOfWork
185 ):
186     logger.info("uninstall with NfDeploymentId: {}".format(
187         cmd.NfDeploymentId))
188     nfdeployment: NfDeployment = _retry_get_nfdeployment(cmd, uow)
189     if nfdeployment is None:
190         raise Exception("Cannot find NfDeployment: {}".format(
191             cmd.NfDeploymentId))
192     # get nfdeploymentdescriptor by descriptorId
193     desc: NfDeploymentDesc = uow.nfdeployment_descs.get(
194         nfdeployment.descriptorId)
195     if desc is None:
196         raise Exception(
197             "Cannot find NfDeploymentDescriptor:{} for NfDeployment:{}".format(
198                 nfdeployment.descriptorId, nfdeployment.id
199             ))
200
201     with uow:
202         entity: NfDeployment = uow.nfdeployments.get(cmd.NfDeploymentId)
203         if entity:
204             entity.set_state(NfDeploymentState.Uninstalling)
205         uow.commit()
206
207     helm = Helm(logger, LOCAL_HELM_BIN, environment_variables={})
208
209     logger.debug('Try to helm del {}'.format(
210         nfdeployment.name))
211     myflags = None
212     # if (len(tokens) > 1):
213     #     myflags = {"name": "version", "value": tokens[1]}
214     result = helm.uninstall(
215         nfdeployment.name, flags=myflags,
216         kubeconfig=K8S_KUBECONFIG,
217         token=K8S_TOKEN, apiserver=K8S_APISERVER)
218     logger.debug('result: {}'.format(result))
219
220     # in case success
221
222     with uow:
223         entity: NfDeployment = uow.nfdeployments.get(cmd.NfDeploymentId)
224         if entity:
225             entity.set_state(NfDeploymentState.Initial)
226             entity.transit_state(NfDeploymentState.Deleting)
227         # uow.nfdeployments.update(
228         #     cmd.NfDeploymentId, status=NfDeploymentState.Initial)
229         uow.commit()
230
231
232 def delete_nfdeployment(
233     cmd: commands.UninstallNfDeployment,
234     uow: AbstractUnitOfWork
235 ):
236     logger.info("delete with NfDeploymentId: {}".format(
237         cmd.NfDeploymentId))
238
239     # nfdeployment: NfDeployment = _retry_get_nfdeployment(cmd, uow)
240     with uow:
241         uow.nfdeployments.delete(cmd.NfDeploymentId)
242         uow.commit()