blob: e43b8a42a1a641b5f86aa8a15197e44a295bf0a7 [file] [log] [blame]
quilesj26c78a42019-10-28 18:10:42 +01001##
2# Copyright 2019 Telefonica Investigacion y Desarrollo, S.A.U.
3# This file is part of OSM
4# All Rights Reserved.
5#
6# Licensed under the Apache License, Version 2.0 (the "License");
7# you may not use this file except in compliance with the License.
8# You may obtain a copy of the License at
9#
10# http://www.apache.org/licenses/LICENSE-2.0
11#
12# Unless required by applicable law or agreed to in writing, software
13# distributed under the License is distributed on an "AS IS" BASIS,
14# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
15# implied.
16# See the License for the specific language governing permissions and
17# limitations under the License.
18#
19# For those usages not covered by the Apache License, Version 2.0 please
20# contact with: nfvlabs@tid.es
21##
quilesj26c78a42019-10-28 18:10:42 +010022import asyncio
beierlmf52cb7c2020-04-21 16:36:35 -040023import os
beierlmf52cb7c2020-04-21 16:36:35 -040024import yaml
quilesj26c78a42019-10-28 18:10:42 +010025
lloretgalleg1c83f2e2020-10-22 09:12:35 +000026from n2vc.k8s_helm_base_conn import K8sHelmBaseConnector
27from n2vc.exceptions import K8sException
quilesj26c78a42019-10-28 18:10:42 +010028
lloretgalleg1c83f2e2020-10-22 09:12:35 +000029
30class K8sHelmConnector(K8sHelmBaseConnector):
quilesj26c78a42019-10-28 18:10:42 +010031
32 """
beierlmf52cb7c2020-04-21 16:36:35 -040033 ####################################################################################
34 ################################### P U B L I C ####################################
35 ####################################################################################
quilesj26c78a42019-10-28 18:10:42 +010036 """
37
38 def __init__(
beierlmf52cb7c2020-04-21 16:36:35 -040039 self,
40 fs: object,
41 db: object,
42 kubectl_command: str = "/usr/bin/kubectl",
43 helm_command: str = "/usr/bin/helm",
44 log: object = None,
45 on_update_db=None,
quilesj26c78a42019-10-28 18:10:42 +010046 ):
47 """
lloretgalleg1c83f2e2020-10-22 09:12:35 +000048 Initializes helm connector for helm v2
quilesj26c78a42019-10-28 18:10:42 +010049
50 :param fs: file system for kubernetes and helm configuration
51 :param db: database object to write current operation status
52 :param kubectl_command: path to kubectl executable
53 :param helm_command: path to helm executable
54 :param log: logger
55 :param on_update_db: callback called when k8s connector updates database
56 """
57
58 # parent class
lloretgalleg1c83f2e2020-10-22 09:12:35 +000059 K8sHelmBaseConnector.__init__(
60 self,
61 db=db,
62 log=log,
63 fs=fs,
64 kubectl_command=kubectl_command,
65 helm_command=helm_command,
66 on_update_db=on_update_db,
67 )
quilesj26c78a42019-10-28 18:10:42 +010068
lloretgalleg1c83f2e2020-10-22 09:12:35 +000069 self.log.info("Initializing K8S Helm2 connector")
quilesj26c78a42019-10-28 18:10:42 +010070
quilesj1be06302019-11-29 11:17:11 +000071 # initialize helm client-only
beierlmf52cb7c2020-04-21 16:36:35 -040072 self.log.debug("Initializing helm client-only...")
David Garcia4395cfa2021-05-28 16:21:51 +020073 command = "{} init --client-only {} ".format(
74 self._helm_command,
75 "--stable-repo-url {}".format(self._stable_repo_url)
76 if self._stable_repo_url
77 else "--skip-repos",
garciadeblas82b591c2021-03-24 09:22:13 +010078 )
quilesj1be06302019-11-29 11:17:11 +000079 try:
beierlmf52cb7c2020-04-21 16:36:35 -040080 asyncio.ensure_future(
81 self._local_async_exec(command=command, raise_exception_on_error=False)
82 )
quilesj1be06302019-11-29 11:17:11 +000083 # loop = asyncio.get_event_loop()
beierlmf52cb7c2020-04-21 16:36:35 -040084 # loop.run_until_complete(self._local_async_exec(command=command,
85 # raise_exception_on_error=False))
quilesj1be06302019-11-29 11:17:11 +000086 except Exception as e:
beierlmf52cb7c2020-04-21 16:36:35 -040087 self.warning(
88 msg="helm init failed (it was already initialized): {}".format(e)
89 )
quilesj1be06302019-11-29 11:17:11 +000090
lloretgalleg1c83f2e2020-10-22 09:12:35 +000091 self.log.info("K8S Helm2 connector initialized")
quilesj26c78a42019-10-28 18:10:42 +010092
lloretgalleg095392b2020-11-20 11:28:08 +000093 async def install(
garciadeblas82b591c2021-03-24 09:22:13 +010094 self,
95 cluster_uuid: str,
96 kdu_model: str,
97 kdu_instance: str,
98 atomic: bool = True,
99 timeout: float = 300,
100 params: dict = None,
101 db_dict: dict = None,
102 kdu_name: str = None,
103 namespace: str = None,
104 **kwargs,
lloretgalleg095392b2020-11-20 11:28:08 +0000105 ):
David Garciaeb8943a2021-04-12 12:07:37 +0200106 """
107 Deploys of a new KDU instance. It would implicitly rely on the `install` call
108 to deploy the Chart/Bundle properly parametrized (in practice, this call would
109 happen before any _initial-config-primitive_of the VNF is called).
110
111 :param cluster_uuid: UUID of a K8s cluster known by OSM
112 :param kdu_model: chart/ reference (string), which can be either
113 of these options:
114 - a name of chart available via the repos known by OSM
115 - a path to a packaged chart
116 - a path to an unpacked chart directory or a URL
117 :param kdu_instance: Kdu instance name
118 :param atomic: If set, installation process purges chart/bundle on fail, also
119 will wait until all the K8s objects are active
120 :param timeout: Time in seconds to wait for the install of the chart/bundle
121 (defaults to Helm default timeout: 300s)
122 :param params: dictionary of key-value pairs for instantiation parameters
123 (overriding default values)
124 :param dict db_dict: where to write into database when the status changes.
125 It contains a dict with {collection: <str>, filter: {},
126 path: <str>},
127 e.g. {collection: "nsrs", filter:
128 {_id: <nsd-id>, path: "_admin.deployed.K8S.3"}
129 :param kdu_name: Name of the KDU instance to be installed
130 :param namespace: K8s namespace to use for the KDU instance
131 :param kwargs: Additional parameters (None yet)
132 :return: True if successful
133 """
Pedro Escaleiraeba0ac32022-04-02 00:44:08 +0100134 self.log.debug("installing {} in cluster {}".format(kdu_model, cluster_uuid))
lloretgalleg095392b2020-11-20 11:28:08 +0000135
136 # sync local dir
Pedro Escaleiraeba0ac32022-04-02 00:44:08 +0100137 self.fs.sync(from_path=cluster_uuid)
lloretgalleg095392b2020-11-20 11:28:08 +0000138
139 # init env, paths
140 paths, env = self._init_paths_env(
Pedro Escaleiraeba0ac32022-04-02 00:44:08 +0100141 cluster_name=cluster_uuid, create_if_not_exist=True
lloretgalleg095392b2020-11-20 11:28:08 +0000142 )
143
David Garciac4da25c2021-02-23 11:47:29 +0100144 await self._install_impl(
Pedro Escaleiraeba0ac32022-04-02 00:44:08 +0100145 cluster_uuid,
David Garciac4da25c2021-02-23 11:47:29 +0100146 kdu_model,
147 paths,
148 env,
149 kdu_instance,
150 atomic=atomic,
151 timeout=timeout,
152 params=params,
153 db_dict=db_dict,
154 kdu_name=kdu_name,
155 namespace=namespace,
156 )
lloretgalleg095392b2020-11-20 11:28:08 +0000157
158 # sync fs
Pedro Escaleiraeba0ac32022-04-02 00:44:08 +0100159 self.fs.reverse_sync(from_path=cluster_uuid)
lloretgalleg095392b2020-11-20 11:28:08 +0000160
161 self.log.debug("Returning kdu_instance {}".format(kdu_instance))
David Garciac4da25c2021-02-23 11:47:29 +0100162 return True
lloretgalleg095392b2020-11-20 11:28:08 +0000163
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000164 async def inspect_kdu(self, kdu_model: str, repo_url: str = None) -> str:
165
166 self.log.debug(
167 "inspect kdu_model {} from (optional) repo: {}".format(kdu_model, repo_url)
168 )
169
170 return await self._exec_inspect_comand(
171 inspect_command="", kdu_model=kdu_model, repo_url=repo_url
172 )
173
174 """
175 ####################################################################################
176 ################################### P R I V A T E ##################################
177 ####################################################################################
178 """
179
180 def _init_paths_env(self, cluster_name: str, create_if_not_exist: bool = True):
tiernoa5728bf2020-06-25 15:48:52 +0000181 """
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000182 Creates and returns base cluster and kube dirs and returns them.
183 Also created helm3 dirs according to new directory specification, paths are
184 returned and also environment variables that must be provided to execute commands
185
186 Helm 2 directory specification uses helm_home dir:
187
188 The variables assigned for this paths are:
189 - Helm hone: $HELM_HOME
190 - helm kubeconfig: $KUBECONFIG
191
192 :param cluster_name: cluster_name
193 :return: Dictionary with config_paths and dictionary with helm environment variables
tiernoa5728bf2020-06-25 15:48:52 +0000194 """
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000195 base = self.fs.path
196 if base.endswith("/") or base.endswith("\\"):
197 base = base[:-1]
tiernoa5728bf2020-06-25 15:48:52 +0000198
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000199 # base dir for cluster
200 cluster_dir = base + "/" + cluster_name
garciadeblas54771fa2019-12-13 13:39:03 +0100201
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000202 # kube dir
203 kube_dir = cluster_dir + "/" + ".kube"
204 if create_if_not_exist and not os.path.exists(kube_dir):
205 self.log.debug("Creating dir {}".format(kube_dir))
206 os.makedirs(kube_dir)
quilesj26c78a42019-10-28 18:10:42 +0100207
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000208 # helm home dir
209 helm_dir = cluster_dir + "/" + ".helm"
210 if create_if_not_exist and not os.path.exists(helm_dir):
211 self.log.debug("Creating dir {}".format(helm_dir))
212 os.makedirs(helm_dir)
quilesj26c78a42019-10-28 18:10:42 +0100213
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000214 config_filename = kube_dir + "/config"
quilesj26c78a42019-10-28 18:10:42 +0100215
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000216 # 2 - Prepare dictionary with paths
217 paths = {
218 "kube_dir": kube_dir,
219 "kube_config": config_filename,
220 "cluster_dir": cluster_dir,
221 "helm_dir": helm_dir,
222 }
223
224 for file_name, file in paths.items():
225 if "dir" in file_name and not os.path.exists(file):
226 err_msg = "{} dir does not exist".format(file)
227 self.log.error(err_msg)
228 raise K8sException(err_msg)
229
230 # 3 - Prepare environment variables
231 env = {"HELM_HOME": helm_dir, "KUBECONFIG": config_filename}
232
233 return paths, env
234
bravof53dd7462021-11-17 11:14:57 -0300235 async def _get_services(self, cluster_id, kdu_instance, namespace, kubeconfig):
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000236
237 # init config, env
238 paths, env = self._init_paths_env(
tiernoa5728bf2020-06-25 15:48:52 +0000239 cluster_name=cluster_id, create_if_not_exist=True
beierlmf52cb7c2020-04-21 16:36:35 -0400240 )
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000241
bravof53dd7462021-11-17 11:14:57 -0300242 command1 = "env KUBECONFIG={} {} get manifest {} ".format(
243 kubeconfig, self._helm_command, kdu_instance
244 )
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000245 command2 = "{} get --namespace={} -f -".format(self.kubectl_command, namespace)
246 output, _rc = await self._local_async_exec_pipe(
247 command1, command2, env=env, raise_exception_on_error=True
248 )
249 services = self._parse_services(output)
250
251 return services
252
garciadeblas82b591c2021-03-24 09:22:13 +0100253 async def _cluster_init(
254 self, cluster_id: str, namespace: str, paths: dict, env: dict
255 ):
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000256 """
257 Implements the helm version dependent cluster initialization:
258 For helm2 it initialized tiller environment if needed
259 """
quilesj26c78a42019-10-28 18:10:42 +0100260
261 # check if tiller pod is up in cluster
beierlmf52cb7c2020-04-21 16:36:35 -0400262 command = "{} --kubeconfig={} --namespace={} get deployments".format(
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000263 self.kubectl_command, paths["kube_config"], namespace
beierlmf52cb7c2020-04-21 16:36:35 -0400264 )
265 output, _rc = await self._local_async_exec(
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000266 command=command, raise_exception_on_error=True, env=env
beierlmf52cb7c2020-04-21 16:36:35 -0400267 )
quilesj26c78a42019-10-28 18:10:42 +0100268
tierno119f7232020-04-21 13:22:26 +0000269 output_table = self._output_to_table(output=output)
quilesj26c78a42019-10-28 18:10:42 +0100270
271 # find 'tiller' pod in all pods
272 already_initialized = False
273 try:
274 for row in output_table:
beierlmf52cb7c2020-04-21 16:36:35 -0400275 if row[0].startswith("tiller-deploy"):
quilesj26c78a42019-10-28 18:10:42 +0100276 already_initialized = True
277 break
beierlmf52cb7c2020-04-21 16:36:35 -0400278 except Exception:
quilesj26c78a42019-10-28 18:10:42 +0100279 pass
280
281 # helm init
282 n2vc_installed_sw = False
283 if not already_initialized:
beierlmf52cb7c2020-04-21 16:36:35 -0400284 self.log.info(
tiernoa5728bf2020-06-25 15:48:52 +0000285 "Initializing helm in client and server: {}".format(cluster_id)
beierlmf52cb7c2020-04-21 16:36:35 -0400286 )
tiernoa5728bf2020-06-25 15:48:52 +0000287 command = "{} --kubeconfig={} --namespace kube-system create serviceaccount {}".format(
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000288 self.kubectl_command, paths["kube_config"], self.service_account
289 )
290 _, _rc = await self._local_async_exec(
291 command=command, raise_exception_on_error=False, env=env
292 )
tiernoa5728bf2020-06-25 15:48:52 +0000293
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000294 command = (
295 "{} --kubeconfig={} create clusterrolebinding osm-tiller-cluster-rule "
296 "--clusterrole=cluster-admin --serviceaccount=kube-system:{}"
297 ).format(self.kubectl_command, paths["kube_config"], self.service_account)
298 _, _rc = await self._local_async_exec(
299 command=command, raise_exception_on_error=False, env=env
300 )
tiernoa5728bf2020-06-25 15:48:52 +0000301
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000302 command = (
303 "{} --kubeconfig={} --tiller-namespace={} --home={} --service-account {} "
David Garcia4395cfa2021-05-28 16:21:51 +0200304 " {} init"
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000305 ).format(
306 self._helm_command,
307 paths["kube_config"],
308 namespace,
309 paths["helm_dir"],
310 self.service_account,
David Garcia4395cfa2021-05-28 16:21:51 +0200311 "--stable-repo-url {}".format(self._stable_repo_url)
312 if self._stable_repo_url
313 else "--skip-repos",
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000314 )
315 _, _rc = await self._local_async_exec(
316 command=command, raise_exception_on_error=True, env=env
317 )
quilesj26c78a42019-10-28 18:10:42 +0100318 n2vc_installed_sw = True
319 else:
320 # check client helm installation
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000321 check_file = paths["helm_dir"] + "/repository/repositories.yaml"
322 if not self._check_file_exists(
323 filename=check_file, exception_if_not_exists=False
324 ):
tiernoa5728bf2020-06-25 15:48:52 +0000325 self.log.info("Initializing helm in client: {}".format(cluster_id))
beierlmf52cb7c2020-04-21 16:36:35 -0400326 command = (
327 "{} --kubeconfig={} --tiller-namespace={} "
David Garcia4395cfa2021-05-28 16:21:51 +0200328 "--home={} init --client-only {} "
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000329 ).format(
330 self._helm_command,
331 paths["kube_config"],
332 namespace,
333 paths["helm_dir"],
David Garcia4395cfa2021-05-28 16:21:51 +0200334 "--stable-repo-url {}".format(self._stable_repo_url)
335 if self._stable_repo_url
336 else "--skip-repos",
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000337 )
beierlmf52cb7c2020-04-21 16:36:35 -0400338 output, _rc = await self._local_async_exec(
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000339 command=command, raise_exception_on_error=True, env=env
beierlmf52cb7c2020-04-21 16:36:35 -0400340 )
quilesj26c78a42019-10-28 18:10:42 +0100341 else:
beierlmf52cb7c2020-04-21 16:36:35 -0400342 self.log.info("Helm client already initialized")
quilesj26c78a42019-10-28 18:10:42 +0100343
Pedro Escaleiraeba0ac32022-04-02 00:44:08 +0100344 repo_list = await self.repo_list(cluster_id)
lloretgalleg83e55892020-12-17 12:42:11 +0000345 for repo in repo_list:
346 if repo["name"] == "stable" and repo["url"] != self._stable_repo_url:
347 self.log.debug("Add new stable repo url: {}")
Pedro Escaleiraeba0ac32022-04-02 00:44:08 +0100348 await self.repo_remove(cluster_id, "stable")
David Garcia4395cfa2021-05-28 16:21:51 +0200349 if self._stable_repo_url:
Pedro Escaleiraeba0ac32022-04-02 00:44:08 +0100350 await self.repo_add(cluster_id, "stable", self._stable_repo_url)
lloretgalleg83e55892020-12-17 12:42:11 +0000351 break
352
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000353 return n2vc_installed_sw
lloretgallege308c712020-09-02 09:40:38 +0000354
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000355 async def _uninstall_sw(self, cluster_id: str, namespace: str):
356 # uninstall Tiller if necessary
quilesj26c78a42019-10-28 18:10:42 +0100357
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000358 self.log.debug("Uninstalling tiller from cluster {}".format(cluster_id))
quilesj26c78a42019-10-28 18:10:42 +0100359
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000360 # init paths, env
361 paths, env = self._init_paths_env(
tiernoa5728bf2020-06-25 15:48:52 +0000362 cluster_name=cluster_id, create_if_not_exist=True
beierlmf52cb7c2020-04-21 16:36:35 -0400363 )
quilesj26c78a42019-10-28 18:10:42 +0100364
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000365 if not namespace:
366 # find namespace for tiller pod
367 command = "{} --kubeconfig={} get deployments --all-namespaces".format(
368 self.kubectl_command, paths["kube_config"]
369 )
370 output, _rc = await self._local_async_exec(
371 command=command, raise_exception_on_error=False, env=env
372 )
373 output_table = self._output_to_table(output=output)
374 namespace = None
375 for r in output_table:
376 try:
377 if "tiller-deploy" in r[1]:
378 namespace = r[0]
379 break
380 except Exception:
381 pass
quilesj26c78a42019-10-28 18:10:42 +0100382 else:
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000383 msg = "Tiller deployment not found in cluster {}".format(cluster_id)
384 self.log.error(msg)
quilesj26c78a42019-10-28 18:10:42 +0100385
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000386 self.log.debug("namespace for tiller: {}".format(namespace))
quilesj26c78a42019-10-28 18:10:42 +0100387
tierno53555f62020-04-07 11:08:16 +0000388 if namespace:
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000389 # uninstall tiller from cluster
390 self.log.debug("Uninstalling tiller from cluster {}".format(cluster_id))
391 command = "{} --kubeconfig={} --home={} reset".format(
392 self._helm_command, paths["kube_config"], paths["helm_dir"]
beierlmf52cb7c2020-04-21 16:36:35 -0400393 )
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000394 self.log.debug("resetting: {}".format(command))
395 output, _rc = await self._local_async_exec(
396 command=command, raise_exception_on_error=True, env=env
quilesj26c78a42019-10-28 18:10:42 +0100397 )
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000398 # Delete clusterrolebinding and serviceaccount.
399 # Ignore if errors for backward compatibility
400 command = (
401 "{} --kubeconfig={} delete clusterrolebinding.rbac.authorization.k8s."
402 "io/osm-tiller-cluster-rule"
403 ).format(self.kubectl_command, paths["kube_config"])
404 output, _rc = await self._local_async_exec(
405 command=command, raise_exception_on_error=False, env=env
quilesj26c78a42019-10-28 18:10:42 +0100406 )
Pedro Escaleiraeba0ac32022-04-02 00:44:08 +0100407 command = (
408 "{} --kubeconfig={} --namespace {} delete serviceaccount/{}".format(
409 self.kubectl_command,
410 paths["kube_config"],
411 namespace,
412 self.service_account,
413 )
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000414 )
415 output, _rc = await self._local_async_exec(
416 command=command, raise_exception_on_error=False, env=env
417 )
quilesj26c78a42019-10-28 18:10:42 +0100418
419 else:
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000420 self.log.debug("namespace not found")
quilesj26c78a42019-10-28 18:10:42 +0100421
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000422 async def _instances_list(self, cluster_id):
quilesj26c78a42019-10-28 18:10:42 +0100423
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000424 # init paths, env
425 paths, env = self._init_paths_env(
tiernoa5728bf2020-06-25 15:48:52 +0000426 cluster_name=cluster_id, create_if_not_exist=True
beierlmf52cb7c2020-04-21 16:36:35 -0400427 )
quilesj26c78a42019-10-28 18:10:42 +0100428
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000429 command = "{} list --output yaml".format(self._helm_command)
quilesj26c78a42019-10-28 18:10:42 +0100430
beierlmf52cb7c2020-04-21 16:36:35 -0400431 output, _rc = await self._local_async_exec(
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000432 command=command, raise_exception_on_error=True, env=env
beierlmf52cb7c2020-04-21 16:36:35 -0400433 )
quilesj26c78a42019-10-28 18:10:42 +0100434
435 if output and len(output) > 0:
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000436 # parse yaml and update keys to lower case to unify with helm3
437 instances = yaml.load(output, Loader=yaml.SafeLoader).get("Releases")
438 new_instances = []
439 for instance in instances:
440 new_instance = dict((k.lower(), v) for k, v in instance.items())
441 new_instances.append(new_instance)
442 return new_instances
quilesj26c78a42019-10-28 18:10:42 +0100443 else:
444 return []
445
garciadeblas82b591c2021-03-24 09:22:13 +0100446 def _get_inspect_command(
447 self, show_command: str, kdu_model: str, repo_str: str, version: str
448 ):
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000449 inspect_command = "{} inspect {} {}{} {}".format(
450 self._helm_command, show_command, kdu_model, repo_str, version
beierlmf52cb7c2020-04-21 16:36:35 -0400451 )
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000452 return inspect_command
quilesj1be06302019-11-29 11:17:11 +0000453
quilesj26c78a42019-10-28 18:10:42 +0100454 async def _status_kdu(
beierlmf52cb7c2020-04-21 16:36:35 -0400455 self,
tiernoa5728bf2020-06-25 15:48:52 +0000456 cluster_id: str,
beierlmf52cb7c2020-04-21 16:36:35 -0400457 kdu_instance: str,
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000458 namespace: str = None,
beierlmf52cb7c2020-04-21 16:36:35 -0400459 show_error_log: bool = False,
460 return_text: bool = False,
quilesj26c78a42019-10-28 18:10:42 +0100461 ):
462
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000463 self.log.debug(
464 "status of kdu_instance: {}, namespace: {} ".format(kdu_instance, namespace)
465 )
quilesj26c78a42019-10-28 18:10:42 +0100466
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000467 # init config, env
468 paths, env = self._init_paths_env(
tiernoa5728bf2020-06-25 15:48:52 +0000469 cluster_name=cluster_id, create_if_not_exist=True
beierlmf52cb7c2020-04-21 16:36:35 -0400470 )
bravof53dd7462021-11-17 11:14:57 -0300471 command = ("env KUBECONFIG={} {} status {} --output yaml").format(
472 paths["kube_config"], self._helm_command, kdu_instance
473 )
quilesj26c78a42019-10-28 18:10:42 +0100474 output, rc = await self._local_async_exec(
475 command=command,
476 raise_exception_on_error=True,
beierlmf52cb7c2020-04-21 16:36:35 -0400477 show_error_log=show_error_log,
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000478 env=env,
quilesj26c78a42019-10-28 18:10:42 +0100479 )
480
quilesj1be06302019-11-29 11:17:11 +0000481 if return_text:
482 return str(output)
483
quilesj26c78a42019-10-28 18:10:42 +0100484 if rc != 0:
485 return None
486
487 data = yaml.load(output, Loader=yaml.SafeLoader)
488
489 # remove field 'notes'
490 try:
beierlmf52cb7c2020-04-21 16:36:35 -0400491 del data.get("info").get("status")["notes"]
quilesj26c78a42019-10-28 18:10:42 +0100492 except KeyError:
493 pass
494
495 # parse field 'resources'
496 try:
beierlmf52cb7c2020-04-21 16:36:35 -0400497 resources = str(data.get("info").get("status").get("resources"))
quilesj26c78a42019-10-28 18:10:42 +0100498 resource_table = self._output_to_table(resources)
beierlmf52cb7c2020-04-21 16:36:35 -0400499 data.get("info").get("status")["resources"] = resource_table
500 except Exception:
quilesj26c78a42019-10-28 18:10:42 +0100501 pass
502
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000503 # set description to lowercase (unify with helm3)
504 try:
505 data.get("info")["description"] = data.get("info").pop("Description")
506 except KeyError:
507 pass
508
quilesj26c78a42019-10-28 18:10:42 +0100509 return data
510
lloretgalleg095392b2020-11-20 11:28:08 +0000511 def _get_helm_chart_repos_ids(self, cluster_uuid) -> list:
512 repo_ids = []
513 cluster_filter = {"_admin.helm-chart.id": cluster_uuid}
514 cluster = self.db.get_one("k8sclusters", cluster_filter)
515 if cluster:
516 repo_ids = cluster.get("_admin").get("helm_chart_repos") or []
517 return repo_ids
518 else:
519 raise K8sException(
520 "k8cluster with helm-id : {} not found".format(cluster_uuid)
521 )
522
tiernoa5728bf2020-06-25 15:48:52 +0000523 async def _is_install_completed(self, cluster_id: str, kdu_instance: str) -> bool:
bravof53dd7462021-11-17 11:14:57 -0300524 # init config, env
525 paths, env = self._init_paths_env(
526 cluster_name=cluster_id, create_if_not_exist=True
527 )
quilesj26c78a42019-10-28 18:10:42 +0100528
beierlmf52cb7c2020-04-21 16:36:35 -0400529 status = await self._status_kdu(
tiernoa5728bf2020-06-25 15:48:52 +0000530 cluster_id=cluster_id, kdu_instance=kdu_instance, return_text=False
beierlmf52cb7c2020-04-21 16:36:35 -0400531 )
quilesj26c78a42019-10-28 18:10:42 +0100532
533 # extract info.status.resources-> str
534 # format:
535 # ==> v1/Deployment
536 # NAME READY UP-TO-DATE AVAILABLE AGE
537 # halting-horse-mongodb 0/1 1 0 0s
538 # halting-petit-mongodb 1/1 1 0 0s
539 # blank line
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000540 resources = K8sHelmBaseConnector._get_deep(
541 status, ("info", "status", "resources")
542 )
quilesj26c78a42019-10-28 18:10:42 +0100543
544 # convert to table
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000545 resources = K8sHelmBaseConnector._output_to_table(resources)
quilesj26c78a42019-10-28 18:10:42 +0100546
547 num_lines = len(resources)
548 index = 0
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000549 ready = True
quilesj26c78a42019-10-28 18:10:42 +0100550 while index < num_lines:
551 try:
552 line1 = resources[index]
553 index += 1
554 # find '==>' in column 0
beierlmf52cb7c2020-04-21 16:36:35 -0400555 if line1[0] == "==>":
quilesj26c78a42019-10-28 18:10:42 +0100556 line2 = resources[index]
557 index += 1
558 # find READY in column 1
beierlmf52cb7c2020-04-21 16:36:35 -0400559 if line2[1] == "READY":
quilesj26c78a42019-10-28 18:10:42 +0100560 # read next lines
561 line3 = resources[index]
562 index += 1
563 while len(line3) > 1 and index < num_lines:
564 ready_value = line3[1]
beierlmf52cb7c2020-04-21 16:36:35 -0400565 parts = ready_value.split(sep="/")
quilesj26c78a42019-10-28 18:10:42 +0100566 current = int(parts[0])
567 total = int(parts[1])
568 if current < total:
beierlmf52cb7c2020-04-21 16:36:35 -0400569 self.log.debug("NOT READY:\n {}".format(line3))
quilesj26c78a42019-10-28 18:10:42 +0100570 ready = False
571 line3 = resources[index]
572 index += 1
573
beierlmf52cb7c2020-04-21 16:36:35 -0400574 except Exception:
quilesj26c78a42019-10-28 18:10:42 +0100575 pass
576
577 return ready
578
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000579 def _get_install_command(
bravof53dd7462021-11-17 11:14:57 -0300580 self,
581 kdu_model,
582 kdu_instance,
583 namespace,
584 params_str,
585 version,
586 atomic,
587 timeout,
588 kubeconfig,
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000589 ) -> str:
lloretgallegd99f3f22020-06-29 14:18:30 +0000590
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000591 timeout_str = ""
592 if timeout:
593 timeout_str = "--timeout {}".format(timeout)
lloretgallegd99f3f22020-06-29 14:18:30 +0000594
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000595 # atomic
596 atomic_str = ""
597 if atomic:
598 atomic_str = "--atomic"
599 # namespace
600 namespace_str = ""
601 if namespace:
602 namespace_str = "--namespace {}".format(namespace)
lloretgallegd99f3f22020-06-29 14:18:30 +0000603
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000604 # version
605 version_str = ""
606 if version:
607 version_str = version_str = "--version {}".format(version)
lloretgallegd99f3f22020-06-29 14:18:30 +0000608
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000609 command = (
bravof53dd7462021-11-17 11:14:57 -0300610 "env KUBECONFIG={kubeconfig} {helm} install {atomic} --output yaml "
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000611 "{params} {timeout} --name={name} {ns} {model} {ver}".format(
bravof53dd7462021-11-17 11:14:57 -0300612 kubeconfig=kubeconfig,
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000613 helm=self._helm_command,
614 atomic=atomic_str,
615 params=params_str,
616 timeout=timeout_str,
617 name=kdu_instance,
618 ns=namespace_str,
619 model=kdu_model,
620 ver=version_str,
beierlmf52cb7c2020-04-21 16:36:35 -0400621 )
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000622 )
623 return command
quilesj26c78a42019-10-28 18:10:42 +0100624
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000625 def _get_upgrade_command(
bravof53dd7462021-11-17 11:14:57 -0300626 self,
627 kdu_model,
628 kdu_instance,
629 namespace,
630 params_str,
631 version,
632 atomic,
633 timeout,
634 kubeconfig,
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000635 ) -> str:
quilesj26c78a42019-10-28 18:10:42 +0100636
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000637 timeout_str = ""
638 if timeout:
639 timeout_str = "--timeout {}".format(timeout)
quilesj26c78a42019-10-28 18:10:42 +0100640
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000641 # atomic
642 atomic_str = ""
643 if atomic:
644 atomic_str = "--atomic"
quilesj26c78a42019-10-28 18:10:42 +0100645
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000646 # version
647 version_str = ""
648 if version:
649 version_str = "--version {}".format(version)
quilesj26c78a42019-10-28 18:10:42 +0100650
bravof53dd7462021-11-17 11:14:57 -0300651 command = (
652 "env KUBECONFIG={kubeconfig} {helm} upgrade {atomic} --output yaml {params} {timeout} {name} {model} {ver}"
653 ).format(
654 kubeconfig=kubeconfig,
garciadeblas82b591c2021-03-24 09:22:13 +0100655 helm=self._helm_command,
656 atomic=atomic_str,
657 params=params_str,
658 timeout=timeout_str,
659 name=kdu_instance,
660 model=kdu_model,
661 ver=version_str,
662 )
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000663 return command
quilesj26c78a42019-10-28 18:10:42 +0100664
bravof53dd7462021-11-17 11:14:57 -0300665 def _get_rollback_command(
666 self, kdu_instance, namespace, revision, kubeconfig
667 ) -> str:
668 return "env KUBECONFIG={} {} rollback {} {} --wait".format(
669 kubeconfig, self._helm_command, kdu_instance, revision
lloretgalleg1c83f2e2020-10-22 09:12:35 +0000670 )
quilesj26c78a42019-10-28 18:10:42 +0100671
bravof53dd7462021-11-17 11:14:57 -0300672 def _get_uninstall_command(
673 self, kdu_instance: str, namespace: str, kubeconfig: str
674 ) -> str:
675 return "env KUBECONFIG={} {} delete --purge {}".format(
676 kubeconfig, self._helm_command, kdu_instance
677 )