Merge "Clean software orchestration states"

This commit is contained in:
Zuul 2024-02-14 19:22:35 +00:00 committed by Gerrit Code Review
commit 49e800a258
31 changed files with 264 additions and 1349 deletions

View File

@ -154,16 +154,6 @@ STRATEGY_STATE_DELETING_LOAD = "deleting load"
# Software orchestration states
STRATEGY_STATE_SW_PRE_CHECK = "software pre check"
STRATEGY_STATE_SW_INSTALL_LICENSE = "software install license"
STRATEGY_STATE_SW_UPLOAD = "software upload"
STRATEGY_STATE_SW_DEPLOY_PRE_CHECK = "software deploy pre check"
STRATEGY_STATE_SW_DEPLOY_START = "software deploy start"
STRATEGY_STATE_SW_LOCK_CONTROLLER = "software lock controller"
STRATEGY_STATE_SW_UNLOCK_CONTROLLER = "software unlock controller"
STRATEGY_STATE_SW_SWACT_CONTROLLER_0 = "software swact controller-0"
STRATEGY_STATE_SW_SWACT_CONTROLLER_1 = "software swact controller-1"
STRATEGY_STATE_SW_DEPLOY_HOST = "software deploy host"
STRATEGY_STATE_SW_DEPLOY_ACTIVATE = "software deploy activate"
STRATEGY_STATE_SW_DEPLOY_COMPLETE = "software deploy complete"
STRATEGY_STATE_SW_CREATE_VIM_STRATEGY = "create VIM software strategy"
STRATEGY_STATE_SW_APPLY_VIM_STRATEGY = "apply VIM software strategy"
STRATEGY_STATE_SW_FINISH_STRATEGY = "finish software strategy"

View File

