2 # Copyright 2019 Telefonica Investigacion y Desarrollo, S.A.U.
3 # This file is part of OSM
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
10 # http://www.apache.org/licenses/LICENSE-2.0
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
16 # See the License for the specific language governing permissions and
17 # limitations under the License.
19 # For those usages not covered by the Apache License, Version 2.0 please
20 # contact with: nfvlabs@tid.es
25 from typing
import Union
28 from n2vc
.loggable
import Loggable
31 class K8sConnector(abc
.ABC
, Loggable
):
33 ####################################################################################
34 ################################### P U B L I C ####################################
35 ####################################################################################
39 def generate_kdu_instance_name(**kwargs
):
40 raise NotImplementedError("Method not implemented")
42 def __init__(self
, db
: object, log
: object = None, on_update_db
=None):
45 :param db: database object to write current operation status
46 :param log: logger for tracing
47 :param on_update_db: callback called when k8s connector updates database
51 Loggable
.__init
__(self
, log
=log
, log_to_console
=True, prefix
="\nK8S")
53 # self.log.info('Initializing generic K8S connector')
55 # the database and update callback
57 self
.on_update_db
= on_update_db
59 # self.log.info('K8S generic connector initialized')
63 self
, k8s_creds
: str, namespace
: str = "kube-system", reuse_cluster_uuid
=None
66 It prepares a given K8s cluster environment to run Charts or juju Bundles on
71 :param k8s_creds: credentials to access a given K8s cluster, i.e. a valid
73 :param namespace: optional namespace to be used for the K8s engine (helm
74 tiller, juju). By default, 'kube-system' will be used
75 :param reuse_cluster_uuid: existing cluster uuid for reuse
76 :return: uuid of the K8s cluster and True if connector has installed some
77 software in the cluster (on error, an exception will be raised)
82 self
, cluster_uuid
: str, name
: str, url
: str, repo_type
: str = "chart"
85 Add a new repository to OSM database
87 :param cluster_uuid: the cluster
88 :param name: name for the repo in OSM
89 :param url: URL of the repo
90 :param repo_type: either "chart" or "bundle"
91 :return: True if successful
95 async def repo_list(self
, cluster_uuid
: str):
97 Get the list of registered repositories
99 :param cluster_uuid: the cluster
100 :return: list of registered repositories: [ (name, url) .... ]
104 async def repo_remove(self
, cluster_uuid
: str, name
: str):
106 Remove a repository from OSM
108 :param name: repo name in OSM
109 :param cluster_uuid: the cluster
110 :return: True if successful
114 async def synchronize_repos(self
, cluster_uuid
: str, name
: str):
116 Synchronizes the list of repositories created in the cluster with
117 the repositories added by the NBI
119 :param cluster_uuid: the cluster
120 :return: List of repositories deleted from the cluster and dictionary with
126 self
, cluster_uuid
: str, force
: bool = False, uninstall_sw
: bool = False
129 Uninstalls Tiller/Charm from a known K8s cluster and removes it from the list
130 of known K8s clusters. Intended to be used e.g. when the NS instance is deleted.
132 :param cluster_uuid: UUID of a K8s cluster known by OSM.
133 :param force: force deletion, even in case there are deployed releases
134 :param uninstall_sw: flag to indicate that sw uninstallation from software is
136 :return: str: kdu_instance generated by helm
146 timeout
: float = 300,
148 db_dict
: dict = None,
149 kdu_name
: str = None,
150 namespace
: str = None,
153 Deploys of a new KDU instance. It would implicitly rely on the `install` call
154 to deploy the Chart/Bundle properly parametrized (in practice, this call would
155 happen before any _initial-config-primitive_of the VNF is called).
157 :param cluster_uuid: UUID of a K8s cluster known by OSM
158 :param kdu_model: chart/bundle:version reference (string), which can be either
160 - a name of chart/bundle available via the repos known by OSM
161 - a path to a packaged chart/bundle
162 - a path to an unpacked chart/bundle directory or a URL
163 :param kdu_instance: Kdu instance name
164 :param atomic: If set, installation process purges chart/bundle on fail, also
165 will wait until all the K8s objects are active
166 :param timeout: Time in seconds to wait for the install of the chart/bundle
167 (defaults to Helm default timeout: 300s)
168 :param params: dictionary of key-value pairs for instantiation parameters
169 (overriding default values)
170 :param dict db_dict: where to write into database when the status changes.
171 It contains a dict with {collection: <str>, filter: {},
173 e.g. {collection: "nsrs", filter:
174 {_id: <nsd-id>, path: "_admin.deployed.K8S.3"}
175 :param kdu_name: Name of the KDU instance to be installed
176 :param namespace: K8s namespace to use for the KDU instance
177 :return: True if successful
185 kdu_model
: str = None,
187 timeout
: float = 300,
189 db_dict
: dict = None,
192 Upgrades an existing KDU instance. It would implicitly use the `upgrade` call
193 over an existing Chart/Bundle. It can be used both to upgrade the chart or to
194 reconfigure it. This would be exposed as Day-2 primitive.
196 :param cluster_uuid: UUID of a K8s cluster known by OSM
197 :param kdu_instance: unique name for the KDU instance to be updated
198 :param kdu_model: new chart/bundle:version reference
199 :param atomic: rollback in case of fail and wait for pods and services are
201 :param timeout: Time in seconds to wait for the install of the chart/bundle
202 (defaults to Helm default timeout: 300s)
203 :param params: new dictionary of key-value pairs for instantiation parameters
204 :param dict db_dict: where to write into database when the status changes.
205 It contains a dict with {collection: <str>, filter: {},
207 e.g. {collection: "nsrs", filter:
208 {_id: <nsd-id>, path: "_admin.deployed.K8S.3"}
209 :return: reference to the new revision number of the KDU instance
218 total_timeout
: float = 1800,
222 Scales an application in KDU instance.
224 :param: kdu_instance str: KDU instance name
225 :param: scale int: Scale to which to set this application
226 :param: resource_name str: Resource name (Application name)
227 :param: timeout float: The time, in seconds, to wait for the install
229 :param kwargs: Additional parameters
231 :return: If successful, returns True
235 async def get_scale_count(
242 Get an application scale count.
244 :param: resource_name str: Resource name (Application name)
245 :param: kdu_instance str: KDU instance name
246 :param kwargs: Additional parameters
248 :return: Return application instance count
253 self
, cluster_uuid
: str, kdu_instance
: str, revision
=0, db_dict
: dict = None
256 Rolls back a previous update of a KDU instance. It would implicitly use the
257 `rollback` call. It can be used both to rollback from a Chart/Bundle version
258 update or from a reconfiguration. This would be exposed as Day-2 primitive.
260 :param cluster_uuid: UUID of a K8s cluster known by OSM
261 :param kdu_instance: unique name for the KDU instance
262 :param revision: revision to which revert changes. If omitted, it will revert
264 :param dict db_dict: where to write into database when the status changes.
265 It contains a dict with {collection: <str>, filter: {},
267 e.g. {collection: "nsrs", filter:
268 {_id: <nsd-id>, path: "_admin.deployed.K8S.3"}
269 :return:If successful, reference to the current active revision of the KDU
270 instance after the rollback
274 async def uninstall(self
, cluster_uuid
: str, kdu_instance
: str):
276 Removes an existing KDU instance. It would implicitly use the `delete` call
277 (this call would happen after all _terminate-config-primitive_ of the VNF are
280 :param cluster_uuid: UUID of a K8s cluster known by OSM
281 :param kdu_instance: unique name for the KDU instance to be deleted
282 :return: True if successful
286 async def exec_primitive(
288 cluster_uuid
: str = None,
289 kdu_instance
: str = None,
290 primitive_name
: str = None,
291 timeout
: float = 300,
293 db_dict
: dict = None,
295 """Exec primitive (Juju action)
297 :param cluster_uuid str: The UUID of the cluster
298 :param kdu_instance str: The unique name of the KDU instance
299 :param primitive_name: Name of action that will be executed
300 :param timeout: Timeout for action execution
301 :param params: Dictionary of all the parameters needed for the action
302 :db_dict: Dictionary for any additional data
304 :return: Returns the output of the action
308 async def inspect_kdu(self
, kdu_model
: str, repo_url
: str = None) -> str:
310 These calls will retrieve from the Chart/Bundle:
312 - The list of configurable values and their defaults (e.g. in Charts,
313 it would retrieve the contents of `values.yaml`).
314 - If available, any embedded help file (e.g. `readme.md`) embedded in the
317 :param kdu_model: chart/bundle reference
318 :param repo_url: optional, reposotory URL (None if tar.gz, URl in other cases,
322 If successful, it will return the available parameters and their default values
323 as provided by the backend.
327 async def help_kdu(self
, kdu_model
: str, repo_url
: str = None) -> str:
330 :param kdu_model: chart/bundle reference
331 :param repo_url: optional, reposotory URL (None if tar.gz, URl in other cases,
333 :return: If successful, it will return the contents of the 'readme.md'
337 async def status_kdu(
338 self
, cluster_uuid
: str, kdu_instance
: str, yaml_format
: str
339 ) -> Union
[str, dict]:
341 This call would retrieve tha current state of a given KDU instance. It would be
342 would allow to retrieve the _composition_ (i.e. K8s objects) and _specific
343 values_ of the configuration parameters applied to a given instance. This call
344 would be based on the `status` call.
346 :param cluster_uuid: UUID of a K8s cluster known by OSM
347 :param kdu_instance: unique name for the KDU instance
348 :param yaml_format: if the return shall be returned as an YAML string or as a
350 :return: If successful, it will return the following vector of arguments:
351 - K8s `namespace` in the cluster where the KDU lives
352 - `state` of the KDU instance. It can be:
359 - List of `resources` (objects) that this release consists of, sorted by kind,
360 and the status of those resources
361 - Last `deployment_time`.
366 async def get_services(
367 self
, cluster_uuid
: str, kdu_instance
: str, namespace
: str
370 Returns a list of services defined for the specified kdu instance.
372 :param cluster_uuid: UUID of a K8s cluster known by OSM
373 :param kdu_instance: unique name for the KDU instance
374 :param namespace: K8s namespace used by the KDU instance
375 :return: If successful, it will return a list of services, Each service
376 can have the following data:
377 - `name` of the service
378 - `type` type of service in the k8 cluster
379 - `ports` List of ports offered by the service, for each port includes at least
381 - `cluster_ip` Internal ip to be used inside k8s cluster
382 - `external_ip` List of external ips (in case they are available)
386 async def get_service(
387 self
, cluster_uuid
: str, service_name
: str, namespace
: str = None
390 Obtains the data of the specified service in the k8cluster.
392 :param cluster_uuid: UUID of a K8s cluster known by OSM
393 :param service_name: name of the K8s service in the specified namespace
394 :param namespace: K8s namespace used by the KDU instance
395 :return: If successful, it will return a list of services, Each service can have
397 - `name` of the service
398 - `type` type of service in the k8 cluster
399 - `ports` List of ports offered by the service, for each port includes at least
401 - `cluster_ip` Internal ip to be used inside k8s cluster
402 - `external_ip` List of external ips (in case they are available)
406 ####################################################################################
407 ################################### P R I V A T E ##################################
408 ####################################################################################
411 async def write_app_status_to_db(
412 self
, db_dict
: dict, status
: str, detailed_status
: str, operation
: str
416 self
.warning("No db => No database write")
420 self
.warning("No db_dict => No database write")
423 self
.log
.debug("status={}".format(status
))
427 the_table
= db_dict
["collection"]
428 the_filter
= db_dict
["filter"]
429 the_path
= db_dict
["path"]
430 if not the_path
[-1] == ".":
431 the_path
= the_path
+ "."
433 the_path
+ "operation": operation
,
434 the_path
+ "status": status
,
435 the_path
+ "detailed-status": detailed_status
,
436 the_path
+ "status-time": str(time
.time()),
442 update_dict
=update_dict
,
447 if self
.on_update_db
:
448 if asyncio
.iscoroutinefunction(self
.on_update_db
):
449 await self
.on_update_db(
450 the_table
, the_filter
, the_path
, update_dict
453 self
.on_update_db(the_table
, the_filter
, the_path
, update_dict
)
457 except Exception as e
:
458 self
.log
.info("Exception writing status to database: {}".format(e
))