Create release_data wrapper classes
Create wraper classes to provide access to release data structure in software_controller. TCs: pass unit tests. pass deploy-start update release state Story: 2010676 Task: 49466 Change-Id: Ideb7a2528853e587f2aa2a0ca4dfa5215cf71d17 Signed-off-by: Bin Qian <bin.qian@windriver.com>
This commit is contained in:
parent
29540ba063
commit
0378788233
|
@ -1,5 +1,5 @@
|
||||||
"""
|
"""
|
||||||
Copyright (c) 2023 Wind River Systems, Inc.
|
Copyright (c) 2023-2024 Wind River Systems, Inc.
|
||||||
|
|
||||||
SPDX-License-Identifier: Apache-2.0
|
SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
@ -26,12 +26,15 @@ SOFTWARE_CONFIG_FILE_LOCAL = "/etc/software/software.conf"
|
||||||
|
|
||||||
AVAILABLE_DIR = "%s/metadata/available" % SOFTWARE_STORAGE_DIR
|
AVAILABLE_DIR = "%s/metadata/available" % SOFTWARE_STORAGE_DIR
|
||||||
UNAVAILABLE_DIR = "%s/metadata/unavailable" % SOFTWARE_STORAGE_DIR
|
UNAVAILABLE_DIR = "%s/metadata/unavailable" % SOFTWARE_STORAGE_DIR
|
||||||
|
DEPLOYING_DIR = "%s/metadata/deploying" % SOFTWARE_STORAGE_DIR
|
||||||
|
DEPLOYED_DIR = "%s/metadata/deployed" % SOFTWARE_STORAGE_DIR
|
||||||
|
REMOVING_DIR = "%s/metadata/removing" % SOFTWARE_STORAGE_DIR
|
||||||
|
|
||||||
|
# TODO(bqian) states to be removed once current references are removed
|
||||||
DEPLOYING_START_DIR = "%s/metadata/deploying_start" % SOFTWARE_STORAGE_DIR
|
DEPLOYING_START_DIR = "%s/metadata/deploying_start" % SOFTWARE_STORAGE_DIR
|
||||||
DEPLOYING_HOST_DIR = "%s/metadata/deploying_host" % SOFTWARE_STORAGE_DIR
|
DEPLOYING_HOST_DIR = "%s/metadata/deploying_host" % SOFTWARE_STORAGE_DIR
|
||||||
DEPLOYING_ACTIVATE_DIR = "%s/metadata/deploying_activate" % SOFTWARE_STORAGE_DIR
|
DEPLOYING_ACTIVATE_DIR = "%s/metadata/deploying_activate" % SOFTWARE_STORAGE_DIR
|
||||||
DEPLOYING_COMPLETE_DIR = "%s/metadata/deploying_complete" % SOFTWARE_STORAGE_DIR
|
DEPLOYING_COMPLETE_DIR = "%s/metadata/deploying_complete" % SOFTWARE_STORAGE_DIR
|
||||||
DEPLOYED_DIR = "%s/metadata/deployed" % SOFTWARE_STORAGE_DIR
|
|
||||||
REMOVING_DIR = "%s/metadata/removing" % SOFTWARE_STORAGE_DIR
|
|
||||||
ABORTING_DIR = "%s/metadata/aborting" % SOFTWARE_STORAGE_DIR
|
ABORTING_DIR = "%s/metadata/aborting" % SOFTWARE_STORAGE_DIR
|
||||||
COMMITTED_DIR = "%s/metadata/committed" % SOFTWARE_STORAGE_DIR
|
COMMITTED_DIR = "%s/metadata/committed" % SOFTWARE_STORAGE_DIR
|
||||||
SEMANTICS_DIR = "%s/semantics" % SOFTWARE_STORAGE_DIR
|
SEMANTICS_DIR = "%s/semantics" % SOFTWARE_STORAGE_DIR
|
||||||
|
@ -40,29 +43,53 @@ DEPLOY_STATE_METADATA_DIR = \
|
||||||
[
|
[
|
||||||
AVAILABLE_DIR,
|
AVAILABLE_DIR,
|
||||||
UNAVAILABLE_DIR,
|
UNAVAILABLE_DIR,
|
||||||
|
DEPLOYING_DIR,
|
||||||
|
DEPLOYED_DIR,
|
||||||
|
REMOVING_DIR,
|
||||||
|
# TODO(bqian) states to be removed once current references are removed
|
||||||
DEPLOYING_START_DIR,
|
DEPLOYING_START_DIR,
|
||||||
DEPLOYING_HOST_DIR,
|
DEPLOYING_HOST_DIR,
|
||||||
DEPLOYING_ACTIVATE_DIR,
|
DEPLOYING_ACTIVATE_DIR,
|
||||||
DEPLOYING_COMPLETE_DIR,
|
DEPLOYING_COMPLETE_DIR,
|
||||||
DEPLOYED_DIR,
|
|
||||||
REMOVING_DIR,
|
|
||||||
ABORTING_DIR,
|
ABORTING_DIR,
|
||||||
COMMITTED_DIR,
|
COMMITTED_DIR,
|
||||||
]
|
]
|
||||||
|
|
||||||
ABORTING = 'aborting'
|
# new release state needs to be added to VALID_RELEASE_STATES list
|
||||||
AVAILABLE = 'available'
|
AVAILABLE = 'available'
|
||||||
COMMITTED = 'committed'
|
UNAVAILABLE = 'unavailable'
|
||||||
|
DEPLOYING = 'deploying'
|
||||||
DEPLOYED = 'deployed'
|
DEPLOYED = 'deployed'
|
||||||
|
REMOVING = 'removing'
|
||||||
|
UNKNOWN = 'n/a'
|
||||||
|
|
||||||
|
# TODO(bqian) states to be removed once current references are removed
|
||||||
|
ABORTING = 'aborting'
|
||||||
|
COMMITTED = 'committed'
|
||||||
DEPLOYING_ACTIVATE = 'deploying-activate'
|
DEPLOYING_ACTIVATE = 'deploying-activate'
|
||||||
DEPLOYING_COMPLETE = 'deploying-complete'
|
DEPLOYING_COMPLETE = 'deploying-complete'
|
||||||
DEPLOYING_HOST = 'deploying-host'
|
DEPLOYING_HOST = 'deploying-host'
|
||||||
DEPLOYING_START = 'deploying-start'
|
DEPLOYING_START = 'deploying-start'
|
||||||
REMOVING = 'removing'
|
|
||||||
UNAVAILABLE = 'unavailable'
|
UNAVAILABLE = 'unavailable'
|
||||||
UNKNOWN = 'n/a'
|
|
||||||
|
|
||||||
|
|
||||||
|
VALID_RELEASE_STATES = [AVAILABLE, UNAVAILABLE, DEPLOYING, DEPLOYED,
|
||||||
|
REMOVING]
|
||||||
|
|
||||||
|
RELEASE_STATE_TO_DIR_MAP = {AVAILABLE: AVAILABLE_DIR,
|
||||||
|
UNAVAILABLE: UNAVAILABLE_DIR,
|
||||||
|
DEPLOYING: DEPLOYING_DIR,
|
||||||
|
DEPLOYED: DEPLOYED_DIR,
|
||||||
|
REMOVING: REMOVING_DIR}
|
||||||
|
|
||||||
|
# valid release state transition below could still be changed as
|
||||||
|
# development continue
|
||||||
|
RELEASE_STATE_VALID_TRANSITION = {
|
||||||
|
AVAILABLE: [DEPLOYING],
|
||||||
|
DEPLOYING: [DEPLOYED],
|
||||||
|
DEPLOYED: [REMOVING, UNAVAILABLE]
|
||||||
|
}
|
||||||
|
|
||||||
STATUS_DEVELOPEMENT = 'DEV'
|
STATUS_DEVELOPEMENT = 'DEV'
|
||||||
STATUS_OBSOLETE = 'OBS'
|
STATUS_OBSOLETE = 'OBS'
|
||||||
STATUS_RELEASED = 'REL'
|
STATUS_RELEASED = 'REL'
|
||||||
|
|
|
@ -117,6 +117,19 @@ class ReleaseVersionDoNotExist(SoftwareError):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class FileSystemError(SoftwareError):
|
||||||
|
"""
|
||||||
|
A failure during a linux file operation.
|
||||||
|
Likely fixable by a root user.
|
||||||
|
"""
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class InternalError(Exception):
|
||||||
|
"""This is an internal error aka bug"""
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
class SoftwareServiceError(Exception):
|
class SoftwareServiceError(Exception):
|
||||||
"""
|
"""
|
||||||
This is a service error, such as file system issue or configuration
|
This is a service error, such as file system issue or configuration
|
||||||
|
|
|
@ -0,0 +1,212 @@
|
||||||
|
#
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
#
|
||||||
|
# Copyright (c) 2024 Wind River Systems, Inc.
|
||||||
|
#
|
||||||
|
|
||||||
|
import os
|
||||||
|
import shutil
|
||||||
|
from software import constants
|
||||||
|
from software.exceptions import FileSystemError
|
||||||
|
from software.exceptions import InternalError
|
||||||
|
from software.software_functions import LOG
|
||||||
|
|
||||||
|
|
||||||
|
class SWRelease(object):
|
||||||
|
'''wrapper class to group matching metadata and contents'''
|
||||||
|
|
||||||
|
def __init__(self, rel_id, metadata, contents):
|
||||||
|
self._id = rel_id
|
||||||
|
self._metadata = metadata
|
||||||
|
self._contents = contents
|
||||||
|
|
||||||
|
@property
|
||||||
|
def metadata(self):
|
||||||
|
return self._metadata
|
||||||
|
|
||||||
|
@property
|
||||||
|
def contents(self):
|
||||||
|
return self._contents
|
||||||
|
|
||||||
|
@property
|
||||||
|
def id(self):
|
||||||
|
return self._id
|
||||||
|
|
||||||
|
@property
|
||||||
|
def state(self):
|
||||||
|
return self.metadata['state']
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def is_valid_state_transition(from_state, to_state):
|
||||||
|
if to_state not in constants.VALID_RELEASE_STATES:
|
||||||
|
msg = "Invalid state %s." % to_state
|
||||||
|
LOG.error(msg)
|
||||||
|
# this is a bug
|
||||||
|
raise InternalError(msg)
|
||||||
|
|
||||||
|
if from_state in constants.RELEASE_STATE_VALID_TRANSITION:
|
||||||
|
if to_state in constants.RELEASE_STATE_VALID_TRANSITION[from_state]:
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def ensure_state_transition(to_state):
|
||||||
|
to_dir = constants.RELEASE_STATE_TO_DIR_MAP[to_state]
|
||||||
|
if not os.path.isdir(to_dir):
|
||||||
|
try:
|
||||||
|
os.makedirs(to_dir, mode=0o755, exist_ok=True)
|
||||||
|
except FileExistsError:
|
||||||
|
error = "Cannot create directory %s" % to_dir
|
||||||
|
raise FileSystemError(error)
|
||||||
|
|
||||||
|
def update_state(self, state):
|
||||||
|
if SWRelease.is_valid_state_transition(self.state, state):
|
||||||
|
LOG.info("%s state from %s to %s" % (self.id, self.state, state))
|
||||||
|
SWRelease.ensure_state_transition(state)
|
||||||
|
|
||||||
|
to_dir = constants.RELEASE_STATE_TO_DIR_MAP[state]
|
||||||
|
from_dir = constants.RELEASE_STATE_TO_DIR_MAP[self.state]
|
||||||
|
try:
|
||||||
|
shutil.move("%s/%s-metadata.xml" % (from_dir, self.id),
|
||||||
|
"%s/%s-metadata.xml" % (to_dir, self.id))
|
||||||
|
except shutil.Error:
|
||||||
|
msg = "Failed to move the metadata for %s" % self.id
|
||||||
|
LOG.exception(msg)
|
||||||
|
raise FileSystemError(msg)
|
||||||
|
|
||||||
|
self.metadata['state'] = state
|
||||||
|
else:
|
||||||
|
# this is a bug
|
||||||
|
error = "Invalid state transition %s, current is %s, target state is %s" % \
|
||||||
|
(self.id, self.state, state)
|
||||||
|
LOG.info(error)
|
||||||
|
raise InternalError(error)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def sw_version(self):
|
||||||
|
return self.metadata['sw_version']
|
||||||
|
|
||||||
|
def _get_latest_commit(self):
|
||||||
|
num_commits = self.contents['number_of_commits']
|
||||||
|
if int(num_commits) > 0:
|
||||||
|
commit_tag = "commit%s" % num_commits
|
||||||
|
return self.contents[commit_tag]
|
||||||
|
else:
|
||||||
|
# may consider raise InvalidRelease exception in this case after
|
||||||
|
# iso metadata comes with commit id
|
||||||
|
LOG.warning("Commit data not found in metadata. Release %s" %
|
||||||
|
self.id)
|
||||||
|
return None
|
||||||
|
|
||||||
|
@property
|
||||||
|
def commit_id(self):
|
||||||
|
commit = self._get_latest_commit()
|
||||||
|
if commit is not None:
|
||||||
|
return commit['commit']
|
||||||
|
else:
|
||||||
|
# may consider raise InvalidRelease exception when iso comes with
|
||||||
|
# latest commit
|
||||||
|
return None
|
||||||
|
|
||||||
|
def _get_by_key(self, key, default=None):
|
||||||
|
if key in self._metadata:
|
||||||
|
return self._metadata[key]
|
||||||
|
else:
|
||||||
|
return default
|
||||||
|
|
||||||
|
@property
|
||||||
|
def summary(self):
|
||||||
|
return self._get_by_key('summary')
|
||||||
|
|
||||||
|
@property
|
||||||
|
def description(self):
|
||||||
|
return self._get_by_key('description')
|
||||||
|
|
||||||
|
@property
|
||||||
|
def install_instructions(self):
|
||||||
|
return self._get_by_key('install_instructions')
|
||||||
|
|
||||||
|
@property
|
||||||
|
def warnings(self):
|
||||||
|
return self._get_by_key('warnings')
|
||||||
|
|
||||||
|
@property
|
||||||
|
def status(self):
|
||||||
|
return self._get_by_key('status')
|
||||||
|
|
||||||
|
@property
|
||||||
|
def unremovable(self):
|
||||||
|
return self._get_by_key('unremovable')
|
||||||
|
|
||||||
|
@property
|
||||||
|
def reboot_required(self):
|
||||||
|
return self._get_by_key('reboot_required')
|
||||||
|
|
||||||
|
@property
|
||||||
|
def restart_script(self):
|
||||||
|
return self._get_by_key('restart_script')
|
||||||
|
|
||||||
|
@property
|
||||||
|
def commit_checksum(self):
|
||||||
|
commit = self._get_latest_commit()
|
||||||
|
if commit is not None:
|
||||||
|
return commit['checksum']
|
||||||
|
else:
|
||||||
|
# may consider raise InvalidRelease exception when iso comes with
|
||||||
|
# latest commit
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
class SWReleaseCollection(object):
|
||||||
|
'''SWReleaseCollection encapsulates aggregated software release collection
|
||||||
|
managed by USM.
|
||||||
|
'''
|
||||||
|
|
||||||
|
def __init__(self, release_data):
|
||||||
|
self._sw_releases = {}
|
||||||
|
for rel_id in release_data.metadata:
|
||||||
|
rel_data = release_data.metadata[rel_id]
|
||||||
|
contents = release_data.contents[rel_id]
|
||||||
|
sw_release = SWRelease(rel_id, rel_data, contents)
|
||||||
|
self._sw_releases[rel_id] = sw_release
|
||||||
|
|
||||||
|
def get_release_by_id(self, rel_id):
|
||||||
|
if rel_id in self._sw_releases:
|
||||||
|
return self._sw_releases[rel_id]
|
||||||
|
return None
|
||||||
|
|
||||||
|
def get_release_by_commit_id(self, commit_id):
|
||||||
|
for _, sw_release in self._sw_releases:
|
||||||
|
if sw_release.commit_id == commit_id:
|
||||||
|
return sw_release
|
||||||
|
return None
|
||||||
|
|
||||||
|
def iterate_releases_by_state(self, state):
|
||||||
|
'''return iteration of releases matching specified state.
|
||||||
|
sorted by id in ascending order
|
||||||
|
'''
|
||||||
|
sorted_list = sorted(self._sw_releases)
|
||||||
|
for rel_id in sorted_list:
|
||||||
|
rel_data = self._sw_releases[rel_id]
|
||||||
|
if rel_data.metadata['state'] == state:
|
||||||
|
yield rel_data
|
||||||
|
|
||||||
|
def iterate_releases(self):
|
||||||
|
'''return iteration of all releases sorted by id in ascending order'''
|
||||||
|
sorted_list = sorted(self._sw_releases)
|
||||||
|
for rel_id in sorted_list:
|
||||||
|
yield self._sw_releases[rel_id]
|
||||||
|
|
||||||
|
def update_state(self, list_of_releases, state):
|
||||||
|
for release_id in list_of_releases:
|
||||||
|
release = self.get_release_by_id(release_id)
|
||||||
|
if release is not None:
|
||||||
|
if SWRelease.is_valid_state_transition(release.state, state):
|
||||||
|
SWRelease.ensure_state_transition(state)
|
||||||
|
else:
|
||||||
|
LOG.error("release %s not found" % release_id)
|
||||||
|
|
||||||
|
for release_id in list_of_releases:
|
||||||
|
release = self.get_release_by_id(release_id)
|
||||||
|
if release is not None:
|
||||||
|
release.update_state(state)
|
|
@ -33,6 +33,7 @@ from software.constants import DEPLOY_STATES
|
||||||
from software.base import PatchService
|
from software.base import PatchService
|
||||||
from software.exceptions import APTOSTreeCommandFail
|
from software.exceptions import APTOSTreeCommandFail
|
||||||
from software.db import api as db_api
|
from software.db import api as db_api
|
||||||
|
from software.exceptions import InternalError
|
||||||
from software.exceptions import MetadataFail
|
from software.exceptions import MetadataFail
|
||||||
from software.exceptions import UpgradeNotSupported
|
from software.exceptions import UpgradeNotSupported
|
||||||
from software.exceptions import OSTreeCommandFail
|
from software.exceptions import OSTreeCommandFail
|
||||||
|
@ -43,6 +44,7 @@ from software.exceptions import ReleaseInvalidRequest
|
||||||
from software.exceptions import ReleaseValidationFailure
|
from software.exceptions import ReleaseValidationFailure
|
||||||
from software.exceptions import ReleaseMismatchFailure
|
from software.exceptions import ReleaseMismatchFailure
|
||||||
from software.exceptions import ReleaseIsoDeleteFailure
|
from software.exceptions import ReleaseIsoDeleteFailure
|
||||||
|
from software.release_data import SWReleaseCollection
|
||||||
from software.software_functions import collect_current_load_for_hosts
|
from software.software_functions import collect_current_load_for_hosts
|
||||||
from software.software_functions import parse_release_metadata
|
from software.software_functions import parse_release_metadata
|
||||||
from software.software_functions import configure_logging
|
from software.software_functions import configure_logging
|
||||||
|
@ -653,6 +655,13 @@ class PatchController(PatchService):
|
||||||
else:
|
else:
|
||||||
self.write_state_file()
|
self.write_state_file()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def release_collection(self):
|
||||||
|
# for this stage, the SWReleaseCollection behaves as a broker which
|
||||||
|
# does not hold any release data. it only last one request
|
||||||
|
swrc = SWReleaseCollection(self.release_data)
|
||||||
|
return swrc
|
||||||
|
|
||||||
def update_config(self):
|
def update_config(self):
|
||||||
cfg.read_config()
|
cfg.read_config()
|
||||||
|
|
||||||
|
@ -2035,12 +2044,15 @@ class PatchController(PatchService):
|
||||||
ret["error"] += "Please fix above issues then retry the deploy.\n"
|
ret["error"] += "Please fix above issues then retry the deploy.\n"
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
collect_current_load_for_hosts()
|
|
||||||
if self._deploy_upgrade_start(to_release):
|
if self._deploy_upgrade_start(to_release):
|
||||||
collect_current_load_for_hosts()
|
collect_current_load_for_hosts()
|
||||||
dbapi = db_api.get_instance()
|
dbapi = db_api.get_instance()
|
||||||
dbapi.create_deploy(SW_VERSION, to_release, True)
|
dbapi.create_deploy(SW_VERSION, to_release, True)
|
||||||
dbapi.update_deploy(DEPLOY_STATES.DATA_MIGRATION)
|
dbapi.update_deploy(DEPLOY_STATES.DATA_MIGRATION)
|
||||||
|
sw_rel = self.release_collection.get_release_by_id(deployment)
|
||||||
|
if sw_rel is None:
|
||||||
|
raise InternalError("%s cannot be found" % to_release)
|
||||||
|
sw_rel.update_state(constants.DEPLOYING)
|
||||||
msg_info = "Deployment for %s started" % deployment
|
msg_info = "Deployment for %s started" % deployment
|
||||||
else:
|
else:
|
||||||
msg_error = "Deployment for %s failed to start" % deployment
|
msg_error = "Deployment for %s failed to start" % deployment
|
||||||
|
|
|
@ -299,9 +299,14 @@ class ReleaseData(object):
|
||||||
:param state: Indicates Applied, Available, or Committed
|
:param state: Indicates Applied, Available, or Committed
|
||||||
:return: Release ID
|
:return: Release ID
|
||||||
"""
|
"""
|
||||||
tree = ElementTree.parse(filename)
|
|
||||||
root = tree.getroot()
|
|
||||||
|
|
||||||
|
with open(filename, "r") as f:
|
||||||
|
text = f.read()
|
||||||
|
|
||||||
|
return self.parse_metadata_string(text, state)
|
||||||
|
|
||||||
|
def parse_metadata_string(self, text, state):
|
||||||
|
root = ElementTree.fromstring(text)
|
||||||
#
|
#
|
||||||
# <patch>
|
# <patch>
|
||||||
# <id>PATCH_0001</id>
|
# <id>PATCH_0001</id>
|
||||||
|
|
|
@ -0,0 +1,187 @@
|
||||||
|
#
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
#
|
||||||
|
# Copyright (c) 2024 Wind River Systems, Inc.
|
||||||
|
#
|
||||||
|
import unittest
|
||||||
|
from software.release_data import SWReleaseCollection
|
||||||
|
from software.software_functions import ReleaseData
|
||||||
|
|
||||||
|
metadata = """<?xml version="1.0" ?>
|
||||||
|
<patch>
|
||||||
|
<id>23.09_RR_ALL_NODES</id>
|
||||||
|
<sw_version>23.09</sw_version>
|
||||||
|
<summary>Debian patch test</summary>
|
||||||
|
<description>Reboot required patch</description>
|
||||||
|
<install_instructions>Sample instructions</install_instructions>
|
||||||
|
<warnings>Sample warning</warnings>
|
||||||
|
<status>TST</status>
|
||||||
|
<unremovable>Y</unremovable>
|
||||||
|
<reboot_required>Y</reboot_required>
|
||||||
|
<contents>
|
||||||
|
<ostree>
|
||||||
|
<number_of_commits>1</number_of_commits>
|
||||||
|
<base>
|
||||||
|
<commit>0db647647b009c5cc02410d461de0870049bdeb66caf1bdc1ccd189ac83b8e92</commit>
|
||||||
|
<checksum>bae3ff59c5f59c95aa8d3ccf8c1364c4c869cd428f7b5032a00a8b777cc132f7</checksum>
|
||||||
|
</base>
|
||||||
|
<commit1>
|
||||||
|
<commit>38453dcb1aeb5bb9394ed02c0e6b8f2f913d00a827c89faf98cb63dff503b8e2</commit>
|
||||||
|
<checksum>2f742b1b719f19b302c306604659ccf4aa61a1fdb7742ac79b009c79af18c79b</checksum>
|
||||||
|
</commit1>
|
||||||
|
</ostree>
|
||||||
|
</contents>
|
||||||
|
<requires/>
|
||||||
|
<semantics/>
|
||||||
|
</patch>"""
|
||||||
|
|
||||||
|
metadata2 = """<?xml version="1.0" ?>
|
||||||
|
<patch>
|
||||||
|
<id>23.09_NRR_INSVC</id>
|
||||||
|
<sw_version>23.09</sw_version>
|
||||||
|
<summary>Debian patch test</summary>
|
||||||
|
<description>In service patch</description>
|
||||||
|
<install_instructions>Sample instructions2</install_instructions>
|
||||||
|
<warnings>Sample warning2</warnings>
|
||||||
|
<status>DEV</status>
|
||||||
|
<unremovable>N</unremovable>
|
||||||
|
<reboot_required>N</reboot_required>
|
||||||
|
<restart_script>23.09_NRR_INSVC_example-cgcs-patch-restart</restart_script>
|
||||||
|
<contents>
|
||||||
|
<ostree>
|
||||||
|
<number_of_commits>1</number_of_commits>
|
||||||
|
<base>
|
||||||
|
<commit>0db647647b009c5cc02410d461de0870049bdeb66caf1bdc1ccd189ac83b8e92</commit>
|
||||||
|
<checksum>bae3ff59c5f59c95aa8d3ccf8c1364c4c869cd428f7b5032a00a8b777cc132f7</checksum>
|
||||||
|
</base>
|
||||||
|
<commit1>
|
||||||
|
<commit>0b53576092a189133d56eac49ae858c1218f480a4a859eaca2b47f2604a4e0e7</commit>
|
||||||
|
<checksum>2f742b1b719f19b302c306604659ccf4aa61a1fdb7742ac79b009c79af18c79b</checksum>
|
||||||
|
</commit1>
|
||||||
|
</ostree>
|
||||||
|
</contents>
|
||||||
|
<requires/>
|
||||||
|
<semantics/>
|
||||||
|
</patch>"""
|
||||||
|
|
||||||
|
expected_values = [
|
||||||
|
{
|
||||||
|
"release_id": "23.09_NRR_INSVC",
|
||||||
|
"version": "23.09",
|
||||||
|
"state": "deployed",
|
||||||
|
"summary": "Debian patch test",
|
||||||
|
"description": "In service patch",
|
||||||
|
"install_instructions": "Sample instructions2",
|
||||||
|
"warnings": "Sample warning2",
|
||||||
|
"status": "DEV",
|
||||||
|
"unremovable": "N",
|
||||||
|
"restart_script": "23.09_NRR_INSVC_example-cgcs-patch-restart",
|
||||||
|
"commit_id": "0b53576092a189133d56eac49ae858c1218f480a4a859eaca2b47f2604a4e0e7",
|
||||||
|
"checksum": "2f742b1b719f19b302c306604659ccf4aa61a1fdb7742ac79b009c79af18c79b",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"release_id": "23.09_RR_ALL_NODES",
|
||||||
|
"version": "23.09",
|
||||||
|
"state": "available",
|
||||||
|
"summary": "Debian patch test",
|
||||||
|
"description": "Reboot required patch",
|
||||||
|
"install_instructions": "Sample instructions",
|
||||||
|
"warnings": "Sample warning",
|
||||||
|
"status": "TST",
|
||||||
|
"unremovable": "Y",
|
||||||
|
"restart_script": None,
|
||||||
|
"commit_id": "38453dcb1aeb5bb9394ed02c0e6b8f2f913d00a827c89faf98cb63dff503b8e2",
|
||||||
|
"checksum": "2f742b1b719f19b302c306604659ccf4aa61a1fdb7742ac79b009c79af18c79b",
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
package_dir = {"23.09": "/var/www/page/feed/rel_23.09"}
|
||||||
|
|
||||||
|
|
||||||
|
class TestSoftwareFunction(unittest.TestCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
@property
|
||||||
|
def release_collection(self):
|
||||||
|
rd = ReleaseData()
|
||||||
|
rd.parse_metadata_string(metadata, "available")
|
||||||
|
rd2 = ReleaseData()
|
||||||
|
rd2.parse_metadata_string(metadata2, "deployed")
|
||||||
|
rd.add_release(rd2)
|
||||||
|
|
||||||
|
rc = SWReleaseCollection(rd)
|
||||||
|
return rc
|
||||||
|
|
||||||
|
def test_SWReleaseCollection_iterate_releases(self):
|
||||||
|
idx = 0
|
||||||
|
for r in self.release_collection.iterate_releases():
|
||||||
|
val = expected_values[idx]
|
||||||
|
idx += 1
|
||||||
|
self.assertEqual(val["release_id"], r.id)
|
||||||
|
self.assertEqual(val["version"], r.sw_version)
|
||||||
|
self.assertEqual(val["state"], r.state)
|
||||||
|
self.assertEqual(val["summary"], r.summary)
|
||||||
|
self.assertEqual(val["description"], r.description)
|
||||||
|
self.assertEqual(val["install_instructions"], r.install_instructions)
|
||||||
|
self.assertEqual(val["warnings"], r.warnings)
|
||||||
|
self.assertEqual(val["status"], r.status)
|
||||||
|
self.assertEqual(val["unremovable"], r.unremovable)
|
||||||
|
if val["restart_script"] is None:
|
||||||
|
self.assertIsNone(r.restart_script)
|
||||||
|
else:
|
||||||
|
self.assertEqual(val["restart_script"], r.restart_script)
|
||||||
|
self.assertEqual(val["commit_id"], r.commit_id)
|
||||||
|
self.assertEqual(val["checksum"], r.commit_checksum)
|
||||||
|
|
||||||
|
def test_SWReleaseCollection_get_release_by_id(self):
|
||||||
|
rd = ReleaseData()
|
||||||
|
rd.parse_metadata_string(metadata, "available")
|
||||||
|
rd2 = ReleaseData()
|
||||||
|
rd2.parse_metadata_string(metadata2, "deployed")
|
||||||
|
rd.add_release(rd2)
|
||||||
|
|
||||||
|
rc = SWReleaseCollection(rd)
|
||||||
|
|
||||||
|
idx = 0
|
||||||
|
rid = expected_values[idx]["release_id"]
|
||||||
|
r = rc.get_release_by_id(rid)
|
||||||
|
val = expected_values[idx]
|
||||||
|
self.assertEqual(val["release_id"], r.id)
|
||||||
|
self.assertEqual(val["version"], r.sw_version)
|
||||||
|
self.assertEqual(val["state"], r.state)
|
||||||
|
self.assertEqual(val["summary"], r.summary)
|
||||||
|
self.assertEqual(val["description"], r.description)
|
||||||
|
self.assertEqual(val["install_instructions"], r.install_instructions)
|
||||||
|
self.assertEqual(val["warnings"], r.warnings)
|
||||||
|
self.assertEqual(val["status"], r.status)
|
||||||
|
self.assertEqual(val["unremovable"], r.unremovable)
|
||||||
|
if val["restart_script"] is None:
|
||||||
|
self.assertIsNone(r.restart_script)
|
||||||
|
else:
|
||||||
|
self.assertEqual(val["restart_script"], r.restart_script)
|
||||||
|
self.assertEqual(val["commit_id"], r.commit_id)
|
||||||
|
self.assertEqual(val["checksum"], r.commit_checksum)
|
||||||
|
|
||||||
|
def test_SWReleaseCollection_iterate_release_by_state(self):
|
||||||
|
val = expected_values[0]
|
||||||
|
for r in self.release_collection.iterate_releases_by_state('deployed'):
|
||||||
|
self.assertEqual(val["release_id"], r.id)
|
||||||
|
self.assertEqual(val["version"], r.sw_version)
|
||||||
|
self.assertEqual(val["state"], r.state)
|
||||||
|
self.assertEqual(val["summary"], r.summary)
|
||||||
|
self.assertEqual(val["description"], r.description)
|
||||||
|
self.assertEqual(val["install_instructions"], r.install_instructions)
|
||||||
|
self.assertEqual(val["warnings"], r.warnings)
|
||||||
|
self.assertEqual(val["status"], r.status)
|
||||||
|
self.assertEqual(val["unremovable"], r.unremovable)
|
||||||
|
if val["restart_script"] is None:
|
||||||
|
self.assertIsNone(r.restart_script)
|
||||||
|
else:
|
||||||
|
self.assertEqual(val["restart_script"], r.restart_script)
|
||||||
|
self.assertEqual(val["commit_id"], r.commit_id)
|
||||||
|
self.assertEqual(val["checksum"], r.commit_checksum)
|
Loading…
Reference in New Issue