From 98cd083cffeead507d9ad9a205f3ca0703ba18b2 Mon Sep 17 00:00:00 2001 From: Luis Eduardo Bonatti Date: Thu, 22 Feb 2024 16:55:32 -0300 Subject: [PATCH] Deploy show implement This commit add some changes to deploy show endpoint, the name was changed to just deploy with GET verb and also changes the deploy to be saved as a list of dict to attend the API requirements. Now the api accepts from_release and to_release as optional params, in case it is provided the endpoint will return a dict otherwise will return a list of dict. Test Plan: PASS: Create deploy PASS: Update deploy PASS: Software deploy start PASS: Software deploy show Story: 2010676 Task: 49645 Change-Id: I68d243c05da88c7eecf2d866c7202c3c7be51a2b Signed-off-by: Luis Eduardo Bonatti --- .../software_client/software_client.py | 7 +- .../software/api/controllers/v1/software.py | 17 +++-- software/software/db/api.py | 11 +++- software/software/software_controller.py | 14 +++- software/software/software_entities.py | 65 +++++++++++++------ 5 files changed, 78 insertions(+), 36 deletions(-) diff --git a/software-client/software_client/software_client.py b/software-client/software_client/software_client.py index f99c277a..f4dae7e1 100644 --- a/software-client/software_client/software_client.py +++ b/software-client/software_client/software_client.py @@ -1011,7 +1011,7 @@ def deploy_complete_req(args): def deploy_show_req(args): - url = "http://%s/v1/software/deploy_show" % api_addr + url = "http://%s/v1/software/deploy" % api_addr headers = {} append_auth_token_if_required(headers) req = requests.get(url, headers=headers) @@ -1023,10 +1023,11 @@ def deploy_show_req(args): print("Respond code %d. Error: %s" % (req.status_code, req.reason)) return 1 - data = json.loads(req.text) + data = req.json().get("data") if not data: - print("No deploy in progress.\n") + print("No deploy in progress.") else: + data = data[0] data["reboot_required"] = "Yes" if data.get("reboot_required") else "No" data_list = [[k, v] for k, v in data.items()] transposed_data_list = list(zip(*data_list)) diff --git a/software/software/api/controllers/v1/software.py b/software/software/api/controllers/v1/software.py index 1861c8bf..929c22cd 100644 --- a/software/software/api/controllers/v1/software.py +++ b/software/software/api/controllers/v1/software.py @@ -10,6 +10,7 @@ import os from oslo_log import log from pecan import expose from pecan import request +from pecan import Response import shutil from software.exceptions import SoftwareError @@ -132,15 +133,13 @@ class SoftwareAPIController(object): return result - @expose('json') - @expose('query.xml', content_type='application/xml') - def deploy_show(self): - try: - result = sc.software_deploy_show_api() - except SoftwareError as e: - return dict(error="Error: %s" % str(e)) - - return result + @expose('json', method="GET") + def deploy(self): + from_release = request.GET.get("from_release") + to_release = request.GET.get("to_release") + result = dict(data=sc.software_deploy_show_api(from_release, to_release)) + response_data = json.dumps(result) + return Response(body=response_data, status_code=200) @expose('json') @expose('query.xml', content_type='application/xml') diff --git a/software/software/db/api.py b/software/software/db/api.py index 253d0d46..13f1397f 100644 --- a/software/software/db/api.py +++ b/software/software/db/api.py @@ -37,10 +37,17 @@ class SoftwareAPI: self.deploy_handler.create(from_release, to_release, reboot_required) self.end_update() - def get_deploy(self): + def get_deploy(self, from_release, to_release): self.begin_update() try: - return self.deploy_handler.query() + return self.deploy_handler.query(from_release, to_release) + finally: + self.end_update() + + def get_deploy_all(self): + self.begin_update() + try: + return self.deploy_handler.query_all() finally: self.end_update() diff --git a/software/software/software_controller.py b/software/software/software_controller.py index acf89cbf..522e858e 100644 --- a/software/software/software_controller.py +++ b/software/software/software_controller.py @@ -2627,9 +2627,13 @@ class PatchController(PatchService): return dict(info=msg_info, warning=msg_warning, error=msg_error) - def software_deploy_show_api(self): + def software_deploy_show_api(self, from_release=None, to_release=None): # Retrieve deploy state from db - return self.db_api_instance.get_deploy() + if from_release and to_release: + return self.db_api_instance.get_deploy(from_release, to_release) + else: + # Retrieve deploy state from db in list format + return self.db_api_instance.get_deploy_all() def software_deploy_host_api(self, host_ip, force, async_req=False): msg_info = "" @@ -2902,7 +2906,11 @@ class PatchController(PatchService): def deploy_host_list(self): query_hosts = self.query_host_cache() deploy_hosts = self.db_api_instance.get_deploy_host() - deploy = self.db_api_instance.get_deploy() + deploy = self.db_api_instance.get_deploy_all() + if not deploy: + return None + deploy = deploy[0] + # If there's a hostname missing, add it to query hosts. hostnames = [] diff --git a/software/software/software_entities.py b/software/software/software_entities.py index 62297781..18932cb7 100644 --- a/software/software/software_entities.py +++ b/software/software/software_entities.py @@ -138,7 +138,7 @@ class Deploy(ABC): """ Create a new deployment entry. - :param from_release: The source release version. + :param from_release: The current release version. :param to_release: The target release version. :param reboot_required: If is required to do host reboot. :param state: The state of the deployment. @@ -149,10 +149,11 @@ class Deploy(ABC): check_instances([state], DEPLOY_STATES) @abstractmethod - def query(self): + def query(self, from_release, to_release): """ - Get deployments based on source and target release versions. + Get deployments based on current and target release versions. """ + validate_versions([from_release, to_release]) pass @abstractmethod @@ -160,7 +161,7 @@ class Deploy(ABC): """ Update a deployment entry. - :param state: The state of the deployment. + :param new_state: The state of the deployment. """ check_instances([new_state], DEPLOY_STATES) @@ -168,7 +169,7 @@ class Deploy(ABC): @abstractmethod def delete(self): """ - Delete a deployment entry based on source and target release versions. + Delete a deployment entry based on current and target release versions. """ pass @@ -236,13 +237,13 @@ class DeployHandler(Deploy): def create(self, from_release, to_release, reboot_required, state=DEPLOY_STATES.START): """ Create a new deploy with given from and to release version - :param from_release: The source release version. + :param from_release: The current release version. :param to_release: The target release version. :param reboot_required: If is required to do host reboot. :param state: The state of the deployment. """ super().create(from_release, to_release, reboot_required, state) - deploy = self.query() + deploy = self.query(from_release, to_release) if deploy: raise DeployAlreadyExist("Error to create. Deploy already exists.") new_deploy = { @@ -253,18 +254,39 @@ class DeployHandler(Deploy): } try: - self.data["deploy"] = new_deploy + deploy_data = self.data.get("deploy", []) + if not deploy_data: + deploy_data = { + "deploy": [] + } + deploy_data["deploy"].append(new_deploy) + self.data.update(deploy_data) + else: + deploy_data.append(new_deploy) save_to_json_file(constants.SOFTWARE_JSON_FILE, self.data) except Exception: - self.data["deploy"] = {} + self.data["deploy"][0] = {} - def query(self): + def query(self, from_release, to_release): """ Query deploy based on from and to release version - :return: A deploy dictionary + :param from_release: The current release version. + :param to_release: The target release version. + :return: A list of deploy dictionary """ - super().query() - return self.data.get("deploy", {}) + super().query(from_release, to_release) + for deploy in self.data.get("deploy", []): + if (deploy.get("from_release") == from_release + and deploy.get("to_release") == to_release): + return deploy + return [] + + def query_all(self): + """ + Query all deployments inside software.json file. + :return: A list of deploy dictionary + """ + return self.data.get("deploy", []) def update(self, new_state: DEPLOY_STATES): """ @@ -272,29 +294,29 @@ class DeployHandler(Deploy): :param new_state: The new state """ super().update(new_state) - deploy = self.query() + deploy = self.query_all() if not deploy: raise DeployDoNotExist("Error to update deploy state. No deploy in progress.") try: - self.data["deploy"]["state"] = new_state.value + self.data["deploy"][0]["state"] = new_state.value save_to_json_file(constants.SOFTWARE_JSON_FILE, self.data) except Exception: - self.data["deploy"] = deploy + self.data["deploy"][0] = deploy def delete(self): """ Delete a deploy based on given from and to release version """ super().delete() - deploy = self.query() + deploy = self.query_all() if not deploy: raise DeployDoNotExist("Error to delete deploy state. No deploy in progress.") try: - self.data["deploy"] = {} + self.data["deploy"].clear() save_to_json_file(constants.SOFTWARE_JSON_FILE, self.data) except Exception: - self.data["deploy"] = deploy + self.data["deploy"][0] = deploy class DeployHostHandler(DeployHosts): @@ -326,6 +348,11 @@ class DeployHostHandler(DeployHosts): save_to_json_file(constants.SOFTWARE_JSON_FILE, self.data) def query(self, hostname): + """ + Query deploy based on hostname + :param hostname: The name of the host. + :return: A list of deploy dictionary + """ super().query(hostname) for deploy in self.data.get("deploy_host", []): if deploy.get("hostname") == hostname: