391 lines
11 KiB
Python
391 lines
11 KiB
Python
"""
|
|
Copyright (c) 2023-2024 Wind River Systems, Inc.
|
|
|
|
SPDX-License-Identifier: Apache-2.0
|
|
|
|
"""
|
|
|
|
import logging
|
|
from abc import ABC
|
|
from abc import abstractmethod
|
|
from enum import Enum
|
|
from typing import List
|
|
|
|
from software import constants
|
|
from software.exceptions import DeployDoNotExist
|
|
from software.exceptions import DeployAlreadyExist
|
|
from software.utils import check_instances
|
|
from software.utils import check_state
|
|
from software.utils import save_to_json_file
|
|
from software.utils import get_software_filesystem_data
|
|
from software.utils import validate_versions
|
|
|
|
from software.constants import DEPLOY_HOST_STATES
|
|
from software.constants import DEPLOY_STATES
|
|
|
|
LOG = logging.getLogger('main_logger')
|
|
|
|
|
|
class Release(ABC):
|
|
|
|
def __init__(self):
|
|
self.states = Enum('States', 'active deploying inactive')
|
|
|
|
@abstractmethod
|
|
def create(self, rel_ver: str, state: str):
|
|
"""
|
|
Create a new release with the given release version and state
|
|
|
|
:param rel_ver: The release version.
|
|
:param state: The state of the release (active, deploying..).
|
|
"""
|
|
check_instances([rel_ver, state], str)
|
|
check_state(state, self.states)
|
|
pass
|
|
|
|
@abstractmethod
|
|
def query(self, rel_ver: str):
|
|
"""
|
|
Get information about a release based on its version.
|
|
|
|
:param rel_ver: The release version.
|
|
"""
|
|
check_instances([rel_ver], str)
|
|
pass
|
|
|
|
@abstractmethod
|
|
def update(self, rel_ver: str, state: str):
|
|
"""
|
|
Update the state of release based on its version.
|
|
|
|
:param rel_ver: The release version
|
|
:param state: The update state of the release.
|
|
|
|
"""
|
|
check_instances([rel_ver, state], str)
|
|
check_state(state, self.states)
|
|
pass
|
|
|
|
@abstractmethod
|
|
def delete(self, rel_ver: str):
|
|
"""
|
|
Delete a release based on its version.
|
|
|
|
:param rel_ver: The release version.
|
|
"""
|
|
check_instances([rel_ver], str)
|
|
pass
|
|
|
|
|
|
class CompatibleRelease(ABC):
|
|
|
|
@abstractmethod
|
|
def create(self, rel_ver: str, compatible_ver: str, required_patches: List[str]):
|
|
"""
|
|
Create a new compatible release entry.
|
|
|
|
:param rel_ver: The release version.
|
|
:param compatible_ver: The compatible release version
|
|
:param required_patches: A list of required patches for compatibility
|
|
|
|
"""
|
|
check_instances([rel_ver, compatible_ver], str)
|
|
check_instances(required_patches, str)
|
|
pass
|
|
|
|
@abstractmethod
|
|
def query(self, rel_ver: str):
|
|
"""
|
|
Get compatible releases for a given release version.
|
|
|
|
:param rel_ver: The release version
|
|
|
|
"""
|
|
check_instances([rel_ver], str)
|
|
pass
|
|
|
|
@abstractmethod
|
|
def update(self, rel_ver: str, compatible_ver: str, required_patches: List[str]):
|
|
"""
|
|
Update a compatible release entry
|
|
|
|
:param rel_ver: The release version.
|
|
:param compatible_ver: The compatible release version
|
|
:param required_patches: A list of required patches for compatibility
|
|
|
|
"""
|
|
check_instances([rel_ver, compatible_ver], str)
|
|
check_instances(required_patches, str)
|
|
pass
|
|
|
|
@abstractmethod
|
|
def delete(self, rel_ver: str):
|
|
"""
|
|
Delete compatible releases for a given release version.
|
|
|
|
:param rel_ver: The release version.
|
|
|
|
"""
|
|
check_instances([rel_ver], str)
|
|
pass
|
|
|
|
|
|
class Deploy(ABC):
|
|
def __init__(self):
|
|
pass
|
|
|
|
@abstractmethod
|
|
def create(self, from_release: str, to_release: str, reboot_required: bool, state: DEPLOY_STATES):
|
|
"""
|
|
Create a new deployment entry.
|
|
|
|
: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.
|
|
|
|
"""
|
|
validate_versions([from_release, to_release])
|
|
check_instances([reboot_required], bool)
|
|
check_instances([state], DEPLOY_STATES)
|
|
|
|
@abstractmethod
|
|
def query(self, from_release, to_release):
|
|
"""
|
|
Get deployments based on current and target release versions.
|
|
"""
|
|
validate_versions([from_release, to_release])
|
|
pass
|
|
|
|
@abstractmethod
|
|
def update(self, new_state: DEPLOY_STATES):
|
|
"""
|
|
Update a deployment entry.
|
|
|
|
:param new_state: The state of the deployment.
|
|
|
|
"""
|
|
check_instances([new_state], DEPLOY_STATES)
|
|
|
|
@abstractmethod
|
|
def delete(self):
|
|
"""
|
|
Delete a deployment entry based on current and target release versions.
|
|
"""
|
|
pass
|
|
|
|
|
|
class DeployHosts(ABC):
|
|
|
|
def __init__(self):
|
|
pass
|
|
|
|
@abstractmethod
|
|
def create(self, hostname: str, state: DEPLOY_HOST_STATES):
|
|
"""
|
|
Create a new deploy-host entry
|
|
|
|
:param hostname: The name of the host.
|
|
:param state: The state of the deploy-host entry.
|
|
|
|
"""
|
|
instances = [hostname]
|
|
if state:
|
|
check_instances([state], DEPLOY_HOST_STATES)
|
|
check_instances(instances, str)
|
|
pass
|
|
|
|
@abstractmethod
|
|
def query(self, hostname: str):
|
|
"""
|
|
Get deploy-host entries for a given host.
|
|
|
|
:param hostname: The name of the host.
|
|
|
|
"""
|
|
check_instances([hostname], str)
|
|
pass
|
|
|
|
@abstractmethod
|
|
def update(self, hostname: str, state: str):
|
|
"""
|
|
Update a deploy-host entry
|
|
|
|
:param hostname: The name of the host.
|
|
:param state: The state of the deploy-host entry.
|
|
"""
|
|
check_instances([hostname], str)
|
|
check_instances([state], DEPLOY_HOST_STATES)
|
|
pass
|
|
|
|
@abstractmethod
|
|
def delete(self, hostname):
|
|
"""
|
|
Delete deploy-host entries for a given host.
|
|
|
|
:param hostname: The name of the host.
|
|
"""
|
|
check_instances([hostname], str)
|
|
pass
|
|
|
|
|
|
class DeployHandler(Deploy):
|
|
def __init__(self):
|
|
super().__init__()
|
|
self.data = get_software_filesystem_data()
|
|
|
|
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 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(from_release, to_release)
|
|
if deploy:
|
|
raise DeployAlreadyExist("Error to create. Deploy already exists.")
|
|
new_deploy = {
|
|
"from_release": from_release,
|
|
"to_release": to_release,
|
|
"reboot_required": reboot_required,
|
|
"state": state.value
|
|
}
|
|
|
|
try:
|
|
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"][0] = {}
|
|
|
|
def query(self, from_release, to_release):
|
|
"""
|
|
Query deploy based on from and to release version
|
|
:param from_release: The current release version.
|
|
:param to_release: The target release version.
|
|
:return: A list of deploy dictionary
|
|
"""
|
|
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):
|
|
"""
|
|
Update deploy state based on from and to release version
|
|
:param new_state: The new state
|
|
"""
|
|
super().update(new_state)
|
|
deploy = self.query_all()
|
|
if not deploy:
|
|
raise DeployDoNotExist("Error to update deploy state. No deploy in progress.")
|
|
|
|
try:
|
|
self.data["deploy"][0]["state"] = new_state.value
|
|
save_to_json_file(constants.SOFTWARE_JSON_FILE, self.data)
|
|
except Exception:
|
|
self.data["deploy"][0] = deploy
|
|
|
|
def delete(self):
|
|
"""
|
|
Delete a deploy based on given from and to release version
|
|
"""
|
|
super().delete()
|
|
deploy = self.query_all()
|
|
if not deploy:
|
|
raise DeployDoNotExist("Error to delete deploy state. No deploy in progress.")
|
|
try:
|
|
self.data["deploy"].clear()
|
|
save_to_json_file(constants.SOFTWARE_JSON_FILE, self.data)
|
|
except Exception:
|
|
self.data["deploy"][0] = deploy
|
|
|
|
|
|
class DeployHostHandler(DeployHosts):
|
|
|
|
def __init__(self):
|
|
super().__init__()
|
|
self.data = get_software_filesystem_data()
|
|
|
|
def create(self, hostname, state: DEPLOY_HOST_STATES = None):
|
|
super().create(hostname, state)
|
|
deploy = self.query(hostname)
|
|
if deploy:
|
|
raise DeployAlreadyExist("Error to create. Deploy host already exist.")
|
|
|
|
new_deploy_host = {
|
|
"hostname": hostname,
|
|
"state": state.value if state else None
|
|
}
|
|
|
|
deploy_data = self.data.get("deploy_host", [])
|
|
if not deploy_data:
|
|
deploy_data = {
|
|
"deploy_host": []
|
|
}
|
|
deploy_data["deploy_host"].append(new_deploy_host)
|
|
self.data.update(deploy_data)
|
|
else:
|
|
deploy_data.append(new_deploy_host)
|
|
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:
|
|
return deploy
|
|
return None
|
|
|
|
def query_all(self):
|
|
return self.data.get("deploy_host", [])
|
|
|
|
def update(self, hostname, state: DEPLOY_HOST_STATES):
|
|
super().update(hostname, state)
|
|
deploy = self.query(hostname)
|
|
if not deploy:
|
|
raise Exception("Error to update. Deploy host do not exist.")
|
|
|
|
index = self.data.get("deploy_host", []).index(deploy)
|
|
updated_entity = {
|
|
"hostname": hostname,
|
|
"state": state.value
|
|
}
|
|
self.data["deploy_host"][index].update(updated_entity)
|
|
save_to_json_file(constants.SOFTWARE_JSON_FILE, self.data)
|
|
return updated_entity
|
|
|
|
def delete_all(self):
|
|
self.data.get("deploy_host").clear()
|
|
save_to_json_file(constants.SOFTWARE_JSON_FILE, self.data)
|
|
|
|
def delete(self, hostname):
|
|
super().delete(hostname)
|
|
deploy = self.query(hostname)
|
|
if not deploy:
|
|
raise DeployDoNotExist("Error to delete. Deploy host do not exist.")
|
|
self.data.get("deploy_host").remove(deploy)
|
|
save_to_json_file(constants.SOFTWARE_JSON_FILE, self.data)
|