blob: 89805b3211fdef9f0998a9b22121aee261d53078 [file] [log] [blame]
quilesja049b7c2019-10-28 18:08:00 +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##
22
quilesja049b7c2019-10-28 18:08:00 +010023import abc
beierlmf52cb7c2020-04-21 16:36:35 -040024import asyncio
quilesja049b7c2019-10-28 18:08:00 +010025import time
26
beierlmf52cb7c2020-04-21 16:36:35 -040027from n2vc.loggable import Loggable
28
quilesja049b7c2019-10-28 18:08:00 +010029
30class K8sConnector(abc.ABC, Loggable):
quilesja049b7c2019-10-28 18:08:00 +010031 """
beierlmf52cb7c2020-04-21 16:36:35 -040032 ####################################################################################
33 ################################### P U B L I C ####################################
34 ####################################################################################
quilesja049b7c2019-10-28 18:08:00 +010035 """
garciadeblas82b591c2021-03-24 09:22:13 +010036
David Garciac4da25c2021-02-23 11:47:29 +010037 @staticmethod
38 def generate_kdu_instance_name(**kwargs):
39 raise NotImplementedError("Method not implemented")
quilesja049b7c2019-10-28 18:08:00 +010040
beierlmf52cb7c2020-04-21 16:36:35 -040041 def __init__(self, db: object, log: object = None, on_update_db=None):
quilesja049b7c2019-10-28 18:08:00 +010042 """
43
44 :param db: database object to write current operation status
45 :param log: logger for tracing
46 :param on_update_db: callback called when k8s connector updates database
47 """
48
49 # parent class
beierlmf52cb7c2020-04-21 16:36:35 -040050 Loggable.__init__(self, log=log, log_to_console=True, prefix="\nK8S")
quilesja049b7c2019-10-28 18:08:00 +010051
tierno8ff11992020-03-26 09:51:11 +000052 # self.log.info('Initializing generic K8S connector')
quilesja049b7c2019-10-28 18:08:00 +010053
54 # the database and update callback
55 self.db = db
56 self.on_update_db = on_update_db
57
tierno8ff11992020-03-26 09:51:11 +000058 # self.log.info('K8S generic connector initialized')
quilesja049b7c2019-10-28 18:08:00 +010059
60 @abc.abstractmethod
61 async def init_env(
beierlmf52cb7c2020-04-21 16:36:35 -040062 self, k8s_creds: str, namespace: str = "kube-system", reuse_cluster_uuid=None
quilesja049b7c2019-10-28 18:08:00 +010063 ) -> (str, bool):
64 """
beierlmf52cb7c2020-04-21 16:36:35 -040065 It prepares a given K8s cluster environment to run Charts or juju Bundles on
66 both sides:
quilesja049b7c2019-10-28 18:08:00 +010067 client (OSM)
68 server (Tiller/Charm)
69
beierlmf52cb7c2020-04-21 16:36:35 -040070 :param k8s_creds: credentials to access a given K8s cluster, i.e. a valid
71 '.kube/config'
72 :param namespace: optional namespace to be used for the K8s engine (helm
73 tiller, juju). By default, 'kube-system' will be used
quilesja049b7c2019-10-28 18:08:00 +010074 :param reuse_cluster_uuid: existing cluster uuid for reuse
beierlmf52cb7c2020-04-21 16:36:35 -040075 :return: uuid of the K8s cluster and True if connector has installed some
76 software in the cluster (on error, an exception will be raised)
quilesja049b7c2019-10-28 18:08:00 +010077 """
78
79 @abc.abstractmethod
80 async def repo_add(
beierlmf52cb7c2020-04-21 16:36:35 -040081 self, cluster_uuid: str, name: str, url: str, repo_type: str = "chart"
quilesja049b7c2019-10-28 18:08:00 +010082 ):
83 """
84 Add a new repository to OSM database
85
86 :param cluster_uuid: the cluster
87 :param name: name for the repo in OSM
88 :param url: URL of the repo
89 :param repo_type: either "chart" or "bundle"
90 :return: True if successful
91 """
92
93 @abc.abstractmethod
beierlmf52cb7c2020-04-21 16:36:35 -040094 async def repo_list(self, cluster_uuid: str):
quilesja049b7c2019-10-28 18:08:00 +010095 """
96 Get the list of registered repositories
97
98 :param cluster_uuid: the cluster
99 :return: list of registered repositories: [ (name, url) .... ]
100 """
101
102 @abc.abstractmethod
beierlmf52cb7c2020-04-21 16:36:35 -0400103 async def repo_remove(self, cluster_uuid: str, name: str):
quilesja049b7c2019-10-28 18:08:00 +0100104 """
105 Remove a repository from OSM
106
107 :param name: repo name in OSM
108 :param cluster_uuid: the cluster
109 :return: True if successful
110 """
111
112 @abc.abstractmethod
beierlmf52cb7c2020-04-21 16:36:35 -0400113 async def synchronize_repos(self, cluster_uuid: str, name: str):
lloretgalleg65ddf852020-02-20 12:01:17 +0100114 """
115 Synchronizes the list of repositories created in the cluster with
116 the repositories added by the NBI
117
118 :param cluster_uuid: the cluster
beierlmf52cb7c2020-04-21 16:36:35 -0400119 :return: List of repositories deleted from the cluster and dictionary with
120 repos added
lloretgalleg65ddf852020-02-20 12:01:17 +0100121 """
122
123 @abc.abstractmethod
quilesja049b7c2019-10-28 18:08:00 +0100124 async def reset(
beierlmf52cb7c2020-04-21 16:36:35 -0400125 self, cluster_uuid: str, force: bool = False, uninstall_sw: bool = False
quilesja049b7c2019-10-28 18:08:00 +0100126 ) -> bool:
127 """
beierlmf52cb7c2020-04-21 16:36:35 -0400128 Uninstalls Tiller/Charm from a known K8s cluster and removes it from the list
129 of known K8s clusters. Intended to be used e.g. when the NS instance is deleted.
quilesja049b7c2019-10-28 18:08:00 +0100130
131 :param cluster_uuid: UUID of a K8s cluster known by OSM.
132 :param force: force deletion, even in case there are deployed releases
beierlmf52cb7c2020-04-21 16:36:35 -0400133 :param uninstall_sw: flag to indicate that sw uninstallation from software is
134 needed
quilesja049b7c2019-10-28 18:08:00 +0100135 :return: str: kdu_instance generated by helm
136 """
137
138 @abc.abstractmethod
139 async def install(
beierlmf52cb7c2020-04-21 16:36:35 -0400140 self,
141 cluster_uuid: str,
142 kdu_model: str,
David Garciac4da25c2021-02-23 11:47:29 +0100143 kdu_instance: str,
beierlmf52cb7c2020-04-21 16:36:35 -0400144 atomic: bool = True,
145 timeout: float = 300,
146 params: dict = None,
147 db_dict: dict = None,
148 kdu_name: str = None,
149 namespace: str = None,
quilesja049b7c2019-10-28 18:08:00 +0100150 ):
151 """
beierlmf52cb7c2020-04-21 16:36:35 -0400152 Deploys of a new KDU instance. It would implicitly rely on the `install` call
153 to deploy the Chart/Bundle properly parametrized (in practice, this call would
154 happen before any _initial-config-primitive_of the VNF is called).
quilesja049b7c2019-10-28 18:08:00 +0100155
156 :param cluster_uuid: UUID of a K8s cluster known by OSM
beierlmf52cb7c2020-04-21 16:36:35 -0400157 :param kdu_model: chart/bundle:version reference (string), which can be either
158 of these options:
quilesja049b7c2019-10-28 18:08:00 +0100159 - a name of chart/bundle available via the repos known by OSM
160 - a path to a packaged chart/bundle
161 - a path to an unpacked chart/bundle directory or a URL
David Garciac4da25c2021-02-23 11:47:29 +0100162 :param kdu_instance: Kdu instance name
beierlmf52cb7c2020-04-21 16:36:35 -0400163 :param atomic: If set, installation process purges chart/bundle on fail, also
164 will wait until all the K8s objects are active
165 :param timeout: Time in seconds to wait for the install of the chart/bundle
166 (defaults to Helm default timeout: 300s)
167 :param params: dictionary of key-value pairs for instantiation parameters
168 (overriding default values)
quilesja049b7c2019-10-28 18:08:00 +0100169 :param dict db_dict: where to write into database when the status changes.
beierlmf52cb7c2020-04-21 16:36:35 -0400170 It contains a dict with {collection: <str>, filter: {},
171 path: <str>},
172 e.g. {collection: "nsrs", filter:
173 {_id: <nsd-id>, path: "_admin.deployed.K8S.3"}
Dominik Fleischmann847f3c02020-02-04 15:32:42 +0100174 :param kdu_name: Name of the KDU instance to be installed
tierno53555f62020-04-07 11:08:16 +0000175 :param namespace: K8s namespace to use for the KDU instance
quilesja049b7c2019-10-28 18:08:00 +0100176 :return: True if successful
177 """
178
179 @abc.abstractmethod
180 async def upgrade(
beierlmf52cb7c2020-04-21 16:36:35 -0400181 self,
182 cluster_uuid: str,
183 kdu_instance: str,
184 kdu_model: str = None,
185 atomic: bool = True,
186 timeout: float = 300,
187 params: dict = None,
188 db_dict: dict = None,
quilesja049b7c2019-10-28 18:08:00 +0100189 ):
190 """
beierlmf52cb7c2020-04-21 16:36:35 -0400191 Upgrades an existing KDU instance. It would implicitly use the `upgrade` call
192 over an existing Chart/Bundle. It can be used both to upgrade the chart or to
193 reconfigure it. This would be exposed as Day-2 primitive.
quilesja049b7c2019-10-28 18:08:00 +0100194
195 :param cluster_uuid: UUID of a K8s cluster known by OSM
196 :param kdu_instance: unique name for the KDU instance to be updated
197 :param kdu_model: new chart/bundle:version reference
beierlmf52cb7c2020-04-21 16:36:35 -0400198 :param atomic: rollback in case of fail and wait for pods and services are
199 available
200 :param timeout: Time in seconds to wait for the install of the chart/bundle
201 (defaults to Helm default timeout: 300s)
quilesja049b7c2019-10-28 18:08:00 +0100202 :param params: new dictionary of key-value pairs for instantiation parameters
203 :param dict db_dict: where to write into database when the status changes.
beierlmf52cb7c2020-04-21 16:36:35 -0400204 It contains a dict with {collection: <str>, filter: {},
205 path: <str>},
206 e.g. {collection: "nsrs", filter:
207 {_id: <nsd-id>, path: "_admin.deployed.K8S.3"}
quilesja049b7c2019-10-28 18:08:00 +0100208 :return: reference to the new revision number of the KDU instance
209 """
210
211 @abc.abstractmethod
aktas2962f3e2021-03-15 11:05:35 +0300212 async def scale(
garciadeblas82b591c2021-03-24 09:22:13 +0100213 self,
214 kdu_instance: str,
215 scale: int,
216 resource_name: str,
217 total_timeout: float = 1800,
218 **kwargs,
aktas2962f3e2021-03-15 11:05:35 +0300219 ) -> bool:
220 """
221 Scales an application in KDU instance.
222
223 :param: kdu_instance str: KDU instance name
224 :param: scale int: Scale to which to set this application
225 :param: resource_name str: Resource name (Application name)
226 :param: timeout float: The time, in seconds, to wait for the install
227 to finish
228 :param kwargs: Additional parameters
229
230 :return: If successful, returns True
231 """
232
233 @abc.abstractmethod
234 async def get_scale_count(
garciadeblas82b591c2021-03-24 09:22:13 +0100235 self,
236 resource_name: str,
237 kdu_instance: str,
238 **kwargs,
aktas2962f3e2021-03-15 11:05:35 +0300239 ) -> int:
240 """
241 Get an application scale count.
242
243 :param: resource_name str: Resource name (Application name)
244 :param: kdu_instance str: KDU instance name
245 :param kwargs: Additional parameters
246
247 :return: Return application instance count
248 """
249
250 @abc.abstractmethod
quilesja049b7c2019-10-28 18:08:00 +0100251 async def rollback(
beierlmf52cb7c2020-04-21 16:36:35 -0400252 self, cluster_uuid: str, kdu_instance: str, revision=0, db_dict: dict = None
quilesja049b7c2019-10-28 18:08:00 +0100253 ):
254 """
beierlmf52cb7c2020-04-21 16:36:35 -0400255 Rolls back a previous update of a KDU instance. It would implicitly use the
256 `rollback` call. It can be used both to rollback from a Chart/Bundle version
257 update or from a reconfiguration. This would be exposed as Day-2 primitive.
quilesja049b7c2019-10-28 18:08:00 +0100258
259 :param cluster_uuid: UUID of a K8s cluster known by OSM
260 :param kdu_instance: unique name for the KDU instance
beierlmf52cb7c2020-04-21 16:36:35 -0400261 :param revision: revision to which revert changes. If omitted, it will revert
262 the last update only
quilesja049b7c2019-10-28 18:08:00 +0100263 :param dict db_dict: where to write into database when the status changes.
beierlmf52cb7c2020-04-21 16:36:35 -0400264 It contains a dict with {collection: <str>, filter: {},
265 path: <str>},
266 e.g. {collection: "nsrs", filter:
267 {_id: <nsd-id>, path: "_admin.deployed.K8S.3"}
268 :return:If successful, reference to the current active revision of the KDU
269 instance after the rollback
quilesja049b7c2019-10-28 18:08:00 +0100270 """
271
272 @abc.abstractmethod
beierlmf52cb7c2020-04-21 16:36:35 -0400273 async def uninstall(self, cluster_uuid: str, kdu_instance: str):
quilesja049b7c2019-10-28 18:08:00 +0100274 """
beierlmf52cb7c2020-04-21 16:36:35 -0400275 Removes an existing KDU instance. It would implicitly use the `delete` call
276 (this call would happen after all _terminate-config-primitive_ of the VNF are
277 invoked).
quilesja049b7c2019-10-28 18:08:00 +0100278
279 :param cluster_uuid: UUID of a K8s cluster known by OSM
280 :param kdu_instance: unique name for the KDU instance to be deleted
281 :return: True if successful
282 """
283
284 @abc.abstractmethod
Dominik Fleischmannfc796cc2020-04-06 14:51:00 +0200285 async def exec_primitive(
286 self,
287 cluster_uuid: str = None,
288 kdu_instance: str = None,
289 primitive_name: str = None,
290 timeout: float = 300,
291 params: dict = None,
292 db_dict: dict = None,
293 ) -> str:
294 """Exec primitive (Juju action)
295
296 :param cluster_uuid str: The UUID of the cluster
297 :param kdu_instance str: The unique name of the KDU instance
298 :param primitive_name: Name of action that will be executed
299 :param timeout: Timeout for action execution
300 :param params: Dictionary of all the parameters needed for the action
301 :db_dict: Dictionary for any additional data
302
303 :return: Returns the output of the action
304 """
305
306 @abc.abstractmethod
beierlmf52cb7c2020-04-21 16:36:35 -0400307 async def inspect_kdu(self, kdu_model: str, repo_url: str = None) -> str:
quilesja049b7c2019-10-28 18:08:00 +0100308 """
quilesj1be06302019-11-29 11:17:11 +0000309 These calls will retrieve from the Chart/Bundle:
quilesja049b7c2019-10-28 18:08:00 +0100310
beierlmf52cb7c2020-04-21 16:36:35 -0400311 - The list of configurable values and their defaults (e.g. in Charts,
312 it would retrieve the contents of `values.yaml`).
313 - If available, any embedded help file (e.g. `readme.md`) embedded in the
314 Chart/Bundle.
quilesja049b7c2019-10-28 18:08:00 +0100315
quilesja049b7c2019-10-28 18:08:00 +0100316 :param kdu_model: chart/bundle reference
beierlmf52cb7c2020-04-21 16:36:35 -0400317 :param repo_url: optional, reposotory URL (None if tar.gz, URl in other cases,
318 even stable URL)
quilesj1be06302019-11-29 11:17:11 +0000319 :return:
320
beierlmf52cb7c2020-04-21 16:36:35 -0400321 If successful, it will return the available parameters and their default values
322 as provided by the backend.
quilesja049b7c2019-10-28 18:08:00 +0100323 """
324
325 @abc.abstractmethod
beierlmf52cb7c2020-04-21 16:36:35 -0400326 async def help_kdu(self, kdu_model: str, repo_url: str = None) -> str:
quilesja049b7c2019-10-28 18:08:00 +0100327 """
328
quilesja049b7c2019-10-28 18:08:00 +0100329 :param kdu_model: chart/bundle reference
beierlmf52cb7c2020-04-21 16:36:35 -0400330 :param repo_url: optional, reposotory URL (None if tar.gz, URl in other cases,
331 even stable URL)
quilesja049b7c2019-10-28 18:08:00 +0100332 :return: If successful, it will return the contents of the 'readme.md'
333 """
334
335 @abc.abstractmethod
beierlmf52cb7c2020-04-21 16:36:35 -0400336 async def status_kdu(self, cluster_uuid: str, kdu_instance: str) -> str:
quilesja049b7c2019-10-28 18:08:00 +0100337 """
beierlmf52cb7c2020-04-21 16:36:35 -0400338 This call would retrieve tha current state of a given KDU instance. It would be
339 would allow to retrieve the _composition_ (i.e. K8s objects) and _specific
340 values_ of the configuration parameters applied to a given instance. This call
341 would be based on the `status` call.
quilesja049b7c2019-10-28 18:08:00 +0100342
343 :param cluster_uuid: UUID of a K8s cluster known by OSM
344 :param kdu_instance: unique name for the KDU instance
345 :return: If successful, it will return the following vector of arguments:
346 - K8s `namespace` in the cluster where the KDU lives
347 - `state` of the KDU instance. It can be:
348 - UNKNOWN
349 - DEPLOYED
350 - DELETED
351 - SUPERSEDED
352 - FAILED or
353 - DELETING
beierlmf52cb7c2020-04-21 16:36:35 -0400354 - List of `resources` (objects) that this release consists of, sorted by kind,
355 and the status of those resources
quilesja049b7c2019-10-28 18:08:00 +0100356 - Last `deployment_time`.
357
358 """
359
lloretgallegd99f3f22020-06-29 14:18:30 +0000360 @abc.abstractmethod
garciadeblas82b591c2021-03-24 09:22:13 +0100361 async def get_services(
362 self, cluster_uuid: str, kdu_instance: str, namespace: str
363 ) -> list:
lloretgallegd99f3f22020-06-29 14:18:30 +0000364 """
365 Returns a list of services defined for the specified kdu instance.
366
367 :param cluster_uuid: UUID of a K8s cluster known by OSM
368 :param kdu_instance: unique name for the KDU instance
369 :param namespace: K8s namespace used by the KDU instance
370 :return: If successful, it will return a list of services, Each service
371 can have the following data:
372 - `name` of the service
373 - `type` type of service in the k8 cluster
374 - `ports` List of ports offered by the service, for each port includes at least
375 name, port, protocol
376 - `cluster_ip` Internal ip to be used inside k8s cluster
377 - `external_ip` List of external ips (in case they are available)
378 """
379
380 @abc.abstractmethod
garciadeblas82b591c2021-03-24 09:22:13 +0100381 async def get_service(
382 self, cluster_uuid: str, service_name: str, namespace: str = None
383 ) -> object:
lloretgallegd99f3f22020-06-29 14:18:30 +0000384 """
385 Obtains the data of the specified service in the k8cluster.
386
387 :param cluster_uuid: UUID of a K8s cluster known by OSM
388 :param service_name: name of the K8s service in the specified namespace
389 :param namespace: K8s namespace used by the KDU instance
390 :return: If successful, it will return a list of services, Each service can have
391 the following data:
392 - `name` of the service
393 - `type` type of service in the k8 cluster
394 - `ports` List of ports offered by the service, for each port includes at least
395 name, port, protocol
396 - `cluster_ip` Internal ip to be used inside k8s cluster
397 - `external_ip` List of external ips (in case they are available)
398 """
399
quilesja049b7c2019-10-28 18:08:00 +0100400 """
beierlmf52cb7c2020-04-21 16:36:35 -0400401 ####################################################################################
402 ################################### P R I V A T E ##################################
403 ####################################################################################
quilesja049b7c2019-10-28 18:08:00 +0100404 """
405
406 async def write_app_status_to_db(
beierlmf52cb7c2020-04-21 16:36:35 -0400407 self, db_dict: dict, status: str, detailed_status: str, operation: str
quilesja049b7c2019-10-28 18:08:00 +0100408 ) -> bool:
409
410 if not self.db:
beierlmf52cb7c2020-04-21 16:36:35 -0400411 self.warning("No db => No database write")
quilesja049b7c2019-10-28 18:08:00 +0100412 return False
413
414 if not db_dict:
beierlmf52cb7c2020-04-21 16:36:35 -0400415 self.warning("No db_dict => No database write")
quilesja049b7c2019-10-28 18:08:00 +0100416 return False
417
beierlmf52cb7c2020-04-21 16:36:35 -0400418 self.log.debug("status={}".format(status))
quilesja049b7c2019-10-28 18:08:00 +0100419
420 try:
421
beierlmf52cb7c2020-04-21 16:36:35 -0400422 the_table = db_dict["collection"]
423 the_filter = db_dict["filter"]
424 the_path = db_dict["path"]
425 if not the_path[-1] == ".":
426 the_path = the_path + "."
quilesja049b7c2019-10-28 18:08:00 +0100427 update_dict = {
beierlmf52cb7c2020-04-21 16:36:35 -0400428 the_path + "operation": operation,
429 the_path + "status": status,
430 the_path + "detailed-status": detailed_status,
431 the_path + "status-time": str(time.time()),
quilesja049b7c2019-10-28 18:08:00 +0100432 }
433
434 self.db.set_one(
435 table=the_table,
436 q_filter=the_filter,
437 update_dict=update_dict,
beierlmf52cb7c2020-04-21 16:36:35 -0400438 fail_on_empty=True,
quilesja049b7c2019-10-28 18:08:00 +0100439 )
440
441 # database callback
442 if self.on_update_db:
443 if asyncio.iscoroutinefunction(self.on_update_db):
beierlmf52cb7c2020-04-21 16:36:35 -0400444 await self.on_update_db(
445 the_table, the_filter, the_path, update_dict
446 )
quilesja049b7c2019-10-28 18:08:00 +0100447 else:
448 self.on_update_db(the_table, the_filter, the_path, update_dict)
449
450 return True
451
452 except Exception as e:
beierlmf52cb7c2020-04-21 16:36:35 -0400453 self.log.info("Exception writing status to database: {}".format(e))
quilesja049b7c2019-10-28 18:08:00 +0100454 return False