665e03e92d9c651ab9e6f9dd07e0addc67b6eefd
[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     nfdeployment.set_state(NfDeploymentState.Uninstalling)
202     helm = Helm(logger, LOCAL_HELM_BIN, environment_variables={})
203
204     logger.debug('Try to helm del {}'.format(
205         nfdeployment.name))
206     myflags = None
207     # if (len(tokens) > 1):
208     #     myflags = {"name": "version", "value": tokens[1]}
209     result = helm.uninstall(
210         nfdeployment.name, flags=myflags,
211         kubeconfig=K8S_KUBECONFIG,
212         token=K8S_TOKEN, apiserver=K8S_APISERVER)
213     logger.debug('result: {}'.format(result))
214
215     # in case success
216
217     with uow:
218         entity: NfDeployment = uow.nfdeployments.get(cmd.NfDeploymentId)
219         if entity:
220             entity.transit_state(NfDeploymentState.Initial)
221         # uow.nfdeployments.update(
222         #     cmd.NfDeploymentId, status=NfDeploymentState.Initial)
223         uow.commit()
224
225
226 def delete_nfdeployment(
227     cmd: commands.UninstallNfDeployment,
228     uow: AbstractUnitOfWork
229 ):
230     logger.info("delete with NfDeploymentId: {}".format(
231         cmd.NfDeploymentId))
232
233     # nfdeployment: NfDeployment = _retry_get_nfdeployment(cmd, uow)
234     with uow:
235         uow.nfdeployments.delete(cmd.NfDeploymentId)
236         uow.commit()