From 7a0c8c9d1d1a8f985e7e4c0380a47d283874058e Mon Sep 17 00:00:00 2001 From: ksaikiranr Date: Tue, 23 Feb 2021 18:52:18 +0530 Subject: [PATCH] Feature-9904: Enhancing NG-UI to enable Juju operational view dashboard Added functions to get action list, config list and executed action list for KNF Change-Id: Ibec764c719da5507168c474cf48fc23efbb9c846 Signed-off-by: jayaramans Signed-off-by: ksaikiranr --- n2vc/k8s_juju_conn.py | 193 +++++++++++++++++++++++++++++++++++++++--- n2vc/utils.py | 31 +++++++ 2 files changed, 211 insertions(+), 13 deletions(-) diff --git a/n2vc/k8s_juju_conn.py b/n2vc/k8s_juju_conn.py index c8be80c..eda2c93 100644 --- a/n2vc/k8s_juju_conn.py +++ b/n2vc/k8s_juju_conn.py @@ -20,10 +20,11 @@ import yaml from juju.controller import Controller from juju.model import Model -from n2vc.exceptions import K8sException +from n2vc.exceptions import K8sException, JujuError from n2vc.k8s_conn import K8sConnector from n2vc.kubectl import Kubectl from .exceptions import MethodNotImplemented, N2VCNotFound +from n2vc.utils import obj_to_dict, obj_to_yaml # from juju.bundle import BundleHandler @@ -320,6 +321,11 @@ class K8sJujuConnector(K8sConnector): kdu_instance = db_dict["filter"]["_id"] self.log.debug("Checking for model named {}".format(kdu_instance)) + try: + if self.on_update_db: + await self.on_update_db(cluster_uuid, kdu_instance, filter=db_dict["filter"]) + except Exception as e: + self.log.debug("Error in updating vca status {}".format(str(e))) # Create the new model self.log.debug("Adding model: {}".format(kdu_instance)) @@ -398,7 +404,8 @@ class K8sJujuConnector(K8sConnector): await model.disconnect() await controller.disconnect() os.chdir(previous_workdir) - + if self.on_update_db: + await self.on_update_db(cluster_uuid, kdu_instance, filter=db_dict["filter"]) return kdu_instance raise Exception("Unable to install") @@ -606,6 +613,8 @@ class K8sJujuConnector(K8sConnector): raise K8sException( "status: {}, output: {}".format(status, output) ) + if self.on_update_db: + await self.on_update_db(cluster_uuid, kdu_instance, filter=db_dict["filter"]) return output @@ -682,32 +691,190 @@ class K8sJujuConnector(K8sConnector): return readme - async def status_kdu(self, cluster_uuid: str, kdu_instance: str,) -> dict: + async def status_kdu( + self, + cluster_uuid: str, + kdu_instance: str, + complete_status: bool = False, + yaml_format: bool = False + ) -> dict: """Get the status of the KDU Get the current status of the KDU instance. :param cluster_uuid str: The UUID of the cluster :param kdu_instance str: The unique id of the KDU instance + :param complete_status: To get the complete_status of the KDU + :param yaml_format: To get the status in proper format for NSR record :return: Returns a dictionary containing namespace, state, resources, - and deployment_time. + and deployment_time and returns complete_status if complete_status is True """ status = {} controller = await self.get_controller(cluster_uuid) - model = await self.get_model(kdu_instance, controller=controller) + try: + model = await self.get_model(kdu_instance, controller=controller) + model_status = await model.get_status() + status = model_status.applications - model_status = await model.get_status() - status = model_status.applications + if not complete_status: + for name in model_status.applications: + application = model_status.applications[name] + status[name] = {"status": application["status"]["status"]} + else: + if yaml_format: + return obj_to_yaml(model_status) + else: + return obj_to_dict(model_status) + except Exception as e: + self.log.debug("Error in getting model_status for kdu_instance: {}. Error: {}" + .format(kdu_instance, str(e))) + finally: + if model: + await model.disconnect() + await controller.disconnect() + return status - for name in model_status.applications: - application = model_status.applications[name] - status[name] = {"status": application["status"]["status"]} + async def get_application_actions( + self, + application_name: str, + model_name: str, + cluster_uuid: str, + kdu_instance: str + ) -> dict: + """ + Get available actions for an application - await model.disconnect() - await controller.disconnect() + :param application_name str: Application name + :model_name str: Model name + :param cluster_uuid str: The UUID of the cluster + :param kdu_instance str: The unique id of the KDU instance - return status + :return: Returns a dictionary which has action list of the Application + """ + model = None + application_actions = {} + controller = await self.get_controller(cluster_uuid) + try: + model = await self.get_model(kdu_instance, controller=controller) + application = model.applications[application_name] + application_actions = await application.get_actions() + except Exception as e: + raise JujuError("Error in getting actions for application: {} in model: {}. Error: {}" + .format(application_name, model_name, str(e))) + finally: + if model: + await model.disconnect() + await controller.disconnect() + + return application_actions + + async def get_application_configs( + self, + application_name: str, + model_name: str, + cluster_uuid: str, + kdu_instance: str + ) -> dict: + """ + Get available configs for an application. + + :param application_name str: Application name + :model_name str: Model name + :param cluster_uuid str: The UUID of the cluster + :param kdu_instance str: The unique id of the KDU instance + + :return: Returns a dictionary which has config list of the Application + """ + model = None + application_configs = {} + controller = await self.get_controller(cluster_uuid) + try: + model = await self.get_model(kdu_instance, controller=controller) + application = model.applications[application_name] + application_configs = await application.get_config() + except Exception as e: + raise JujuError("Error in getting configs for application: {} in model: {}. Error: {}" + .format(application_name, model_name, str(e))) + finally: + if model: + await model.disconnect() + await controller.disconnect() + return application_configs + + async def get_executed_actions( + self, + model_name: str, + cluster_uuid: str, + kdu_instance: str + ) -> list: + """ + Get executed/history of actions for a model. + :model_name str: Model name + :param cluster_uuid str: The UUID of the cluster + :param kdu_instance str: The unique id of the KDU instance + + :return: List of executed actions for a model. + """ + model = None + executed_actions = [] + controller = await self.get_controller(cluster_uuid) + try: + model = await self.get_model(kdu_instance, controller=controller) + # Get all unique action names + actions = {} + for application in model.applications: + application_actions = await self.get_application_actions(application, model, + cluster_uuid, kdu_instance) + actions.update(application_actions) + # Get status of all actions + for application_action in actions: + application_action_status_list = \ + await model.get_action_status(name=application_action) + for action_id, action_status in application_action_status_list.items(): + executed_action = {"id": action_id, + "action": application_action, + "status": action_status} + # Get action output by id + action_status = await model.get_action_output(executed_action["id"]) + for k, v in action_status.items(): + executed_action[k] = v + executed_actions.append(executed_action) + except Exception as e: + raise JujuError("Error in getting executed actions for model: {}. Error: {}" + .format(model_name, str(e))) + finally: + if model: + await model.disconnect() + await controller.disconnect() + return executed_actions + + async def update_vca_status(self, vcastatus: dict, cluster_uuid: str, kdu_instance: str): + """ + Add all configs, actions, executed actions of all applications in a model to vcastatus dict + + :param vcastatus dict: dict containing vcastatus + :param cluster_uuid str: The UUID of the cluster + :param kdu_instance str: The unique id of the KDU instance + :return: None + """ + try: + for model_name in vcastatus: + # Adding executed actions + vcastatus[model_name]["executedActions"] = \ + await self.get_executed_actions(model_name, cluster_uuid, kdu_instance) + + for application in vcastatus[model_name]["applications"]: + # Adding application actions + vcastatus[model_name]["applications"][application]["actions"] = \ + await self.get_application_actions(application, model_name, + cluster_uuid, kdu_instance) + # Adding application configs + vcastatus[model_name]["applications"][application]["configs"] = \ + await self.get_application_configs(application, model_name, + cluster_uuid, kdu_instance) + except Exception as e: + self.log.debug("Error in updating vca status: {}".format(str(e))) async def get_services( self, cluster_uuid: str, kdu_instance: str, namespace: str diff --git a/n2vc/utils.py b/n2vc/utils.py index e8cf64d..14ac5d1 100644 --- a/n2vc/utils.py +++ b/n2vc/utils.py @@ -17,6 +17,7 @@ from juju.machine import Machine from juju.application import Application from juju.action import Action from juju.unit import Unit +import yaml class N2VCDeploymentStatus(Enum): @@ -100,3 +101,33 @@ DB_DATA = Dict( ) } ) + + +def obj_to_yaml(obj: object) -> str: + """ + Converts object to yaml format + :return: yaml data + """ + # dump to yaml + dump_text = yaml.dump(obj, default_flow_style=False, indent=2) + # split lines + lines = dump_text.splitlines() + # remove !!python/object tags + yaml_text = "" + for line in lines: + index = line.find("!!python/object") + if index >= 0: + line = line[:index] + yaml_text += line + "\n" + return yaml_text + + +def obj_to_dict(obj: object) -> dict: + """ + Converts object to dictionary format + :return: dict data + """ + # convert obj to yaml + yaml_text = obj_to_yaml(obj) + # parse to dict + return yaml.load(yaml_text, Loader=yaml.Loader) -- 2.25.1