blob: a3ad29aa3529d01a1545274c133e29d8d9bfbaf0 [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
beierlm32862bb2020-04-21 16:36:35 -040024import asyncio
quilesja049b7c2019-10-28 18:08:00 +010025import time
26
beierlm32862bb2020-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 """
beierlm32862bb2020-04-21 16:36:35 -040032 ####################################################################################
33 ################################### P U B L I C ####################################
34 ####################################################################################
quilesja049b7c2019-10-28 18:08:00 +010035 """
36
beierlm32862bb2020-04-21 16:36:35 -040037 def __init__(self, db: object, log: object = None, on_update_db=None):
quilesja049b7c2019-10-28 18:08:00 +010038 """
39
40 :param db: database object to write current operation status
41 :param log: logger for tracing
42 :param on_update_db: callback called when k8s connector updates database
43 """
44
45 # parent class
beierlm32862bb2020-04-21 16:36:35 -040046 Loggable.__init__(self, log=log, log_to_console=True, prefix="\nK8S")
quilesja049b7c2019-10-28 18:08:00 +010047
tiernoe2bd3da2020-03-26 09:51:11 +000048 # self.log.info('Initializing generic K8S connector')
quilesja049b7c2019-10-28 18:08:00 +010049
50 # the database and update callback
51 self.db = db
52 self.on_update_db = on_update_db
53
tiernoe2bd3da2020-03-26 09:51:11 +000054 # self.log.info('K8S generic connector initialized')
quilesja049b7c2019-10-28 18:08:00 +010055
56 @abc.abstractmethod
57 async def init_env(
beierlm32862bb2020-04-21 16:36:35 -040058 self, k8s_creds: str, namespace: str = "kube-system", reuse_cluster_uuid=None
quilesja049b7c2019-10-28 18:08:00 +010059 ) -> (str, bool):
60 """
beierlm32862bb2020-04-21 16:36:35 -040061 It prepares a given K8s cluster environment to run Charts or juju Bundles on
62 both sides:
quilesja049b7c2019-10-28 18:08:00 +010063 client (OSM)
64 server (Tiller/Charm)
65
beierlm32862bb2020-04-21 16:36:35 -040066 :param k8s_creds: credentials to access a given K8s cluster, i.e. a valid
67 '.kube/config'
68 :param namespace: optional namespace to be used for the K8s engine (helm
69 tiller, juju). By default, 'kube-system' will be used
quilesja049b7c2019-10-28 18:08:00 +010070 :param reuse_cluster_uuid: existing cluster uuid for reuse
beierlm32862bb2020-04-21 16:36:35 -040071 :return: uuid of the K8s cluster and True if connector has installed some
72 software in the cluster (on error, an exception will be raised)
quilesja049b7c2019-10-28 18:08:00 +010073 """
74
75 @abc.abstractmethod
76 async def repo_add(
beierlm32862bb2020-04-21 16:36:35 -040077 self, cluster_uuid: str, name: str, url: str, repo_type: str = "chart"
quilesja049b7c2019-10-28 18:08:00 +010078 ):
79 """
80 Add a new repository to OSM database
81
82 :param cluster_uuid: the cluster
83 :param name: name for the repo in OSM
84 :param url: URL of the repo
85 :param repo_type: either "chart" or "bundle"
86 :return: True if successful
87 """
88
89 @abc.abstractmethod
beierlm32862bb2020-04-21 16:36:35 -040090 async def repo_list(self, cluster_uuid: str):
quilesja049b7c2019-10-28 18:08:00 +010091 """
92 Get the list of registered repositories
93
94 :param cluster_uuid: the cluster
95 :return: list of registered repositories: [ (name, url) .... ]
96 """
97
98 @abc.abstractmethod
beierlm32862bb2020-04-21 16:36:35 -040099 async def repo_remove(self, cluster_uuid: str, name: str):
quilesja049b7c2019-10-28 18:08:00 +0100100 """
101 Remove a repository from OSM
102
103 :param name: repo name in OSM
104 :param cluster_uuid: the cluster
105 :return: True if successful
106 """
107
108 @abc.abstractmethod
beierlm32862bb2020-04-21 16:36:35 -0400109 async def synchronize_repos(self, cluster_uuid: str, name: str):
lloretgallegf00dcae2020-02-20 12:01:17 +0100110 """
111 Synchronizes the list of repositories created in the cluster with
112 the repositories added by the NBI
113
114 :param cluster_uuid: the cluster
beierlm32862bb2020-04-21 16:36:35 -0400115 :return: List of repositories deleted from the cluster and dictionary with
116 repos added
lloretgallegf00dcae2020-02-20 12:01:17 +0100117 """
118
119 @abc.abstractmethod
quilesja049b7c2019-10-28 18:08:00 +0100120 async def reset(
beierlm32862bb2020-04-21 16:36:35 -0400121 self, cluster_uuid: str, force: bool = False, uninstall_sw: bool = False
quilesja049b7c2019-10-28 18:08:00 +0100122 ) -> bool:
123 """
beierlm32862bb2020-04-21 16:36:35 -0400124 Uninstalls Tiller/Charm from a known K8s cluster and removes it from the list
125 of known K8s clusters. Intended to be used e.g. when the NS instance is deleted.
quilesja049b7c2019-10-28 18:08:00 +0100126
127 :param cluster_uuid: UUID of a K8s cluster known by OSM.
128 :param force: force deletion, even in case there are deployed releases
beierlm32862bb2020-04-21 16:36:35 -0400129 :param uninstall_sw: flag to indicate that sw uninstallation from software is
130 needed
quilesja049b7c2019-10-28 18:08:00 +0100131 :return: str: kdu_instance generated by helm
132 """
133
134 @abc.abstractmethod
135 async def install(
beierlm32862bb2020-04-21 16:36:35 -0400136 self,
137 cluster_uuid: str,
138 kdu_model: str,
139 atomic: bool = True,
140 timeout: float = 300,
141 params: dict = None,
142 db_dict: dict = None,
143 kdu_name: str = None,
144 namespace: str = None,
quilesja049b7c2019-10-28 18:08:00 +0100145 ):
146 """
beierlm32862bb2020-04-21 16:36:35 -0400147 Deploys of a new KDU instance. It would implicitly rely on the `install` call
148 to deploy the Chart/Bundle properly parametrized (in practice, this call would
149 happen before any _initial-config-primitive_of the VNF is called).
quilesja049b7c2019-10-28 18:08:00 +0100150
151 :param cluster_uuid: UUID of a K8s cluster known by OSM
beierlm32862bb2020-04-21 16:36:35 -0400152 :param kdu_model: chart/bundle:version reference (string), which can be either
153 of these options:
quilesja049b7c2019-10-28 18:08:00 +0100154 - a name of chart/bundle available via the repos known by OSM
155 - a path to a packaged chart/bundle
156 - a path to an unpacked chart/bundle directory or a URL
beierlm32862bb2020-04-21 16:36:35 -0400157 :param atomic: If set, installation process purges chart/bundle on fail, also
158 will wait until all the K8s objects are active
159 :param timeout: Time in seconds to wait for the install of the chart/bundle
160 (defaults to Helm default timeout: 300s)
161 :param params: dictionary of key-value pairs for instantiation parameters
162 (overriding default values)
quilesja049b7c2019-10-28 18:08:00 +0100163 :param dict db_dict: where to write into database when the status changes.
beierlm32862bb2020-04-21 16:36:35 -0400164 It contains a dict with {collection: <str>, filter: {},
165 path: <str>},
166 e.g. {collection: "nsrs", filter:
167 {_id: <nsd-id>, path: "_admin.deployed.K8S.3"}
Dominik Fleischmann12aa0842020-02-04 15:32:42 +0100168 :param kdu_name: Name of the KDU instance to be installed
tiernod5d83a42020-04-07 11:08:16 +0000169 :param namespace: K8s namespace to use for the KDU instance
quilesja049b7c2019-10-28 18:08:00 +0100170 :return: True if successful
171 """
172
173 @abc.abstractmethod
174 async def upgrade(
beierlm32862bb2020-04-21 16:36:35 -0400175 self,
176 cluster_uuid: str,
177 kdu_instance: str,
178 kdu_model: str = None,
179 atomic: bool = True,
180 timeout: float = 300,
181 params: dict = None,
182 db_dict: dict = None,
quilesja049b7c2019-10-28 18:08:00 +0100183 ):
184 """
beierlm32862bb2020-04-21 16:36:35 -0400185 Upgrades an existing KDU instance. It would implicitly use the `upgrade` call
186 over an existing Chart/Bundle. It can be used both to upgrade the chart or to
187 reconfigure it. This would be exposed as Day-2 primitive.
quilesja049b7c2019-10-28 18:08:00 +0100188
189 :param cluster_uuid: UUID of a K8s cluster known by OSM
190 :param kdu_instance: unique name for the KDU instance to be updated
191 :param kdu_model: new chart/bundle:version reference
beierlm32862bb2020-04-21 16:36:35 -0400192 :param atomic: rollback in case of fail and wait for pods and services are
193 available
194 :param timeout: Time in seconds to wait for the install of the chart/bundle
195 (defaults to Helm default timeout: 300s)
quilesja049b7c2019-10-28 18:08:00 +0100196 :param params: new dictionary of key-value pairs for instantiation parameters
197 :param dict db_dict: where to write into database when the status changes.
beierlm32862bb2020-04-21 16:36:35 -0400198 It contains a dict with {collection: <str>, filter: {},
199 path: <str>},
200 e.g. {collection: "nsrs", filter:
201 {_id: <nsd-id>, path: "_admin.deployed.K8S.3"}
quilesja049b7c2019-10-28 18:08:00 +0100202 :return: reference to the new revision number of the KDU instance
203 """
204
205 @abc.abstractmethod
206 async def rollback(
beierlm32862bb2020-04-21 16:36:35 -0400207 self, cluster_uuid: str, kdu_instance: str, revision=0, db_dict: dict = None
quilesja049b7c2019-10-28 18:08:00 +0100208 ):
209 """
beierlm32862bb2020-04-21 16:36:35 -0400210 Rolls back a previous update of a KDU instance. It would implicitly use the
211 `rollback` call. It can be used both to rollback from a Chart/Bundle version
212 update or from a reconfiguration. This would be exposed as Day-2 primitive.
quilesja049b7c2019-10-28 18:08:00 +0100213
214 :param cluster_uuid: UUID of a K8s cluster known by OSM
215 :param kdu_instance: unique name for the KDU instance
beierlm32862bb2020-04-21 16:36:35 -0400216 :param revision: revision to which revert changes. If omitted, it will revert
217 the last update only
quilesja049b7c2019-10-28 18:08:00 +0100218 :param dict db_dict: where to write into database when the status changes.
beierlm32862bb2020-04-21 16:36:35 -0400219 It contains a dict with {collection: <str>, filter: {},
220 path: <str>},
221 e.g. {collection: "nsrs", filter:
222 {_id: <nsd-id>, path: "_admin.deployed.K8S.3"}
223 :return:If successful, reference to the current active revision of the KDU
224 instance after the rollback
quilesja049b7c2019-10-28 18:08:00 +0100225 """
226
227 @abc.abstractmethod
beierlm32862bb2020-04-21 16:36:35 -0400228 async def uninstall(self, cluster_uuid: str, kdu_instance: str):
quilesja049b7c2019-10-28 18:08:00 +0100229 """
beierlm32862bb2020-04-21 16:36:35 -0400230 Removes an existing KDU instance. It would implicitly use the `delete` call
231 (this call would happen after all _terminate-config-primitive_ of the VNF are
232 invoked).
quilesja049b7c2019-10-28 18:08:00 +0100233
234 :param cluster_uuid: UUID of a K8s cluster known by OSM
235 :param kdu_instance: unique name for the KDU instance to be deleted
236 :return: True if successful
237 """
238
239 @abc.abstractmethod
Dominik Fleischmannd14e4212020-04-06 14:51:00 +0200240 async def exec_primitive(
241 self,
242 cluster_uuid: str = None,
243 kdu_instance: str = None,
244 primitive_name: str = None,
245 timeout: float = 300,
246 params: dict = None,
247 db_dict: dict = None,
248 ) -> str:
249 """Exec primitive (Juju action)
250
251 :param cluster_uuid str: The UUID of the cluster
252 :param kdu_instance str: The unique name of the KDU instance
253 :param primitive_name: Name of action that will be executed
254 :param timeout: Timeout for action execution
255 :param params: Dictionary of all the parameters needed for the action
256 :db_dict: Dictionary for any additional data
257
258 :return: Returns the output of the action
259 """
260
261 @abc.abstractmethod
beierlm32862bb2020-04-21 16:36:35 -0400262 async def inspect_kdu(self, kdu_model: str, repo_url: str = None) -> str:
quilesja049b7c2019-10-28 18:08:00 +0100263 """
quilesj1be06302019-11-29 11:17:11 +0000264 These calls will retrieve from the Chart/Bundle:
quilesja049b7c2019-10-28 18:08:00 +0100265
beierlm32862bb2020-04-21 16:36:35 -0400266 - The list of configurable values and their defaults (e.g. in Charts,
267 it would retrieve the contents of `values.yaml`).
268 - If available, any embedded help file (e.g. `readme.md`) embedded in the
269 Chart/Bundle.
quilesja049b7c2019-10-28 18:08:00 +0100270
quilesja049b7c2019-10-28 18:08:00 +0100271 :param kdu_model: chart/bundle reference
beierlm32862bb2020-04-21 16:36:35 -0400272 :param repo_url: optional, reposotory URL (None if tar.gz, URl in other cases,
273 even stable URL)
quilesj1be06302019-11-29 11:17:11 +0000274 :return:
275
beierlm32862bb2020-04-21 16:36:35 -0400276 If successful, it will return the available parameters and their default values
277 as provided by the backend.
quilesja049b7c2019-10-28 18:08:00 +0100278 """
279
280 @abc.abstractmethod
beierlm32862bb2020-04-21 16:36:35 -0400281 async def help_kdu(self, kdu_model: str, repo_url: str = None) -> str:
quilesja049b7c2019-10-28 18:08:00 +0100282 """
283
quilesja049b7c2019-10-28 18:08:00 +0100284 :param kdu_model: chart/bundle reference
beierlm32862bb2020-04-21 16:36:35 -0400285 :param repo_url: optional, reposotory URL (None if tar.gz, URl in other cases,
286 even stable URL)
quilesja049b7c2019-10-28 18:08:00 +0100287 :return: If successful, it will return the contents of the 'readme.md'
288 """
289
290 @abc.abstractmethod
beierlm32862bb2020-04-21 16:36:35 -0400291 async def status_kdu(self, cluster_uuid: str, kdu_instance: str) -> str:
quilesja049b7c2019-10-28 18:08:00 +0100292 """
beierlm32862bb2020-04-21 16:36:35 -0400293 This call would retrieve tha current state of a given KDU instance. It would be
294 would allow to retrieve the _composition_ (i.e. K8s objects) and _specific
295 values_ of the configuration parameters applied to a given instance. This call
296 would be based on the `status` call.
quilesja049b7c2019-10-28 18:08:00 +0100297
298 :param cluster_uuid: UUID of a K8s cluster known by OSM
299 :param kdu_instance: unique name for the KDU instance
300 :return: If successful, it will return the following vector of arguments:
301 - K8s `namespace` in the cluster where the KDU lives
302 - `state` of the KDU instance. It can be:
303 - UNKNOWN
304 - DEPLOYED
305 - DELETED
306 - SUPERSEDED
307 - FAILED or
308 - DELETING
beierlm32862bb2020-04-21 16:36:35 -0400309 - List of `resources` (objects) that this release consists of, sorted by kind,
310 and the status of those resources
quilesja049b7c2019-10-28 18:08:00 +0100311 - Last `deployment_time`.
312
313 """
314
315 """
beierlm32862bb2020-04-21 16:36:35 -0400316 ####################################################################################
317 ################################### P R I V A T E ##################################
318 ####################################################################################
quilesja049b7c2019-10-28 18:08:00 +0100319 """
320
321 async def write_app_status_to_db(
beierlm32862bb2020-04-21 16:36:35 -0400322 self, db_dict: dict, status: str, detailed_status: str, operation: str
quilesja049b7c2019-10-28 18:08:00 +0100323 ) -> bool:
324
325 if not self.db:
beierlm32862bb2020-04-21 16:36:35 -0400326 self.warning("No db => No database write")
quilesja049b7c2019-10-28 18:08:00 +0100327 return False
328
329 if not db_dict:
beierlm32862bb2020-04-21 16:36:35 -0400330 self.warning("No db_dict => No database write")
quilesja049b7c2019-10-28 18:08:00 +0100331 return False
332
beierlm32862bb2020-04-21 16:36:35 -0400333 self.log.debug("status={}".format(status))
quilesja049b7c2019-10-28 18:08:00 +0100334
335 try:
336
beierlm32862bb2020-04-21 16:36:35 -0400337 the_table = db_dict["collection"]
338 the_filter = db_dict["filter"]
339 the_path = db_dict["path"]
340 if not the_path[-1] == ".":
341 the_path = the_path + "."
quilesja049b7c2019-10-28 18:08:00 +0100342 update_dict = {
beierlm32862bb2020-04-21 16:36:35 -0400343 the_path + "operation": operation,
344 the_path + "status": status,
345 the_path + "detailed-status": detailed_status,
346 the_path + "status-time": str(time.time()),
quilesja049b7c2019-10-28 18:08:00 +0100347 }
348
349 self.db.set_one(
350 table=the_table,
351 q_filter=the_filter,
352 update_dict=update_dict,
beierlm32862bb2020-04-21 16:36:35 -0400353 fail_on_empty=True,
quilesja049b7c2019-10-28 18:08:00 +0100354 )
355
356 # database callback
357 if self.on_update_db:
358 if asyncio.iscoroutinefunction(self.on_update_db):
beierlm32862bb2020-04-21 16:36:35 -0400359 await self.on_update_db(
360 the_table, the_filter, the_path, update_dict
361 )
quilesja049b7c2019-10-28 18:08:00 +0100362 else:
363 self.on_update_db(the_table, the_filter, the_path, update_dict)
364
365 return True
366
367 except Exception as e:
beierlm32862bb2020-04-21 16:36:35 -0400368 self.log.info("Exception writing status to database: {}".format(e))
quilesja049b7c2019-10-28 18:08:00 +0100369 return False