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