@ -1,11 +1,9 @@
#
# Copyright (c) 2023 Wind River Systems, Inc.
# Copyright (c) 2023-2024 Wind River Systems, Inc.
#
# SPDX-License-Identifier: Apache-2.0
#
from oslo_log import log as logging
from dccommon.drivers.openstack import vim
from dcmanager.common import consts
from dcmanager.orchestrator.orch_thread import OrchThread
@ -15,34 +13,11 @@ from dcmanager.orchestrator.states.software.cache.shared_cache_repository import
SharedCacheRepository
from dcmanager.orchestrator.states.software.create_vim_software_strategy \
import CreateVIMSoftwareStrategyState
from dcmanager.orchestrator.states.software.deploy_activate \
import DeployActivateState
from dcmanager.orchestrator.states.software.deploy_complete \
import DeployCompleteState
from dcmanager.orchestrator.states.software.deploy_host \
import DeployHostState
from dcmanager.orchestrator.states.software.deploy_pre_check \
import DeployPreCheckState
from dcmanager.orchestrator.states.software.deploy_start \
import DeployStartState
from dcmanager.orchestrator.states.software.finish_strategy \
import FinishStrategyState
from dcmanager.orchestrator.states.software.install_license \
import InstallLicenseState
from dcmanager.orchestrator.states.software.lock_controller \
import LockControllerState
from dcmanager.orchestrator.states.software.pre_check \
import PreCheckState
from dcmanager.orchestrator.states.software.swact_controller0 \
import SwactController0State
from dcmanager.orchestrator.states.software.swact_controller1 \
import SwactController1State
from dcmanager.orchestrator.states.software.unlock_controller \
import UnlockControllerState
from dcmanager.orchestrator.states.software.upload \
import UploadState
LOG = logging.getLogger(__name__)
from dcmanager.orchestrator.states.software.pre_check import PreCheckState
class SoftwareOrchThread(OrchThread):
@ -66,26 +41,16 @@ class SoftwareOrchThread(OrchThread):
STATE_OPERATORS = {
consts.STRATEGY_STATE_SW_PRE_CHECK: PreCheckState,
consts.STRATEGY_STATE_SW_INSTALL_LICENSE: InstallLicenseState,
consts.STRATEGY_STATE_SW_UPLOAD: UploadState,
consts.STRATEGY_STATE_SW_DEPLOY_PRE_CHECK: DeployPreCheckState,
consts.STRATEGY_STATE_SW_DEPLOY_START: DeployStartState,
consts.STRATEGY_STATE_SW_LOCK_CONTROLLER: LockControllerState,
consts.STRATEGY_STATE_SW_DEPLOY_HOST: DeployHostState,
consts.STRATEGY_STATE_SW_UNLOCK_CONTROLLER: UnlockControllerState,
consts.STRATEGY_STATE_SW_SWACT_CONTROLLER_0: SwactController0State,
consts.STRATEGY_STATE_SW_CREATE_VIM_STRATEGY: CreateVIMSoftwareStrategyState,
consts.STRATEGY_STATE_SW_APPLY_VIM_STRATEGY: ApplyVIMSoftwareStrategyState,
consts.STRATEGY_STATE_SW_SWACT_CONTROLLER_1: SwactController1State,
consts.STRATEGY_STATE_SW_DEPLOY_ACTIVATE: DeployActivateState,
consts.STRATEGY_STATE_SW_DEPLOY_COMPLETE: DeployCompleteState,
consts.STRATEGY_STATE_SW_FINISH_STRATEGY: FinishStrategyState,
}
def __init__(self, strategy_lock, audit_rpc_client):
super(SoftwareOrchThread, self).__init__(
super().__init__(
strategy_lock,
audit_rpc_client,
consts.SW_UPDATE_TYPE_UPGRADE, # software update strategy type
consts.SW_UPDATE_TYPE_SOFTWARE, # software update strategy type
vim.STRATEGY_NAME_SW_UPGRADE, # strategy type used by vim
consts.STRATEGY_STATE_SW_PRE_CHECK) # starting state
@ -100,10 +65,9 @@ class SoftwareOrchThread(OrchThread):
def pre_apply_setup(self):
# Restart caches for next strategy
self._shared_caches.initialize_caches()
super(SoftwareOrchThread, self).pre_apply_setup()
super().pre_apply_setup()
def determine_state_operator(self, strategy_step):
state = super(SoftwareOrchThread, self).determine_state_operator(
strategy_step)
state = super().determine_state_operator(strategy_step)
state.add_shared_caches(self._shared_caches)
return state

View File

@ -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
#
@ -12,8 +12,8 @@ class ApplyVIMSoftwareStrategyState(BaseState):
"""Apply VIM Software Strategy software orchestration state"""
def __init__(self, region_name):
super(ApplyVIMSoftwareStrategyState, self).__init__(
next_state=consts.STRATEGY_STATE_SW_SWACT_CONTROLLER_1,
super().__init__(
next_state=consts.STRATEGY_STATE_SW_FINISH_STRATEGY,
region_name=region_name
)

View File

@ -1,22 +0,0 @@
#
# Copyright (c) 2023 Wind River Systems, Inc.
#
# SPDX-License-Identifier: Apache-2.0
#
from dcmanager.common import consts
from dcmanager.orchestrator.states.base import BaseState
class DeployActivateState(BaseState):
"""Deploy activate software orchestration state"""
def __init__(self, region_name):
super(DeployActivateState, self).__init__(
next_state=consts.STRATEGY_STATE_SW_DEPLOY_COMPLETE,
region_name=region_name,
)
def perform_state_action(self, strategy_step):
"""Deploy Activate region status"""
return self.next_state

View File

@ -1,22 +0,0 @@
#
# Copyright (c) 2023 Wind River Systems, Inc.
#
# SPDX-License-Identifier: Apache-2.0
#
from dcmanager.common import consts
from dcmanager.orchestrator.states.base import BaseState
class DeployCompleteState(BaseState):
"""Deploy complete software orchestration state"""
def __init__(self, region_name):
super(DeployCompleteState, self).__init__(
next_state=consts.STRATEGY_STATE_SW_FINISH_STRATEGY,
region_name=region_name,
)
def perform_state_action(self, strategy_step):
"""Deploy complete region status"""
return self.next_state

View File

@ -1,22 +0,0 @@
#
# Copyright (c) 2023 Wind River Systems, Inc.
#
# SPDX-License-Identifier: Apache-2.0
#
from dcmanager.common import consts
from dcmanager.orchestrator.states.base import BaseState
class DeployHostState(BaseState):
"""Deploy host software orchestration state"""
def __init__(self, region_name):
super(DeployHostState, self).__init__(
next_state=consts.STRATEGY_STATE_SW_UNLOCK_CONTROLLER,
region_name=region_name,
)
def perform_state_action(self, strategy_step):
"""Deploy host region status"""
return self.next_state

View File

@ -1,22 +0,0 @@
#
# Copyright (c) 2023 Wind River Systems, Inc.
#
# SPDX-License-Identifier: Apache-2.0
#
from dcmanager.common import consts
from dcmanager.orchestrator.states.base import BaseState
class DeployPreCheckState(BaseState):
"""Deploy pre check software orchestration state"""
def __init__(self, region_name):
super(DeployPreCheckState, self).__init__(
next_state=consts.STRATEGY_STATE_SW_DEPLOY_START,
region_name=region_name,
)
def perform_state_action(self, strategy_step):
"""Deploy pre check region status"""
return self.next_state

View File

@ -1,94 +0,0 @@
#
# Copyright (c) 2023-2024 Wind River Systems, Inc.
#
# SPDX-License-Identifier: Apache-2.0
#
from dccommon.drivers.openstack import software_v1
from dcmanager.common import consts
from dcmanager.orchestrator.states.base import BaseState
from dcmanager.orchestrator.states.software.cache.cache_specifications import \
REGION_ONE_RELEASE_USM_CACHE_TYPE
# Max time: 1 minute = 6 queries x 10 seconds between
DEFAULT_MAX_QUERIES = 6
DEFAULT_SLEEP_DURATION = 10
class DeployStartState(BaseState):
"""Software orchestration state for deploy start releases"""
def __init__(self, region_name):
super(DeployStartState, self).__init__(
next_state=consts.STRATEGY_STATE_SW_DEPLOY_HOST,
region_name=region_name)
self.max_queries = DEFAULT_MAX_QUERIES
self.sleep_duration = DEFAULT_SLEEP_DURATION
def _get_deployed_controller_patches(self):
regionone_releases = self._read_from_cache(REGION_ONE_RELEASE_USM_CACHE_TYPE)
deployed_releases = {}
for release_id, release_info in regionone_releases.items():
if release_info['state'] == software_v1.DEPLOYED:
deployed_releases[release_id] = release_info
return deployed_releases
def perform_state_action(self, strategy_step):
"""Deploy start releases in this subcloud"""
self.info_log(strategy_step, "Applying releases")
deployed_releases = self._get_deployed_controller_patches()
self.debug_log(strategy_step,
f"SystemController deployed releases: {deployed_releases}")
# Find the max version deployed on the SystemController
max_version = None
for deployed_releases_values in deployed_releases.values():
release_sw_version = deployed_releases_values['sw_version']
if max_version is None or release_sw_version > max_version:
max_version = release_sw_version
# Retrieve all subcloud releases
try:
subcloud_releases = self.get_software_client(self.region_name).query()
self.debug_log(strategy_step,
f"Subcloud releases: {subcloud_releases}")
except Exception:
message = ("Cannot retrieve subcloud releases. Please see logs for "
"details.")
self.exception_log(strategy_step, message)
raise Exception(message)
deploy_start_release = None
for release_id in subcloud_releases:
is_reboot_required = subcloud_releases[release_id][
'reboot_required'] == "Y"
is_available = subcloud_releases[release_id]['state'] == (
software_v1.AVAILABLE)
is_deployed = subcloud_releases[release_id]['state'] == (
software_v1.DEPLOYED)
release_sw_version = subcloud_releases[release_id]['sw_version']
# Check if any release is reboot required
if deployed_releases.get(
release_id) and is_available and is_reboot_required:
self.override_next_state(consts.STRATEGY_STATE_SW_LOCK_CONTROLLER)
# Get the only release needed to be deployed
if (is_deployed and release_sw_version == max_version) or (
is_available and release_sw_version == max_version):
deploy_start_release = release_id
if deploy_start_release:
self.info_log(strategy_step,
f"Deploy start release {deploy_start_release} to subcloud")
try:
self.get_software_client(self.region_name).deploy_start(
deploy_start_release)
except Exception:
message = (
"Cannot deploy start releases to subcloud. Please see logs "
"for details.")
self.exception_log(strategy_step, message)
raise Exception(message)
return self.next_state

View File

@ -15,7 +15,7 @@ class FinishStrategyState(BaseState):
"""Finish Software Strategy software orchestration state"""
def __init__(self, region_name):
super(FinishStrategyState, self).__init__(
super().__init__(
next_state=consts.STRATEGY_STATE_COMPLETE,
region_name=region_name,
)
@ -32,20 +32,20 @@ class FinishStrategyState(BaseState):
self.debug_log(
strategy_step,
"regionone_committed_releases: %s" % regionone_committed_releases
f"regionone_committed_releases: {regionone_committed_releases}"
)
try:
software_client = self.get_software_client(self.region_name)
subcloud_releases = software_client.query()
except Exception:
message = ("Cannot retrieve subcloud releases. Please see logs for"
" details.")
message = ("Cannot retrieve subcloud releases. Please see logs for "
"details.")
self.exception_log(strategy_step, message)
raise Exception(message)
self.debug_log(strategy_step,
"Releases for subcloud: %s" % subcloud_releases)
f"Releases for subcloud: {subcloud_releases}")
releases_to_commit = list()
releases_to_delete = list()
@ -58,13 +58,13 @@ class FinishStrategyState(BaseState):
subcloud_releases[release_id]['state'] ==
software_v1.UNAVAILABLE):
releases_to_delete.append(release_id)
elif subcloud_releases[release_id]['state'] == \
software_v1.DEPLOYED:
elif (subcloud_releases[release_id]['state'] ==
software_v1.DEPLOYED):
if release_id in regionone_committed_releases:
releases_to_commit.append(release_id)
if releases_to_delete:
self.info_log(strategy_step, "Deleting releases %s" % releases_to_delete)
self.info_log(strategy_step, f"Deleting releases {releases_to_delete}")
try:
software_client.delete(releases_to_delete)
except Exception:
@ -78,12 +78,12 @@ class FinishStrategyState(BaseState):
if releases_to_commit:
self.info_log(strategy_step,
"Committing releases %s to subcloud" % releases_to_commit)
f"Committing releases {releases_to_commit} to subcloud")
try:
software_client.commit_patch(releases_to_commit)
except Exception:
message = ("Cannot commit releases to subcloud. Please see logs for"
" details.")
message = ("Cannot commit releases to subcloud. Please see logs for "
"details.")
self.exception_log(strategy_step, message)
raise Exception(message)

View File

