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 await libjuju
.deploy(bundle
, model_name
=namespace
, wait
=atomic
, timeout
=timeout
)
334 os
.chdir(previous_workdir
)
336 # update information in the database (first, the VCA status, and then, the namespace)
337 if self
.on_update_db
:
338 await self
.on_update_db(
341 filter=db_dict
["filter"],
342 vca_id
=kwargs
.get("vca_id"),
347 q_filter
={"_admin.deployed.K8s.kdu-instance": kdu_instance
},
348 update_dict
={"_admin.deployed.K8s.$.namespace": namespace
},
358 total_timeout
: float = 1800,
359 namespace
: str = None,
362 """Scale an application in a model
364 :param: kdu_instance str: KDU instance name
365 :param: scale int: Scale to which to set the application
366 :param: resource_name str: The application name in the Juju Bundle
367 :param: timeout float: The time, in seconds, to wait for the install
369 :param namespace str: The namespace (model) where the Bundle was deployed
370 :param kwargs: Additional parameters
373 :return: If successful, returns True
376 model_name
= self
._obtain
_namespace
(
377 kdu_instance
=kdu_instance
, namespace
=namespace
380 libjuju
= await self
._get
_libjuju
(kwargs
.get("vca_id"))
381 await libjuju
.scale_application(
382 model_name
=model_name
,
383 application_name
=resource_name
,
385 total_timeout
=total_timeout
,
387 except Exception as e
:
388 error_msg
= "Error scaling application {} of the model {} of the kdu instance {}: {}".format(
389 resource_name
, model_name
, kdu_instance
, e
391 self
.log
.error(error_msg
)
392 raise K8sException(message
=error_msg
)
395 async def get_scale_count(
396 self
, resource_name
: str, kdu_instance
: str, namespace
: str = None, **kwargs
398 """Get an application scale count
400 :param: resource_name str: The application name in the Juju Bundle
401 :param: kdu_instance str: KDU instance name
402 :param namespace str: The namespace (model) where the Bundle was deployed
403 :param kwargs: Additional parameters
405 :return: Return application instance count
408 model_name
= self
._obtain
_namespace
(
409 kdu_instance
=kdu_instance
, namespace
=namespace
412 libjuju
= await self
._get
_libjuju
(kwargs
.get("vca_id"))
413 status
= await libjuju
.get_model_status(model_name
=model_name
)
414 return len(status
.applications
[resource_name
].units
)
415 except Exception as e
:
417 f
"Error getting scale count from application {resource_name} of the model {model_name} of "
418 f
"the kdu instance {kdu_instance}: {e}"
420 self
.log
.error(error_msg
)
421 raise K8sException(message
=error_msg
)
423 async def instances_list(self
, cluster_uuid
: str) -> list:
425 returns a list of deployed releases in a cluster
427 :param cluster_uuid: the cluster
436 kdu_model
: str = None,
441 :param cluster_uuid str: The UUID of the cluster to upgrade
442 :param kdu_instance str: The unique name of the KDU instance
443 :param kdu_model str: The name or path of the bundle to upgrade to
444 :param params dict: Key-value pairs of instantiation parameters
446 :return: If successful, reference to the new revision number of the
450 # TODO: Loop through the bundle and upgrade each charm individually
453 The API doesn't have a concept of bundle upgrades, because there are
454 many possible changes: charm revision, disk, number of units, etc.
456 As such, we are only supporting a limited subset of upgrades. We'll
457 upgrade the charm revision but leave storage and scale untouched.
459 Scale changes should happen through OSM constructs, and changes to
460 storage would require a redeployment of the service, at least in this
463 raise MethodNotImplemented()
468 self
, cluster_uuid
: str, kdu_instance
: str, revision
: int = 0
472 :param cluster_uuid str: The UUID of the cluster to rollback
473 :param kdu_instance str: The unique name of the KDU instance
474 :param revision int: The revision to revert to. If omitted, rolls back
475 the previous upgrade.
477 :return: If successful, returns the revision of active KDU instance,
478 or raises an exception
480 raise MethodNotImplemented()
485 self
, cluster_uuid
: str, kdu_instance
: str, namespace
: str = None, **kwargs
487 """Uninstall a KDU instance
489 :param cluster_uuid str: The UUID of the cluster
490 :param kdu_instance str: The unique name of the KDU instance
491 :param namespace str: The namespace (model) where the Bundle was deployed
492 :param kwargs: Additional parameters
495 :return: Returns True if successful, or raises an exception
497 model_name
= self
._obtain
_namespace
(
498 kdu_instance
=kdu_instance
, namespace
=namespace
501 self
.log
.debug(f
"[uninstall] Destroying model: {model_name}")
503 will_not_delete
= False
504 if model_name
not in self
.uninstall_locks
:
505 self
.uninstall_locks
[model_name
] = asyncio
.Lock(loop
=self
.loop
)
506 delete_lock
= self
.uninstall_locks
[model_name
]
508 while delete_lock
.locked():
509 will_not_delete
= True
510 await asyncio
.sleep(0.1)
513 self
.log
.info("Model {} deleted by another worker.".format(model_name
))
517 async with delete_lock
:
518 libjuju
= await self
._get
_libjuju
(kwargs
.get("vca_id"))
520 await libjuju
.destroy_model(model_name
, total_timeout
=3600)
522 self
.uninstall_locks
.pop(model_name
)
524 self
.log
.debug(f
"[uninstall] Model {model_name} destroyed")
527 async def upgrade_charm(
531 charm_id
: str = None,
532 charm_type
: str = None,
533 timeout
: float = None,
535 """This method upgrade charms in VNFs
538 ee_id: Execution environment id
539 path: Local path to the charm
541 charm_type: Charm type can be lxc-proxy-charm, native-charm or k8s-proxy-charm
542 timeout: (Float) Timeout for the ns update operation
545 The output of the update operation if status equals to "completed"
548 "KDUs deployed with Juju Bundle do not support charm upgrade"
551 async def exec_primitive(
553 cluster_uuid
: str = None,
554 kdu_instance
: str = None,
555 primitive_name
: str = None,
556 timeout
: float = 300,
558 db_dict
: dict = None,
559 namespace
: str = None,
562 """Exec primitive (Juju action)
564 :param cluster_uuid str: The UUID of the cluster
565 :param kdu_instance str: The unique name of the KDU instance
566 :param primitive_name: Name of action that will be executed
567 :param timeout: Timeout for action execution
568 :param params: Dictionary of all the parameters needed for the action
569 :param db_dict: Dictionary for any additional data
570 :param namespace str: The namespace (model) where the Bundle was deployed
571 :param kwargs: Additional parameters
574 :return: Returns the output of the action
576 libjuju
= await self
._get
_libjuju
(kwargs
.get("vca_id"))
578 namespace
= self
._obtain
_namespace
(
579 kdu_instance
=kdu_instance
, namespace
=namespace
582 if not params
or "application-name" not in params
:
584 "Missing application-name argument, \
585 argument needed for K8s actions"
589 "[exec_primitive] Getting model "
590 "{} for the kdu_instance: {}".format(namespace
, kdu_instance
)
592 application_name
= params
["application-name"]
593 actions
= await libjuju
.get_actions(
594 application_name
=application_name
, model_name
=namespace
596 if primitive_name
not in actions
:
597 raise K8sException("Primitive {} not found".format(primitive_name
))
598 output
, status
= await libjuju
.execute_action(
599 application_name
=application_name
,
600 model_name
=namespace
,
601 action_name
=primitive_name
,
605 if status
!= "completed":
607 "status is not completed: {} output: {}".format(status
, output
)
609 if self
.on_update_db
:
610 await self
.on_update_db(
611 cluster_uuid
=cluster_uuid
,
612 kdu_instance
=kdu_instance
,
613 filter=db_dict
["filter"],
618 except Exception as e
:
619 error_msg
= "Error executing primitive {}: {}".format(primitive_name
, e
)
620 self
.log
.error(error_msg
)
621 raise K8sException(message
=error_msg
)
625 async def inspect_kdu(self
, kdu_model
: str) -> dict:
628 Inspects a bundle and returns a dictionary of config parameters and
629 their default values.
631 :param kdu_model str: The name or path of the bundle to inspect.
633 :return: If successful, returns a dictionary of available parameters
634 and their default values.
638 if not os
.path
.exists(kdu_model
):
639 raise K8sException("file {} not found".format(kdu_model
))
641 with
open(kdu_model
, "r") as f
:
642 bundle
= yaml
.safe_load(f
.read())
646 'description': 'Test bundle',
647 'bundle': 'kubernetes',
650 'charm': 'cs:~charmed-osm/mariadb-k8s-20',
653 'password': 'manopw',
654 'root_password': 'osm4u',
657 'series': 'kubernetes'
662 # TODO: This should be returned in an agreed-upon format
663 kdu
= bundle
["applications"]
667 async def help_kdu(self
, kdu_model
: str) -> str:
670 If available, returns the README of the bundle.
672 :param kdu_model str: The name or path of a bundle
674 :return: If found, returns the contents of the README.
678 files
= ["README", "README.txt", "README.md"]
679 path
= os
.path
.dirname(kdu_model
)
680 for file in os
.listdir(path
):
682 with
open(file, "r") as f
:
688 async def status_kdu(
692 complete_status
: bool = False,
693 yaml_format
: bool = False,
694 namespace
: str = None,
696 ) -> Union
[str, dict]:
697 """Get the status of the KDU
699 Get the current status of the KDU instance.
701 :param cluster_uuid str: The UUID of the cluster
702 :param kdu_instance str: The unique id of the KDU instance
703 :param complete_status: To get the complete_status of the KDU
704 :param yaml_format: To get the status in proper format for NSR record
705 :param namespace str: The namespace (model) where the Bundle was deployed
706 :param: kwargs: Additional parameters
709 :return: Returns a dictionary containing namespace, state, resources,
710 and deployment_time and returns complete_status if complete_status is True
712 libjuju
= await self
._get
_libjuju
(kwargs
.get("vca_id"))
715 model_name
= self
._obtain
_namespace
(
716 kdu_instance
=kdu_instance
, namespace
=namespace
718 model_status
= await libjuju
.get_model_status(model_name
=model_name
)
720 if not complete_status
:
721 for name
in model_status
.applications
:
722 application
= model_status
.applications
[name
]
723 status
[name
] = {"status": application
["status"]["status"]}
726 return obj_to_yaml(model_status
)
728 return obj_to_dict(model_status
)
732 async def add_relation(
733 self
, provider
: RelationEndpoint
, requirer
: RelationEndpoint
736 Add relation between two charmed endpoints
738 :param: provider: Provider relation endpoint
739 :param: requirer: Requirer relation endpoint
741 self
.log
.debug(f
"adding new relation between {provider} and {requirer}")
742 cross_model_relation
= (
743 provider
.model_name
!= requirer
.model_name
744 or provider
.vca_id
!= requirer
.vca_id
747 if cross_model_relation
:
748 # Cross-model relation
749 provider_libjuju
= await self
._get
_libjuju
(provider
.vca_id
)
750 requirer_libjuju
= await self
._get
_libjuju
(requirer
.vca_id
)
751 offer
= await provider_libjuju
.offer(provider
)
753 saas_name
= await requirer_libjuju
.consume(
754 requirer
.model_name
, offer
, provider_libjuju
756 await requirer_libjuju
.add_relation(
757 requirer
.model_name
, requirer
.endpoint
, saas_name
761 vca_id
= provider
.vca_id
762 model
= provider
.model_name
763 libjuju
= await self
._get
_libjuju
(vca_id
)
764 # add juju relations between two applications
765 await libjuju
.add_relation(
767 endpoint_1
=provider
.endpoint
,
768 endpoint_2
=requirer
.endpoint
,
770 except Exception as e
:
771 message
= f
"Error adding relation between {provider} and {requirer}: {e}"
772 self
.log
.error(message
)
773 raise Exception(message
=message
)
775 async def update_vca_status(
776 self
, vcastatus
: dict, kdu_instance
: str, namespace
: str = None, **kwargs
779 Add all configs, actions, executed actions of all applications in a model to vcastatus dict
781 :param vcastatus dict: dict containing vcastatus
782 :param kdu_instance str: The unique id of the KDU instance
783 :param namespace str: The namespace (model) where the Bundle was deployed
784 :param: kwargs: Additional parameters
790 model_name
= self
._obtain
_namespace
(
791 kdu_instance
=kdu_instance
, namespace
=namespace
794 libjuju
= await self
._get
_libjuju
(kwargs
.get("vca_id"))
796 for vca_model_name
in vcastatus
:
797 # Adding executed actions
798 vcastatus
[vca_model_name
][
800 ] = await libjuju
.get_executed_actions(model_name
=model_name
)
802 for application
in vcastatus
[vca_model_name
]["applications"]:
803 # Adding application actions
804 vcastatus
[vca_model_name
]["applications"][application
][
807 # Adding application configs
808 vcastatus
[vca_model_name
]["applications"][application
][
810 ] = await libjuju
.get_application_configs(
811 model_name
=model_name
, application_name
=application
814 except Exception as e
:
815 self
.log
.debug("Error in updating vca status: {}".format(str(e
)))
817 async def get_services(
818 self
, cluster_uuid
: str, kdu_instance
: str, namespace
: str
820 """Return a list of services of a kdu_instance"""
822 namespace
= self
._obtain
_namespace
(
823 kdu_instance
=kdu_instance
, namespace
=namespace
826 credentials
= self
.get_credentials(cluster_uuid
=cluster_uuid
)
827 kubectl
= self
._get
_kubectl
(credentials
)
828 return kubectl
.get_services(
829 field_selector
="metadata.namespace={}".format(namespace
)
832 async def get_service(
833 self
, cluster_uuid
: str, service_name
: str, namespace
: str
835 """Return data for a specific service inside a namespace"""
837 credentials
= self
.get_credentials(cluster_uuid
=cluster_uuid
)
838 kubectl
= self
._get
_kubectl
(credentials
)
839 return kubectl
.get_services(
840 field_selector
="metadata.name={},metadata.namespace={}".format(
841 service_name
, namespace
845 def get_credentials(self
, cluster_uuid
: str) -> str:
847 Get Cluster Kubeconfig
849 k8scluster
= self
.db
.get_one(
850 "k8sclusters", q_filter
={"_id": cluster_uuid
}, fail_on_empty
=False
853 self
.db
.encrypt_decrypt_fields(
854 k8scluster
.get("credentials"),
856 ["password", "secret"],
857 schema_version
=k8scluster
["schema_version"],
858 salt
=k8scluster
["_id"],
861 return yaml
.safe_dump(k8scluster
.get("credentials"))
863 def _get_credential_name(self
, cluster_uuid
: str) -> str:
865 Get credential name for a k8s cloud
867 We cannot use the cluster_uuid for the credential name directly,
868 because it cannot start with a number, it must start with a letter.
869 Therefore, the k8s cloud credential name will be "cred-" followed
872 :param: cluster_uuid: Cluster UUID of the kubernetes cloud (=cloud_name)
874 :return: Name to use for the credential name.
876 return "cred-{}".format(cluster_uuid
)
878 def get_namespace(self
, cluster_uuid
: str) -> str:
879 """Get the namespace UUID
880 Gets the namespace's unique name
882 :param cluster_uuid str: The UUID of the cluster
883 :returns: The namespace UUID, or raises an exception
888 def generate_kdu_instance_name(**kwargs
):
889 db_dict
= kwargs
.get("db_dict")
890 kdu_name
= kwargs
.get("kdu_name", None)
892 kdu_instance
= "{}-{}".format(kdu_name
, db_dict
["filter"]["_id"])
894 kdu_instance
= db_dict
["filter"]["_id"]
897 async def _get_libjuju(self
, vca_id
: str = None) -> Libjuju
:
901 :param: vca_id: VCA ID
902 If None, get a libjuju object with a Connection to the default VCA
903 Else, geta libjuju object with a Connection to the specified VCA
906 while self
.loading_libjuju
.locked():
907 await asyncio
.sleep(0.1)
909 async with self
.loading_libjuju
:
910 vca_connection
= await get_connection(self
._store
)
911 self
.libjuju
= Libjuju(vca_connection
, loop
=self
.loop
, log
=self
.log
)
914 vca_connection
= await get_connection(self
._store
, vca_id
)
915 return Libjuju(vca_connection
, loop
=self
.loop
, log
=self
.log
, n2vc
=self
)
917 def _get_kubectl(self
, credentials
: str) -> Kubectl
:
921 :param: kubeconfig_credentials: Kubeconfig credentials
923 kubecfg
= tempfile
.NamedTemporaryFile()
924 with
open(kubecfg
.name
, "w") as kubecfg_file
:
925 kubecfg_file
.write(credentials
)
926 return Kubectl(config_file
=kubecfg
.name
)
928 def _obtain_namespace(self
, kdu_instance
: str, namespace
: str = None) -> str:
930 Obtain the namespace/model name to use in the instantiation of a Juju Bundle in K8s. The default namespace is
931 the kdu_instance name. However, if the user passes the namespace where he wants to deploy the bundle,
932 that namespace will be used.
934 :param kdu_instance: the default KDU instance name
935 :param namespace: the namespace passed by the User
938 # deault the namespace/model name to the kdu_instance name TODO -> this should be the real return... But
939 # once the namespace is not passed in most methods, I had to do this in another way. But I think this should
940 # be the procedure in the future return namespace if namespace else kdu_instance
942 # TODO -> has referred above, this should be avoided in the future, this is temporary, in order to avoid
943 # compatibility issues
947 else self
._obtain
_namespace
_from
_db
(kdu_instance
=kdu_instance
)
950 def _obtain_namespace_from_db(self
, kdu_instance
: str) -> str:
951 db_nsrs
= self
.db
.get_one(
952 table
="nsrs", q_filter
={"_admin.deployed.K8s.kdu-instance": kdu_instance
}
954 for k8s
in db_nsrs
["_admin"]["deployed"]["K8s"]:
955 if k8s
.get("kdu-instance") == kdu_instance
:
956 return k8s
.get("namespace")