From fc796cc98009d16e124dd47c0596c5541dc10f4d Mon Sep 17 00:00:00 2001 From: Dominik Fleischmann Date: Mon, 6 Apr 2020 14:51:00 +0200 Subject: [PATCH] K8s action support This commit enables k8s actions by executing them the same way as any other ns-action. It needs a mandatory parameter which is application-name. This commit depends on another commit in LCM. https://osm.etsi.org/gerrit/#/c/osm/LCM/+/8767/ Change-Id: I375fb93d55f1255d425f229fe1a88968b19ae3d0 Signed-off-by: Dominik Fleischmann --- n2vc/k8s_conn.py | 22 ++++++++++++++ n2vc/k8s_helm_conn.py | 23 ++++++++++++++ n2vc/k8s_juju_conn.py | 71 ++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 115 insertions(+), 1 deletion(-) diff --git a/n2vc/k8s_conn.py b/n2vc/k8s_conn.py index 3305ac5..b1f3230 100644 --- a/n2vc/k8s_conn.py +++ b/n2vc/k8s_conn.py @@ -253,6 +253,28 @@ class K8sConnector(abc.ABC, Loggable): :return: True if successful """ + @abc.abstractmethod + async def exec_primitive( + self, + cluster_uuid: str = None, + kdu_instance: str = None, + primitive_name: str = None, + timeout: float = 300, + params: dict = None, + db_dict: dict = None, + ) -> str: + """Exec primitive (Juju action) + + :param cluster_uuid str: The UUID of the cluster + :param kdu_instance str: The unique name of the KDU instance + :param primitive_name: Name of action that will be executed + :param timeout: Timeout for action execution + :param params: Dictionary of all the parameters needed for the action + :db_dict: Dictionary for any additional data + + :return: Returns the output of the action + """ + @abc.abstractmethod async def inspect_kdu( self, diff --git a/n2vc/k8s_helm_conn.py b/n2vc/k8s_helm_conn.py index b367e74..d3fbed6 100644 --- a/n2vc/k8s_helm_conn.py +++ b/n2vc/k8s_helm_conn.py @@ -656,6 +656,29 @@ class K8sHelmConnector(K8sConnector): return self._output_to_table(output) + async def exec_primitive( + self, + cluster_uuid: str = None, + kdu_instance: str = None, + primitive_name: str = None, + timeout: float = 300, + params: dict = None, + db_dict: dict = None, + ) -> str: + """Exec primitive (Juju action) + + :param cluster_uuid str: The UUID of the cluster + :param kdu_instance str: The unique name of the KDU instance + :param primitive_name: Name of action that will be executed + :param timeout: Timeout for action execution + :param params: Dictionary of all the parameters needed for the action + :db_dict: Dictionary for any additional data + + :return: Returns the output of the action + """ + raise K8sException("KDUs deployed with Helm don't support actions " + "different from rollback, upgrade and status") + async def inspect_kdu( self, kdu_model: str, diff --git a/n2vc/k8s_juju_conn.py b/n2vc/k8s_juju_conn.py index 550ad12..e01fa0b 100644 --- a/n2vc/k8s_juju_conn.py +++ b/n2vc/k8s_juju_conn.py @@ -22,6 +22,7 @@ import juju from juju.controller import Controller from juju.model import Model from juju.errors import JujuAPIError, JujuError +from n2vc.exceptions import K8sException from n2vc.k8s_conn import K8sConnector @@ -546,6 +547,75 @@ class K8sJujuConnector(K8sConnector): return True + async def exec_primitive( + self, + cluster_uuid: str = None, + kdu_instance: str = None, + primitive_name: str = None, + timeout: float = 300, + params: dict = None, + db_dict: dict = None, + ) -> str: + """Exec primitive (Juju action) + + :param cluster_uuid str: The UUID of the cluster + :param kdu_instance str: The unique name of the KDU instance + :param primitive_name: Name of action that will be executed + :param timeout: Timeout for action execution + :param params: Dictionary of all the parameters needed for the action + :db_dict: Dictionary for any additional data + + :return: Returns the output of the action + """ + if not self.authenticated: + self.log.debug("[exec_primitive] Connecting to controller") + await self.login(cluster_uuid) + + if not params or "application-name" not in params: + raise K8sException("Missing application-name argument, \ + argument needed for K8s actions") + try: + self.log.debug("[exec_primitive] Getting model " + "kdu_instance: {}".format(kdu_instance)) + + model = await self.get_model(kdu_instance, cluster_uuid) + + application_name = params["application-name"] + application = model.applications[application_name] + + actions = await application.get_actions() + if primitive_name not in actions: + raise K8sException("Primitive {} not found".format(primitive_name)) + + unit = None + for u in application.units: + if await u.is_leader_from_status(): + unit = u + break + + if unit is None: + raise K8sException("No leader unit found to execute action") + + self.log.debug("[exec_primitive] Running action: {}".format(primitive_name)) + action = await unit.run_action(primitive_name, **params) + + output = await model.get_action_output(action_uuid=action.entity_id) + status = await model.get_action_status(uuid_or_prefix=action.entity_id) + + status = ( + status[action.entity_id] if action.entity_id in status else "failed" + ) + + if status != "completed": + raise K8sException("status is not completed: {} output: {}".format(status, output)) + + return output + + except Exception as e: + error_msg = "Error executing primitive {}: {}".format(primitive_name, e) + self.log.error(error_msg) + raise K8sException(message=error_msg) + """Introspection""" async def inspect_kdu( self, @@ -842,7 +912,6 @@ class K8sJujuConnector(K8sConnector): model = None models = await self.controller.list_models() - self.log.debug(models) if model_name in models: self.log.debug("Found model: {}".format(model_name)) model = await self.controller.get_model( -- 2.17.1