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
27 from n2vc
.loggable
import Loggable
30 class K8sConnector(abc
.ABC
, Loggable
):
32 ####################################################################################
33 ################################### P U B L I C ####################################
34 ####################################################################################
38 def generate_kdu_instance_name(**kwargs
):
39 raise NotImplementedError("Method not implemented")
41 def __init__(self
, db
: object, log
: object = None, on_update_db
=None):
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
50 Loggable
.__init
__(self
, log
=log
, log_to_console
=True, prefix
="\nK8S")
52 # self.log.info('Initializing generic K8S connector')
54 # the database and update callback
56 self
.on_update_db
= on_update_db
58 # self.log.info('K8S generic connector initialized')
62 self
, k8s_creds
: str, namespace
: str = "kube-system", reuse_cluster_uuid
=None
65 It prepares a given K8s cluster environment to run Charts or juju Bundles on
70 :param k8s_creds: credentials to access a given K8s cluster, i.e. a valid
72 :param namespace: optional namespace to be used for the K8s engine (helm
73 tiller, juju). By default, 'kube-system' will be used
74 :param reuse_cluster_uuid: existing cluster uuid for reuse
75 :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)
81 self
, cluster_uuid
: str, name
: str, url
: str, repo_type
: str = "chart"
84 Add a new repository to OSM database
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
94 async def repo_list(self
, cluster_uuid
: str):
96 Get the list of registered repositories
98 :param cluster_uuid: the cluster
99 :return: list of registered repositories: [ (name, url) .... ]
103 async def repo_remove(self
, cluster_uuid
: str, name
: str):
105 Remove a repository from OSM
107 :param name: repo name in OSM
108 :param cluster_uuid: the cluster
109 :return: True if successful
113 async def synchronize_repos(self
, cluster_uuid
: str, name
: str):
115 Synchronizes the list of repositories created in the cluster with
116 the repositories added by the NBI
118 :param cluster_uuid: the cluster
119 :return: List of repositories deleted from the cluster and dictionary with
125 self
, cluster_uuid
: str, force
: bool = False, uninstall_sw
: bool = False
128 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.
131 :param cluster_uuid: UUID of a K8s cluster known by OSM.
132 :param force: force deletion, even in case there are deployed releases
133 :param uninstall_sw: flag to indicate that sw uninstallation from software is
135 :return: str: kdu_instance generated by helm
145 timeout
: float = 300,
147 db_dict
: dict = None,
148 kdu_name
: str = None,
149 namespace
: str = None,
152 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).
156 :param cluster_uuid: UUID of a K8s cluster known by OSM
157 :param kdu_model: chart/bundle:version reference (string), which can be either
159 - 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
162 :param kdu_instance: Kdu instance name
163 :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)
169 :param dict db_dict: where to write into database when the status changes.
170 It contains a dict with {collection: <str>, filter: {},
172 e.g. {collection: "nsrs", filter:
173 {_id: <nsd-id>, path: "_admin.deployed.K8S.3"}
174 :param kdu_name: Name of the KDU instance to be installed
175 :param namespace: K8s namespace to use for the KDU instance
176 :return: True if successful
184 kdu_model
: str = None,
186 timeout
: float = 300,
188 db_dict
: dict = None,
191 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.
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
198 :param atomic: rollback in case of fail and wait for pods and services are
200 :param timeout: Time in seconds to wait for the install of the chart/bundle
201 (defaults to Helm default timeout: 300s)
202 :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.
204 It contains a dict with {collection: <str>, filter: {},
206 e.g. {collection: "nsrs", filter:
207 {_id: <nsd-id>, path: "_admin.deployed.K8S.3"}
208 :return: reference to the new revision number of the KDU instance
217 total_timeout
: float = 1800,
221 Scales an application in KDU instance.
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
228 :param kwargs: Additional parameters
230 :return: If successful, returns True
234 async def get_scale_count(
241 Get an application scale count.
243 :param: resource_name str: Resource name (Application name)
244 :param: kdu_instance str: KDU instance name
245 :param kwargs: Additional parameters
247 :return: Return application instance count
252 self
, cluster_uuid
: str, kdu_instance
: str, revision
=0, db_dict
: dict = None
255 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.
259 :param cluster_uuid: UUID of a K8s cluster known by OSM
260 :param kdu_instance: unique name for the KDU instance
261 :param revision: revision to which revert changes. If omitted, it will revert
263 :param dict db_dict: where to write into database when the status changes.
264 It contains a dict with {collection: <str>, filter: {},
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
273 async def uninstall(self
, cluster_uuid
: str, kdu_instance
: str):
275 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
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
285 async def exec_primitive(
287 cluster_uuid
: str = None,
288 kdu_instance
: str = None,
289 primitive_name
: str = None,
290 timeout
: float = 300,
292 db_dict
: dict = None,
294 """Exec primitive (Juju action)
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
303 :return: Returns the output of the action
307 async def inspect_kdu(self
, kdu_model
: str, repo_url
: str = None) -> str:
309 These calls will retrieve from the Chart/Bundle:
311 - 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
316 :param kdu_model: chart/bundle reference
317 :param repo_url: optional, reposotory URL (None if tar.gz, URl in other cases,
321 If successful, it will return the available parameters and their default values
322 as provided by the backend.
326 async def help_kdu(self
, kdu_model
: str, repo_url
: str = None) -> str:
329 :param kdu_model: chart/bundle reference
330 :param repo_url: optional, reposotory URL (None if tar.gz, URl in other cases,
332 :return: If successful, it will return the contents of the 'readme.md'
336 async def status_kdu(self
, cluster_uuid
: str, kdu_instance
: str) -> str:
338 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.
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:
354 - List of `resources` (objects) that this release consists of, sorted by kind,
355 and the status of those resources
356 - Last `deployment_time`.
361 async def get_services(
362 self
, cluster_uuid
: str, kdu_instance
: str, namespace
: str
365 Returns a list of services defined for the specified kdu instance.
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
376 - `cluster_ip` Internal ip to be used inside k8s cluster
377 - `external_ip` List of external ips (in case they are available)
381 async def get_service(
382 self
, cluster_uuid
: str, service_name
: str, namespace
: str = None
385 Obtains the data of the specified service in the k8cluster.
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
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
396 - `cluster_ip` Internal ip to be used inside k8s cluster
397 - `external_ip` List of external ips (in case they are available)
401 ####################################################################################
402 ################################### P R I V A T E ##################################
403 ####################################################################################
406 async def write_app_status_to_db(
407 self
, db_dict
: dict, status
: str, detailed_status
: str, operation
: str
411 self
.warning("No db => No database write")
415 self
.warning("No db_dict => No database write")
418 self
.log
.debug("status={}".format(status
))
422 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
+ "."
428 the_path
+ "operation": operation
,
429 the_path
+ "status": status
,
430 the_path
+ "detailed-status": detailed_status
,
431 the_path
+ "status-time": str(time
.time()),
437 update_dict
=update_dict
,
442 if self
.on_update_db
:
443 if asyncio
.iscoroutinefunction(self
.on_update_db
):
444 await self
.on_update_db(
445 the_table
, the_filter
, the_path
, update_dict
448 self
.on_update_db(the_table
, the_filter
, the_path
, update_dict
)
452 except Exception as e
:
453 self
.log
.info("Exception writing status to database: {}".format(e
))