From 952cbeb7e4af1643844cdcb700ab28edcf06ba6c Mon Sep 17 00:00:00 2001 From: Christopher Souza Date: Thu, 7 Sep 2023 08:01:41 -0300 Subject: [PATCH] Add the --upload-only option to dcmanager upgrade_strategy This commit adds a new option --upload-only to dcmanager upgrade-strategy create. If this option is used, the releases are uploaded to the subclouds and the strategy state is set to complete. Depends-on: https://review.opendev.org/c/starlingx/distcloud/+/893425 Test Plan: 1. PASS - Create and apply a patch strategy using the --upload-only option and verify that the strategy completes after uploading the patches to the subclouds. 2. PASS - Create and apply a patch strategy without using the --upload-only option and verify that the strategy continues after uploading the patches to the subclouds. Story: 2010676 Task: 48747 Signed-off-by: Christopher Souza Change-Id: I3941151e71e356e5e91dbbf931e2302116c40277 (cherry picked from commit f3817f6c4484f72f11ce731fef08a014b341263e) --- .../software/cache/cache_specifications.py | 15 ++++++-- .../orchestrator/states/software/upload.py | 11 +++++- .../orchestrator/sw_update_manager.py | 3 +- .../states/software/test_upload.py | 34 +++++++++++++++---- 4 files changed, 52 insertions(+), 11 deletions(-) diff --git a/distributedcloud/dcmanager/orchestrator/states/software/cache/cache_specifications.py b/distributedcloud/dcmanager/orchestrator/states/software/cache/cache_specifications.py index 14350a3bf..207579be3 100644 --- a/distributedcloud/dcmanager/orchestrator/states/software/cache/cache_specifications.py +++ b/distributedcloud/dcmanager/orchestrator/states/software/cache/cache_specifications.py @@ -4,6 +4,8 @@ # SPDX-License-Identifier: Apache-2.0 # from dcmanager.common import consts +from dcmanager.common import context +from dcmanager.common import utils from dcmanager.orchestrator.states.software.cache import clients from dcmanager.orchestrator.states.software.cache.clients import \ CLIENT_READ_EXCEPTIONS @@ -48,6 +50,7 @@ class CacheSpecification(object): 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' +STRATEGY_EXTRA_ARGS_CACHE_TYPE = 'Strategy extra args' """Cache specifications""" @@ -68,18 +71,26 @@ REGION_ONE_RELEASE_USM_CACHE_SPECIFICATION = CacheSpecification( {'state'} ) +STRATEGY_EXTRA_ARGS_CACHE_SPECIFICATION = CacheSpecification( + lambda: utils.get_sw_update_strategy_extra_args(context.get_admin_context()).get( + consts.EXTRA_ARGS_UPLOAD_ONLY + ) +) + # 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} + REGION_ONE_RELEASE_USM_CACHE_TYPE, + STRATEGY_EXTRA_ARGS_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 + REGION_ONE_RELEASE_USM_CACHE_TYPE: REGION_ONE_RELEASE_USM_CACHE_SPECIFICATION, + STRATEGY_EXTRA_ARGS_CACHE_TYPE: STRATEGY_EXTRA_ARGS_CACHE_SPECIFICATION } diff --git a/distributedcloud/dcmanager/orchestrator/states/software/upload.py b/distributedcloud/dcmanager/orchestrator/states/software/upload.py index 5b724974c..6c7e4c452 100644 --- a/distributedcloud/dcmanager/orchestrator/states/software/upload.py +++ b/distributedcloud/dcmanager/orchestrator/states/software/upload.py @@ -14,6 +14,8 @@ 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 +from dcmanager.orchestrator.states.software.cache.cache_specifications import \ + STRATEGY_EXTRA_ARGS_CACHE_TYPE # Max time: 30 minutes = 180 queries x 10 seconds between DEFAULT_MAX_QUERIES = 180 @@ -43,7 +45,6 @@ class UploadState(BaseState): 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: @@ -52,6 +53,8 @@ class UploadState(BaseState): software_v1.COMMITTED]: applied_releases_ids.append(release_id) + upload_only = self._read_from_cache(STRATEGY_EXTRA_ARGS_CACHE_TYPE) + # Retrieve all subcloud releases try: subcloud_releases = self.get_software_client( @@ -149,4 +152,10 @@ class UploadState(BaseState): self.error_log(strategy_step, message) raise Exception(message) + if upload_only: + self.info_log(strategy_step, + (f"{consts.EXTRA_ARGS_UPLOAD_ONLY} option enabled, skipping" + f" forward to state:({consts.STRATEGY_STATE_COMPLETE})")) + return consts.STRATEGY_STATE_COMPLETE + return self.next_state diff --git a/distributedcloud/dcmanager/orchestrator/sw_update_manager.py b/distributedcloud/dcmanager/orchestrator/sw_update_manager.py index 2e5ec2be3..a76944d82 100644 --- a/distributedcloud/dcmanager/orchestrator/sw_update_manager.py +++ b/distributedcloud/dcmanager/orchestrator/sw_update_manager.py @@ -425,7 +425,8 @@ class SwUpdateManager(manager.Manager): consts.PRESTAGE_SOFTWARE_VERSION: software_version if software_version else SW_VERSION } - elif strategy_type == consts.SW_UPDATE_TYPE_PATCH: + elif (strategy_type == consts.SW_UPDATE_TYPE_PATCH or + strategy_type == consts.SW_UPDATE_TYPE_UPGRADE): upload_only_str = payload.get(consts.EXTRA_ARGS_UPLOAD_ONLY) upload_only_bool = True if upload_only_str == 'true' else False extra_args = {consts.EXTRA_ARGS_UPLOAD_ONLY: upload_only_bool} diff --git a/distributedcloud/dcmanager/tests/unit/orchestrator/states/software/test_upload.py b/distributedcloud/dcmanager/tests/unit/orchestrator/states/software/test_upload.py index efc0dfc79..70f12ac34 100644 --- a/distributedcloud/dcmanager/tests/unit/orchestrator/states/software/test_upload.py +++ b/distributedcloud/dcmanager/tests/unit/orchestrator/states/software/test_upload.py @@ -70,9 +70,8 @@ class TestUploadState(TestSoftwareOrchestrator): 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_read_from_cache.side_effect = [REGION_ONE_RELEASES, False] mock_is_file.return_value = True - self.software_client.query.side_effect = [SUBCLOUD_RELEASES] # invoke the strategy state operation on the orch thread @@ -87,11 +86,32 @@ class TestUploadState(TestSoftwareOrchestrator): self.assert_step_updated(self.strategy_step.subcloud_id, self.on_success_state) + @mock.patch.object(UploadState, '_read_from_cache') + @mock.patch.object(os.path, 'isfile') + def test_software_upload_strategy_upload_only(self, mock_is_file, + mock_read_from_cache): + """Test software upload when the API call succeeds.""" + mock_read_from_cache.side_effect = [REGION_ONE_RELEASES, True] + 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, + consts.SW_UPDATE_STATE_COMPLETE) + @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 + mock_read_from_cache.side_effect = [REGION_ONE_RELEASES, False] self.software_client.query.side_effect = [REGION_ONE_RELEASES] @@ -109,7 +129,7 @@ class TestUploadState(TestSoftwareOrchestrator): 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_read_from_cache.side_effect = [REGION_ONE_RELEASES_2, False] mock_is_dir.return_value = True mock_listdir.return_value = ["DC_22.12.0.iso"] self.software_client.query.side_effect = [SUBCLOUD_RELEASES] @@ -129,7 +149,7 @@ class TestUploadState(TestSoftwareOrchestrator): 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_read_from_cache.side_effect = [REGION_ONE_RELEASES_2, False] 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] @@ -154,7 +174,7 @@ class TestUploadState(TestSoftwareOrchestrator): 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_read_from_cache.side_effect = [REGION_ONE_RELEASES_3, False] 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"] @@ -180,7 +200,7 @@ class TestUploadState(TestSoftwareOrchestrator): 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_read_from_cache.side_effect = [REGION_ONE_RELEASES_3, False] 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"]