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",
57 :param fs: file system for kubernetes and helm configuration
58 :param db: Database object
59 :param kubectl_command: path to kubectl executable
60 :param helm_command: path to helm executable
65 K8sConnector
.__init
__(self
, db
, log
=log
, on_update_db
=on_update_db
)
68 self
.log
.debug("Initializing K8S Juju connector")
70 db_uri
= EnvironConfig(prefixes
=["OSMLCM_", "OSMMON_"]).get("database_uri")
71 self
._store
= MotorStore(db_uri
)
72 self
.loading_libjuju
= asyncio
.Lock()
73 self
.uninstall_locks
= {}
75 self
.log
.debug("K8S Juju connector initialized")
76 # TODO: Remove these commented lines:
77 # self.authenticated = False
79 # self.juju_secret = ""
86 namespace
: str = "kube-system",
87 reuse_cluster_uuid
: str = None,
91 It prepares a given K8s cluster environment to run Juju bundles.
93 :param k8s_creds: credentials to access a given K8s cluster, i.e. a valid
95 :param namespace: optional namespace to be used for juju. By default,
96 'kube-system' will be used
97 :param reuse_cluster_uuid: existing cluster uuid for reuse
98 :param: kwargs: Additional parameters
101 :return: uuid of the K8s cluster and True if connector has installed some
102 software in the cluster
103 (on error, an exception will be raised)
105 libjuju
= await self
._get
_libjuju
(kwargs
.get("vca_id"))
107 cluster_uuid
= reuse_cluster_uuid
or str(uuid
.uuid4())
108 kubectl
= self
._get
_kubectl
(k8s_creds
)
110 # CREATING RESOURCES IN K8S
111 rbac_id
= generate_rbac_id()
112 metadata_name
= "{}-{}".format(RBAC_STACK_PREFIX
, rbac_id
)
113 labels
= {RBAC_STACK_PREFIX
: rbac_id
}
115 # Create cleanup dictionary to clean up created resources
116 # if it fails in the middle of the process
119 self
.log
.debug("Initializing K8s cluster for juju")
120 kubectl
.create_cluster_role(name
=metadata_name
, labels
=labels
)
121 self
.log
.debug("Cluster role created")
123 {"delete": kubectl
.delete_cluster_role
, "args": (metadata_name
,)}
126 kubectl
.create_service_account(name
=metadata_name
, labels
=labels
)
127 self
.log
.debug("Service account created")
129 {"delete": kubectl
.delete_service_account
, "args": (metadata_name
,)}
132 kubectl
.create_cluster_role_binding(name
=metadata_name
, labels
=labels
)
133 self
.log
.debug("Role binding created")
136 "delete": kubectl
.delete_cluster_role_binding
,
137 "args": (metadata_name
,),
140 token
, client_cert_data
= await kubectl
.get_secret_data(metadata_name
)
142 default_storage_class
= kubectl
.get_default_storage_class()
143 self
.log
.debug("Default storage class: {}".format(default_storage_class
))
144 await libjuju
.add_k8s(
148 client_cert_data
=client_cert_data
,
149 configuration
=kubectl
.configuration
,
150 storage_class
=default_storage_class
,
151 credential_name
=self
._get
_credential
_name
(cluster_uuid
),
153 self
.log
.debug("K8s cluster added to juju controller")
154 return cluster_uuid
, True
155 except Exception as e
:
156 self
.log
.error("Error initializing k8scluster: {}".format(e
), exc_info
=True)
157 if len(cleanup_data
) > 0:
158 self
.log
.debug("Cleaning up created resources in k8s cluster...")
159 for item
in cleanup_data
:
160 delete_function
= item
["delete"]
161 delete_args
= item
["args"]
162 delete_function(*delete_args
)
163 self
.log
.debug("Cleanup finished")
166 """Repo Management"""
172 _type
: str = "charm",
175 password
: str = None,
177 raise MethodNotImplemented()
179 async def repo_list(self
):
180 raise MethodNotImplemented()
182 async def repo_remove(self
, name
: str):
183 raise MethodNotImplemented()
185 async def synchronize_repos(self
, cluster_uuid
: str, name
: str):
187 Returns None as currently add_repo is not implemented
197 uninstall_sw
: bool = False,
202 Resets the Kubernetes cluster by removing the model that represents it.
204 :param cluster_uuid str: The UUID of the cluster to reset
205 :param force: Force reset
206 :param uninstall_sw: Boolean to uninstall sw
207 :param: kwargs: Additional parameters
210 :return: Returns True if successful or raises an exception.
214 self
.log
.debug("[reset] Removing k8s cloud")
215 libjuju
= await self
._get
_libjuju
(kwargs
.get("vca_id"))
217 cloud
= Cloud(cluster_uuid
, self
._get
_credential
_name
(cluster_uuid
))
219 cloud_creds
= await libjuju
.get_cloud_credentials(cloud
)
221 await libjuju
.remove_cloud(cluster_uuid
)
223 credentials
= self
.get_credentials(cluster_uuid
=cluster_uuid
)
225 kubectl
= self
._get
_kubectl
(credentials
)
228 kubectl
.delete_cluster_role_binding
,
229 kubectl
.delete_service_account
,
230 kubectl
.delete_cluster_role
,
233 credential_attrs
= cloud_creds
[0].result
["attrs"]
234 if RBAC_LABEL_KEY_NAME
in credential_attrs
:
235 rbac_id
= credential_attrs
[RBAC_LABEL_KEY_NAME
]
236 metadata_name
= "{}-{}".format(RBAC_STACK_PREFIX
, rbac_id
)
237 for delete_func
in delete_functions
:
239 delete_func(metadata_name
)
240 except Exception as e
:
241 self
.log
.warning("Cannot remove resource in K8s {}".format(e
))
243 except Exception as e
:
244 self
.log
.debug("Caught exception during reset: {}".format(e
))
256 timeout
: float = 1800,
258 db_dict
: dict = None,
259 kdu_name
: str = None,
260 namespace
: str = None,
265 :param cluster_uuid str: The UUID of the cluster to install to
266 :param kdu_model str: The name or path of a bundle to install
267 :param kdu_instance: Kdu instance name
268 :param atomic bool: If set, waits until the model is active and resets
269 the cluster on failure.
270 :param timeout int: The time, in seconds, to wait for the install
272 :param params dict: Key-value pairs of instantiation parameters
273 :param kdu_name: Name of the KDU instance to be installed
274 :param namespace: K8s namespace to use for the KDU instance
275 :param kwargs: Additional parameters
278 :return: If successful, returns ?
280 libjuju
= await self
._get
_libjuju
(kwargs
.get("vca_id"))
284 raise K8sException("db_dict must be set")
286 raise K8sException("bundle must be set")
288 if bundle
.startswith("cs:"):
289 # For Juju Bundles provided by the Charm Store
291 elif bundle
.startswith("ch:"):
292 # For Juju Bundles provided by the Charm Hub (this only works for juju version >= 2.9)
294 elif bundle
.startswith("http"):
298 new_workdir
= kdu_model
.strip(kdu_model
.split("/")[-1])
299 os
.chdir(new_workdir
)
300 bundle
= "local:{}".format(kdu_model
)
302 # default namespace to kdu_instance
304 namespace
= kdu_instance
306 self
.log
.debug("Checking for model named {}".format(namespace
))
308 # Create the new model
309 self
.log
.debug("Adding model: {}".format(namespace
))
310 cloud
= Cloud(cluster_uuid
, self
._get
_credential
_name
(cluster_uuid
))
311 await libjuju
.add_model(namespace
, cloud
)
314 # TODO: Instantiation parameters
317 "Juju bundle that models the KDU, in any of the following ways:
318 - <juju-repo>/<juju-bundle>
319 - <juju-bundle folder under k8s_models folder in the package>
320 - <juju-bundle tgz file (w/ or w/o extension) under k8s_models folder
322 - <URL_where_to_fetch_juju_bundle>
325 previous_workdir
= os
.getcwd()
326 except FileNotFoundError
:
327 previous_workdir
= "/app/storage"
329 self
.log
.debug("[install] deploying {}".format(bundle
))
330 instantiation_params
= params
.get("overlay") if params
else None
331 await libjuju
.deploy(
333 model_name
=namespace
,
336 instantiation_params
=instantiation_params
,
338 os
.chdir(previous_workdir
)
340 # update information in the database (first, the VCA status, and then, the namespace)
341 if self
.on_update_db
:
342 await self
.on_update_db(
345 filter=db_dict
["filter"],
346 vca_id
=kwargs
.get("vca_id"),
351 q_filter
={"_admin.deployed.K8s.kdu-instance": kdu_instance
},
352 update_dict
={"_admin.deployed.K8s.$.namespace": namespace
},
362 total_timeout
: float = 1800,
363 namespace
: str = None,
366 """Scale an application in a model
368 :param: kdu_instance str: KDU instance name
369 :param: scale int: Scale to which to set the application
370 :param: resource_name str: The application name in the Juju Bundle
371 :param: timeout float: The time, in seconds, to wait for the install
373 :param namespace str: The namespace (model) where the Bundle was deployed
374 :param kwargs: Additional parameters
377 :return: If successful, returns True
380 model_name
= self
._obtain
_namespace
(
381 kdu_instance
=kdu_instance
, namespace
=namespace
384 libjuju
= await self
._get
_libjuju
(kwargs
.get("vca_id"))
385 await libjuju
.scale_application(
386 model_name
=model_name
,
387 application_name
=resource_name
,
389 total_timeout
=total_timeout
,
391 except Exception as e
:
392 error_msg
= "Error scaling application {} of the model {} of the kdu instance {}: {}".format(
393 resource_name
, model_name
, kdu_instance
, e
395 self
.log
.error(error_msg
)
396 raise K8sException(message
=error_msg
)
399 async def get_scale_count(
400 self
, resource_name
: str, kdu_instance
: str, namespace
: str = None, **kwargs
402 """Get an application scale count
404 :param: resource_name str: The application name in the Juju Bundle
405 :param: kdu_instance str: KDU instance name
406 :param namespace str: The namespace (model) where the Bundle was deployed
407 :param kwargs: Additional parameters
409 :return: Return application instance count
412 model_name
= self
._obtain
_namespace
(
413 kdu_instance
=kdu_instance
, namespace
=namespace
416 libjuju
= await self
._get
_libjuju
(kwargs
.get("vca_id"))
417 status
= await libjuju
.get_model_status(model_name
=model_name
)
418 return len(status
.applications
[resource_name
].units
)
419 except Exception as e
:
421 f
"Error getting scale count from application {resource_name} of the model {model_name} of "
422 f
"the kdu instance {kdu_instance}: {e}"
424 self
.log
.error(error_msg
)
425 raise K8sException(message
=error_msg
)
427 async def instances_list(self
, cluster_uuid
: str) -> list:
429 returns a list of deployed releases in a cluster
431 :param cluster_uuid: the cluster
440 kdu_model
: str = None,
445 :param cluster_uuid str: The UUID of the cluster to upgrade
446 :param kdu_instance str: The unique name of the KDU instance
447 :param kdu_model str: The name or path of the bundle to upgrade to
448 :param params dict: Key-value pairs of instantiation parameters
450 :return: If successful, reference to the new revision number of the
454 # TODO: Loop through the bundle and upgrade each charm individually
457 The API doesn't have a concept of bundle upgrades, because there are
458 many possible changes: charm revision, disk, number of units, etc.
460 As such, we are only supporting a limited subset of upgrades. We'll
461 upgrade the charm revision but leave storage and scale untouched.
463 Scale changes should happen through OSM constructs, and changes to
464 storage would require a redeployment of the service, at least in this
467 raise MethodNotImplemented()
472 self
, cluster_uuid
: str, kdu_instance
: str, revision
: int = 0
476 :param cluster_uuid str: The UUID of the cluster to rollback
477 :param kdu_instance str: The unique name of the KDU instance
478 :param revision int: The revision to revert to. If omitted, rolls back
479 the previous upgrade.
481 :return: If successful, returns the revision of active KDU instance,
482 or raises an exception
484 raise MethodNotImplemented()
489 self
, cluster_uuid
: str, kdu_instance
: str, namespace
: str = None, **kwargs
491 """Uninstall a KDU instance
493 :param cluster_uuid str: The UUID of the cluster
494 :param kdu_instance str: The unique name of the KDU instance
495 :param namespace str: The namespace (model) where the Bundle was deployed
496 :param kwargs: Additional parameters
499 :return: Returns True if successful, or raises an exception
501 model_name
= self
._obtain
_namespace
(
502 kdu_instance
=kdu_instance
, namespace
=namespace
505 self
.log
.debug(f
"[uninstall] Destroying model: {model_name}")
507 will_not_delete
= False
508 if model_name
not in self
.uninstall_locks
:
509 self
.uninstall_locks
[model_name
] = asyncio
.Lock()
510 delete_lock
= self
.uninstall_locks
[model_name
]
512 while delete_lock
.locked():
513 will_not_delete
= True
514 await asyncio
.sleep(0.1)
517 self
.log
.info("Model {} deleted by another worker.".format(model_name
))
521 async with delete_lock
:
522 libjuju
= await self
._get
_libjuju
(kwargs
.get("vca_id"))
524 await libjuju
.destroy_model(model_name
, total_timeout
=3600)
526 self
.uninstall_locks
.pop(model_name
)
528 self
.log
.debug(f
"[uninstall] Model {model_name} destroyed")
531 async def upgrade_charm(
535 charm_id
: str = None,
536 charm_type
: str = None,
537 timeout
: float = None,
539 """This method upgrade charms in VNFs
542 ee_id: Execution environment id
543 path: Local path to the charm
545 charm_type: Charm type can be lxc-proxy-charm, native-charm or k8s-proxy-charm
546 timeout: (Float) Timeout for the ns update operation
549 The output of the update operation if status equals to "completed"
552 "KDUs deployed with Juju Bundle do not support charm upgrade"
555 async def exec_primitive(
557 cluster_uuid
: str = None,
558 kdu_instance
: str = None,
559 primitive_name
: str = None,
560 timeout
: float = 300,
562 db_dict
: dict = None,
563 namespace
: str = None,
566 """Exec primitive (Juju action)
568 :param cluster_uuid str: The UUID of the cluster
569 :param kdu_instance str: The unique name of the KDU instance
570 :param primitive_name: Name of action that will be executed
571 :param timeout: Timeout for action execution
572 :param params: Dictionary of all the parameters needed for the action
573 :param db_dict: Dictionary for any additional data
574 :param namespace str: The namespace (model) where the Bundle was deployed
575 :param kwargs: Additional parameters
578 :return: Returns the output of the action
580 libjuju
= await self
._get
_libjuju
(kwargs
.get("vca_id"))
582 namespace
= self
._obtain
_namespace
(
583 kdu_instance
=kdu_instance
, namespace
=namespace
586 if not params
or "application-name" not in params
:
588 "Missing application-name argument, \
589 argument needed for K8s actions"
593 "[exec_primitive] Getting model "
594 "{} for the kdu_instance: {}".format(namespace
, kdu_instance
)
596 application_name
= params
["application-name"]
597 actions
= await libjuju
.get_actions(
598 application_name
=application_name
, model_name
=namespace
600 if primitive_name
not in actions
:
601 raise K8sException("Primitive {} not found".format(primitive_name
))
602 output
, status
= await libjuju
.execute_action(
603 application_name
=application_name
,
604 model_name
=namespace
,
605 action_name
=primitive_name
,
609 if status
!= "completed":
611 "status is not completed: {} output: {}".format(status
, output
)
613 if self
.on_update_db
:
614 await self
.on_update_db(
615 cluster_uuid
=cluster_uuid
,
616 kdu_instance
=kdu_instance
,
617 filter=db_dict
["filter"],
622 except Exception as e
:
623 error_msg
= "Error executing primitive {}: {}".format(primitive_name
, e
)
624 self
.log
.error(error_msg
)
625 raise K8sException(message
=error_msg
)
629 async def inspect_kdu(self
, kdu_model
: str) -> dict:
632 Inspects a bundle and returns a dictionary of config parameters and
633 their default values.
635 :param kdu_model str: The name or path of the bundle to inspect.
637 :return: If successful, returns a dictionary of available parameters
638 and their default values.
642 if not os
.path
.exists(kdu_model
):
643 raise K8sException("file {} not found".format(kdu_model
))
645 with
open(kdu_model
, "r") as f
:
646 bundle
= yaml
.safe_load(f
.read())
650 'description': 'Test bundle',
651 'bundle': 'kubernetes',
654 'charm': 'cs:~charmed-osm/mariadb-k8s-20',
657 'password': 'manopw',
658 'root_password': 'osm4u',
661 'series': 'kubernetes'
666 # TODO: This should be returned in an agreed-upon format
667 kdu
= bundle
["applications"]
671 async def help_kdu(self
, kdu_model
: str) -> str:
674 If available, returns the README of the bundle.
676 :param kdu_model str: The name or path of a bundle
678 :return: If found, returns the contents of the README.
682 files
= ["README", "README.txt", "README.md"]
683 path
= os
.path
.dirname(kdu_model
)
684 for file in os
.listdir(path
):
686 with
open(file, "r") as f
:
692 async def status_kdu(
696 complete_status
: bool = False,
697 yaml_format
: bool = False,
698 namespace
: str = None,
700 ) -> Union
[str, dict]:
701 """Get the status of the KDU
703 Get the current status of the KDU instance.
705 :param cluster_uuid str: The UUID of the cluster
706 :param kdu_instance str: The unique id of the KDU instance
707 :param complete_status: To get the complete_status of the KDU
708 :param yaml_format: To get the status in proper format for NSR record
709 :param namespace str: The namespace (model) where the Bundle was deployed
710 :param: kwargs: Additional parameters
713 :return: Returns a dictionary containing namespace, state, resources,
714 and deployment_time and returns complete_status if complete_status is True
716 libjuju
= await self
._get
_libjuju
(kwargs
.get("vca_id"))
719 model_name
= self
._obtain
_namespace
(
720 kdu_instance
=kdu_instance
, namespace
=namespace
722 model_status
= await libjuju
.get_model_status(model_name
=model_name
)
724 if not complete_status
:
725 for name
in model_status
.applications
:
726 application
= model_status
.applications
[name
]
727 status
[name
] = {"status": application
["status"]["status"]}
730 return obj_to_yaml(model_status
)
732 return obj_to_dict(model_status
)
736 async def add_relation(
737 self
, provider
: RelationEndpoint
, requirer
: RelationEndpoint
740 Add relation between two charmed endpoints
742 :param: provider: Provider relation endpoint
743 :param: requirer: Requirer relation endpoint
745 self
.log
.debug(f
"adding new relation between {provider} and {requirer}")
746 cross_model_relation
= (
747 provider
.model_name
!= requirer
.model_name
748 or provider
.vca_id
!= requirer
.vca_id
751 if cross_model_relation
:
752 # Cross-model relation
753 provider_libjuju
= await self
._get
_libjuju
(provider
.vca_id
)
754 requirer_libjuju
= await self
._get
_libjuju
(requirer
.vca_id
)
755 offer
= await provider_libjuju
.offer(provider
)
757 saas_name
= await requirer_libjuju
.consume(
758 requirer
.model_name
, offer
, provider_libjuju
760 await requirer_libjuju
.add_relation(
761 requirer
.model_name
, requirer
.endpoint
, saas_name
765 vca_id
= provider
.vca_id
766 model
= provider
.model_name
767 libjuju
= await self
._get
_libjuju
(vca_id
)
768 # add juju relations between two applications
769 await libjuju
.add_relation(
771 endpoint_1
=provider
.endpoint
,
772 endpoint_2
=requirer
.endpoint
,
774 except Exception as e
:
775 message
= f
"Error adding relation between {provider} and {requirer}: {e}"
776 self
.log
.error(message
)
777 raise Exception(message
=message
)
779 async def update_vca_status(
780 self
, vcastatus
: dict, kdu_instance
: str, namespace
: str = None, **kwargs
783 Add all configs, actions, executed actions of all applications in a model to vcastatus dict
785 :param vcastatus dict: dict containing vcastatus
786 :param kdu_instance str: The unique id of the KDU instance
787 :param namespace str: The namespace (model) where the Bundle was deployed
788 :param: kwargs: Additional parameters
794 model_name
= self
._obtain
_namespace
(
795 kdu_instance
=kdu_instance
, namespace
=namespace
798 libjuju
= await self
._get
_libjuju
(kwargs
.get("vca_id"))
800 for vca_model_name
in vcastatus
:
801 # Adding executed actions
802 vcastatus
[vca_model_name
][
804 ] = await libjuju
.get_executed_actions(model_name
=model_name
)
806 for application
in vcastatus
[vca_model_name
]["applications"]:
807 # Adding application actions
808 vcastatus
[vca_model_name
]["applications"][application
][
811 # Adding application configs
812 vcastatus
[vca_model_name
]["applications"][application
][
814 ] = await libjuju
.get_application_configs(
815 model_name
=model_name
, application_name
=application
818 except Exception as e
:
819 self
.log
.debug("Error in updating vca status: {}".format(str(e
)))
821 async def get_services(
822 self
, cluster_uuid
: str, kdu_instance
: str, namespace
: str
824 """Return a list of services of a kdu_instance"""
826 namespace
= self
._obtain
_namespace
(
827 kdu_instance
=kdu_instance
, namespace
=namespace
830 credentials
= self
.get_credentials(cluster_uuid
=cluster_uuid
)
831 kubectl
= self
._get
_kubectl
(credentials
)
832 return kubectl
.get_services(
833 field_selector
="metadata.namespace={}".format(namespace
)
836 async def get_service(
837 self
, cluster_uuid
: str, service_name
: str, namespace
: str
839 """Return data for a specific service inside a namespace"""
841 credentials
= self
.get_credentials(cluster_uuid
=cluster_uuid
)
842 kubectl
= self
._get
_kubectl
(credentials
)
843 return kubectl
.get_services(
844 field_selector
="metadata.name={},metadata.namespace={}".format(
845 service_name
, namespace
849 def get_credentials(self
, cluster_uuid
: str) -> str:
851 Get Cluster Kubeconfig
853 k8scluster
= self
.db
.get_one(
854 "k8sclusters", q_filter
={"_id": cluster_uuid
}, fail_on_empty
=False
857 self
.db
.encrypt_decrypt_fields(
858 k8scluster
.get("credentials"),
860 ["password", "secret"],
861 schema_version
=k8scluster
["schema_version"],
862 salt
=k8scluster
["_id"],
865 return yaml
.safe_dump(k8scluster
.get("credentials"))
867 def _get_credential_name(self
, cluster_uuid
: str) -> str:
869 Get credential name for a k8s cloud
871 We cannot use the cluster_uuid for the credential name directly,
872 because it cannot start with a number, it must start with a letter.
873 Therefore, the k8s cloud credential name will be "cred-" followed
876 :param: cluster_uuid: Cluster UUID of the kubernetes cloud (=cloud_name)
878 :return: Name to use for the credential name.
880 return "cred-{}".format(cluster_uuid
)
882 def get_namespace(self
, cluster_uuid
: str) -> str:
883 """Get the namespace UUID
884 Gets the namespace's unique name
886 :param cluster_uuid str: The UUID of the cluster
887 :returns: The namespace UUID, or raises an exception
892 def generate_kdu_instance_name(**kwargs
):
893 db_dict
= kwargs
.get("db_dict")
894 kdu_name
= kwargs
.get("kdu_name", None)
896 kdu_instance
= "{}-{}".format(kdu_name
, db_dict
["filter"]["_id"])
898 kdu_instance
= db_dict
["filter"]["_id"]
901 async def _get_libjuju(self
, vca_id
: str = None) -> Libjuju
:
905 :param: vca_id: VCA ID
906 If None, get a libjuju object with a Connection to the default VCA
907 Else, geta libjuju object with a Connection to the specified VCA
910 while self
.loading_libjuju
.locked():
911 await asyncio
.sleep(0.1)
913 async with self
.loading_libjuju
:
914 vca_connection
= await get_connection(self
._store
)
915 self
.libjuju
= Libjuju(vca_connection
, log
=self
.log
)
918 vca_connection
= await get_connection(self
._store
, vca_id
)
919 return Libjuju(vca_connection
, log
=self
.log
, n2vc
=self
)
921 def _get_kubectl(self
, credentials
: str) -> Kubectl
:
925 :param: kubeconfig_credentials: Kubeconfig credentials
927 kubecfg
= tempfile
.NamedTemporaryFile()
928 with
open(kubecfg
.name
, "w") as kubecfg_file
:
929 kubecfg_file
.write(credentials
)
930 return Kubectl(config_file
=kubecfg
.name
)
932 def _obtain_namespace(self
, kdu_instance
: str, namespace
: str = None) -> str:
934 Obtain the namespace/model name to use in the instantiation of a Juju Bundle in K8s. The default namespace is
935 the kdu_instance name. However, if the user passes the namespace where he wants to deploy the bundle,
936 that namespace will be used.
938 :param kdu_instance: the default KDU instance name
939 :param namespace: the namespace passed by the User
942 # deault the namespace/model name to the kdu_instance name TODO -> this should be the real return... But
943 # once the namespace is not passed in most methods, I had to do this in another way. But I think this should
944 # be the procedure in the future return namespace if namespace else kdu_instance
946 # TODO -> has referred above, this should be avoided in the future, this is temporary, in order to avoid
947 # compatibility issues
951 else self
._obtain
_namespace
_from
_db
(kdu_instance
=kdu_instance
)
954 def _obtain_namespace_from_db(self
, kdu_instance
: str) -> str:
955 db_nsrs
= self
.db
.get_one(
956 table
="nsrs", q_filter
={"_admin.deployed.K8s.kdu-instance": kdu_instance
}
958 for k8s
in db_nsrs
["_admin"]["deployed"]["K8s"]:
959 if k8s
.get("kdu-instance") == kdu_instance
:
960 return k8s
.get("namespace")