K8s action support 70/8870/1
authorDominik Fleischmann <dominik.fleischmann@canonical.com>
Mon, 6 Apr 2020 12:51:00 +0000 (14:51 +0200)
committerDavid Garcia <david.garcia@canonical.com>
Thu, 7 May 2020 09:03:32 +0000 (11:03 +0200)
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 <dominik.fleischmann@canonical.com>
n2vc/k8s_conn.py
n2vc/k8s_helm_conn.py
n2vc/k8s_juju_conn.py

index 3305ac5..b1f3230 100644 (file)
@@ -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,
index b367e74..d3fbed6 100644 (file)
@@ -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,
index 550ad12..e01fa0b 100644 (file)
@@ -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(