Delete deployment

This commit adds the function to delete a deployment. There's some
validations in order to be able to delete it some of them is:
The deploy host state should be one of: [pending, failed]
The deploy state should be one of: [start-done, start-failed,
abort-done]
All nodes are running from release software.

In case of delete a major release the data generated during the deploy
should also be deleted which include /sysroot/upgrade/ostree_repo,
/sysroot/upgrade/sysroot and the folders [config, fluxcd, helm,
nfv/vim, sysinv, puppet, deploy] under
/opt/platform/<folders>/<sw_version>.

Also delete the /var/lib/postgres/<sw_version>.

Note: The function is not called by any endpoint at this point as the
New USM REST API is in development to avoid conflicts and rework.

Test Plan:
PASS: Folders deleted on /opt/platform/<folders>/<sw_version>
with start-done and start-failed deploy state.
PASS: Cleanup of staging data.
PASS: Failed to attempt deletion with a deployed host N+1 release

Story: 2010676
Task: 49979

Change-Id: I1789172edc730e6c94fa6bec7f5881c0bdfd7eab
Signed-off-by: Luis Eduardo Bonatti <LuizEduardo.Bonatti@windriver.com>
This commit is contained in:
Luis Eduardo Bonatti 2024-04-25 20:13:09 -03:00
parent f99f8377d0
commit 46a49a09fa
4 changed files with 78 additions and 1 deletions

View File

@ -34,6 +34,7 @@ RC_UNHEALTHY = 3
DEPLOY_PRECHECK_SCRIPT = "deploy-precheck"
DEPLOY_START_SCRIPT = "software-deploy-start"
DEPLOY_CLEANUP_SCRIPT = "deploy-cleanup"
SEMANTICS_DIR = "%s/semantics" % SOFTWARE_STORAGE_DIR
@ -56,6 +57,18 @@ DEBIAN_RELEASE = "bullseye"
STARLINGX_RELEASE = SW_VERSION
PATCH_SCRIPTS_STAGING_DIR = "/var/www/pages/updates/software-scripts"
SYSROOT_OSTREE = "/sysroot/ostree/repo"
STAGING_DIR = "/sysroot/upgrade"
ROOT_DIR = "%s/sysroot" % STAGING_DIR
POSTGRES_PATH = "/var/lib/postgresql"
PLATFORM_PATH = "/opt/platform"
CONFIG = "config"
FLUXCD = "fluxcd"
HELM = "helm"
VIM = "nfv/vim"
SYSINV = "sysinv"
PUPPET = "puppet"
DEPLOY = "deploy"
CLEANUP_FOLDERS_NAME = [CONFIG, FLUXCD, HELM, VIM, SYSINV, PUPPET, DEPLOY]
LOOPBACK_INTERFACE_NAME = "lo"

View File

@ -86,6 +86,13 @@ class SoftwareAPI:
finally:
self.end_update()
def get_all_current_hosts_states(self):
self.begin_update()
try:
return self.deploy_host_handler.query_all_current_states()
finally:
self.end_update()
def update_deploy_host(self, hostname, state):
self.begin_update()
try:

View File

@ -27,7 +27,6 @@ from wsgiref import simple_server
from fm_api import fm_api
from fm_api import constants as fm_constants
from oslo_config import cfg as oslo_cfg
import software.apt_utils as apt_utils
@ -79,6 +78,7 @@ from software.deploy_state import DeployState
from software.release_verify import verify_files
import software.config as cfg
import software.utils as utils
from software.sysinv_utils import get_ihost_list
from software.sysinv_utils import get_k8s_ver
from software.sysinv_utils import is_system_controller
@ -2651,6 +2651,56 @@ class PatchController(PatchService):
# a deployment)
return True
@require_deploy_state([DEPLOY_STATES.START_DONE, DEPLOY_STATES.START_FAILED, DEPLOY_STATES.ABORT_DONE],
"Deploy must be in the following states to be able to delete: %s, %s, %s" % (
DEPLOY_STATES.START_DONE.value, DEPLOY_STATES.START_FAILED.value,
DEPLOY_STATES.ABORT_DONE.value))
def software_deploy_delete_api(self) -> dict:
"""
Delete deployment and the data generated during the deploy.
:return: dict of info, warning and error messages
"""
msg_info = ""
msg_warning = ""
msg_error = ""
deploy = self.db_api_instance.get_deploy_all()[0]
to_release = deploy.get("to_release")
from_release = deploy.get("from_release")
major_from_release = utils.get_major_release_version(from_release)
for host in get_ihost_list():
# TODO(lbonatti) change it to sw_version once load is deprecated
if host.software_load != major_from_release:
raise SoftwareServiceError(f"Delete not allow because {host.hostname} is not running from release"
f" {major_from_release}")
hosts_states = self.db_api_instance.get_all_current_hosts_states()
if states.DEPLOY_HOST_STATES.DEPLOYED in hosts_states or states.DEPLOY_HOST_STATES.DEPLOYING in hosts_states:
raise SoftwareServiceError(f"There's host already {states.DEPLOY_HOST_STATES.DEPLOYED.value} "
f"or in {states.DEPLOY_HOST_STATES.DEPLOYING.value} process")
major_release = utils.get_major_release_version(to_release)
major_to_release = constants.MAJOR_RELEASE % major_release
if to_release == major_to_release:
try:
cmd_path = utils.get_software_deploy_script(to_release, constants.DEPLOY_CLEANUP_SCRIPT)
if (os.path.exists(f"{constants.STAGING_DIR}/{constants.OSTREE_REPO}") and
os.path.exists(constants.ROOT_DIR)):
subprocess.check_output([cmd_path, f"{constants.STAGING_DIR}/{constants.OSTREE_REPO}",
constants.ROOT_DIR, "all"], stderr=subprocess.STDOUT)
except subprocess.CalledProcessError as e:
msg_error = "Failed to delete deploy."
LOG.error("%s: %s" % (msg_error, e))
return dict(info=msg_info, warning=msg_warning, error=msg_error)
for folder in constants.CLEANUP_FOLDERS_NAME:
path = os.path.join(constants.PLATFORM_PATH, folder, major_release, "")
shutil.rmtree(path, ignore_errors=True)
shutil.rmtree(f"{constants.POSTGRES_PATH}/{major_release}")
msg_info += "Deploy deleted with success"
self.db_api_instance.delete_deploy_host_all()
self.db_api_instance.delete_deploy()
return dict(info=msg_info, warning=msg_warning, error=msg_error)
@require_deploy_state([DEPLOY_STATES.ACTIVATE_DONE],
"Must complete deploy activate before completing the deployment")
def software_deploy_complete_api(self) -> dict:

View File

@ -367,6 +367,13 @@ class DeployHostHandler(DeployHosts):
data = get_software_filesystem_data()
return data.get("deploy_host", [])
def query_all_current_states(self):
data = get_software_filesystem_data()
states = []
for host in data.get("deploy_host", []):
states.append(host.get("state"))
return states
def update(self, hostname, state: DEPLOY_HOST_STATES):
super().update(hostname, state)
deploy = self.query(hostname)