From b870c3a2bb97ce5cffe3bee3eee90b97beebdc0c Mon Sep 17 00:00:00 2001 From: Luis Eduardo Bonatti Date: Fri, 27 Oct 2023 13:22:43 -0300 Subject: [PATCH] create data-api to access current deploy state Change-Id: I25c542e9cac07c781790652ed01921181ad1916c Signed-off-by: Luis Eduardo Bonatti --- software/software/api/controllers/root.py | 84 +++++++++++ software/software/exceptions.py | 4 + software/software/software_controller.py | 175 ++++++++++++++++++++++ software/software/software_entities.py | 116 +++++++++++++- software/software/utils.py | 11 ++ 5 files changed, 386 insertions(+), 4 deletions(-) diff --git a/software/software/api/controllers/root.py b/software/software/api/controllers/root.py index 7fad376a..797d494a 100644 --- a/software/software/api/controllers/root.py +++ b/software/software/api/controllers/root.py @@ -108,6 +108,90 @@ class SoftwareAPIController(object): return result + @expose('json') + @expose('query.xml', content_type='application/xml') + def create_deploy_host(self, *args): + sc.software_create_deploy_host(list(args)[0]) + return dict(succes="Deploy host created.") + + @expose('json') + @expose('query.xml', content_type='application/xml') + def get_deploy_host(self, *args): + try: + deploy = sc.software_get_deploy_host(list(args)[0]) + except Exception as e: + LOG.exception("Failed to get deploy %s. Error: %s", list(args)[0], e) + return dict(error="Failed to get the deploy.") + except DeployDoNotExist: + return dict(error=f"Deploy host %s do not exist." % list(args)[0]) + return dict(data=deploy) + + @expose('json') + @expose('query.xml', content_type='application/xml') + def update_deploy_host(self, *args): + try: + deploy = sc.software_update_deploy_host(list(args)[0]) + except Exception as e: + LOG.exception("Failed to update deploy %s. Error: %s", list(args)[0], e) + return dict(error="Failed to get the deploy.") + except DeployDoNotExist: + return dict(error=f"Deploy host %s do not exist." % list(args)[0]) + return dict(succes=deploy) + + @expose('json') + @expose('query.xml', content_type='application/xml') + def delete_all_deploy_host(self, *args): + try: + deploy = sc.software_delete_all_deploy_host(list(args)[0]) + except Exception as e: + LOG.exception("Failed to delete deploy host %s. Error: %s", list(args)[0], e) + return dict(error="Failed to delete the deploy host.") + except DeployDoNotExist: + return dict(error=f"Deploy host %s do not exist." % list(args)[0]) + return dict(succes=deploy) + + @expose('json') + @expose('query.xml', content_type='application/xml') + def create_deploy(self, *args): + sc.software_create_deploy(list(args)[0]) + return dict(succes="Deploy created.") + + @expose('json') + @expose('query.xml', content_type='application/xml') + def get_deploy(self, *args): + try: + deploy = sc.software_get_deploy(list(args)[0]) + except Exception as e: + LOG.exception("Failed to get deploy %s. Error: %s", list(args)[0], e) + return dict(error="Failed to get the deploy.") + except DeployDoNotExist: + return dict(error=f"Deploy %s do not exist." % list(args)[0]) + return dict(data=deploy) + + @expose('json') + @expose('query.xml', content_type='application/xml') + def update_deploy(self, *args): + try: + deploy = sc.software_update_deploy(list(args)[0]) + except Exception as e: + LOG.exception("Failed to update deploy %s. Error: %s", list(args)[0], e) + return dict(error="Failed to get the deploy.") + except DeployDoNotExist: + return dict(error=f"Deploy %s do not exist." % list(args)[0]) + return dict(succes=deploy) + + @expose('json') + @expose('query.xml', content_type='application/xml') + def delete_deploy(self, *args): + try: + deploy = sc.software_delete_deploy(list(args)[0]) + except Exception as e: + LOG.exception("Failed to delete deploy %s. Error: %s", list(args)[0], e) + return dict(error="Failed to get the deploy.") + except DeployDoNotExist: + return dict(error=f"Deploy %s do not exist." % list(args)[0]) + return dict(succes=deploy) + @expose('json') @expose('query.xml', content_type='application/xml') def deploy_start(self, *args, **kwargs): diff --git a/software/software/exceptions.py b/software/software/exceptions.py index 5dee1006..0da3b916 100644 --- a/software/software/exceptions.py +++ b/software/software/exceptions.py @@ -95,3 +95,7 @@ class SysinvClientNotInitialized(SoftwareError): class StateValidationFailure(SoftwareError): """State Validation Failure""" pass + +class DeployDoNotExist(SoftwareError): + """Deploy Do Not Exist""" + pass \ No newline at end of file diff --git a/software/software/software_controller.py b/software/software/software_controller.py index a3a11462..8fc3e7b4 100644 --- a/software/software/software_controller.py +++ b/software/software/software_controller.py @@ -27,6 +27,8 @@ from wsgiref import simple_server from oslo_config import cfg as oslo_cfg import software.ostree_utils as ostree_utils +from software.software_entities import DeployHandler +from software.software_entities import DeployHostHandler from software.api import app from software.authapi import app as auth_app from software.base import PatchService @@ -1787,6 +1789,179 @@ class PatchController(PatchService): msg = "Failed to delete the restart script for %s" % patch_id LOG.exception(msg) + def software_create_deploy_host(self, deployment: str) -> dict: + """ + Verify if system is capable to upgrade to a specified deployment + return: dict of info, warning and error messages + """ + msg_info = "" + msg_warning = "" + msg_error = "" + + # We need to verify that the software release exists + release = self.release_data.metadata.get(deployment, None) + if not release: + msg = "Software release version corresponding to the specified deployment " \ + "%s does not exist." % deployment + LOG.error(msg) + msg_error += msg + "\n" + return dict(info=msg_info, warning=msg_warning, error=msg_error) + + # Check if software release directory location exists + release_version = utils.get_major_release_version(release["sw_version"]) + + return DeployHostHandler().create(from_release=SW_VERSION, to_release=release_version) + + def software_get_deploy_host(self, hostname) -> dict: + """ + Verify if system is capable to upgrade to a specified deployment + return: dict of info, warning and error messages + """ + return DeployHostHandler().get(hostname=hostname) + + def software_update_deploy_host(self, deployment: str, hostname: str, state: str) -> dict: + """ + Verify if system is capable to upgrade to a specified deployment + return: dict of info, warning and error messages + """ + msg_info = "" + msg_warning = "" + msg_error = "" + + # We need to verify that the software release exists + release = self.release_data.metadata.get(deployment, None) + if not release: + msg = "Software release version corresponding to the specified deployment " \ + "%s does not exist." % deployment + LOG.error(msg) + msg_error += msg + "\n" + return dict(info=msg_info, warning=msg_warning, error=msg_error) + + # Check if software release directory location exists + release_version = utils.get_major_release_version(release["sw_version"]) + + return DeployHostHandler().update(hostname=hostname, from_release=SW_VERSION, + to_release=release_version, state=state) + + def software_delete_all_deploy_host(self, deployment: str) -> dict: + """ + Verify if system is capable to upgrade to a specified deployment + return: dict of info, warning and error messages + """ + msg_info = "" + msg_warning = "" + msg_error = "" + + # We need to verify that the software release exists + release = self.release_data.metadata.get(deployment, None) + if not release: + msg = "Software release version corresponding to the specified deployment " \ + "%s does not exist." % deployment + LOG.error(msg) + msg_error += msg + "\n" + return dict(info=msg_info, warning=msg_warning, error=msg_error) + + # Check if software release directory location exists + release_version = utils.get_major_release_version(release["sw_version"]) + + return DeployHostHandler().delete_all(from_release=SW_VERSION, to_release=release_version) + + def software_create_deploy(self, deployment: str) -> dict: + """ + Verify if system is capable to upgrade to a specified deployment + return: dict of info, warning and error messages + """ + msg_info = "" + msg_warning = "" + msg_error = "" + + # We need to verify that the software release exists + release = self.release_data.metadata.get(deployment, None) + if not release: + msg = "Software release version corresponding to the specified deployment " \ + "%s does not exist." % deployment + LOG.error(msg) + msg_error += msg + "\n" + return dict(info=msg_info, warning=msg_warning, error=msg_error) + + # Check if software release directory location exists + release_version = utils.get_major_release_version(release["sw_version"]) + + return DeployHandler().create(from_release=SW_VERSION, to_release=release_version) +# return dict(info=msg_info, warning=msg_warning, error=msg_error) + + def software_get_deploy(self, deployment: str) -> dict: + """ + Verify if system is capable to upgrade to a specified deployment + return: dict of info, warning and error messages + """ + msg_info = "" + msg_warning = "" + msg_error = "" + + # We need to verify that the software release exists + release = self.release_data.metadata.get(deployment, None) + if not release: + msg = "Software release version corresponding to the specified deployment " \ + "%s does not exist." % deployment + LOG.error(msg) + msg_error += msg + "\n" + return dict(info=msg_info, warning=msg_warning, error=msg_error) + + # Check if software release directory location exists + release_version = utils.get_major_release_version(release["sw_version"]) + + return DeployHandler().get(from_release=SW_VERSION, to_release=release_version) +# return dict(info=msg_info, warning=msg_warning, error=msg_error) + + def software_update_deploy(self, deployment: str) -> dict: + """ + Verify if system is capable to upgrade to a specified deployment + return: dict of info, warning and error messages + """ + msg_info = "" + msg_warning = "" + msg_error = "" + + # We need to verify that the software release exists + release = self.release_data.metadata.get(deployment, None) + if not release: + msg = "Software release version corresponding to the specified deployment " \ + "%s does not exist." % deployment + LOG.error(msg) + msg_error += msg + "\n" + return dict(info=msg_info, warning=msg_warning, error=msg_error) + + # Check if software release directory location exists + release_version = utils.get_major_release_version(release["sw_version"]) + + DeployHandler().update(from_release=SW_VERSION, to_release=release_version) + return dict(info=msg_info, warning=msg_warning, error=msg_error) + + def software_delete_deploy(self, deployment: str) -> dict: + """ + Verify if system is capable to upgrade to a specified deployment + return: dict of info, warning and error messages + """ + msg_info = "" + msg_warning = "" + msg_error = "" + + # We need to verify that the software release exists + release = self.release_data.metadata.get(deployment, None) + if not release: + msg = "Software release version corresponding to the specified deployment " \ + "%s does not exist." % deployment + LOG.error(msg) + msg_error += msg + "\n" + return dict(info=msg_info, warning=msg_warning, error=msg_error) + + # Check if software release directory location exists + release_version = utils.get_major_release_version(release["sw_version"]) + + DeployHandler().delete(from_release=SW_VERSION, to_release=release_version) + return dict(info=msg_info, warning=msg_warning, error=msg_error) + def software_deploy_precheck_api(self, deployment: str, **kwargs) -> dict: """ Verify if system is capable to upgrade to a specified deployment diff --git a/software/software/software_entities.py b/software/software/software_entities.py index ba5d8e02..8b141fda 100644 --- a/software/software/software_entities.py +++ b/software/software/software_entities.py @@ -6,11 +6,15 @@ SPDX-License-Identifier: Apache-2.0 """ import logging +import os from abc import ABC, abstractmethod from enum import Enum from typing import List -from software.utils import check_instances, check_state +from software.utils import check_instances, check_state, load_from_json_file, save_to_json_file, get_software_filesystem_data + +from software import constants +from software.exceptions import DeployDoNotExist LOG = logging.getLogger('main_logger') @@ -140,11 +144,12 @@ class Deploy(ABC): """ check_instances([from_release, to_release, state], str) - check_state(state, self.states) + if state: + check_state(state, self.states) pass @abstractmethod - def query(self, from_release: str, to_release: str): + def get(self, from_release: str, to_release: str): """ Get deployments based on source and target release versions. @@ -227,11 +232,114 @@ class DeployHosts(ABC): pass @abstractmethod - def delete(self, host_name: str): + def delete(self, host_name: str, software_release: str, target_release: str): """ Delete deploy-host entries for a given host. :param host_name: The name of the host. + :param software_release: The software release version. + :param target_release: The target release version. """ check_instances([host_name], str) pass + +class DeployHandler(Deploy): + def __init__(self): + self.data = get_software_filesystem_data() + def create(self, from_release, to_release, state=None): + deploy = self.get(from_release, to_release) + if deploy: + raise DeployAlreadyExist("Error to create. Deploy already exist.") + deploy_data = { + "deploy": [] + } + deploy_data["deploy"].append({ + "from_release": from_release, + "to_release": to_release, + "state": state + }) + self.data.update(deploy_data) + save_to_json_file(constants.SOFTWARE_JSON_FILE, self.data) + + def get(self, from_release, to_release): + for deploy in self.data.get("deploys", []): + if deploy.get("from_release") == from_release and deploy.get("to_release") == to_release: + return deploy + def update(self, from_release, to_release, state): + deploy = self.get(from_release, to_release) + if not deploy: + raise DeployDoNotExist("Error to update. Deploy do not exist.") + deploy_data = { + "deploy": [] + } + deploy_data["deploy"].append({ + "from_release": from_release, + "to_release": to_release, + "state": state + }) + self.data.update(deploy_data) + save_to_json_file(constants.SOFTWARE_JSON_FILE, self.data) + def delete(self, from_release, to_release): + deploy = self.get(from_release, to_release) + if not deploy: + raise DeployDoNotExist("Error to delete. Deploy do not exist.") + else: + self.data.get("deploy").remove(deploy) + self.data.update(data) + save_to_json_file(constants.SOFTWARE_JSON_FILE, self.data) + +class DeployHostHandler(DeployHosts): + def __init__(self): + self.data = get_software_filesystem_data() + def create(self, hostname, from_release, to_release, state=None): + deploy = self.get(hostname, from_release, to_release) + if deploy: + raise DeployAlreadyExist("Error to create. Deploy host already exist.") + deploy_data = self.data.get("deploy_host", []) + if not deploy_data: + deploy_data = { + "deploy_host": [] + } + deploy_data["deploy_host"].append({ + "hostname": hostname, + "from_release": from_release, + "to_release": to_release, + "state": state + }) + self.data.update(deploy_data) + else: + deploy_data.append({ + "hostname": hostname, + "from_release": from_release, + "to_release": to_release, + "state": state + }) + save_to_json_file(constants.SOFTWARE_JSON_FILE, self.data) + + def get(self, hostname, from_release, to_release): + for deploy in self.data.get("deploys", []): + if (deploy.get("hostname") == hostname and deploy.get("from_release") == from_release + and deploy.get("to_release") == to_release): + return deploy + + def get_by_hostname(self, hostname): + for deploy in self.data.get("deploys", []): + if deploy.get("hostname") == hostname: + return deploy + + def update(self, hostname, from_release, to_release, state): + deploy = self.get(hostname, from_release, to_release) + if not deploy: + raise Exception("Error to update. Deploy host do not exist.") + + index = self.data.get("deploy_host", []).index(deploy) + self.data["deploy_host"][index].update({ + "hostname": hostname, + "from_release": from_release, + "to_release": to_release, + "state": state + }) + save_to_json_file(constants.SOFTWARE_JSON_FILE, self.data) + def delete_all(self): + self.data.get("deploy_host").clear() + save_to_json_file(constants.SOFTWARE_JSON_FILE, self.data) diff --git a/software/software/utils.py b/software/software/utils.py index 25da6177..09d21f6f 100644 --- a/software/software/utils.py +++ b/software/software/utils.py @@ -269,6 +269,10 @@ def load_from_json_file(file): except FileNotFoundError: LOG.error("File %s not found", file) return None + + except ValueError: + return {} + except Exception as e: LOG.error("Problem reading from %s: %s", file, e) return None @@ -292,3 +296,10 @@ def check_instances(params: list, instance): msg = "Param %s must be type: %s" % (p, instance) LOG.exception(msg) raise ValueError(msg) + +def get_software_filesystem_data(): + if os.path.exists(constants.SOFTWARE_JSON_FILE): + data = load_from_json_file(constants.SOFTWARE_JSON_FILE) + else: + data = {} + return data \ No newline at end of file