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