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 ####################################################################################
37 def __init__(self
, db
: object, log
: object = None, on_update_db
=None):
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
46 Loggable
.__init
__(self
, log
=log
, log_to_console
=True, prefix
="\nK8S")
48 # self.log.info('Initializing generic K8S connector')
50 # the database and update callback
52 self
.on_update_db
= on_update_db
54 # self.log.info('K8S generic connector initialized')
58 self
, k8s_creds
: str, namespace
: str = "kube-system", reuse_cluster_uuid
=None
61 It prepares a given K8s cluster environment to run Charts or juju Bundles on
66 :param k8s_creds: credentials to access a given K8s cluster, i.e. a valid
68 :param namespace: optional namespace to be used for the K8s engine (helm
69 tiller, juju). By default, 'kube-system' will be used
70 :param reuse_cluster_uuid: existing cluster uuid for reuse
71 :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)
77 self
, cluster_uuid
: str, name
: str, url
: str, repo_type
: str = "chart"
80 Add a new repository to OSM database
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
90 async def repo_list(self
, cluster_uuid
: str):
92 Get the list of registered repositories
94 :param cluster_uuid: the cluster
95 :return: list of registered repositories: [ (name, url) .... ]
99 async def repo_remove(self
, cluster_uuid
: str, name
: str):
101 Remove a repository from OSM
103 :param name: repo name in OSM
104 :param cluster_uuid: the cluster
105 :return: True if successful
109 async def synchronize_repos(self
, cluster_uuid
: str, name
: str):
111 Synchronizes the list of repositories created in the cluster with
112 the repositories added by the NBI
114 :param cluster_uuid: the cluster
115 :return: List of repositories deleted from the cluster and dictionary with
121 self
, cluster_uuid
: str, force
: bool = False, uninstall_sw
: bool = False
124 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.
127 :param cluster_uuid: UUID of a K8s cluster known by OSM.
128 :param force: force deletion, even in case there are deployed releases
129 :param uninstall_sw: flag to indicate that sw uninstallation from software is
131 :return: str: kdu_instance generated by helm
140 timeout
: float = 300,
142 db_dict
: dict = None,
143 kdu_name
: str = None,
144 namespace
: str = None,
147 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).
151 :param cluster_uuid: UUID of a K8s cluster known by OSM
152 :param kdu_model: chart/bundle:version reference (string), which can be either
154 - 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
157 :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)
163 :param dict db_dict: where to write into database when the status changes.
164 It contains a dict with {collection: <str>, filter: {},
166 e.g. {collection: "nsrs", filter:
167 {_id: <nsd-id>, path: "_admin.deployed.K8S.3"}
168 :param kdu_name: Name of the KDU instance to be installed
169 :param namespace: K8s namespace to use for the KDU instance
170 :return: True if successful
178 kdu_model
: str = None,
180 timeout
: float = 300,
182 db_dict
: dict = None,
185 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.
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
192 :param atomic: rollback in case of fail and wait for pods and services are
194 :param timeout: Time in seconds to wait for the install of the chart/bundle
195 (defaults to Helm default timeout: 300s)
196 :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.
198 It contains a dict with {collection: <str>, filter: {},
200 e.g. {collection: "nsrs", filter:
201 {_id: <nsd-id>, path: "_admin.deployed.K8S.3"}
202 :return: reference to the new revision number of the KDU instance
207 self
, cluster_uuid
: str, kdu_instance
: str, revision
=0, db_dict
: dict = None
210 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.
214 :param cluster_uuid: UUID of a K8s cluster known by OSM
215 :param kdu_instance: unique name for the KDU instance
216 :param revision: revision to which revert changes. If omitted, it will revert
218 :param dict db_dict: where to write into database when the status changes.
219 It contains a dict with {collection: <str>, filter: {},
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
228 async def uninstall(self
, cluster_uuid
: str, kdu_instance
: str):
230 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
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
240 async def exec_primitive(
242 cluster_uuid
: str = None,
243 kdu_instance
: str = None,
244 primitive_name
: str = None,
245 timeout
: float = 300,
247 db_dict
: dict = None,
249 """Exec primitive (Juju action)
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
258 :return: Returns the output of the action
262 async def inspect_kdu(self
, kdu_model
: str, repo_url
: str = None) -> str:
264 These calls will retrieve from the Chart/Bundle:
266 - 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
271 :param kdu_model: chart/bundle reference
272 :param repo_url: optional, reposotory URL (None if tar.gz, URl in other cases,
276 If successful, it will return the available parameters and their default values
277 as provided by the backend.
281 async def help_kdu(self
, kdu_model
: str, repo_url
: str = None) -> str:
284 :param kdu_model: chart/bundle reference
285 :param repo_url: optional, reposotory URL (None if tar.gz, URl in other cases,
287 :return: If successful, it will return the contents of the 'readme.md'
291 async def status_kdu(self
, cluster_uuid
: str, kdu_instance
: str) -> str:
293 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.
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:
309 - List of `resources` (objects) that this release consists of, sorted by kind,
310 and the status of those resources
311 - Last `deployment_time`.
316 async def get_services(self
,
319 namespace
: str) -> list:
321 Returns a list of services defined for the specified kdu instance.
323 :param cluster_uuid: UUID of a K8s cluster known by OSM
324 :param kdu_instance: unique name for the KDU instance
325 :param namespace: K8s namespace used by the KDU instance
326 :return: If successful, it will return a list of services, Each service
327 can have the following data:
328 - `name` of the service
329 - `type` type of service in the k8 cluster
330 - `ports` List of ports offered by the service, for each port includes at least
332 - `cluster_ip` Internal ip to be used inside k8s cluster
333 - `external_ip` List of external ips (in case they are available)
337 async def get_service(self
,
340 namespace
: str = None) -> object:
342 Obtains the data of the specified service in the k8cluster.
344 :param cluster_uuid: UUID of a K8s cluster known by OSM
345 :param service_name: name of the K8s service in the specified namespace
346 :param namespace: K8s namespace used by the KDU instance
347 :return: If successful, it will return a list of services, Each service can have
349 - `name` of the service
350 - `type` type of service in the k8 cluster
351 - `ports` List of ports offered by the service, for each port includes at least
353 - `cluster_ip` Internal ip to be used inside k8s cluster
354 - `external_ip` List of external ips (in case they are available)
358 ####################################################################################
359 ################################### P R I V A T E ##################################
360 ####################################################################################
363 async def write_app_status_to_db(
364 self
, db_dict
: dict, status
: str, detailed_status
: str, operation
: str
368 self
.warning("No db => No database write")
372 self
.warning("No db_dict => No database write")
375 self
.log
.debug("status={}".format(status
))
379 the_table
= db_dict
["collection"]
380 the_filter
= db_dict
["filter"]
381 the_path
= db_dict
["path"]
382 if not the_path
[-1] == ".":
383 the_path
= the_path
+ "."
385 the_path
+ "operation": operation
,
386 the_path
+ "status": status
,
387 the_path
+ "detailed-status": detailed_status
,
388 the_path
+ "status-time": str(time
.time()),
394 update_dict
=update_dict
,
399 if self
.on_update_db
:
400 if asyncio
.iscoroutinefunction(self
.on_update_db
):
401 await self
.on_update_db(
402 the_table
, the_filter
, the_path
, update_dict
405 self
.on_update_db(the_table
, the_filter
, the_path
, update_dict
)
409 except Exception as e
:
410 self
.log
.info("Exception writing status to database: {}".format(e
))