From 40c8359f411b5fb9dbfedbbdbd6c55a562090d66 Mon Sep 17 00:00:00 2001 From: Hugo Brito Date: Fri, 15 Sep 2023 17:28:38 -0300 Subject: [PATCH] Create USM dummy states and tests This commit introduces dummy classes for all states that will be utilized in the new USM API. The implementation of these classes will be addressed in subsequent commits. Test Plan: 1. Perform an upgrade-strategy with use_usm=True - Check software orchestration will be called. - All states will execute and only move to the next state - Check the usm states tests pass. Story: 2010676 Task: 48153 Change-Id: I3d2c236656d5caee6a4a827d03146e6c160103aa Signed-off-by: Hugo Brito --- distributedcloud/dcmanager/common/consts.py | 17 +++ .../orchestrator/software_orch_thread.py | 85 +++++++++--- .../orchestrator/states/software/__init__.py | 0 .../software/apply_vim_software_strategy.py | 22 +++ .../states/software/cache/__init__.py | 0 .../software/cache/cache_specifications.py | 90 +++++++++++++ .../states/software/cache/clients.py | 50 +++++++ .../software/cache/shared_cache_repository.py | 39 ++++++ .../software/cache/shared_client_cache.py | 125 ++++++++++++++++++ .../software/create_vim_software_strategy.py | 22 +++ .../states/software/deploy_activate.py | 22 +++ .../states/software/deploy_complete.py | 22 +++ .../states/software/deploy_host.py | 22 +++ .../states/software/deploy_pre_check.py | 22 +++ .../states/software/deploy_start.py | 22 +++ .../states/software/finish_strategy.py | 22 +++ .../states/software/install_license.py | 22 +++ .../states/software/lock_controller.py | 22 +++ .../orchestrator/states/software/pre_check.py | 22 +++ .../states/software/swact_controller.py | 22 +++ .../states/software/unlock_controller.py | 22 +++ .../orchestrator/states/software/upload.py | 22 +++ .../orchestrator/states/software/__init__.py | 0 .../test_apply_vim_software_strategy.py | 32 +++++ .../orchestrator/states/software/test_base.py | 37 ++++++ .../test_create_vim_software_strategy.py | 32 +++++ .../states/software/test_deploy_activate.py | 32 +++++ .../states/software/test_deploy_complete.py | 32 +++++ .../states/software/test_deploy_host.py | 32 +++++ .../states/software/test_deploy_pre_check.py | 32 +++++ .../states/software/test_deploy_start.py | 32 +++++ .../states/software/test_finish_strategy.py | 32 +++++ .../states/software/test_install_license.py | 35 +++++ .../states/software/test_lock_controller.py | 32 +++++ .../states/software/test_pre_check.py | 32 +++++ .../states/software/test_swact_controller.py | 32 +++++ .../states/software/test_unlock_controller.py | 32 +++++ .../states/software/test_upload.py | 32 +++++ .../tests/unit/orchestrator/test_base.py | 13 ++ 39 files changed, 1194 insertions(+), 21 deletions(-) create mode 100644 distributedcloud/dcmanager/orchestrator/states/software/__init__.py create mode 100644 distributedcloud/dcmanager/orchestrator/states/software/apply_vim_software_strategy.py create mode 100644 distributedcloud/dcmanager/orchestrator/states/software/cache/__init__.py create mode 100644 distributedcloud/dcmanager/orchestrator/states/software/cache/cache_specifications.py create mode 100644 distributedcloud/dcmanager/orchestrator/states/software/cache/clients.py create mode 100644 distributedcloud/dcmanager/orchestrator/states/software/cache/shared_cache_repository.py create mode 100644 distributedcloud/dcmanager/orchestrator/states/software/cache/shared_client_cache.py create mode 100644 distributedcloud/dcmanager/orchestrator/states/software/create_vim_software_strategy.py create mode 100644 distributedcloud/dcmanager/orchestrator/states/software/deploy_activate.py create mode 100644 distributedcloud/dcmanager/orchestrator/states/software/deploy_complete.py create mode 100644 distributedcloud/dcmanager/orchestrator/states/software/deploy_host.py create mode 100644 distributedcloud/dcmanager/orchestrator/states/software/deploy_pre_check.py create mode 100644 distributedcloud/dcmanager/orchestrator/states/software/deploy_start.py create mode 100644 distributedcloud/dcmanager/orchestrator/states/software/finish_strategy.py create mode 100644 distributedcloud/dcmanager/orchestrator/states/software/install_license.py create mode 100644 distributedcloud/dcmanager/orchestrator/states/software/lock_controller.py create mode 100644 distributedcloud/dcmanager/orchestrator/states/software/pre_check.py create mode 100644 distributedcloud/dcmanager/orchestrator/states/software/swact_controller.py create mode 100644 distributedcloud/dcmanager/orchestrator/states/software/unlock_controller.py create mode 100644 distributedcloud/dcmanager/orchestrator/states/software/upload.py create mode 100644 distributedcloud/dcmanager/tests/unit/orchestrator/states/software/__init__.py create mode 100644 distributedcloud/dcmanager/tests/unit/orchestrator/states/software/test_apply_vim_software_strategy.py create mode 100644 distributedcloud/dcmanager/tests/unit/orchestrator/states/software/test_base.py create mode 100644 distributedcloud/dcmanager/tests/unit/orchestrator/states/software/test_create_vim_software_strategy.py create mode 100644 distributedcloud/dcmanager/tests/unit/orchestrator/states/software/test_deploy_activate.py create mode 100644 distributedcloud/dcmanager/tests/unit/orchestrator/states/software/test_deploy_complete.py create mode 100644 distributedcloud/dcmanager/tests/unit/orchestrator/states/software/test_deploy_host.py create mode 100644 distributedcloud/dcmanager/tests/unit/orchestrator/states/software/test_deploy_pre_check.py create mode 100644 distributedcloud/dcmanager/tests/unit/orchestrator/states/software/test_deploy_start.py create mode 100644 distributedcloud/dcmanager/tests/unit/orchestrator/states/software/test_finish_strategy.py create mode 100644 distributedcloud/dcmanager/tests/unit/orchestrator/states/software/test_install_license.py create mode 100644 distributedcloud/dcmanager/tests/unit/orchestrator/states/software/test_lock_controller.py create mode 100644 distributedcloud/dcmanager/tests/unit/orchestrator/states/software/test_pre_check.py create mode 100644 distributedcloud/dcmanager/tests/unit/orchestrator/states/software/test_swact_controller.py create mode 100644 distributedcloud/dcmanager/tests/unit/orchestrator/states/software/test_unlock_controller.py create mode 100644 distributedcloud/dcmanager/tests/unit/orchestrator/states/software/test_upload.py diff --git a/distributedcloud/dcmanager/common/consts.py b/distributedcloud/dcmanager/common/consts.py index 422dab185..223bd24da 100644 --- a/distributedcloud/dcmanager/common/consts.py +++ b/distributedcloud/dcmanager/common/consts.py @@ -78,6 +78,7 @@ SW_UPDATE_TYPE_KUBERNETES = "kubernetes" SW_UPDATE_TYPE_PATCH = "patch" SW_UPDATE_TYPE_PRESTAGE = "prestage" SW_UPDATE_TYPE_UPGRADE = "upgrade" +SW_UPDATE_TYPE_SOFTWARE = "software" # Software update states SW_UPDATE_STATE_INITIAL = "initial" @@ -144,6 +145,22 @@ STRATEGY_STATE_CREATING_VIM_UPGRADE_STRATEGY = "creating VIM upgrade strategy" STRATEGY_STATE_APPLYING_VIM_UPGRADE_STRATEGY = "applying VIM upgrade strategy" 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 = "software swact controller" +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" + # Firmware update orchestration states STRATEGY_STATE_IMPORTING_FIRMWARE = "importing firmware" STRATEGY_STATE_CREATING_FW_UPDATE_STRATEGY = "creating fw update strategy" diff --git a/distributedcloud/dcmanager/orchestrator/software_orch_thread.py b/distributedcloud/dcmanager/orchestrator/software_orch_thread.py index 66f7ecebf..bb912c976 100644 --- a/distributedcloud/dcmanager/orchestrator/software_orch_thread.py +++ b/distributedcloud/dcmanager/orchestrator/software_orch_thread.py @@ -8,8 +8,37 @@ from oslo_log import log as logging from dccommon.drivers.openstack import vim from dcmanager.common import consts -from dcmanager.db import api as db_api from dcmanager.orchestrator.orch_thread import OrchThread +from dcmanager.orchestrator.states.software.apply_vim_software_strategy \ + import ApplyVIMSoftwareStrategyState +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_controller \ + import SwactControllerState +from dcmanager.orchestrator.states.software.unlock_controller \ + import UnlockControllerState +from dcmanager.orchestrator.states.software.upload \ + import UploadState LOG = logging.getLogger(__name__) @@ -31,33 +60,47 @@ class SoftwareOrchThread(OrchThread): database as it goes, with state and progress information. """ + # every state in sw upgrade orchestration should have an operator + 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: SwactControllerState, + consts.STRATEGY_STATE_SW_CREATE_VIM_STRATEGY: CreateVIMSoftwareStrategyState, + consts.STRATEGY_STATE_SW_APPLY_VIM_STRATEGY: ApplyVIMSoftwareStrategyState, + 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__( strategy_lock, audit_rpc_client, - consts.SW_UPDATE_TYPE_UPGRADE, # software update strategy type - vim.STRATEGY_NAME_SW_UPGRADE, # strategy type used by vim - consts.STRATEGY_STATE_COMPLETE) # starting state + consts.SW_UPDATE_TYPE_UPGRADE, # software update strategy type + vim.STRATEGY_NAME_SW_UPGRADE, # strategy type used by vim + consts.STRATEGY_STATE_SW_PRE_CHECK) # starting state + + # Initialize shared cache instances for the states that require them + self._shared_caches = SharedCacheRepository(consts.SW_UPDATE_TYPE_SOFTWARE) + self._shared_caches.initialize_caches() def trigger_audit(self): """Trigger an audit for upgrade (which is combined with patch audit)""" self.audit_rpc_client.trigger_patch_audit(self.context) - def delete(self, sw_update_strategy): - super(SoftwareOrchThread, self).delete(sw_update_strategy) + def pre_apply_setup(self): + # Restart caches for next strategy + self._shared_caches.initialize_caches() + super(SoftwareOrchThread, self).pre_apply_setup() - def apply(self, sw_update_strategy): - LOG.info("(%s) Applying update strategy" % self.update_type) - - LOG.info("(%s) Strategy application is complete." - % self.update_type) - with self.strategy_lock: - db_api.sw_update_strategy_update( - self.context, - state=consts.SW_UPDATE_STATE_COMPLETE, - update_type=self.update_type) - - self.subcloud_workers.clear() - # Trigger audit to update the sync status for each subcloud. - self.trigger_audit() - return + def determine_state_operator(self, strategy_step): + state = super(SoftwareOrchThread, self).determine_state_operator( + strategy_step) + state.add_shared_caches(self._shared_caches) + return state diff --git a/distributedcloud/dcmanager/orchestrator/states/software/__init__.py b/distributedcloud/dcmanager/orchestrator/states/software/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/distributedcloud/dcmanager/orchestrator/states/software/apply_vim_software_strategy.py b/distributedcloud/dcmanager/orchestrator/states/software/apply_vim_software_strategy.py new file mode 100644 index 000000000..e673620cf --- /dev/null +++ b/distributedcloud/dcmanager/orchestrator/states/software/apply_vim_software_strategy.py @@ -0,0 +1,22 @@ +# +# 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 ApplyVIMSoftwareStrategyState(BaseState): + """Apply VIM Software Strategy software orchestration state""" + + def __init__(self, region_name): + super(ApplyVIMSoftwareStrategyState, self).__init__( + next_state=consts.STRATEGY_STATE_SW_DEPLOY_ACTIVATE, + region_name=region_name + ) + + def perform_state_action(self, strategy_step): + """Apply VIM Software Strategy region status""" + return self.next_state diff --git a/distributedcloud/dcmanager/orchestrator/states/software/cache/__init__.py b/distributedcloud/dcmanager/orchestrator/states/software/cache/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/distributedcloud/dcmanager/orchestrator/states/software/cache/cache_specifications.py b/distributedcloud/dcmanager/orchestrator/states/software/cache/cache_specifications.py new file mode 100644 index 000000000..14350a3bf --- /dev/null +++ b/distributedcloud/dcmanager/orchestrator/states/software/cache/cache_specifications.py @@ -0,0 +1,90 @@ +# +# Copyright (c) 2023 Wind River Systems, Inc. +# +# SPDX-License-Identifier: Apache-2.0 +# +from dcmanager.common import consts +from dcmanager.orchestrator.states.software.cache import clients +from dcmanager.orchestrator.states.software.cache.clients import \ + CLIENT_READ_EXCEPTIONS +from dcmanager.orchestrator.states.software.cache.clients import \ + CLIENT_READ_MAX_ATTEMPTS + + +class CacheSpecification(object): + def __init__(self, fetch_implementation, + post_filter_implementation=None, valid_filters=frozenset(), + retry_on_exception=CLIENT_READ_EXCEPTIONS, + max_attempts=CLIENT_READ_MAX_ATTEMPTS, + retry_sleep_msecs=consts.PLATFORM_RETRY_SLEEP_MILLIS): + """Create cache specification. + + :param fetch_implementation: implementation on how to retrieve data from + client + :type fetch_implementation: function + :param post_filter_implementation: implementation on how to post-filter + cached data, if any + :type post_filter_implementation: function + :param valid_filters: valid post-filter parameters + :type valid_filters: set + :param retry_on_exception: exceptions to be retried on client read + :type retry_on_exception: type|tuple + :param max_attempts: Maximum number of client read attempts if retryable + exceptions occur + :param retry_sleep_msecs: Fixed backoff interval + """ + self.fetch_implementation = fetch_implementation + self.post_filter_implementation = post_filter_implementation + self.valid_filters = valid_filters + + # Retry configurations + self.retry_on_exception = retry_on_exception + self.max_attempts = max_attempts + self.retry_sleep_msecs = retry_sleep_msecs + + +"""Cache types""" + +REGION_ONE_LICENSE_CACHE_TYPE = 'RegionOne system license' +REGION_ONE_SYSTEM_INFO_CACHE_TYPE = 'RegionOne system info' +REGION_ONE_RELEASE_USM_CACHE_TYPE = 'RegionOne release usm' + +"""Cache specifications""" + +REGION_ONE_LICENSE_CACHE_SPECIFICATION = CacheSpecification( + lambda: clients.get_sysinv_client().get_license()) + +REGION_ONE_SYSTEM_INFO_CACHE_SPECIFICATION = CacheSpecification( + lambda: clients.get_sysinv_client().get_system()) + +REGION_ONE_RELEASE_USM_CACHE_SPECIFICATION = CacheSpecification( + lambda: clients.get_software_client().query(), + # Filter results by patching state, if any is given + lambda patches, **filter_params: { + patch_id: patch for patch_id, patch in patches.items() + if filter_params.get('state') is None + or patch.get('state') == filter_params.get('state') + }, + {'state'} +) + +# Map each expected operation type to its required cache types +CACHE_TYPES_BY_OPERATION_TYPE = { + consts.SW_UPDATE_TYPE_SOFTWARE: {REGION_ONE_LICENSE_CACHE_TYPE, + REGION_ONE_SYSTEM_INFO_CACHE_TYPE, + REGION_ONE_RELEASE_USM_CACHE_TYPE} +} + +# Map each cache type to its corresponding cache specification +SPECIFICATION_BY_CACHE_TYPE = { + REGION_ONE_LICENSE_CACHE_TYPE: REGION_ONE_LICENSE_CACHE_SPECIFICATION, + REGION_ONE_SYSTEM_INFO_CACHE_TYPE: REGION_ONE_SYSTEM_INFO_CACHE_SPECIFICATION, + REGION_ONE_RELEASE_USM_CACHE_TYPE: REGION_ONE_RELEASE_USM_CACHE_SPECIFICATION +} + + +def get_specifications_for_operation(operation_type): + # Retrieve all cache specifications required by a given operation type + # Return a mapping between each required type to its corresponding specification + return {cache_type: SPECIFICATION_BY_CACHE_TYPE.get(cache_type) + for cache_type in CACHE_TYPES_BY_OPERATION_TYPE.get(operation_type)} diff --git a/distributedcloud/dcmanager/orchestrator/states/software/cache/clients.py b/distributedcloud/dcmanager/orchestrator/states/software/cache/clients.py new file mode 100644 index 000000000..34712a857 --- /dev/null +++ b/distributedcloud/dcmanager/orchestrator/states/software/cache/clients.py @@ -0,0 +1,50 @@ +# +# Copyright (c) 2023 Wind River Systems, Inc. +# +# SPDX-License-Identifier: Apache-2.0 +# +import socket + +from keystoneauth1 import exceptions as keystone_exceptions +from oslo_log import log as logging + +from dccommon import consts as dccommon_consts +from dccommon.drivers.openstack.sdk_platform import OpenStackDriver +from dccommon.drivers.openstack.software_v1 import SoftwareClient +from dccommon.drivers.openstack.sysinv_v1 import SysinvClient + +LOG = logging.getLogger(__name__) + +""" Default timeout configurations for client reads """ + +CLIENT_READ_TIMEOUT_SECONDS = 60 +CLIENT_READ_EXCEPTIONS = (socket.timeout, keystone_exceptions.ServiceUnavailable) +CLIENT_READ_MAX_ATTEMPTS = 2 + +""" Helper functions to retrieve clients for caching """ + + +def get_sysinv_client(): + ks_client = get_keystone_client() + return SysinvClient(dccommon_consts.DEFAULT_REGION_NAME, ks_client.session, + endpoint=ks_client.endpoint_cache.get_endpoint('sysinv'), + timeout=CLIENT_READ_TIMEOUT_SECONDS) + + +def get_software_client(): + ks_client = get_keystone_client() + return SoftwareClient(dccommon_consts.DEFAULT_REGION_NAME, ks_client.session, + endpoint=ks_client.endpoint_cache.get_endpoint('usm')) + + +def get_keystone_client(region_name=dccommon_consts.DEFAULT_REGION_NAME): + """Construct a (cached) keystone client (and token)""" + + try: + os_client = OpenStackDriver(region_name=region_name, + region_clients=['sysinv']) + return os_client.keystone_client + except Exception: + LOG.warning('Failure initializing KeystoneClient for region: %s' + % region_name) + raise diff --git a/distributedcloud/dcmanager/orchestrator/states/software/cache/shared_cache_repository.py b/distributedcloud/dcmanager/orchestrator/states/software/cache/shared_cache_repository.py new file mode 100644 index 000000000..a7bdd587c --- /dev/null +++ b/distributedcloud/dcmanager/orchestrator/states/software/cache/shared_cache_repository.py @@ -0,0 +1,39 @@ +# +# Copyright (c) 2023 Wind River Systems, Inc. +# +# SPDX-License-Identifier: Apache-2.0 +# + +from oslo_log import log + +from dcmanager.common.exceptions import InvalidParameterValue +from dcmanager.orchestrator.states.software.cache import cache_specifications +from dcmanager.orchestrator.states.software.cache.shared_client_cache import \ + SharedClientCache + +LOG = log.getLogger(__name__) + + +class SharedCacheRepository(object): + + def __init__(self, operation_type): + self._shared_caches = {} + self._operation_type = operation_type + + def initialize_caches(self): + # Retrieve specifications for each cache type required by the operation + # Return mapping between each required type to a single cache instance of it + self._shared_caches = { + cache_type: SharedClientCache(cache_type, cache_specification) + for cache_type, cache_specification in + cache_specifications.get_specifications_for_operation( + self._operation_type).items() + } + + def read(self, cache_type, **filter_params): + cache = self._shared_caches.get(cache_type) + if cache: + return cache.read(**filter_params) + else: + raise InvalidParameterValue(err="Specified cache type '%s' not " + "present" % cache_type) diff --git a/distributedcloud/dcmanager/orchestrator/states/software/cache/shared_client_cache.py b/distributedcloud/dcmanager/orchestrator/states/software/cache/shared_client_cache.py new file mode 100644 index 000000000..239721f28 --- /dev/null +++ b/distributedcloud/dcmanager/orchestrator/states/software/cache/shared_client_cache.py @@ -0,0 +1,125 @@ +# +# Copyright (c) 2023 Wind River Systems, Inc. +# +# SPDX-License-Identifier: Apache-2.0 +# +import retrying + +from oslo_concurrency import lockutils +from oslo_log import log + +from dcmanager.common.exceptions import InvalidParameterValue + +LOG = log.getLogger(__name__) + + +class SharedClientCache(object): + """Data cache for sharing client or API data between concurrent threads + + Used to avoid repeated requests and prevent client overload. + + Cache is not self refreshing. User of the cache is responsible for triggering + the refresh. + + """ + + def __init__(self, cache_type, cache_specification): + """Create cache instance. + + :param cache_type: type of data being cached, for logging + :type cache_type: str + :param cache_specification: specifications on how the cache should + operate + :type cache_specification: dcmanager.orchestrator.states.upgrade.cache + .cache_specifications.CacheSpecification + """ + self._client_lock = lockutils.ReaderWriterLock() + self._cache = None + + # Cache configurations + self._cache_type = cache_type + self._valid_filters = cache_specification.valid_filters + + # Retry configurations + self._max_attempts = cache_specification.max_attempts + self._retry_sleep_msecs = cache_specification.retry_sleep_msecs + + # Add retry to client read if any retryable exception is provided + self._load_data_from_client = cache_specification.fetch_implementation + retry_on_exception = cache_specification.retry_on_exception + if retry_on_exception: + retry = retrying.retry(retry_on_exception=lambda + ex: isinstance(ex, retry_on_exception), + stop_max_attempt_number=self._max_attempts, + wait_fixed=self._retry_sleep_msecs, + wait_func=self._retry_client_read) + self._load_data_from_client = \ + retry(cache_specification.fetch_implementation) + + # Use default implementation with no filtering if none is provided + self._post_filter_impl = cache_specification.post_filter_implementation\ + or (lambda data, **filter_params: data) + + def read(self, **filter_params): + """Retrieve data from cache, if available. + + Read from client and (re)populate cache, if not. + + Only one thread may access the client at a time to prevent overload. + + Concurrent reads are blocked until client read completes/fails. A recheck + for updates on the cache is performed afterwards. + + Post-filtering can be applied to the results before returning. Data saved to + the cache will not include any filtering applied to returned data. + + :param filter_params: parameters to be used for post-filtering + :type filter_params: string + :return: Cached data, filtered according to parameters given + :raises RuntimeError: If cache read fails due to concurrent client read error + :raises InvalidParameterError: If invalid filter parameters are given + """ + # Use data stored in the cache, if present. Otherwise, read and cache + # data from client + if self._cache is None: + self._cache_data_from_client() + + # Filter cached data and return results + return self._post_filter(self._cache, **filter_params) + + def _cache_data_from_client(self): + # Read from the client and update cache if no concurrent write is in progress + if self._client_lock.owner != lockutils.ReaderWriterLock.WRITER: + with self._client_lock.write_lock(): + # Atomically fetch data from client and update the cache + LOG.info("Reading data from %s client for caching" % + self._cache_type) + self._cache = self._load_data_from_client() + else: + # If a concurrent write is in progress, wait for it and recheck cache + with self._client_lock.read_lock(): + if self._cache is None: + raise RuntimeError("Failed to retrieve data from %s cache. " + "Possible failure on concurrent client " + "read." % self._cache_type) + + def _retry_client_read(self, attempt, _): + # To be called when a client read operation fails with a retryable error + # After this, read operation should be retried + LOG.warn("Retryable error occurred while reading from %s client " + "(Attempt %s/%s)" % (self._cache_type, attempt, self._max_attempts)) + return self._retry_sleep_msecs + + def _post_filter(self, data, **filter_params): + # Validate the parameters and apply specified filter implementation + self._validate_filter_params(**filter_params) + return self._post_filter_impl(data, **filter_params) + + def _validate_filter_params(self, **filter_params): + # Compare each passed parameter against the specified valid parameters + # Raise an exception if any unexpected parameter is found + if filter_params: + invalid_params = set(filter_params.keys()) - self._valid_filters + if invalid_params: + raise InvalidParameterValue(err="Invalid filter parameters: %s" % + invalid_params) diff --git a/distributedcloud/dcmanager/orchestrator/states/software/create_vim_software_strategy.py b/distributedcloud/dcmanager/orchestrator/states/software/create_vim_software_strategy.py new file mode 100644 index 000000000..7474d2655 --- /dev/null +++ b/distributedcloud/dcmanager/orchestrator/states/software/create_vim_software_strategy.py @@ -0,0 +1,22 @@ +# +# 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 CreateVIMSoftwareStrategyState(BaseState): + """Create VIM Software Strategy software orchestration state""" + + def __init__(self, region_name): + super(CreateVIMSoftwareStrategyState, self).__init__( + next_state=consts.STRATEGY_STATE_SW_APPLY_VIM_STRATEGY, + region_name=region_name + ) + + def perform_state_action(self, strategy_step): + """Create VIM Software Strategy region status""" + return self.next_state diff --git a/distributedcloud/dcmanager/orchestrator/states/software/deploy_activate.py b/distributedcloud/dcmanager/orchestrator/states/software/deploy_activate.py new file mode 100644 index 000000000..88fbb2822 --- /dev/null +++ b/distributedcloud/dcmanager/orchestrator/states/software/deploy_activate.py @@ -0,0 +1,22 @@ +# +# 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 diff --git a/distributedcloud/dcmanager/orchestrator/states/software/deploy_complete.py b/distributedcloud/dcmanager/orchestrator/states/software/deploy_complete.py new file mode 100644 index 000000000..eb764a4a7 --- /dev/null +++ b/distributedcloud/dcmanager/orchestrator/states/software/deploy_complete.py @@ -0,0 +1,22 @@ +# +# 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 diff --git a/distributedcloud/dcmanager/orchestrator/states/software/deploy_host.py b/distributedcloud/dcmanager/orchestrator/states/software/deploy_host.py new file mode 100644 index 000000000..8df969d78 --- /dev/null +++ b/distributedcloud/dcmanager/orchestrator/states/software/deploy_host.py @@ -0,0 +1,22 @@ +# +# 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 diff --git a/distributedcloud/dcmanager/orchestrator/states/software/deploy_pre_check.py b/distributedcloud/dcmanager/orchestrator/states/software/deploy_pre_check.py new file mode 100644 index 000000000..175dab8b5 --- /dev/null +++ b/distributedcloud/dcmanager/orchestrator/states/software/deploy_pre_check.py @@ -0,0 +1,22 @@ +# +# 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 diff --git a/distributedcloud/dcmanager/orchestrator/states/software/deploy_start.py b/distributedcloud/dcmanager/orchestrator/states/software/deploy_start.py new file mode 100644 index 000000000..de4f7148d --- /dev/null +++ b/distributedcloud/dcmanager/orchestrator/states/software/deploy_start.py @@ -0,0 +1,22 @@ +# +# 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 DeployStartState(BaseState): + """Deploy start software orchestration state""" + + def __init__(self, region_name): + super(DeployStartState, self).__init__( + next_state=consts.STRATEGY_STATE_SW_LOCK_CONTROLLER, + region_name=region_name, + ) + + def perform_state_action(self, strategy_step): + """Deploy Start region status""" + return self.next_state diff --git a/distributedcloud/dcmanager/orchestrator/states/software/finish_strategy.py b/distributedcloud/dcmanager/orchestrator/states/software/finish_strategy.py new file mode 100644 index 000000000..0ac19395a --- /dev/null +++ b/distributedcloud/dcmanager/orchestrator/states/software/finish_strategy.py @@ -0,0 +1,22 @@ +# +# 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 FinishStrategyState(BaseState): + """Finish Strategy software orchestration state""" + + def __init__(self, region_name): + super(FinishStrategyState, self).__init__( + next_state=consts.STRATEGY_STATE_COMPLETE, + region_name=region_name, + ) + + def perform_state_action(self, strategy_step): + """Finish Strategy region status""" + return self.next_state diff --git a/distributedcloud/dcmanager/orchestrator/states/software/install_license.py b/distributedcloud/dcmanager/orchestrator/states/software/install_license.py new file mode 100644 index 000000000..7b7ef8651 --- /dev/null +++ b/distributedcloud/dcmanager/orchestrator/states/software/install_license.py @@ -0,0 +1,22 @@ +# +# 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 InstallLicenseState(BaseState): + """Install license software orchestration state""" + + def __init__(self, region_name): + super(InstallLicenseState, self).__init__( + next_state=consts.STRATEGY_STATE_SW_UPLOAD, + region_name=region_name, + ) + + def perform_state_action(self, strategy_step): + """Install license region status""" + return self.next_state diff --git a/distributedcloud/dcmanager/orchestrator/states/software/lock_controller.py b/distributedcloud/dcmanager/orchestrator/states/software/lock_controller.py new file mode 100644 index 000000000..6d9e54288 --- /dev/null +++ b/distributedcloud/dcmanager/orchestrator/states/software/lock_controller.py @@ -0,0 +1,22 @@ +# +# 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 LockControllerState(BaseState): + """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, + ) + + def perform_state_action(self, strategy_step): + """Lock controller region status""" + return self.next_state diff --git a/distributedcloud/dcmanager/orchestrator/states/software/pre_check.py b/distributedcloud/dcmanager/orchestrator/states/software/pre_check.py new file mode 100644 index 000000000..592a9a07d --- /dev/null +++ b/distributedcloud/dcmanager/orchestrator/states/software/pre_check.py @@ -0,0 +1,22 @@ +# +# 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 PreCheckState(BaseState): + """Pre check software orchestration state""" + + def __init__(self, region_name): + super(PreCheckState, self).__init__( + next_state=consts.STRATEGY_STATE_SW_INSTALL_LICENSE, + region_name=region_name, + ) + + def perform_state_action(self, strategy_step): + """Pre check region status""" + return self.next_state diff --git a/distributedcloud/dcmanager/orchestrator/states/software/swact_controller.py b/distributedcloud/dcmanager/orchestrator/states/software/swact_controller.py new file mode 100644 index 000000000..40f7211a9 --- /dev/null +++ b/distributedcloud/dcmanager/orchestrator/states/software/swact_controller.py @@ -0,0 +1,22 @@ +# +# 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 SwactControllerState(BaseState): + """Swact controller software orchestration state""" + + def __init__(self, region_name): + super(SwactControllerState, self).__init__( + next_state=consts.STRATEGY_STATE_SW_CREATE_VIM_STRATEGY, + region_name=region_name, + ) + + def perform_state_action(self, strategy_step): + """Swact controller region status""" + return self.next_state diff --git a/distributedcloud/dcmanager/orchestrator/states/software/unlock_controller.py b/distributedcloud/dcmanager/orchestrator/states/software/unlock_controller.py new file mode 100644 index 000000000..c0d60de0d --- /dev/null +++ b/distributedcloud/dcmanager/orchestrator/states/software/unlock_controller.py @@ -0,0 +1,22 @@ +# +# 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 diff --git a/distributedcloud/dcmanager/orchestrator/states/software/upload.py b/distributedcloud/dcmanager/orchestrator/states/software/upload.py new file mode 100644 index 000000000..800464de9 --- /dev/null +++ b/distributedcloud/dcmanager/orchestrator/states/software/upload.py @@ -0,0 +1,22 @@ +# +# 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 UploadState(BaseState): + """Upload software orchestration state""" + + def __init__(self, region_name): + super(UploadState, self).__init__( + next_state=consts.STRATEGY_STATE_SW_DEPLOY_PRE_CHECK, + region_name=region_name, + ) + + def perform_state_action(self, strategy_step): + """Upload region status""" + return self.next_state diff --git a/distributedcloud/dcmanager/tests/unit/orchestrator/states/software/__init__.py b/distributedcloud/dcmanager/tests/unit/orchestrator/states/software/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/distributedcloud/dcmanager/tests/unit/orchestrator/states/software/test_apply_vim_software_strategy.py b/distributedcloud/dcmanager/tests/unit/orchestrator/states/software/test_apply_vim_software_strategy.py new file mode 100644 index 000000000..c759086b9 --- /dev/null +++ b/distributedcloud/dcmanager/tests/unit/orchestrator/states/software/test_apply_vim_software_strategy.py @@ -0,0 +1,32 @@ +# +# 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 TestApplyVIMSoftwareStrategyState(TestSoftwareOrchestrator): + def setUp(self): + super(TestApplyVIMSoftwareStrategyState, 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_APPLY_VIM_STRATEGY) + + def test_apply_vim_software_strategy_success(self): + """Test apply vim software strategy 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) diff --git a/distributedcloud/dcmanager/tests/unit/orchestrator/states/software/test_base.py b/distributedcloud/dcmanager/tests/unit/orchestrator/states/software/test_base.py new file mode 100644 index 000000000..200089c94 --- /dev/null +++ b/distributedcloud/dcmanager/tests/unit/orchestrator/states/software/test_base.py @@ -0,0 +1,37 @@ +# +# Copyright (c) 2023 Wind River Systems, Inc. +# +# SPDX-License-Identifier: Apache-2.0 +# + +import mock + +from dcmanager.common import consts +from dcmanager.tests.unit.orchestrator.test_base import TestSwUpdate + +CACHE_CLIENT_PATH = "dcmanager.orchestrator.states.software.cache.clients" + + +class TestSoftwareOrchestrator(TestSwUpdate): + # Setting DEFAULT_STRATEGY_TYPE to software will setup the software + # orchestration worker, and will mock away the other orch threads + DEFAULT_STRATEGY_TYPE = consts.SW_UPDATE_TYPE_SOFTWARE + + def setUp(self): + super(TestSoftwareOrchestrator, self).setUp() + + # Modify cache helpers to return client mocks + self.software_cache_client_mock = mock.patch( + "%s.get_software_client" % CACHE_CLIENT_PATH, + return_value=self.software_client, + ) + self.sysinv_cache_client_mock = mock.patch( + "%s.get_sysinv_client" % CACHE_CLIENT_PATH, return_value=self.sysinv_client + ) + self.software_cache_client_mock.start() + self.sysinv_cache_client_mock.start() + + def tearDown(self): + self.software_cache_client_mock.stop() + self.sysinv_cache_client_mock.stop() + super(TestSoftwareOrchestrator, self).tearDown() diff --git a/distributedcloud/dcmanager/tests/unit/orchestrator/states/software/test_create_vim_software_strategy.py b/distributedcloud/dcmanager/tests/unit/orchestrator/states/software/test_create_vim_software_strategy.py new file mode 100644 index 000000000..e0ed3fac0 --- /dev/null +++ b/distributedcloud/dcmanager/tests/unit/orchestrator/states/software/test_create_vim_software_strategy.py @@ -0,0 +1,32 @@ +# +# 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 TestCreateVIMSoftwareStrategyState(TestSoftwareOrchestrator): + def setUp(self): + super(TestCreateVIMSoftwareStrategyState, self).setUp() + + self.on_success_state = consts.STRATEGY_STATE_SW_APPLY_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_CREATE_VIM_STRATEGY) + + def test_create_vim_software_strategy_success(self): + """Test create vim software strategy 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) diff --git a/distributedcloud/dcmanager/tests/unit/orchestrator/states/software/test_deploy_activate.py b/distributedcloud/dcmanager/tests/unit/orchestrator/states/software/test_deploy_activate.py new file mode 100644 index 000000000..411d4b4ac --- /dev/null +++ b/distributedcloud/dcmanager/tests/unit/orchestrator/states/software/test_deploy_activate.py @@ -0,0 +1,32 @@ +# +# 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) diff --git a/distributedcloud/dcmanager/tests/unit/orchestrator/states/software/test_deploy_complete.py b/distributedcloud/dcmanager/tests/unit/orchestrator/states/software/test_deploy_complete.py new file mode 100644 index 000000000..d3edd934c --- /dev/null +++ b/distributedcloud/dcmanager/tests/unit/orchestrator/states/software/test_deploy_complete.py @@ -0,0 +1,32 @@ +# +# 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) diff --git a/distributedcloud/dcmanager/tests/unit/orchestrator/states/software/test_deploy_host.py b/distributedcloud/dcmanager/tests/unit/orchestrator/states/software/test_deploy_host.py new file mode 100644 index 000000000..873fcb031 --- /dev/null +++ b/distributedcloud/dcmanager/tests/unit/orchestrator/states/software/test_deploy_host.py @@ -0,0 +1,32 @@ +# +# 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) diff --git a/distributedcloud/dcmanager/tests/unit/orchestrator/states/software/test_deploy_pre_check.py b/distributedcloud/dcmanager/tests/unit/orchestrator/states/software/test_deploy_pre_check.py new file mode 100644 index 000000000..e35336ba4 --- /dev/null +++ b/distributedcloud/dcmanager/tests/unit/orchestrator/states/software/test_deploy_pre_check.py @@ -0,0 +1,32 @@ +# +# 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) diff --git a/distributedcloud/dcmanager/tests/unit/orchestrator/states/software/test_deploy_start.py b/distributedcloud/dcmanager/tests/unit/orchestrator/states/software/test_deploy_start.py new file mode 100644 index 000000000..9d4efe4b2 --- /dev/null +++ b/distributedcloud/dcmanager/tests/unit/orchestrator/states/software/test_deploy_start.py @@ -0,0 +1,32 @@ +# +# 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 TestDeployStartState(TestSoftwareOrchestrator): + def setUp(self): + super(TestDeployStartState, self).setUp() + + self.on_success_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) + + def test_deploy_start_success(self): + """Test deploy start 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) diff --git a/distributedcloud/dcmanager/tests/unit/orchestrator/states/software/test_finish_strategy.py b/distributedcloud/dcmanager/tests/unit/orchestrator/states/software/test_finish_strategy.py new file mode 100644 index 000000000..dbf7b100b --- /dev/null +++ b/distributedcloud/dcmanager/tests/unit/orchestrator/states/software/test_finish_strategy.py @@ -0,0 +1,32 @@ +# +# 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 TestFinishStrategyState(TestSoftwareOrchestrator): + def setUp(self): + super(TestFinishStrategyState, self).setUp() + + self.on_success_state = consts.STRATEGY_STATE_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_FINISH_STRATEGY) + + def test_finish_strategy_success(self): + """Test finish strategy 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) diff --git a/distributedcloud/dcmanager/tests/unit/orchestrator/states/software/test_install_license.py b/distributedcloud/dcmanager/tests/unit/orchestrator/states/software/test_install_license.py new file mode 100644 index 000000000..a3bd735e9 --- /dev/null +++ b/distributedcloud/dcmanager/tests/unit/orchestrator/states/software/test_install_license.py @@ -0,0 +1,35 @@ +# +# 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 TestInstallLicenseState(TestSoftwareOrchestrator): + + def setUp(self): + super(TestInstallLicenseState, self).setUp() + + # next state after install a license is 'upload' + self.on_success_state = consts.STRATEGY_STATE_SW_UPLOAD + + # 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) + + def test_upgrade_subcloud_license_install_success(self): + """Test the install license step succeeds.""" + + # invoke the strategy state operation on the orch thread + self.worker.perform_state_action(self.strategy_step) + + # On success, the next state after installing license is importing load + self.assert_step_updated(self.strategy_step.subcloud_id, + self.on_success_state) diff --git a/distributedcloud/dcmanager/tests/unit/orchestrator/states/software/test_lock_controller.py b/distributedcloud/dcmanager/tests/unit/orchestrator/states/software/test_lock_controller.py new file mode 100644 index 000000000..d193bc197 --- /dev/null +++ b/distributedcloud/dcmanager/tests/unit/orchestrator/states/software/test_lock_controller.py @@ -0,0 +1,32 @@ +# +# 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 TestLockControllerState(TestSoftwareOrchestrator): + def setUp(self): + super(TestLockControllerState, self).setUp() + + 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, consts.STRATEGY_STATE_SW_LOCK_CONTROLLER) + + def test_lock_controller_success(self): + """Test lock 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) diff --git a/distributedcloud/dcmanager/tests/unit/orchestrator/states/software/test_pre_check.py b/distributedcloud/dcmanager/tests/unit/orchestrator/states/software/test_pre_check.py new file mode 100644 index 000000000..41bb42a64 --- /dev/null +++ b/distributedcloud/dcmanager/tests/unit/orchestrator/states/software/test_pre_check.py @@ -0,0 +1,32 @@ +# +# 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 TestPreCheckState(TestSoftwareOrchestrator): + def setUp(self): + super(TestPreCheckState, self).setUp() + + self.on_success_state = consts.STRATEGY_STATE_SW_INSTALL_LICENSE + + # 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_PRE_CHECK) + + def test_pre_check_success(self): + """Test 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) diff --git a/distributedcloud/dcmanager/tests/unit/orchestrator/states/software/test_swact_controller.py b/distributedcloud/dcmanager/tests/unit/orchestrator/states/software/test_swact_controller.py new file mode 100644 index 000000000..16d5eff98 --- /dev/null +++ b/distributedcloud/dcmanager/tests/unit/orchestrator/states/software/test_swact_controller.py @@ -0,0 +1,32 @@ +# +# 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 TestSwactControllerState(TestSoftwareOrchestrator): + def setUp(self): + super(TestSwactControllerState, self).setUp() + + 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_SWACT_CONTROLLER) + + def test_swact_controller_success(self): + """Test swact 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) diff --git a/distributedcloud/dcmanager/tests/unit/orchestrator/states/software/test_unlock_controller.py b/distributedcloud/dcmanager/tests/unit/orchestrator/states/software/test_unlock_controller.py new file mode 100644 index 000000000..323b89269 --- /dev/null +++ b/distributedcloud/dcmanager/tests/unit/orchestrator/states/software/test_unlock_controller.py @@ -0,0 +1,32 @@ +# +# 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) diff --git a/distributedcloud/dcmanager/tests/unit/orchestrator/states/software/test_upload.py b/distributedcloud/dcmanager/tests/unit/orchestrator/states/software/test_upload.py new file mode 100644 index 000000000..4493d93aa --- /dev/null +++ b/distributedcloud/dcmanager/tests/unit/orchestrator/states/software/test_upload.py @@ -0,0 +1,32 @@ +# +# 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 TestUploadState(TestSoftwareOrchestrator): + def setUp(self): + 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) + + def test_upload_success(self): + """Test upload 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) diff --git a/distributedcloud/dcmanager/tests/unit/orchestrator/test_base.py b/distributedcloud/dcmanager/tests/unit/orchestrator/test_base.py index 6e3dd4b7c..a988cc25d 100644 --- a/distributedcloud/dcmanager/tests/unit/orchestrator/test_base.py +++ b/distributedcloud/dcmanager/tests/unit/orchestrator/test_base.py @@ -115,6 +115,19 @@ class TestSwUpdate(base.DCManagerTestCase): self.fake_sw_upgrade_orch_thread self.addCleanup(p.stop) + if strategy_type == consts.SW_UPDATE_TYPE_SOFTWARE: + sw_update_manager.SoftwareOrchThread.stopped = lambda x: False + worker = sw_update_manager.SoftwareOrchThread( + mock_strategy_lock, mock_dcmanager_audit_api) + else: + # mock the software orch thread + self.fake_software_orch_thread = FakeOrchThread() + p = mock.patch.object(sw_update_manager, 'SoftwareOrchThread') + self.mock_software_orch_thread = p.start() + self.mock_software_orch_thread.return_value = \ + self.fake_software_orch_thread + self.addCleanup(p.stop) + if strategy_type == consts.SW_UPDATE_TYPE_PATCH: sw_update_manager.PatchOrchThread.stopped = lambda x: False worker = \