1 # Copyright 2019 Canonical Ltd.
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
7 # http://www.apache.org/licenses/LICENSE-2.0
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
16 from typing
import Union
23 from n2vc
.config
import EnvironConfig
24 from n2vc
.definitions
import RelationEndpoint
25 from n2vc
.exceptions
import K8sException
26 from n2vc
.k8s_conn
import K8sConnector
27 from n2vc
.kubectl
import Kubectl
28 from .exceptions
import MethodNotImplemented
29 from n2vc
.libjuju
import Libjuju
30 from n2vc
.utils
import obj_to_dict
, obj_to_yaml
31 from n2vc
.store
import MotorStore
32 from n2vc
.vca
.cloud
import Cloud
33 from n2vc
.vca
.connection
import get_connection
36 RBAC_LABEL_KEY_NAME
= "rbac-id"
37 RBAC_STACK_PREFIX
= "juju-credential"
40 def generate_rbac_id():
41 return binascii
.hexlify(os
.urandom(4)).decode()
44 class K8sJujuConnector(K8sConnector
):
51 kubectl_command
: str = "/usr/bin/kubectl",
52 juju_command
: str = "/usr/bin/juju",
58 :param fs: file system for kubernetes and helm configuration
59 :param db: Database object
60 :param kubectl_command: path to kubectl executable
61 :param helm_command: path to helm executable
63 :param: loop: Asyncio loop
67 K8sConnector
.__init
__(self
, db
, log
=log
, on_update_db
=on_update_db
)
70 self
.loop
= loop
or asyncio
.get_event_loop()
71 self
.log
.debug("Initializing K8S Juju connector")
73 db_uri
= EnvironConfig(prefixes
=["OSMLCM_", "OSMMON_"]).get("database_uri")
74 self
._store
= MotorStore(db_uri
)
75 self
.loading_libjuju
= asyncio
.Lock(loop
=self
.loop
)
76 self
.uninstall_locks
= {}
78 self
.log
.debug("K8S Juju connector initialized")
79 # TODO: Remove these commented lines:
80 # self.authenticated = False
82 # self.juju_secret = ""
89 namespace
: str = "kube-system",
90 reuse_cluster_uuid
: str = None,
94 It prepares a given K8s cluster environment to run Juju bundles.
96 :param k8s_creds: credentials to access a given K8s cluster, i.e. a valid
98 :param namespace: optional namespace to be used for juju. By default,
99 'kube-system' will be used
100 :param reuse_cluster_uuid: existing cluster uuid for reuse
101 :param: kwargs: Additional parameters
104 :return: uuid of the K8s cluster and True if connector has installed some
105 software in the cluster
106 (on error, an exception will be raised)
108 libjuju
= await self
._get
_libjuju
(kwargs
.get("vca_id"))
110 cluster_uuid
= reuse_cluster_uuid
or str(uuid
.uuid4())
111 kubectl
= self
._get
_kubectl
(k8s_creds
)
113 # CREATING RESOURCES IN K8S
114 rbac_id
= generate_rbac_id()
115 metadata_name
= "{}-{}".format(RBAC_STACK_PREFIX
, rbac_id
)
116 labels
= {RBAC_STACK_PREFIX
: rbac_id
}
118 # Create cleanup dictionary to clean up created resources
119 # if it fails in the middle of the process
122 self
.log
.debug("Initializing K8s cluster for juju")
123 kubectl
.create_cluster_role(name
=metadata_name
, labels
=labels
)
124 self
.log
.debug("Cluster role created")
126 {"delete": kubectl
.delete_cluster_role
, "args": (metadata_name
,)}
129 kubectl
.create_service_account(name
=metadata_name
, labels
=labels
)
130 self
.log
.debug("Service account created")
132 {"delete": kubectl
.delete_service_account
, "args": (metadata_name
,)}
135 kubectl
.create_cluster_role_binding(name
=metadata_name
, labels
=labels
)
136 self
.log
.debug("Role binding created")
139 "delete": kubectl
.delete_cluster_role_binding
,
140 "args": (metadata_name
,),
143 token
, client_cert_data
= await kubectl
.get_secret_data(metadata_name
)
145 default_storage_class
= kubectl
.get_default_storage_class()
146 self
.log
.debug("Default storage class: {}".format(default_storage_class
))
147 await libjuju
.add_k8s(
151 client_cert_data
=client_cert_data
,
152 configuration
=kubectl
.configuration
,
153 storage_class
=default_storage_class
,
154 credential_name
=self
._get
_credential
_name
(cluster_uuid
),
156 self
.log
.debug("K8s cluster added to juju controller")
157 return cluster_uuid
, True
158 except Exception as e
:
159 self
.log
.error("Error initializing k8scluster: {}".format(e
), exc_info
=True)
160 if len(cleanup_data
) > 0:
161 self
.log
.debug("Cleaning up created resources in k8s cluster...")
162 for item
in cleanup_data
:
163 delete_function
= item
["delete"]
164 delete_args
= item
["args"]
165 delete_function(*delete_args
)
166 self
.log
.debug("Cleanup finished")
169 """Repo Management"""
175 _type
: str = "charm",
178 password
: str = None,
180 raise MethodNotImplemented()
182 async def repo_list(self
):
183 raise MethodNotImplemented()
185 async def repo_remove(self
, name
: str):
186 raise MethodNotImplemented()
188 async def synchronize_repos(self
, cluster_uuid
: str, name
: str):
190 Returns None as currently add_repo is not implemented
200 uninstall_sw
: bool = False,
205 Resets the Kubernetes cluster by removing the model that represents it.
207 :param cluster_uuid str: The UUID of the cluster to reset
208 :param force: Force reset
209 :param uninstall_sw: Boolean to uninstall sw
210 :param: kwargs: Additional parameters
213 :return: Returns True if successful or raises an exception.
217 self
.log
.debug("[reset] Removing k8s cloud")
218 libjuju
= await self
._get
_libjuju
(kwargs
.get("vca_id"))
220 cloud
= Cloud(cluster_uuid
, self
._get
_credential
_name
(cluster_uuid
))
222 cloud_creds
= await libjuju
.get_cloud_credentials(cloud
)
224 await libjuju
.remove_cloud(cluster_uuid
)
226 credentials
= self
.get_credentials(cluster_uuid
=cluster_uuid
)
228 kubectl
= self
._get
_kubectl
(credentials
)
231 kubectl
.delete_cluster_role_binding
,
232 kubectl
.delete_service_account
,
233 kubectl
.delete_cluster_role
,
236 credential_attrs
= cloud_creds
[0].result
["attrs"]
237 if RBAC_LABEL_KEY_NAME
in credential_attrs
:
238 rbac_id
= credential_attrs
[RBAC_LABEL_KEY_NAME
]
239 metadata_name
= "{}-{}".format(RBAC_STACK_PREFIX
, rbac_id
)
240 for delete_func
in delete_functions
:
242 delete_func(metadata_name
)
243 except Exception as e
:
244 self
.log
.warning("Cannot remove resource in K8s {}".format(e
))
246 except Exception as e
:
247 self
.log
.debug("Caught exception during reset: {}".format(e
))
259 timeout
: float = 1800,
261 db_dict
: dict = None,
262 kdu_name
: str = None,
263 namespace
: str = None,
268 :param cluster_uuid str: The UUID of the cluster to install to
269 :param kdu_model str: The name or path of a bundle to install
270 :param kdu_instance: Kdu instance name
271 :param atomic bool: If set, waits until the model is active and resets
272 the cluster on failure.
273 :param timeout int: The time, in seconds, to wait for the install
275 :param params dict: Key-value pairs of instantiation parameters
276 :param kdu_name: Name of the KDU instance to be installed
277 :param namespace: K8s namespace to use for the KDU instance
278 :param kwargs: Additional parameters
281 :return: If successful, returns ?
283 libjuju
= await self
._get
_libjuju
(kwargs
.get("vca_id"))
287 raise K8sException("db_dict must be set")
289 raise K8sException("bundle must be set")
291 if bundle
.startswith("cs:"):
292 # For Juju Bundles provided by the Charm Store
294 elif bundle
.startswith("ch:"):
295 # For Juju Bundles provided by the Charm Hub (this only works for juju version >= 2.9)
297 elif bundle
.startswith("http"):
301 new_workdir
= kdu_model
.strip(kdu_model
.split("/")[-1])
302 os
.chdir(new_workdir
)
303 bundle
= "local:{}".format(kdu_model
)
305 # default namespace to kdu_instance
307 namespace
= kdu_instance
309 self
.log
.debug("Checking for model named {}".format(namespace
))
311 # Create the new model
312 self
.log
.debug("Adding model: {}".format(namespace
))
313 cloud
= Cloud(cluster_uuid
, self
._get
_credential
_name
(cluster_uuid
))
314 await libjuju
.add_model(namespace
, cloud
)
317 # TODO: Instantiation parameters
320 "Juju bundle that models the KDU, in any of the following ways:
321 - <juju-repo>/<juju-bundle>
322 - <juju-bundle folder under k8s_models folder in the package>
323 - <juju-bundle tgz file (w/ or w/o extension) under k8s_models folder
325 - <URL_where_to_fetch_juju_bundle>
328 previous_workdir
= os
.getcwd()
329 except FileNotFoundError
:
330 previous_workdir
= "/app/storage"
332 self
.log
.debug("[install] deploying {}".format(bundle
))
333 instantiation_params
= params
.get("overlay") if params
else None
334 await libjuju
.deploy(
336 model_name
=namespace
,
339 instantiation_params
=instantiation_params
,
341 os
.chdir(previous_workdir
)
343 # update information in the database (first, the VCA status, and then, the namespace)
344 if self
.on_update_db
:
345 await self
.on_update_db(
348 filter=db_dict
["filter"],
349 vca_id
=kwargs
.get("vca_id"),
354 q_filter
={"_admin.deployed.K8s.kdu-instance": kdu_instance
},
355 update_dict
={"_admin.deployed.K8s.$.namespace": namespace
},
365 total_timeout
: float = 1800,
366 namespace
: str = None,
369 """Scale an application in a model
371 :param: kdu_instance str: KDU instance name
372 :param: scale int: Scale to which to set the application
373 :param: resource_name str: The application name in the Juju Bundle
374 :param: timeout float: The time, in seconds, to wait for the install
376 :param namespace str: The namespace (model) where the Bundle was deployed
377 :param kwargs: Additional parameters
380 :return: If successful, returns True
383 model_name
= self
._obtain
_namespace
(
384 kdu_instance
=kdu_instance
, namespace
=namespace
387 libjuju
= await self
._get
_libjuju
(kwargs
.get("vca_id"))
388 await libjuju
.scale_application(
389 model_name
=model_name
,
390 application_name
=resource_name
,
392 total_timeout
=total_timeout
,
394 except Exception as e
:
395 error_msg
= "Error scaling application {} of the model {} of the kdu instance {}: {}".format(
396 resource_name
, model_name
, kdu_instance
, e
398 self
.log
.error(error_msg
)
399 raise K8sException(message
=error_msg
)
402 async def get_scale_count(
403 self
, resource_name
: str, kdu_instance
: str, namespace
: str = None, **kwargs
405 """Get an application scale count
407 :param: resource_name str: The application name in the Juju Bundle
408 :param: kdu_instance str: KDU instance name
409 :param namespace str: The namespace (model) where the Bundle was deployed
410 :param kwargs: Additional parameters
412 :return: Return application instance count
415 model_name
= self
._obtain
_namespace
(
416 kdu_instance
=kdu_instance
, namespace
=namespace
419 libjuju
= await self
._get
_libjuju
(kwargs
.get("vca_id"))
420 status
= await libjuju
.get_model_status(model_name
=model_name
)
421 return len(status
.applications
[resource_name
].units
)
422 except Exception as e
:
424 f
"Error getting scale count from application {resource_name} of the model {model_name} of "
425 f
"the kdu instance {kdu_instance}: {e}"
427 self
.log
.error(error_msg
)
428 raise K8sException(message
=error_msg
)
430 async def instances_list(self
, cluster_uuid
: str) -> list:
432 returns a list of deployed releases in a cluster
434 :param cluster_uuid: the cluster
443 kdu_model
: str = None,
448 :param cluster_uuid str: The UUID of the cluster to upgrade
449 :param kdu_instance str: The unique name of the KDU instance
450 :param kdu_model str: The name or path of the bundle to upgrade to
451 :param params dict: Key-value pairs of instantiation parameters
453 :return: If successful, reference to the new revision number of the
457 # TODO: Loop through the bundle and upgrade each charm individually
460 The API doesn't have a concept of bundle upgrades, because there are
461 many possible changes: charm revision, disk, number of units, etc.
463 As such, we are only supporting a limited subset of upgrades. We'll
464 upgrade the charm revision but leave storage and scale untouched.
466 Scale changes should happen through OSM constructs, and changes to
467 storage would require a redeployment of the service, at least in this
470 raise MethodNotImplemented()
475 self
, cluster_uuid
: str, kdu_instance
: str, revision
: int = 0
479 :param cluster_uuid str: The UUID of the cluster to rollback
480 :param kdu_instance str: The unique name of the KDU instance
481 :param revision int: The revision to revert to. If omitted, rolls back
482 the previous upgrade.
484 :return: If successful, returns the revision of active KDU instance,
485 or raises an exception
487 raise MethodNotImplemented()
492 self
, cluster_uuid
: str, kdu_instance
: str, namespace
: str = None, **kwargs
494 """Uninstall a KDU instance
496 :param cluster_uuid str: The UUID of the cluster
497 :param kdu_instance str: The unique name of the KDU instance
498 :param namespace str: The namespace (model) where the Bundle was deployed
499 :param kwargs: Additional parameters
502 :return: Returns True if successful, or raises an exception
504 model_name
= self
._obtain
_namespace
(
505 kdu_instance
=kdu_instance
, namespace
=namespace
508 self
.log
.debug(f
"[uninstall] Destroying model: {model_name}")
510 will_not_delete
= False
511 if model_name
not in self
.uninstall_locks
:
512 self
.uninstall_locks
[model_name
] = asyncio
.Lock(loop
=self
.loop
)
513 delete_lock
= self
.uninstall_locks
[model_name
]
515 while delete_lock
.locked():
516 will_not_delete
= True
517 await asyncio
.sleep(0.1)
520 self
.log
.info("Model {} deleted by another worker.".format(model_name
))
524 async with delete_lock
:
525 libjuju
= await self
._get
_libjuju
(kwargs
.get("vca_id"))
527 await libjuju
.destroy_model(model_name
, total_timeout
=3600)
529 self
.uninstall_locks
.pop(model_name
)
531 self
.log
.debug(f
"[uninstall] Model {model_name} destroyed")
534 async def upgrade_charm(
538 charm_id
: str = None,
539 charm_type
: str = None,
540 timeout
: float = None,
542 """This method upgrade charms in VNFs
545 ee_id: Execution environment id
546 path: Local path to the charm
548 charm_type: Charm type can be lxc-proxy-charm, native-charm or k8s-proxy-charm
549 timeout: (Float) Timeout for the ns update operation
552 The output of the update operation if status equals to "completed"
555 "KDUs deployed with Juju Bundle do not support charm upgrade"
558 async def exec_primitive(
560 cluster_uuid
: str = None,
561 kdu_instance
: str = None,
562 primitive_name
: str = None,
563 timeout
: float = 300,
565 db_dict
: dict = None,
566 namespace
: str = None,
569 """Exec primitive (Juju action)
571 :param cluster_uuid str: The UUID of the cluster
572 :param kdu_instance str: The unique name of the KDU instance
573 :param primitive_name: Name of action that will be executed
574 :param timeout: Timeout for action execution
575 :param params: Dictionary of all the parameters needed for the action
576 :param db_dict: Dictionary for any additional data
577 :param namespace str: The namespace (model) where the Bundle was deployed
578 :param kwargs: Additional parameters
581 :return: Returns the output of the action
583 libjuju
= await self
._get
_libjuju
(kwargs
.get("vca_id"))
585 namespace
= self
._obtain
_namespace
(
586 kdu_instance
=kdu_instance
, namespace
=namespace
589 if not params
or "application-name" not in params
:
591 "Missing application-name argument, \
592 argument needed for K8s actions"
596 "[exec_primitive] Getting model "
597 "{} for the kdu_instance: {}".format(namespace
, kdu_instance
)
599 application_name
= params
["application-name"]
600 actions
= await libjuju
.get_actions(
601 application_name
=application_name
, model_name
=namespace
603 if primitive_name
not in actions
:
604 raise K8sException("Primitive {} not found".format(primitive_name
))
605 output
, status
= await libjuju
.execute_action(
606 application_name
=application_name
,
607 model_name
=namespace
,
608 action_name
=primitive_name
,
612 if status
!= "completed":
614 "status is not completed: {} output: {}".format(status
, output
)
616 if self
.on_update_db
:
617 await self
.on_update_db(
618 cluster_uuid
=cluster_uuid
,
619 kdu_instance
=kdu_instance
,
620 filter=db_dict
["filter"],
625 except Exception as e
:
626 error_msg
= "Error executing primitive {}: {}".format(primitive_name
, e
)
627 self
.log
.error(error_msg
)
628 raise K8sException(message
=error_msg
)
632 async def inspect_kdu(self
, kdu_model
: str) -> dict:
635 Inspects a bundle and returns a dictionary of config parameters and
636 their default values.
638 :param kdu_model str: The name or path of the bundle to inspect.
640 :return: If successful, returns a dictionary of available parameters
641 and their default values.
645 if not os
.path
.exists(kdu_model
):
646 raise K8sException("file {} not found".format(kdu_model
))
648 with
open(kdu_model
, "r") as f
:
649 bundle
= yaml
.safe_load(f
.read())
653 'description': 'Test bundle',
654 'bundle': 'kubernetes',
657 'charm': 'cs:~charmed-osm/mariadb-k8s-20',
660 'password': 'manopw',
661 'root_password': 'osm4u',
664 'series': 'kubernetes'
669 # TODO: This should be returned in an agreed-upon format
670 kdu
= bundle
["applications"]
674 async def help_kdu(self
, kdu_model
: str) -> str:
677 If available, returns the README of the bundle.
679 :param kdu_model str: The name or path of a bundle
681 :return: If found, returns the contents of the README.
685 files
= ["README", "README.txt", "README.md"]
686 path
= os
.path
.dirname(kdu_model
)
687 for file in os
.listdir(path
):
689 with
open(file, "r") as f
:
695 async def status_kdu(
699 complete_status
: bool = False,
700 yaml_format
: bool = False,
701 namespace
: str = None,
703 ) -> Union
[str, dict]:
704 """Get the status of the KDU
706 Get the current status of the KDU instance.
708 :param cluster_uuid str: The UUID of the cluster
709 :param kdu_instance str: The unique id of the KDU instance
710 :param complete_status: To get the complete_status of the KDU
711 :param yaml_format: To get the status in proper format for NSR record
712 :param namespace str: The namespace (model) where the Bundle was deployed
713 :param: kwargs: Additional parameters
716 :return: Returns a dictionary containing namespace, state, resources,
717 and deployment_time and returns complete_status if complete_status is True
719 libjuju
= await self
._get
_libjuju
(kwargs
.get("vca_id"))
722 model_name
= self
._obtain
_namespace
(
723 kdu_instance
=kdu_instance
, namespace
=namespace
725 model_status
= await libjuju
.get_model_status(model_name
=model_name
)
727 if not complete_status
:
728 for name
in model_status
.applications
:
729 application
= model_status
.applications
[name
]
730 status
[name
] = {"status": application
["status"]["status"]}
733 return obj_to_yaml(model_status
)
735 return obj_to_dict(model_status
)
739 async def add_relation(
740 self
, provider
: RelationEndpoint
, requirer
: RelationEndpoint
743 Add relation between two charmed endpoints
745 :param: provider: Provider relation endpoint
746 :param: requirer: Requirer relation endpoint
748 self
.log
.debug(f
"adding new relation between {provider} and {requirer}")
749 cross_model_relation
= (
750 provider
.model_name
!= requirer
.model_name
751 or provider
.vca_id
!= requirer
.vca_id
754 if cross_model_relation
:
755 # Cross-model relation
756 provider_libjuju
= await self
._get
_libjuju
(provider
.vca_id
)
757 requirer_libjuju
= await self
._get
_libjuju
(requirer
.vca_id
)
758 offer
= await provider_libjuju
.offer(provider
)
760 saas_name
= await requirer_libjuju
.consume(
761 requirer
.model_name
, offer
, provider_libjuju
763 await requirer_libjuju
.add_relation(
764 requirer
.model_name
, requirer
.endpoint
, saas_name
768 vca_id
= provider
.vca_id
769 model
= provider
.model_name
770 libjuju
= await self
._get
_libjuju
(vca_id
)
771 # add juju relations between two applications
772 await libjuju
.add_relation(
774 endpoint_1
=provider
.endpoint
,
775 endpoint_2
=requirer
.endpoint
,
777 except Exception as e
:
778 message
= f
"Error adding relation between {provider} and {requirer}: {e}"
779 self
.log
.error(message
)
780 raise Exception(message
=message
)
782 async def update_vca_status(
783 self
, vcastatus
: dict, kdu_instance
: str, namespace
: str = None, **kwargs
786 Add all configs, actions, executed actions of all applications in a model to vcastatus dict
788 :param vcastatus dict: dict containing vcastatus
789 :param kdu_instance str: The unique id of the KDU instance
790 :param namespace str: The namespace (model) where the Bundle was deployed
791 :param: kwargs: Additional parameters
797 model_name
= self
._obtain
_namespace
(
798 kdu_instance
=kdu_instance
, namespace
=namespace
801 libjuju
= await self
._get
_libjuju
(kwargs
.get("vca_id"))
803 for vca_model_name
in vcastatus
:
804 # Adding executed actions
805 vcastatus
[vca_model_name
][
807 ] = await libjuju
.get_executed_actions(model_name
=model_name
)
809 for application
in vcastatus
[vca_model_name
]["applications"]:
810 # Adding application actions
811 vcastatus
[vca_model_name
]["applications"][application
][
814 # Adding application configs
815 vcastatus
[vca_model_name
]["applications"][application
][
817 ] = await libjuju
.get_application_configs(
818 model_name
=model_name
, application_name
=application
821 except Exception as e
:
822 self
.log
.debug("Error in updating vca status: {}".format(str(e
)))
824 async def get_services(
825 self
, cluster_uuid
: str, kdu_instance
: str, namespace
: str
827 """Return a list of services of a kdu_instance"""
829 namespace
= self
._obtain
_namespace
(
830 kdu_instance
=kdu_instance
, namespace
=namespace
833 credentials
= self
.get_credentials(cluster_uuid
=cluster_uuid
)
834 kubectl
= self
._get
_kubectl
(credentials
)
835 return kubectl
.get_services(
836 field_selector
="metadata.namespace={}".format(namespace
)
839 async def get_service(
840 self
, cluster_uuid
: str, service_name
: str, namespace
: str
842 """Return data for a specific service inside a namespace"""
844 credentials
= self
.get_credentials(cluster_uuid
=cluster_uuid
)
845 kubectl
= self
._get
_kubectl
(credentials
)
846 return kubectl
.get_services(
847 field_selector
="metadata.name={},metadata.namespace={}".format(
848 service_name
, namespace
852 def get_credentials(self
, cluster_uuid
: str) -> str:
854 Get Cluster Kubeconfig
856 k8scluster
= self
.db
.get_one(
857 "k8sclusters", q_filter
={"_id": cluster_uuid
}, fail_on_empty
=False
860 self
.db
.encrypt_decrypt_fields(
861 k8scluster
.get("credentials"),
863 ["password", "secret"],
864 schema_version
=k8scluster
["schema_version"],
865 salt
=k8scluster
["_id"],
868 return yaml
.safe_dump(k8scluster
.get("credentials"))
870 def _get_credential_name(self
, cluster_uuid
: str) -> str:
872 Get credential name for a k8s cloud
874 We cannot use the cluster_uuid for the credential name directly,
875 because it cannot start with a number, it must start with a letter.
876 Therefore, the k8s cloud credential name will be "cred-" followed
879 :param: cluster_uuid: Cluster UUID of the kubernetes cloud (=cloud_name)
881 :return: Name to use for the credential name.
883 return "cred-{}".format(cluster_uuid
)
885 def get_namespace(self
, cluster_uuid
: str) -> str:
886 """Get the namespace UUID
887 Gets the namespace's unique name
889 :param cluster_uuid str: The UUID of the cluster
890 :returns: The namespace UUID, or raises an exception
895 def generate_kdu_instance_name(**kwargs
):
896 db_dict
= kwargs
.get("db_dict")
897 kdu_name
= kwargs
.get("kdu_name", None)
899 kdu_instance
= "{}-{}".format(kdu_name
, db_dict
["filter"]["_id"])
901 kdu_instance
= db_dict
["filter"]["_id"]
904 async def _get_libjuju(self
, vca_id
: str = None) -> Libjuju
:
908 :param: vca_id: VCA ID
909 If None, get a libjuju object with a Connection to the default VCA
910 Else, geta libjuju object with a Connection to the specified VCA
913 while self
.loading_libjuju
.locked():
914 await asyncio
.sleep(0.1)
916 async with self
.loading_libjuju
:
917 vca_connection
= await get_connection(self
._store
)
918 self
.libjuju
= Libjuju(vca_connection
, loop
=self
.loop
, log
=self
.log
)
921 vca_connection
= await get_connection(self
._store
, vca_id
)
922 return Libjuju(vca_connection
, loop
=self
.loop
, log
=self
.log
, n2vc
=self
)
924 def _get_kubectl(self
, credentials
: str) -> Kubectl
:
928 :param: kubeconfig_credentials: Kubeconfig credentials
930 kubecfg
= tempfile
.NamedTemporaryFile()
931 with
open(kubecfg
.name
, "w") as kubecfg_file
:
932 kubecfg_file
.write(credentials
)
933 return Kubectl(config_file
=kubecfg
.name
)
935 def _obtain_namespace(self
, kdu_instance
: str, namespace
: str = None) -> str:
937 Obtain the namespace/model name to use in the instantiation of a Juju Bundle in K8s. The default namespace is
938 the kdu_instance name. However, if the user passes the namespace where he wants to deploy the bundle,
939 that namespace will be used.
941 :param kdu_instance: the default KDU instance name
942 :param namespace: the namespace passed by the User
945 # deault the namespace/model name to the kdu_instance name TODO -> this should be the real return... But
946 # once the namespace is not passed in most methods, I had to do this in another way. But I think this should
947 # be the procedure in the future return namespace if namespace else kdu_instance
949 # TODO -> has referred above, this should be avoided in the future, this is temporary, in order to avoid
950 # compatibility issues
954 else self
._obtain
_namespace
_from
_db
(kdu_instance
=kdu_instance
)
957 def _obtain_namespace_from_db(self
, kdu_instance
: str) -> str:
958 db_nsrs
= self
.db
.get_one(
959 table
="nsrs", q_filter
={"_admin.deployed.K8s.kdu-instance": kdu_instance
}
961 for k8s
in db_nsrs
["_admin"]["deployed"]["K8s"]:
962 if k8s
.get("kdu-instance") == kdu_instance
:
963 return k8s
.get("namespace")