Clean software orchestration states
The VIM orchestration will handle the following commands for USM: - software deploy precheck - software deploy start <release> - software deploy host <hostname> - every lock/unlock/swact needed - software deploy activate - software deploy complete Now, the DC orchestration doesn't need the states to handle the operations above. This commit deletes all unneeded states. Test Plan: PASS: Execute the software orchestration successfully: - Enable the USM orchestration (set use_usm parameter to True) - dcorch configuration file - dcmanager configuration file - Create and apply an upgrade-strategy - Check that 5 states were executed PASS. Tox test successful. Story: 2010676 Task: 49555 Change-Id: I67db35b6807f9ddcedab7c9742816c782daecd68 Signed-off-by: Hugo Brito <hugo.brito@windriver.com>
This commit is contained in:
parent
30812c4411
commit
9e42f10470
@ -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"
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
)
|
||||
|
||||
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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)
|
||||
|
||||
|
@ -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
|
||||
|
@ -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"
|
@ -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,
|
||||
)
|
||||
|
@ -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",)
|
@ -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")
|
@ -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
|
@ -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
|
@ -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)
|
||||
|
@ -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()
|
||||
|
@ -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)
|
||||
|
@ -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)
|
@ -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)
|
@ -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)
|
@ -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)
|
@ -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
|
||||
)
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
)
|
||||
|
@ -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')
|
@ -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
|
||||
|
||||
|
@ -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")
|
@ -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)
|
@ -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)
|
Loading…
Reference in New Issue
Block a user