0fdd00a6cf90d9a28a124905ed5e76cf93819276
[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         elif cmd.ToState == NfDeploymentState.Deleting:
60             cmd2 = commands.DeleteNfDeployment(cmd.NfDeploymentId)
61             delete_nfdeployment(cmd2, uow)
62         else:
63             logger.debug("Not insterested state change: {}".format(cmd))
64     elif cmd.FromState == NfDeploymentState.Installed \
65             or cmd.FromState == NfDeploymentState.Installing \
66             or cmd.FromState == NfDeploymentState.Updating \
67             or cmd.FromState == NfDeploymentState.Abnormal:
68
69         if cmd.ToState == NfDeploymentState.Uninstalling:
70             cmd2 = commands.UninstallNfDeployment(cmd.NfDeploymentId)
71             uninstall_nfdeployment(cmd2, uow)
72         else:
73             logger.debug("Not insterested state change: {}".format(cmd))
74     elif cmd.FromState == NfDeploymentState.Abnormal:
75         if cmd.ToState == NfDeploymentState.Deleting:
76             # cmd2 = commands.UninstallNfDeployment(cmd.NfDeploymentId)
77             # uninstall_nfdeployment(cmd2, uow)
78             cmd2 = commands.DeleteNfDeployment(cmd.NfDeploymentId)
79             delete_nfdeployment(cmd2, uow)
80         else:
81             logger.debug("Not insterested state change: {}".format(cmd))
82     else:
83         logger.debug("Not insterested state change: {}".format(cmd))
84
85
86 # retry 10 seconds
87 @retry(
88     (NfdeploymentNotFoundError),
89     tries=100,
90     delay=2, max_delay=10000, backoff=1)
91 def _retry_get_nfdeployment(
92         cmd: commands.InstallNfDeployment,
93         uow: AbstractUnitOfWork):
94     nfdeployment: NfDeployment = uow.nfdeployments.get(
95         cmd.NfDeploymentId)
96     if nfdeployment is None:
97         raise NfdeploymentNotFoundError(
98             "Cannot find NfDeployment: {}".format(
99                 cmd.NfDeploymentId))
100     return nfdeployment
101
102
103 def install_nfdeployment(
104     cmd: commands.InstallNfDeployment,
105     uow: AbstractUnitOfWork
106 ):
107     logger.info("install with NfDeploymentId: {}".format(
108         cmd.NfDeploymentId))
109     nfdeployment: NfDeployment = _retry_get_nfdeployment(cmd, uow)
110     if nfdeployment is None:
111         raise Exception("Cannot find NfDeployment: {}".format(
112             cmd.NfDeploymentId))
113     # get nfdeploymentdescriptor by descriptorId
114     desc: NfDeploymentDesc = uow.nfdeployment_descs.get(
115         nfdeployment.descriptorId)
116     if desc is None:
117         raise Exception(
118             "Cannot find NfDeploymentDescriptor:{} for NfDeployment:{}".format(
119                 nfdeployment.descriptorId, nfdeployment.id
120             ))
121
122     nfdeployment.set_state(NfDeploymentState.Installing)
123
124     # helm repo add
125     repourl = desc.artifactRepoUrl
126     helm = Helm(logger, LOCAL_HELM_BIN, environment_variables={})
127     repoName = None
128     try:
129         repolist = helm.repo_list()
130         for repo in repolist:
131             if repo['url'] == repourl:
132                 repoName = repo['name']
133                 break
134     except Exception:
135         # repoExisted
136         repoName = None
137
138     if not repoName:
139         repoName = "repo4{}".format(nfdeployment.name)
140         logger.debug("Trying to add repo:{}".format(repourl))
141         helm.repo_add(repoName, repourl)
142     helm.repo_update(None)
143
144     repolist = helm.repo_list()
145     logger.debug('repo list:{}'.format(repolist))
146
147     # helm install name chart
148     values_file_path = '/tmp/override_{}.yaml'.format(nfdeployment.name)
149     if len(desc.inputParams) > 0:
150         logger.info("dump override yaml:{}".format(values_file_path))
151         values = json.loads(desc.inputParams)
152         _create_values_file(values_file_path, values)
153     else:
154         values_file_path = None
155
156     logger.debug('Try to helm install {}/{} {} -f {}'.format(
157         repoName, nfdeployment.name, desc.artifactName, values_file_path))
158     tokens = desc.artifactName.split(':')
159     chartname = tokens[0]
160     myflags = None
161     # if (len(tokens) > 1):
162     #     myflags = {"name": "version", "value": tokens[1]}
163     result = helm.install(
164         nfdeployment.name, "{}/{}".format(repoName, chartname), flags=myflags,
165         values_file=values_file_path, kubeconfig=K8S_KUBECONFIG,
166         token=K8S_TOKEN, apiserver=K8S_APISERVER)
167     logger.debug('result: {}'.format(result))
168
169     # in case success
170     with uow:
171         entity: NfDeployment = uow.nfdeployments.get(cmd.NfDeploymentId)
172         if entity:
173             entity.set_state(NfDeploymentState.Installed)
174             entity.transit_state(NfDeploymentState.Installed)
175         uow.commit()
176
177
178 def _create_values_file(filePath: str, content: dict):
179     with open(filePath, "w", encoding="utf-8") as f:
180         yaml.dump(content, f, Dumper=yaml.RoundTripDumper)
181
182
183 def uninstall_nfdeployment(
184     cmd: commands.UninstallNfDeployment,
185     uow: AbstractUnitOfWork
186 ):
187     logger.info("uninstall with NfDeploymentId: {}".format(
188         cmd.NfDeploymentId))
189     nfdeployment: NfDeployment = _retry_get_nfdeployment(cmd, uow)
190     if nfdeployment is None:
191         raise Exception("Cannot find NfDeployment: {}".format(
192             cmd.NfDeploymentId))
193     # get nfdeploymentdescriptor by descriptorId
194     desc: NfDeploymentDesc = uow.nfdeployment_descs.get(
195         nfdeployment.descriptorId)
196     if desc is None:
197         raise Exception(
198             "Cannot find NfDeploymentDescriptor:{} for NfDeployment:{}".format(
199                 nfdeployment.descriptorId, nfdeployment.id
200             ))
201
202     with uow:
203         entity: NfDeployment = uow.nfdeployments.get(cmd.NfDeploymentId)
204         if entity:
205             entity.set_state(NfDeploymentState.Uninstalling)
206         uow.commit()
207
208     helm = Helm(logger, LOCAL_HELM_BIN, environment_variables={})
209
210     logger.debug('Try to helm del {}'.format(
211         nfdeployment.name))
212     myflags = None
213     # if (len(tokens) > 1):
214     #     myflags = {"name": "version", "value": tokens[1]}
215     result = helm.uninstall(
216         nfdeployment.name, flags=myflags,
217         kubeconfig=K8S_KUBECONFIG,
218         token=K8S_TOKEN, apiserver=K8S_APISERVER)
219     logger.debug('result: {}'.format(result))
220
221     # in case success
222
223     with uow:
224         entity: NfDeployment = uow.nfdeployments.get(cmd.NfDeploymentId)
225         if entity:
226             entity.set_state(NfDeploymentState.Initial)
227             entity.transit_state(NfDeploymentState.Deleting)
228         # uow.nfdeployments.update(
229         #     cmd.NfDeploymentId, status=NfDeploymentState.Initial)
230         uow.commit()
231
232
233 def delete_nfdeployment(
234     cmd: commands.UninstallNfDeployment,
235     uow: AbstractUnitOfWork
236 ):
237     logger.info("delete with NfDeploymentId: {}".format(
238         cmd.NfDeploymentId))
239
240     # nfdeployment: NfDeployment = _retry_get_nfdeployment(cmd, uow)
241     with uow:
242         uow.nfdeployments.delete(cmd.NfDeploymentId)
243         uow.commit()