@ -1,22 +1,102 @@
#
# Copyright (c) 2023 Wind River Systems, Inc.
# Copyright (c) 2020-2024 Wind River Systems, Inc.
#
# SPDX-License-Identifier: Apache-2.0
#
from dccommon import consts as dccommon_consts
from dcmanager.common import consts
from dcmanager.common import exceptions
from dcmanager.db import api as db_api
from dcmanager.orchestrator.states.base import BaseState
from dcmanager.orchestrator.states.software.cache.cache_specifications import \
REGION_ONE_LICENSE_CACHE_TYPE
# When a license is not installed, this will be part of the API error string
LICENSE_FILE_NOT_FOUND_SUBSTRING = "License file not found"
class InstallLicenseState(BaseState):
"""Install license software orchestration state"""
"""Software orchestration state action for installing a license"""
def __init__(self, region_name):
super(InstallLicenseState, self).__init__(
next_state=consts.STRATEGY_STATE_SW_UPLOAD,
region_name=region_name,
super().__init__(
next_state=consts.STRATEGY_STATE_SW_CREATE_VIM_STRATEGY,
region_name=region_name
)
@staticmethod
def license_up_to_date(target_license, existing_license):
return target_license == existing_license
def perform_state_action(self, strategy_step):
"""Install license region status"""
"""Install the License for a software deploy in this subcloud
Returns the next state in the state machine on success.
Any exceptions raised by this method set the strategy to FAILED.
"""
# check if the system controller has a license
system_controller_license = self._read_from_cache(
REGION_ONE_LICENSE_CACHE_TYPE)
# get_license returns a dictionary with keys: content and error
# 'content' can be an empty string in success or failure case.
# 'error' is an empty string only in success case.
target_license = system_controller_license.get('content')
target_error = system_controller_license.get('error')
# If the system controller does not have a license, do not attempt
# to install licenses on subclouds, simply proceed to the next stage
if len(target_error) != 0:
if LICENSE_FILE_NOT_FOUND_SUBSTRING in target_error:
self.info_log(strategy_step,
f"System Controller License missing: {target_error}.")
return self.next_state
else:
# An unexpected error occurred querying the license
message = ('An unexpected error occurred querying the license '
f'{dccommon_consts.SYSTEM_CONTROLLER_NAME}. '
f'Detail: {target_error}')
db_api.subcloud_update(
self.context, strategy_step.subcloud_id,
error_description=message[0:consts.ERROR_DESCRIPTION_LENGTH])
raise exceptions.LicenseInstallError(
subcloud_id=dccommon_consts.SYSTEM_CONTROLLER_NAME,
error_message=target_error)
# retrieve the keystone session for the subcloud and query its license
subcloud_sysinv_client = self.get_sysinv_client(
strategy_step.subcloud.region_name)
subcloud_license_response = subcloud_sysinv_client.get_license()
subcloud_license = subcloud_license_response.get('content')
subcloud_error = subcloud_license_response.get('error')
# Skip license install if the license is already up to date
# If there was not an error, there might be a license
if len(subcloud_error) == 0:
if self.license_up_to_date(target_license, subcloud_license):
self.info_log(strategy_step, "License up to date.")
return self.next_state
else:
self.debug_log(strategy_step, "License mismatch. Updating.")
else:
self.debug_log(strategy_step, "License missing. Installing.")
# Install the license
install_rc = subcloud_sysinv_client.install_license(target_license)
install_error = install_rc.get('error')
if len(install_error) != 0:
# Save error response from sysinv into subcloud error description.
# Provide exception with sysinv error response to strategy_step details
message = ('Error installing license on subcloud '
f'{strategy_step.subcloud.name}. Detail: {install_error}')
db_api.subcloud_update(
self.context, strategy_step.subcloud_id,
error_description=message[0:consts.ERROR_DESCRIPTION_LENGTH])
raise exceptions.LicenseInstallError(
subcloud_id=strategy_step.subcloud_id,
error_message=install_error)
# The license has been successfully installed. Move to the next stage
self.info_log(strategy_step, "License installed.")
return self.next_state

View File

@ -1,27 +0,0 @@
#
# Copyright (c) 2023 Wind River Systems, Inc.
#
# SPDX-License-Identifier: Apache-2.0
#
from dcmanager.common import consts
from dcmanager.orchestrator.states.lock_host import LockHostState
class LockControllerState(LockHostState):
"""Lock controller software orchestration state"""
def __init__(self, region_name):
super(LockControllerState, self).__init__(
next_state=consts.STRATEGY_STATE_SW_DEPLOY_HOST,
region_name=region_name,
hostname=self.get_hostname(region_name)
)
def get_hostname(self, region_name):
subcloud_type = self.get_sysinv_client(
region_name).get_system().system_mode
if subcloud_type == consts.SYSTEM_MODE_SIMPLEX:
return "controller-0"
else:
return "controller-1"

View File

@ -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
#
@ -12,7 +12,7 @@ class PreCheckState(BaseState):
"""Pre check software orchestration state"""
def __init__(self, region_name):
super(PreCheckState, self).__init__(
super().__init__(
next_state=consts.STRATEGY_STATE_SW_INSTALL_LICENSE,
region_name=region_name,
)

View File

@ -1,19 +0,0 @@
#
# Copyright (c) 2023 Wind River Systems, Inc.
#
# SPDX-License-Identifier: Apache-2.0
#
from dcmanager.common import consts
from dcmanager.orchestrator.states.swact_host import SwactHostState
class SwactController0State(SwactHostState):
"""Software orchestration state to swact away from controller-0"""
def __init__(self, region_name):
super(SwactController0State, self).__init__(
next_state=consts.STRATEGY_STATE_SW_CREATE_VIM_STRATEGY,
region_name=region_name,
active="controller-1",
standby="controller-0",)

View File

@ -1,18 +0,0 @@
#
# Copyright (c) 2023 Wind River Systems, Inc.
#
# SPDX-License-Identifier: Apache-2.0
#
from dcmanager.common import consts
from dcmanager.orchestrator.states.swact_host import SwactHostState
class SwactController1State(SwactHostState):
"""Software orchestration state to swact away from controller-1"""
def __init__(self, region_name):
super(SwactController1State, self).__init__(
next_state=consts.STRATEGY_STATE_SW_DEPLOY_ACTIVATE,
region_name=region_name,
active="controller-0",
standby="controller-1")

View File

@ -1,22 +0,0 @@
#
# Copyright (c) 2023 Wind River Systems, Inc.
#
# SPDX-License-Identifier: Apache-2.0
#
from dcmanager.common import consts
from dcmanager.orchestrator.states.base import BaseState
class UnlockControllerState(BaseState):
"""Unlock controller software orchestration state"""
def __init__(self, region_name):
super(UnlockControllerState, self).__init__(
next_state=consts.STRATEGY_STATE_SW_DEPLOY_ACTIVATE,
region_name=region_name,
)
def perform_state_action(self, strategy_step):
"""Unlock controller region status"""
return self.next_state

View File

@ -1,155 +0,0 @@
#
# Copyright (c) 2023-2024 Wind River Systems, Inc.
#
# SPDX-License-Identifier: Apache-2.0
#
import os
import time
from dccommon.drivers.openstack import software_v1
from dcmanager.common import consts
from dcmanager.common.exceptions import StrategyStoppedException
from dcmanager.common import utils
from dcmanager.orchestrator.states.base import BaseState
from dcmanager.orchestrator.states.software.cache.cache_specifications import \
REGION_ONE_RELEASE_USM_CACHE_TYPE
# Max time: 30 minutes = 180 queries x 10 seconds between
DEFAULT_MAX_QUERIES = 180
DEFAULT_SLEEP_DURATION = 10
class UploadState(BaseState):
"""Software orchestration state for uploading releases"""
def __init__(self, region_name):
super(UploadState, self).__init__(
next_state=consts.STRATEGY_STATE_SW_DEPLOY_PRE_CHECK,
region_name=region_name)
self.sleep_duration = DEFAULT_SLEEP_DURATION
self.max_queries = DEFAULT_MAX_QUERIES
def _get_major_minor_versions(self, release_sw_version):
return release_sw_version.rsplit('.', 1)
def _find_missing_patches(self, subcloud_releases,
potential_missing_patches):
return [potential_missing_patch for potential_missing_patch
in potential_missing_patches
if potential_missing_patch not in subcloud_releases]
def perform_state_action(self, strategy_step):
"""Upload releases in this subcloud"""
self.info_log(strategy_step, "Uploading releases")
regionone_releases = self._read_from_cache(REGION_ONE_RELEASE_USM_CACHE_TYPE)
applied_releases_ids = list()
for release_id in regionone_releases:
if regionone_releases[release_id]['state'] in [
software_v1.DEPLOYED,
software_v1.COMMITTED]:
applied_releases_ids.append(release_id)
# Retrieve all subcloud releases
try:
subcloud_releases = self.get_software_client(
self.region_name).query()
except Exception:
message = ("Cannot retrieve subcloud releases. Please "
"see /var/log/software.log for details.")
self.exception_log(strategy_step, message)
raise Exception(message)
releases_to_upload = []
# RegionOne applied releases not present on the subcloud needs to be uploaded
for release_id in applied_releases_ids:
if release_id not in subcloud_releases:
self.info_log(strategy_step, (f"Release {release_id} missing from "
"subloud"))
releases_to_upload.append(release_id)
if releases_to_upload:
self.info_log(strategy_step,
f"Uploading releases {releases_to_upload} to subcloud")
files_to_upload = []
potential_missing_patches = []
iso_release = None
for release in releases_to_upload:
major_sw_version, minor_sw_version = self._get_major_minor_versions(
regionone_releases[release]['sw_version'])
# when minor is 0, it means that the release is an iso
if minor_sw_version == consts.ISO_VERSION:
iso_path, sig_path = utils.get_vault_load_files(major_sw_version)
files_to_upload.extend([iso_path, sig_path])
iso_release = release
else:
patch_path = (f"{consts.RELEASE_VAULT_DIR}/"
f"{major_sw_version}/{release}.patch")
if not os.path.isfile(patch_path):
# patch wasn't found but it may be included in an iso
potential_missing_patches.append(release)
else:
files_to_upload.append(patch_path)
if files_to_upload:
try:
self.get_software_client(
self.region_name).upload(files_to_upload)
except Exception:
message = ("Cannot upload releases to subcloud. Please "
"see /var/log/software.log for details.")
self.exception_log(strategy_step, message)
raise Exception(message)
if self.stopped():
self.info_log(strategy_step, "Exiting because task was stopped")
raise StrategyStoppedException()
if iso_release:
audit_counter = 0
while True:
time.sleep(self.sleep_duration)
if self.stopped():
raise StrategyStoppedException()
try:
subcloud_releases = self.get_software_client(
self.region_name).query()
except Exception:
self.debug_log(strategy_step, "failed to retrieve releases.")
if iso_release in subcloud_releases:
if potential_missing_patches:
# Retrieve patches that are present in the system
# controller and not in the subcloud after uploading
# load to the subcloud.
missing_patches = self. \
_find_missing_patches(subcloud_releases,
potential_missing_patches)
if missing_patches:
message = (f"Release files {missing_patches} "
"are missing")
self.error_log(strategy_step, message)
raise Exception(message)
break
audit_counter += 1
if audit_counter >= self.max_queries:
details = ("Timeout waiting for load import to complete. "
"Please check software.log on the subcloud.")
self.exception_log(strategy_step, details)
raise Exception(details)
else:
# No load was uploaded therefore the patches are really missing.
if potential_missing_patches:
message = (f"Release files {potential_missing_patches} "
"are missing")
self.error_log(strategy_step, message)
raise Exception(message)
return self.next_state

View File

@ -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
#
@ -11,9 +11,9 @@ from dcmanager.tests.unit.orchestrator.states.software.test_base import \
class TestApplyVIMSoftwareStrategyState(TestSoftwareOrchestrator):
def setUp(self):
super(TestApplyVIMSoftwareStrategyState, self).setUp()
super().setUp()
self.on_success_state = consts.STRATEGY_STATE_SW_SWACT_CONTROLLER_1
self.on_success_state = consts.STRATEGY_STATE_SW_FINISH_STRATEGY
# Add the subcloud being processed by this unit test
self.subcloud = self.setup_subcloud()
@ -28,5 +28,5 @@ class TestApplyVIMSoftwareStrategyState(TestSoftwareOrchestrator):
self.worker.perform_state_action(self.strategy_step)
# On success, the state should transition to the next state
self.assert_step_updated(self.strategy_step.subcloud_id,
self.on_success_state)
self.assert_step_updated(
self.strategy_step.subcloud_id, self.on_success_state)

View File

@ -18,15 +18,15 @@ class TestSoftwareOrchestrator(TestSwUpdate):
DEFAULT_STRATEGY_TYPE = consts.SW_UPDATE_TYPE_SOFTWARE
def setUp(self):
super(TestSoftwareOrchestrator, self).setUp()
super().setUp()
# Modify cache helpers to return client mocks
self.software_cache_client_mock = mock.patch(
"%s.get_software_client" % CACHE_CLIENT_PATH,
f"{CACHE_CLIENT_PATH}.get_software_client",
return_value=self.software_client,
)
self.sysinv_cache_client_mock = mock.patch(
"%s.get_sysinv_client" % CACHE_CLIENT_PATH,
f"{CACHE_CLIENT_PATH}.get_sysinv_client",
return_value=self.sysinv_client
)
self.software_cache_client_mock.start()
@ -35,4 +35,4 @@ class TestSoftwareOrchestrator(TestSwUpdate):
def tearDown(self):
self.software_cache_client_mock.stop()
self.sysinv_cache_client_mock.stop()
super(TestSoftwareOrchestrator, self).tearDown()
super().tearDown()

View File

@ -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
#
@ -11,7 +11,7 @@ from dcmanager.tests.unit.orchestrator.states.software.test_base import \
class TestCreateVIMSoftwareStrategyState(TestSoftwareOrchestrator):
def setUp(self):
super(TestCreateVIMSoftwareStrategyState, self).setUp()
super().setUp()
self.on_success_state = consts.STRATEGY_STATE_SW_APPLY_VIM_STRATEGY
@ -28,5 +28,5 @@ class TestCreateVIMSoftwareStrategyState(TestSoftwareOrchestrator):
self.worker.perform_state_action(self.strategy_step)
# On success, the state should transition to the next state
self.assert_step_updated(self.strategy_step.subcloud_id,
self.on_success_state)
self.assert_step_updated(
self.strategy_step.subcloud_id, self.on_success_state)

View File

@ -1,32 +0,0 @@
#
# Copyright (c) 2023 Wind River Systems, Inc.
#
# SPDX-License-Identifier: Apache-2.0
#
from dcmanager.common import consts
from dcmanager.tests.unit.orchestrator.states.software.test_base import \
TestSoftwareOrchestrator
class TestDeployActivateState(TestSoftwareOrchestrator):
def setUp(self):
super(TestDeployActivateState, self).setUp()
self.on_success_state = consts.STRATEGY_STATE_SW_DEPLOY_COMPLETE
# Add the subcloud being processed by this unit test
self.subcloud = self.setup_subcloud()
# Add the strategy_step state being processed by this unit test
self.strategy_step = self.setup_strategy_step(
self.subcloud.id, consts.STRATEGY_STATE_SW_DEPLOY_ACTIVATE)
def test_deploy_activate_success(self):
"""Test deploy activate when the API call succeeds."""
self.worker.perform_state_action(self.strategy_step)
# On success, the state should transition to the next state
self.assert_step_updated(self.strategy_step.subcloud_id,
self.on_success_state)

View File

@ -1,32 +0,0 @@
#
# Copyright (c) 2023 Wind River Systems, Inc.
#
# SPDX-License-Identifier: Apache-2.0
#
from dcmanager.common import consts
from dcmanager.tests.unit.orchestrator.states.software.test_base import \
TestSoftwareOrchestrator
class TestDeployCompleteState(TestSoftwareOrchestrator):
def setUp(self):
super(TestDeployCompleteState, self).setUp()
self.on_success_state = consts.STRATEGY_STATE_SW_FINISH_STRATEGY
# Add the subcloud being processed by this unit test
self.subcloud = self.setup_subcloud()
# Add the strategy_step state being processed by this unit test
self.strategy_step = self.setup_strategy_step(
self.subcloud.id, consts.STRATEGY_STATE_SW_DEPLOY_COMPLETE)
def test_deploy_complete_success(self):
"""Test deploy complete when the API call succeeds."""
self.worker.perform_state_action(self.strategy_step)
# On success, the state should transition to the next state
self.assert_step_updated(self.strategy_step.subcloud_id,
self.on_success_state)

View File

@ -1,32 +0,0 @@
#
# Copyright (c) 2023 Wind River Systems, Inc.
#
# SPDX-License-Identifier: Apache-2.0
#
from dcmanager.common import consts
from dcmanager.tests.unit.orchestrator.states.software.test_base import \
TestSoftwareOrchestrator
class TestDeployHostState(TestSoftwareOrchestrator):
def setUp(self):
super(TestDeployHostState, self).setUp()
self.on_success_state = consts.STRATEGY_STATE_SW_UNLOCK_CONTROLLER
# Add the subcloud being processed by this unit test
self.subcloud = self.setup_subcloud()
# Add the strategy_step state being processed by this unit test
self.strategy_step = self.setup_strategy_step(
self.subcloud.id, consts.STRATEGY_STATE_SW_DEPLOY_HOST)
def test_deploy_host_success(self):
"""Test deploy host when the API call succeeds."""
self.worker.perform_state_action(self.strategy_step)
# On success, the state should transition to the next state
self.assert_step_updated(self.strategy_step.subcloud_id,
self.on_success_state)

View File

@ -1,32 +0,0 @@
#
# Copyright (c) 2023 Wind River Systems, Inc.
#
# SPDX-License-Identifier: Apache-2.0
#
from dcmanager.common import consts
from dcmanager.tests.unit.orchestrator.states.software.test_base import \
TestSoftwareOrchestrator
class TestDeployPreCheckState(TestSoftwareOrchestrator):
def setUp(self):
super(TestDeployPreCheckState, self).setUp()
self.on_success_state = consts.STRATEGY_STATE_SW_DEPLOY_START
# Add the subcloud being processed by this unit test
self.subcloud = self.setup_subcloud()
# Add the strategy_step state being processed by this unit test
self.strategy_step = self.setup_strategy_step(
self.subcloud.id, consts.STRATEGY_STATE_SW_DEPLOY_PRE_CHECK)
def test_deploy_pre_check_success(self):
"""Test deploy pre check when the API call succeeds."""
self.worker.perform_state_action(self.strategy_step)
# On success, the state should transition to the next state
self.assert_step_updated(self.strategy_step.subcloud_id,
self.on_success_state)

View File

@ -1,133 +0,0 @@
#
# Copyright (c) 2023 Wind River Systems, Inc.
#
# SPDX-License-Identifier: Apache-2.0
#
import mock
from dcmanager.common import consts
from dcmanager.orchestrator.states.software.deploy_start import DeployStartState
from dcmanager.tests.unit.orchestrator.states.fakes import FakeSystem
from dcmanager.tests.unit.orchestrator.states.software.test_base import \
TestSoftwareOrchestrator
REGION_ONE_RR_RELEASES = {
"stx_23.09.0": {
"sw_version": "23.09.0",
"state": "deployed",
"reboot_required": "Y",
},
"stx_23.09.1": {
"sw_version": "23.09.1",
"state": "deployed",
"reboot_required": "N",
},
"stx_23.09.2": {
"sw_version": "23.09.1",
"state": "deployed",
"reboot_required": "N",
},
}
REGION_ONE_NRR_RELEASES = {
"stx_23.09.0": {
"sw_version": "23.09.0",
"state": "deployed",
"reboot_required": "N",
},
"stx_23.09.1": {
"sw_version": "23.09.1",
"state": "deployed",
"reboot_required": "N",
},
}
SUBCLOUD_RR_RELEASES = {
"stx_23.09.0": {
"sw_version": "23.09.0",
"state": "available",
"reboot_required": "Y",
},
"stx_23.09.1": {
"sw_version": "23.09.1",
"state": "available",
"reboot_required": "N",
},
"stx_23.09.2": {
"sw_version": "23.09.1",
"state": "available",
"reboot_required": "N",
},
}
SUBCLOUD_NRR_RELEASES = {
"stx_23.09.0": {
"sw_version": "23.09.0",
"state": "available",
"reboot_required": "N",
},
"stx_23.09.1": {
"sw_version": "23.09.1",
"state": "available",
"reboot_required": "N",
},
}
class TestDeployStartState(TestSoftwareOrchestrator):
def setUp(self):
super(TestDeployStartState, self).setUp()
self.on_success_state = consts.STRATEGY_STATE_SW_DEPLOY_HOST
self.on_success_lock_state = consts.STRATEGY_STATE_SW_LOCK_CONTROLLER
# Add the subcloud being processed by this unit test
self.subcloud = self.setup_subcloud()
# Add the strategy_step state being processed by this unit test
self.strategy_step = self.setup_strategy_step(
self.subcloud.id, consts.STRATEGY_STATE_SW_DEPLOY_START
)
# Add mock API endpoints for sysinv client calls invoked by this state
self.sysinv_client.get_system = mock.MagicMock()
system_values = FakeSystem()
system_values.system_mode = consts.SYSTEM_MODE_SIMPLEX
self.sysinv_client.get_system.return_value = system_values
# Add mock API endpoints for software client calls invoked by this state
self.software_client.query = mock.MagicMock()
self.software_client.deploy_start = mock.MagicMock()
self._read_from_cache = mock.MagicMock()
@mock.patch.object(DeployStartState, "_read_from_cache")
def test_deploy_start_nrr_success(self, mock_read_from_cache):
"""Test deploy start when the API call succeeds."""
mock_read_from_cache.return_value = REGION_ONE_NRR_RELEASES
self.software_client.query.side_effect = [SUBCLOUD_NRR_RELEASES]
self.worker.perform_state_action(self.strategy_step)
self.software_client.deploy_start.assert_called_once_with("stx_23.09.1")
# On success, the state should transition to the next state
self.assert_step_updated(
self.strategy_step.subcloud_id, self.on_success_state
)
@mock.patch.object(DeployStartState, "_read_from_cache")
def test_deploy_start_rr_success(self, mock_read_from_cache):
"""Test deploy start when the API call succeeds."""
mock_read_from_cache.return_value = REGION_ONE_RR_RELEASES
self.software_client.query.side_effect = [SUBCLOUD_RR_RELEASES]
self.worker.perform_state_action(self.strategy_step)
self.software_client.deploy_start.assert_called_once_with("stx_23.09.2")
# On success, the state should transition to the next state
self.assert_step_updated(
self.strategy_step.subcloud_id, self.on_success_lock_state
)

View File

@ -1,9 +1,10 @@
#
# Copyright (c) 2023 Wind River Systems, Inc.
# Copyright (c) 2023-2024 Wind River Systems, Inc.
#
# SPDX-License-Identifier: Apache-2.0
#
import mock
from oslo_config import cfg
from dcmanager.common import consts
@ -38,7 +39,7 @@ class TestFinishStrategyState(TestSoftwareOrchestrator):
self.mock_use_usm = p.start()
self.mock_use_usm.return_value = True
self.addCleanup(p.stop)
super(TestFinishStrategyState, self).setUp()
super().setUp()
self.on_success_state = consts.STRATEGY_STATE_COMPLETE

View File

@ -1,35 +1,165 @@
#
# Copyright (c) 2023 Wind River Systems, Inc.
# Copyright (c) 2023-2024 Wind River Systems, Inc.
#
# SPDX-License-Identifier: Apache-2.0
#
import mock
from dcmanager.common import consts
from dcmanager.tests.unit.orchestrator.states.software.test_base import \
TestSoftwareOrchestrator
MISSING_LICENSE_RESPONSE = {
"content": "",
"error": "License file not found. A license may not have been installed.",
}
LICENSE_VALID_RESPONSE = {"content": "A valid license", "error": ""}
ALTERNATE_LICENSE_RESPONSE = {"content": "A different valid license", "error": ""}
class TestInstallLicenseState(TestSoftwareOrchestrator):
def setUp(self):
super(TestInstallLicenseState, self).setUp()
super().setUp()
# next state after install a license is 'upload'
self.on_success_state = consts.STRATEGY_STATE_SW_UPLOAD
self.on_success_state = consts.STRATEGY_STATE_SW_CREATE_VIM_STRATEGY
# Add the subcloud being processed by this unit test
self.subcloud = self.setup_subcloud()
# Add the strategy_step state being processed by this unit test
self.strategy_step = self.setup_strategy_step(
self.subcloud.id, consts.STRATEGY_STATE_SW_INSTALL_LICENSE)
self.subcloud.id, consts.STRATEGY_STATE_SW_INSTALL_LICENSE
)
def test_upgrade_subcloud_license_install_success(self):
"""Test the install license step succeeds."""
# Add mock API endpoints for sysinv client calls invoked by this state
self.sysinv_client.get_license = mock.MagicMock()
self.sysinv_client.install_license = mock.MagicMock()
def test_install_license_failure(self):
"""Test the installing license step where the install fails.
The system controller has a license, but the API call to install on the
subcloud fails.
"""
# Order of get_license calls:
# first license query is to system controller
# second license query is to subcloud (should be missing)
self.sysinv_client.get_license.side_effect = [
LICENSE_VALID_RESPONSE,
MISSING_LICENSE_RESPONSE,
]
# Simulate a license install failure on the subcloud
self.sysinv_client.install_license.return_value = MISSING_LICENSE_RESPONSE
# invoke the strategy state operation on the orch thread
self.worker.perform_state_action(self.strategy_step)
# verify the license install was invoked
self.sysinv_client.install_license.assert_called()
# Verify a install_license failure leads to a state failure
self.assert_step_updated(
self.strategy_step.subcloud_id, consts.STRATEGY_STATE_FAILED
)
def test_install_license_success(self):
"""Test the install license step succeeds.
The license will be installed on the subcloud when system controller
has a license, the subcloud does not have a license, and the API call
succeeds.
"""
# Order of get_license calls:
# first license query is to system controller
# second license query is to subcloud (should be missing)
self.sysinv_client.get_license.side_effect = [
LICENSE_VALID_RESPONSE,
MISSING_LICENSE_RESPONSE,
]
# A license install should return a success
self.sysinv_client.install_license.return_value = LICENSE_VALID_RESPONSE
# invoke the strategy state operation on the orch thread
self.worker.perform_state_action(self.strategy_step)
# verify the license install was invoked
self.sysinv_client.install_license.assert_called()
# On success, the next state after installing license is importing load
self.assert_step_updated(self.strategy_step.subcloud_id,
self.on_success_state)
self.assert_step_updated(
self.strategy_step.subcloud_id, self.on_success_state
)
def test_install_license_skip_existing(self):
"""Test the install license step skipped due to license up to date"""
# Order of get_license calls:
# first license query is to system controller
# second license query is to subcloud
self.sysinv_client.get_license.side_effect = [
LICENSE_VALID_RESPONSE,
LICENSE_VALID_RESPONSE,
]
# invoke the strategy state operation on the orch thread
self.worker.perform_state_action(self.strategy_step)
# A license install should not have been attempted due to the license
# already being up to date
self.sysinv_client.install_license.assert_not_called()
# On success, the next state after installing license is importing load
self.assert_step_updated(
self.strategy_step.subcloud_id, self.on_success_state
)
def test_install_license_overrides_mismatched_license(self):
"""Test the install license overrides a mismatched license"""
# Order of get_license calls:
# first license query is to system controller
# second license query is to subcloud (should be valid but different)
self.sysinv_client.get_license.side_effect = [
LICENSE_VALID_RESPONSE,
ALTERNATE_LICENSE_RESPONSE,
]
# A license install should return a success
self.sysinv_client.install_license.return_value = LICENSE_VALID_RESPONSE
# invoke the strategy state operation on the orch thread
self.worker.perform_state_action(self.strategy_step)
# verify the license install was invoked
self.sysinv_client.install_license.assert_called()
# Verify it successfully moves to the next step
self.assert_step_updated(
self.strategy_step.subcloud_id, self.on_success_state
)
def test_install_license_skip_when_no_sys_controller_lic(self):
"""Test license install skipped when no license on system controller."""
# Only makes one query: to system controller
self.sysinv_client.get_license.return_value = MISSING_LICENSE_RESPONSE
# invoke the strategy state operation on the orch thread
self.worker.perform_state_action(self.strategy_step)
# Should skip install_license API call
self.sysinv_client.install_license.assert_not_called()
# Verify it successfully moves to the next step
self.assert_step_updated(
self.strategy_step.subcloud_id, self.on_success_state
)

View File

@ -1,181 +0,0 @@
#
# Copyright (c) 2023-2024 Wind River Systems, Inc.
#
# SPDX-License-Identifier: Apache-2.0
#
import itertools
import mock
from dcmanager.common import consts
from dcmanager.orchestrator.states import lock_host
from dcmanager.tests.unit.orchestrator.states.fakes import FakeController
from dcmanager.tests.unit.orchestrator.states.fakes import FakeSystem
from dcmanager.tests.unit.orchestrator.states.software.test_base import \
TestSoftwareOrchestrator
@mock.patch("dcmanager.orchestrator.states.lock_host.DEFAULT_MAX_QUERIES", 3)
@mock.patch("dcmanager.orchestrator.states.lock_host.DEFAULT_SLEEP_DURATION", 1)
class TestSwLockSimplexStage(TestSoftwareOrchestrator):
state = consts.STRATEGY_STATE_SW_LOCK_CONTROLLER
def setUp(self):
super(TestSwLockSimplexStage, self).setUp()
# next state after a successful lock is deploy host
self.on_success_state = consts.STRATEGY_STATE_SW_DEPLOY_HOST
# Add the subcloud being processed by this unit test
self.subcloud = self.setup_subcloud()
# Add the strategy_step state being processed by this unit test
self.strategy_step = self.setup_strategy_step(self.subcloud.id, self.state)
# Add mock API endpoints for sysinv client calls invoked by this state
self.sysinv_client.get_host = mock.MagicMock()
self.sysinv_client.lock_host = mock.MagicMock()
self.sysinv_client.get_system = mock.MagicMock()
system_values = FakeSystem()
system_values.system_mode = consts.SYSTEM_MODE_SIMPLEX
self.sysinv_client.get_system.return_value = system_values
self.setup_fake_controllers('controller-0')
def setup_fake_controllers(self, host_name):
self.CONTROLLER_UNLOCKED = FakeController(
hostname=host_name, administrative=consts.ADMIN_UNLOCKED
)
self.CONTROLLER_LOCKED = FakeController(hostname=host_name,
administrative=consts.ADMIN_LOCKED)
self.CONTROLLER_LOCKING = FakeController(
hostname=host_name,
administrative=consts.ADMIN_UNLOCKED,
ihost_action='lock',
task='Locking'
)
self.CONTROLLER_LOCKING_FAILED = \
FakeController(hostname=host_name,
administrative=consts.ADMIN_UNLOCKED,
ihost_action='force-swact',
task='Swacting')
def test_lock_success(self):
"""Test the lock command returns a success"""
# mock the controller host queries
# first query is the starting state
# query 2,3 are are during the lock phase
# query 4 : the host is now locked
self.sysinv_client.get_host.side_effect = [self.CONTROLLER_UNLOCKED,
self.CONTROLLER_LOCKING,
self.CONTROLLER_LOCKING,
self.CONTROLLER_LOCKED]
# mock the API call as failed on the subcloud
self.sysinv_client.lock_host.return_value = self.CONTROLLER_LOCKING
# invoke the strategy state operation on the orch thread
self.worker.perform_state_action(self.strategy_step)
# verify the lock command was actually attempted
self.sysinv_client.lock_host.assert_called()
# verify that the API moved to the next state on success
self.assert_step_updated(self.strategy_step.subcloud_id,
self.on_success_state)
def test_lock_skipped_when_already_locked(self):
"""Test the lock command skips if host is already locked"""
# mock the controller host query as being already locked
self.sysinv_client.get_host.return_value = self.CONTROLLER_LOCKED
# invoke the strategy state operation on the orch thread
self.worker.perform_state_action(self.strategy_step)
# verify the lock command was never attempted
self.sysinv_client.lock_host.assert_not_called()
# verify that the state moves to the next state
self.assert_step_updated(self.strategy_step.subcloud_id,
self.on_success_state)
def test_lock_attempt_timeout(self):
"""Test lock invoked and fails if timeout before host becomes locked"""
# mock the get_host queries
# first query is the starting state
# all remaining queries, the host returns 'locking'
self.sysinv_client.get_host.side_effect = itertools.chain(
[self.CONTROLLER_UNLOCKED, ],
itertools.repeat(self.CONTROLLER_LOCKING))
# mock the API call as successful on the subcloud
self.sysinv_client.lock_host.return_value = self.CONTROLLER_LOCKING
# invoke the strategy state operation on the orch thread
self.worker.perform_state_action(self.strategy_step)
# verify the lock command was actually attempted
self.sysinv_client.lock_host.assert_called()
# verify the query was invoked: 1 + max_attempts times
self.assertEqual(lock_host.DEFAULT_MAX_QUERIES + 1,
self.sysinv_client.get_host.call_count)
# verify that state failed due to subcloud never finishing the lock
self.assert_step_updated(self.strategy_step.subcloud_id,
consts.STRATEGY_STATE_FAILED)
def test_lock_failure(self):
"""Test the lock command returns a failure"""
# mock the controller get_host query
self.sysinv_client.get_host.return_value = self.CONTROLLER_UNLOCKED
# mock the API call as failed on the subcloud
self.sysinv_client.lock_host.return_value = self.CONTROLLER_LOCKING_FAILED
# invoke the strategy state operation on the orch thread
self.worker.perform_state_action(self.strategy_step)
# verify the lock command was actually attempted
self.sysinv_client.lock_host.assert_called()
# verify that the API error for the lock leads to a failed state
self.assert_step_updated(self.strategy_step.subcloud_id,
consts.STRATEGY_STATE_FAILED)
def test_lock_fails_when_host_query_fails(self):
"""Test the lock command fails when it cannot get the controllers"""
# mock the get_host query is empty and raises an exception
self.sysinv_client.get_host.side_effect = \
Exception("Unable to find host controller-0")
# invoke the strategy state operation on the orch thread
self.worker.perform_state_action(self.strategy_step)
# verify the lock command was never attempted
self.sysinv_client.lock_host.assert_not_called()
# verify that the state moves to the next state
self.assert_step_updated(self.strategy_step.subcloud_id,
consts.STRATEGY_STATE_FAILED)
class TestSwLockDuplexStage(TestSwLockSimplexStage):
def setUp(self):
self.state = consts.STRATEGY_STATE_SW_LOCK_CONTROLLER
super(TestSwLockDuplexStage, self).setUp()
system_values = FakeSystem()
system_values.system_mode = consts.SYSTEM_MODE_DUPLEX
self.sysinv_client.get_system.return_value = system_values
# next state after a successful lock is deploy host
self.on_success_state = consts.STRATEGY_STATE_SW_DEPLOY_HOST
# Add mock API endpoints for sysinv client calls invoked by this state
self.setup_fake_controllers('controller-1')

View File

@ -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
#
@ -11,7 +11,7 @@ from dcmanager.tests.unit.orchestrator.states.software.test_base import \
class TestPreCheckState(TestSoftwareOrchestrator):
def setUp(self):
super(TestPreCheckState, self).setUp()
super().setUp()
self.on_success_state = consts.STRATEGY_STATE_SW_INSTALL_LICENSE

View File

@ -1,148 +0,0 @@
#
# Copyright (c) 2023 Wind River Systems, Inc.
#
# SPDX-License-Identifier: Apache-2.0
#
import itertools
import mock
from dcmanager.common import consts
from dcmanager.orchestrator.states import swact_host
from dcmanager.tests.unit.orchestrator.states.fakes import FakeController
from dcmanager.tests.unit.orchestrator.states.software.test_base import \
TestSoftwareOrchestrator
@mock.patch("dcmanager.orchestrator.states.swact_host.DEFAULT_SWACT_SLEEP", 1)
@mock.patch("dcmanager.orchestrator.states.swact_host.DEFAULT_MAX_QUERIES", 3)
@mock.patch("dcmanager.orchestrator.states.swact_host.DEFAULT_SLEEP_DURATION", 1)
class TestSwactController0State(TestSoftwareOrchestrator):
state = consts.STRATEGY_STATE_SW_SWACT_CONTROLLER_0
def setUp(self):
super(TestSwactController0State, self).setUp()
# Add the subcloud being processed by this unit test
self.subcloud = self.setup_subcloud()
# Add the strategy_step state being processed by this unit test
self.strategy_step = self.setup_strategy_step(self.subcloud.id, self.state)
self.on_success_state = consts.STRATEGY_STATE_SW_CREATE_VIM_STRATEGY
# Add mock API endpoints for sysinv client calls invoked by this state
self.sysinv_client.get_host = mock.MagicMock()
self.sysinv_client.swact_host = mock.MagicMock()
# In order to swact to controller-1, we run "system host-swact controller-0"
self.setup_fake_controllers("controller-0")
def setup_fake_controllers(self, host_name):
self.CONTROLLER_ACTIVE = FakeController(hostname=host_name)
self.CONTROLLER_STANDBY = FakeController(
hostname=host_name, capabilities={"Personality": "Controller-Standby"}
)
self.CONTROLLER_SWACTING = FakeController(
hostname=host_name, task="Swacting"
)
def test_swact_controller_success(self):
"""Test swact controller when the API call succeeds."""
# mock the controller host queries
# first query is the starting state
# query 2 is during the ongoing swact phase
# query 3 is after successful host swact
self.sysinv_client.get_host.side_effect = [
self.CONTROLLER_STANDBY,
self.CONTROLLER_STANDBY,
self.CONTROLLER_ACTIVE,
]
# mock the API call as failed on the subcloud
self.sysinv_client.swact_host.return_value = self.CONTROLLER_SWACTING
self.worker.perform_state_action(self.strategy_step)
# verify the swact command was actually attempted
self.sysinv_client.swact_host.assert_called()
# On success, the state should transition to the next state
self.assert_step_updated(
self.strategy_step.subcloud_id, self.on_success_state
)
def test_swact_skipped_when_already_active(self):
"""Test the swact command skips if host is already active controller"""
# mock the controller host query as being already Controller-Active
self.sysinv_client.get_host.return_value = self.CONTROLLER_ACTIVE
# invoke the strategy state operation on the orch thread
self.worker.perform_state_action(self.strategy_step)
# verify the swact command was never attempted
self.sysinv_client.swact_host.assert_not_called()
# verify that the state moves to the next state
self.assert_step_updated(
self.strategy_step.subcloud_id, self.on_success_state
)
def test_swact_attempt_timeout(self):
"""Test swact invoked and fails if timeout"""
# mock the get_host queries
# all remaining queries, the host returns 'Controller-Standby'
self.sysinv_client.get_host.side_effect = itertools.chain(
itertools.repeat(self.CONTROLLER_STANDBY)
)
# mock the API call as successful on the subcloud
self.sysinv_client.swact_host.return_value = self.CONTROLLER_SWACTING
# invoke the strategy state operation on the orch thread
self.worker.perform_state_action(self.strategy_step)
# verify the swact command was actually attempted
self.sysinv_client.swact_host.assert_called()
# verify the query was invoked: 1 + max_attempts times
self.assertEqual(
swact_host.DEFAULT_MAX_QUERIES + 2,
self.sysinv_client.get_host.call_count,
)
# verify that state failed due to subcloud never finishing the swact
self.assert_step_updated(
self.strategy_step.subcloud_id, consts.STRATEGY_STATE_FAILED
)
def test_swact_fails_when_host_query_fails(self):
"""Test the swact command fails when it cannot get the controllers"""
# mock the get_host query is empty and raises an exception
self.sysinv_client.get_host.side_effect = Exception("Unable to find host")
# invoke the strategy state operation on the orch thread
self.worker.perform_state_action(self.strategy_step)
# verify the swact command was never attempted
self.sysinv_client.swact_host.assert_not_called()
# verify that the state moves to the next state
self.assert_step_updated(
self.strategy_step.subcloud_id, consts.STRATEGY_STATE_FAILED
)
class TestSwactController1State(TestSwactController0State):
def setUp(self):
self.state = consts.STRATEGY_STATE_SW_SWACT_CONTROLLER_1
super(TestSwactController1State, self).setUp()
# next state after a successful swact controller-1 is deploy activate
self.on_success_state = consts.STRATEGY_STATE_SW_DEPLOY_ACTIVATE
# In order to swact to controller-0, we run "system host-swact controller-1"
self.setup_fake_controllers("controller-1")

View File

@ -1,32 +0,0 @@
#
# Copyright (c) 2023 Wind River Systems, Inc.
#
# SPDX-License-Identifier: Apache-2.0
#
from dcmanager.common import consts
from dcmanager.tests.unit.orchestrator.states.software.test_base import \
TestSoftwareOrchestrator
class TestUnlockControllerState(TestSoftwareOrchestrator):
def setUp(self):
super(TestUnlockControllerState, self).setUp()
self.on_success_state = consts.STRATEGY_STATE_SW_DEPLOY_ACTIVATE
# Add the subcloud being processed by this unit test
self.subcloud = self.setup_subcloud()
# Add the strategy_step state being processed by this unit test
self.strategy_step = self.setup_strategy_step(
self.subcloud.id, consts.STRATEGY_STATE_SW_UNLOCK_CONTROLLER)
def test_unlock_controller_success(self):
"""Test unlock controller when the API call succeeds."""
self.worker.perform_state_action(self.strategy_step)
# On success, the state should transition to the next state
self.assert_step_updated(self.strategy_step.subcloud_id,
self.on_success_state)

View File

@ -1,205 +0,0 @@
#
# Copyright (c) 2023-2024 Wind River Systems, Inc.
#
# SPDX-License-Identifier: Apache-2.0
#
import os
import mock
from oslo_config import cfg
from dcmanager.common import consts
from dcmanager.orchestrator.states.software.upload import UploadState
from dcmanager.tests.unit.orchestrator.states.software.test_base \
import TestSoftwareOrchestrator
REGION_ONE_RELEASES = {"DC_20.12.1": {"sw_version": "20.12.1",
"state": "deployed"},
"DC_20.12.2": {"sw_version": "20.12.2",
"state": "deployed"},
"DC_20.12.3": {"sw_version": "20.12.3",
"state": "committed"},
"DC_20.12.4": {"sw_version": "20.12.4",
"state": "committed"}}
REGION_ONE_RELEASES_2 = {"DC_20.12.1": {"sw_version": "20.12.1",
"state": "deployed"},
"DC_22.12.0": {"sw_version": "22.12.0",
"state": "deployed"},
"DC_20.12.2": {"sw_version": "20.12.2",
"state": "deployed"}}
REGION_ONE_RELEASES_3 = {"DC_22.12.1": {"sw_version": "22.12.1",
"state": "deployed"},
"DC_22.12.0": {"sw_version": "22.12.0",
"state": "deployed"}}
SUBCLOUD_RELEASES = {"DC_20.12.1": {"sw_version": "20.12.1",
"state": "deployed"},
"DC_20.12.2": {"sw_version": "20.12.2",
"state": "deployed"}}
@mock.patch("dcmanager.orchestrator.states.software.upload."
"DEFAULT_MAX_QUERIES", 3)
@mock.patch("dcmanager.orchestrator.states.software.upload"
".DEFAULT_SLEEP_DURATION", 1)
class TestUploadState(TestSoftwareOrchestrator):
def setUp(self):
p = mock.patch.object(cfg.CONF, 'use_usm')
self.mock_use_usm = p.start()
self.mock_use_usm.return_value = True
self.addCleanup(p.stop)
super(TestUploadState, self).setUp()
self.on_success_state = consts.STRATEGY_STATE_SW_DEPLOY_PRE_CHECK
# Add the subcloud being processed by this unit test
self.subcloud = self.setup_subcloud()
# Add the strategy_step state being processed by this unit test
self.strategy_step = self.setup_strategy_step(
self.subcloud.id, consts.STRATEGY_STATE_SW_UPLOAD)
# Add mock API endpoints for software client calls
# invoked by this state
self.software_client.query = mock.MagicMock()
self.software_client.upload = mock.MagicMock()
self._read_from_cache = mock.MagicMock()
@mock.patch.object(UploadState, '_read_from_cache')
@mock.patch.object(os.path, 'isfile')
def test_software_upload_strategy_success(self, mock_is_file,
mock_read_from_cache):
"""Test software upload when the API call succeeds."""
mock_read_from_cache.return_value = REGION_ONE_RELEASES
mock_is_file.return_value = True
self.software_client.query.side_effect = [SUBCLOUD_RELEASES]
# invoke the strategy state operation on the orch thread
self.worker.perform_state_action(self.strategy_step)
self.software_client.upload.assert_called_once_with([
consts.RELEASE_VAULT_DIR + '/20.12/DC_20.12.3.patch',
consts.RELEASE_VAULT_DIR + '/20.12/DC_20.12.4.patch'
])
# On success, the state should transition to the next state
self.assert_step_updated(self.strategy_step.subcloud_id,
self.on_success_state)
@mock.patch.object(UploadState, '_read_from_cache')
def test_software_upload_strategy_no_operation_required(self,
mock_read_from_cache):
"""Test software upload when no software operation is required."""
mock_read_from_cache.return_value = REGION_ONE_RELEASES
self.software_client.query.side_effect = [REGION_ONE_RELEASES]
# invoke the strategy state operation on the orch thread
self.worker.perform_state_action(self.strategy_step)
self.software_client.upload.assert_not_called()
# On success, the state should transition to the next state
self.assert_step_updated(self.strategy_step.subcloud_id,
self.on_success_state)
@mock.patch.object(UploadState, '_read_from_cache')
@mock.patch.object(os, 'listdir')
@mock.patch.object(os.path, 'isdir')
def test_software_upload_strategy_missing_sig(self, mock_is_dir, mock_listdir,
mock_read_from_cache):
"""Test software upload when release is missing signature"""
mock_read_from_cache.return_value = REGION_ONE_RELEASES_2
mock_is_dir.return_value = True
mock_listdir.return_value = ["DC_22.12.0.iso"]
self.software_client.query.side_effect = [SUBCLOUD_RELEASES]
# invoke the strategy state operation on the orch thread
self.worker.perform_state_action(self.strategy_step)
self.software_client.upload.assert_not_called()
# On success, the state should transition to the next state
self.assert_step_updated(self.strategy_step.subcloud_id,
consts.STRATEGY_STATE_FAILED)
@mock.patch.object(UploadState, '_read_from_cache')
@mock.patch.object(os, 'listdir')
@mock.patch.object(os.path, 'isdir')
def test_software_upload_strategy_success_load(self, mock_is_dir, mock_listdir,
mock_read_from_cache):
"""Test software upload when the API call succeeds."""
mock_read_from_cache.return_value = REGION_ONE_RELEASES_2
mock_is_dir.return_value = True
mock_listdir.return_value = ["DC_22.12.0.iso", "DC_22.12.0.sig"]
self.software_client.query.side_effect = [
SUBCLOUD_RELEASES, REGION_ONE_RELEASES_2]
# invoke the strategy state operation on the orch thread
self.worker.perform_state_action(self.strategy_step)
self.software_client.upload.assert_called_once_with([
consts.RELEASE_VAULT_DIR + '/22.12/DC_22.12.0.iso',
consts.RELEASE_VAULT_DIR + '/22.12/DC_22.12.0.sig'
])
# On success, the state should transition to the next state
self.assert_step_updated(self.strategy_step.subcloud_id,
self.on_success_state)
@mock.patch.object(UploadState, '_read_from_cache')
@mock.patch.object(os, 'listdir')
@mock.patch.object(os.path, 'isdir')
@mock.patch.object(os.path, 'isfile')
def test_software_upload_prepatched_load(self, mock_isfile,
mock_is_dir, mock_listdir,
mock_read_from_cache):
"""Test software upload when release is a prepatched iso."""
mock_read_from_cache.return_value = REGION_ONE_RELEASES_3
mock_is_dir.return_value = True
mock_isfile.return_value = False
mock_listdir.return_value = ["DC_22.12.0.iso", "DC_22.12.0.sig"]
self.software_client.query.side_effect = [{}, REGION_ONE_RELEASES_3]
# invoke the strategy state operation on the orch thread
self.worker.perform_state_action(self.strategy_step)
self.software_client.upload.assert_called_once_with([
consts.RELEASE_VAULT_DIR + '/22.12/DC_22.12.0.iso',
consts.RELEASE_VAULT_DIR + '/22.12/DC_22.12.0.sig'
])
# On success, the state should transition to the next state
self.assert_step_updated(self.strategy_step.subcloud_id,
self.on_success_state)
@mock.patch.object(UploadState, '_read_from_cache')
@mock.patch.object(os, 'listdir')
@mock.patch.object(os.path, 'isdir')
@mock.patch.object(os.path, 'isfile')
def test_software_upload_patch_and_load(self, mock_isfile,
mock_is_dir, mock_listdir,
mock_read_from_cache):
"""Test software upload when both patch and load is uploaded."""
mock_read_from_cache.return_value = REGION_ONE_RELEASES_3
mock_is_dir.return_value = True
mock_isfile.return_value = True
mock_listdir.return_value = ["DC_22.12.0.iso", "DC_22.12.0.sig"]
self.software_client.query.side_effect = [{}, REGION_ONE_RELEASES_3]
# invoke the strategy state operation on the orch thread
self.worker.perform_state_action(self.strategy_step)
self.software_client.upload.assert_called_once_with([
consts.RELEASE_VAULT_DIR + '/22.12/DC_22.12.1.patch',
consts.RELEASE_VAULT_DIR + '/22.12/DC_22.12.0.iso',
consts.RELEASE_VAULT_DIR + '/22.12/DC_22.12.0.sig'
])
# On success, the state should transition to the next state
self.assert_step_updated(self.strategy_step.subcloud_id,
self.on_success_state)