# Copyright (c) 2017-2024 Wind River Systems, Inc. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. # import base64 import copy import mock from oslo_config import cfg from oslo_utils import uuidutils from dccommon import consts as dccommon_consts from dcmanager.common import consts from dcmanager.common import context from dcmanager.common import exceptions from dcmanager.common import prestage from dcmanager.common import utils as cutils from dcmanager.db.sqlalchemy import api as db_api from dcmanager.orchestrator import sw_update_manager from dcmanager.tests import base from dcmanager.tests import utils OAM_FLOATING_IP = "10.10.10.12" CONF = cfg.CONF FAKE_ID = "1" FAKE_SW_UPDATE_DATA = { "type": consts.SW_UPDATE_TYPE_PATCH, "subcloud-apply-type": consts.SUBCLOUD_APPLY_TYPE_PARALLEL, "max-parallel-subclouds": "2", "stop-on-failure": "true", "force": "false", "state": consts.SW_UPDATE_STATE_INITIAL, } FAKE_SW_PRESTAGE_DATA = { "type": consts.SW_UPDATE_TYPE_PRESTAGE, "subcloud-apply-type": consts.SUBCLOUD_APPLY_TYPE_PARALLEL, "max-parallel-subclouds": "2", "stop-on-failure": "true", "force": "false", "state": consts.SW_UPDATE_STATE_INITIAL, } FAKE_SW_PATCH_DATA = { "type": consts.SW_UPDATE_TYPE_PATCH, "subcloud-apply-type": consts.SUBCLOUD_APPLY_TYPE_PARALLEL, "max-parallel-subclouds": "2", "stop-on-failure": "true", "force": "false", "state": consts.SW_UPDATE_STATE_INITIAL, } health_report_no_mgmt_alarm = "System Health:\n \ All hosts are provisioned: [Fail]\n \ 1 Unprovisioned hosts\n \ All hosts are unlocked/enabled: [OK]\n \ All hosts have current configurations: [OK]\n \ All hosts are patch current: [OK]\n \ No alarms: [OK]\n \ All kubernetes nodes are ready: [OK]\n \ All kubernetes control plane pods are ready: [OK]" class Subcloud(object): def __init__(self, id, name, group_id, is_managed, is_online): self.id = id self.name = name self.software_version = "12.04" self.group_id = group_id if is_managed: self.management_state = dccommon_consts.MANAGEMENT_MANAGED else: self.management_state = dccommon_consts.MANAGEMENT_UNMANAGED if is_online: self.availability_status = dccommon_consts.AVAILABILITY_ONLINE else: self.availability_status = dccommon_consts.AVAILABILITY_OFFLINE # All orch_threads can be mocked the same way class FakeOrchThread(object): def __init__(self): # Mock methods that are called in normal execution of this thread self.start = mock.MagicMock() class FakeDCManagerAuditAPI(object): def __init__(self): self.trigger_patch_audit = mock.MagicMock() class TestSwUpdateManager(base.DCManagerTestCase): @staticmethod def create_subcloud(ctxt, name, group_id, is_managed, is_online): values = { "name": name, "description": "subcloud1 description", "location": "subcloud1 location", "software_version": "18.03", "management_subnet": "192.168.101.0/24", "management_gateway_ip": "192.168.101.1", "management_start_ip": "192.168.101.3", "management_end_ip": "192.168.101.4", "systemcontroller_gateway_ip": "192.168.204.101", 'deploy_status': "not-deployed", 'error_description': 'No errors present', 'region_name': uuidutils.generate_uuid().replace("-", ""), 'openstack_installed': False, 'group_id': group_id, 'data_install': 'data from install', } subcloud = db_api.subcloud_create(ctxt, **values) if is_managed: state = dccommon_consts.MANAGEMENT_MANAGED subcloud = db_api.subcloud_update( ctxt, subcloud.id, management_state=state ) if is_online: status = dccommon_consts.AVAILABILITY_ONLINE subcloud = db_api.subcloud_update( ctxt, subcloud.id, availability_status=status ) return subcloud @staticmethod def create_subcloud_group(ctxt, name, update_apply_type, max_parallel_subclouds): values = { "name": name, "description": "subcloud1 description", "update_apply_type": update_apply_type, "max_parallel_subclouds": max_parallel_subclouds, } return db_api.subcloud_group_create(ctxt, **values) @staticmethod def update_subcloud_status(ctxt, subcloud_id, endpoint=None, status=None): if endpoint: endpoint_type = endpoint else: endpoint_type = dccommon_consts.ENDPOINT_TYPE_PATCHING if status: sync_status = status else: sync_status = dccommon_consts.SYNC_STATUS_OUT_OF_SYNC subcloud_status = db_api.subcloud_status_update( ctxt, subcloud_id, endpoint_type, sync_status ) return subcloud_status @staticmethod def create_strategy(ctxt, strategy_type, state): values = { "type": strategy_type, "subcloud_apply_type": consts.SUBCLOUD_APPLY_TYPE_PARALLEL, "max_parallel_subclouds": 2, "stop_on_failure": True, "state": state, } return db_api.sw_update_strategy_create(ctxt, **values) @staticmethod def create_strategy_step(ctxt, state): values = { "subcloud_id": 1, "stage": 1, "state": state, "details": "Dummy details", } return db_api.strategy_step_create(ctxt, **values) def setUp(self): super(TestSwUpdateManager, self).setUp() # Mock the context self.ctxt = utils.dummy_context() p = mock.patch.object(context, "get_admin_context") self.mock_get_admin_context = p.start() self.mock_get_admin_context.return_value = self.ctx self.addCleanup(p.stop) # Note: mock where an item is used, not where it comes from self.fake_sw_upgrade_orch_thread = FakeOrchThread() p = mock.patch.object(sw_update_manager, "SwUpgradeOrchThread") self.mock_sw_upgrade_orch_thread = p.start() self.mock_sw_upgrade_orch_thread.return_value = ( self.fake_sw_upgrade_orch_thread ) self.addCleanup(p.stop) self.fake_software_orch_thread = FakeOrchThread() p = mock.patch.object(sw_update_manager, "SoftwareOrchThread") self.fake_software_orch_thread = p.start() self.fake_software_orch_thread.return_value = ( self.fake_software_orch_thread ) self.addCleanup(p.stop) self.fake_fw_update_orch_thread = FakeOrchThread() p = mock.patch.object(sw_update_manager, "FwUpdateOrchThread") self.mock_fw_update_orch_thread = p.start() self.mock_fw_update_orch_thread.return_value = ( self.fake_fw_update_orch_thread ) self.addCleanup(p.stop) self.fake_kube_upgrade_orch_thread = FakeOrchThread() p = mock.patch.object(sw_update_manager, "KubeUpgradeOrchThread") self.mock_kube_upgrade_orch_thread = p.start() self.mock_kube_upgrade_orch_thread.return_value = ( self.fake_kube_upgrade_orch_thread ) self.addCleanup(p.stop) self.fake_kube_rootca_update_orch_thread = FakeOrchThread() p = mock.patch.object(sw_update_manager, "KubeRootcaUpdateOrchThread") self.mock_kube_rootca_update_orch_thread = p.start() self.mock_kube_rootca_update_orch_thread.return_value = ( self.fake_kube_rootca_update_orch_thread ) self.addCleanup(p.stop) self.fake_prestage_orch_thread = FakeOrchThread() p = mock.patch.object(sw_update_manager, "PrestageOrchThread") self.mock_prestage_orch_thread = p.start() self.mock_prestage_orch_thread.return_value = self.fake_prestage_orch_thread self.addCleanup(p.stop) # Mock the dcmanager audit API self.fake_dcmanager_audit_api = FakeDCManagerAuditAPI() p = mock.patch("dcmanager.audit.rpcapi.ManagerAuditClient") self.mock_dcmanager_audit_api = p.start() self.mock_dcmanager_audit_api.return_value = self.fake_dcmanager_audit_api self.addCleanup(p.stop) # Fake subcloud groups # Group 1 exists by default in database with max_parallel 2 and # apply_type parallel self.fake_group2 = self.create_subcloud_group( self.ctxt, "Group2", consts.SUBCLOUD_APPLY_TYPE_SERIAL, 2 ) self.fake_group3 = self.create_subcloud_group( self.ctxt, "Group3", consts.SUBCLOUD_APPLY_TYPE_PARALLEL, 2 ) self.fake_group4 = self.create_subcloud_group( self.ctxt, "Group4", consts.SUBCLOUD_APPLY_TYPE_SERIAL, 2 ) self.fake_group5 = self.create_subcloud_group( self.ctxt, "Group5", consts.SUBCLOUD_APPLY_TYPE_PARALLEL, 2 ) @mock.patch.object(sw_update_manager, "PatchOrchThread") def test_init(self, mock_patch_orch_thread): um = sw_update_manager.SwUpdateManager() self.assertIsNotNone(um) self.assertEqual("sw_update_manager", um.service_name) self.assertEqual("localhost", um.host) @mock.patch.object(sw_update_manager, "PatchOrchThread") def test_create_sw_update_strategy_no_subclouds(self, mock_patch_orch_thread): um = sw_update_manager.SwUpdateManager() # No strategy will be created, so it should raise: # 'Bad strategy request: Strategy has no steps to apply' self.assertRaises( exceptions.BadRequest, um.create_sw_update_strategy, self.ctxt, payload=FAKE_SW_UPDATE_DATA, ) @mock.patch.object(sw_update_manager, "PatchOrchThread") def test_create_sw_update_strategy_for_a_single_group( self, mock_patch_orch_thread ): # Create fake subclouds and respective status # Subcloud1 will be patched fake_subcloud1 = self.create_subcloud( self.ctxt, "subcloud1", self.fake_group2.id, is_managed=True, is_online=True, ) self.update_subcloud_status(self.ctxt, fake_subcloud1.id) # Subcloud2 will not be patched because not managed fake_subcloud2 = self.create_subcloud( self.ctxt, "subcloud2", self.fake_group2.id, is_managed=False, is_online=True, ) self.update_subcloud_status(self.ctxt, fake_subcloud2.id) data = copy.copy(FAKE_SW_UPDATE_DATA) data["subcloud_group"] = str(self.fake_group2.id) um = sw_update_manager.SwUpdateManager() response = um.create_sw_update_strategy(self.ctxt, payload=data) # Verify strategy was created as expected using group values self.assertEqual(response["max-parallel-subclouds"], 1) self.assertEqual( response["subcloud-apply-type"], consts.SUBCLOUD_APPLY_TYPE_SERIAL ) self.assertEqual(response["type"], FAKE_SW_UPDATE_DATA["type"]) # Verify strategy step was created as expected strategy_steps = db_api.strategy_step_get_all(self.ctx) self.assertEqual(strategy_steps[0]["state"], consts.STRATEGY_STATE_INITIAL) self.assertEqual(strategy_steps[0]["details"], "") self.assertEqual(strategy_steps[0]["subcloud_id"], 1) @mock.patch.object(sw_update_manager, "PatchOrchThread") def test_create_sw_update_strategy_parallel_for_a_single_group( self, mock_patch_orch_thread ): # Create fake subclouds and respective status fake_subcloud1 = self.create_subcloud( self.ctxt, "subcloud1", self.fake_group3.id, is_managed=True, is_online=True, ) self.update_subcloud_status( self.ctxt, fake_subcloud1.id, endpoint=dccommon_consts.ENDPOINT_TYPE_LOAD ) fake_subcloud2 = self.create_subcloud( self.ctxt, "subcloud2", self.fake_group3.id, is_managed=True, is_online=True, ) self.update_subcloud_status( self.ctxt, fake_subcloud2.id, endpoint=dccommon_consts.ENDPOINT_TYPE_LOAD ) data = copy.copy(FAKE_SW_UPDATE_DATA) data["type"] = consts.SW_UPDATE_TYPE_UPGRADE data["subcloud_group"] = str(self.fake_group3.id) um = sw_update_manager.SwUpdateManager() response = um.create_sw_update_strategy(self.ctxt, payload=data) # Verify strategy was created as expected using group values self.assertEqual(response["max-parallel-subclouds"], 2) self.assertEqual( response["subcloud-apply-type"], consts.SUBCLOUD_APPLY_TYPE_PARALLEL ) self.assertEqual(response["type"], consts.SW_UPDATE_TYPE_UPGRADE) # Verify the strategy step list subcloud_ids = [1, 2] strategy_step_list = db_api.strategy_step_get_all(self.ctxt) for index, strategy_step in enumerate(strategy_step_list): self.assertEqual(subcloud_ids[index], strategy_step.subcloud_id) @mock.patch.object(prestage, "initial_subcloud_validate") @mock.patch.object(prestage, "global_prestage_validate") @mock.patch.object(sw_update_manager, "PatchOrchThread") def test_create_sw_prestage_strategy_parallel_for_a_single_group( self, mock_patch_orch_thread, mock_global_prestage_validate, mock_initial_subcloud_validate, ): # Create fake subclouds and respective status fake_subcloud1 = self.create_subcloud( self.ctxt, "subcloud1", self.fake_group3.id, is_managed=True, is_online=True, ) self.update_subcloud_status( self.ctxt, fake_subcloud1.id, endpoint=dccommon_consts.ENDPOINT_TYPE_LOAD ) fake_subcloud2 = self.create_subcloud( self.ctxt, "subcloud2", self.fake_group3.id, is_managed=True, is_online=True, ) self.update_subcloud_status( self.ctxt, fake_subcloud2.id, endpoint=dccommon_consts.ENDPOINT_TYPE_LOAD ) mock_global_prestage_validate.return_value = None mock_initial_subcloud_validate.return_value = None data = copy.copy(FAKE_SW_PRESTAGE_DATA) fake_password = (base64.b64encode("testpass".encode("utf-8"))).decode( "ascii" ) data["sysadmin_password"] = fake_password data["subcloud_group"] = str(self.fake_group3.id) um = sw_update_manager.SwUpdateManager() response = um.create_sw_update_strategy(self.ctxt, payload=data) # Verify strategy was created as expected using group values self.assertEqual(response["max-parallel-subclouds"], 2) self.assertEqual( response["subcloud-apply-type"], consts.SUBCLOUD_APPLY_TYPE_PARALLEL ) self.assertEqual(response["type"], consts.SW_UPDATE_TYPE_PRESTAGE) # Verify the strategy step list subcloud_ids = [1, 2] strategy_step_list = db_api.strategy_step_get_all(self.ctxt) for index, strategy_step in enumerate(strategy_step_list): self.assertEqual(subcloud_ids[index], strategy_step.subcloud_id) @mock.patch.object(prestage, "initial_subcloud_validate") @mock.patch.object(prestage, "global_prestage_validate") @mock.patch.object(sw_update_manager, "PatchOrchThread") def test_create_sw_prestage_strategy_load_insync_out_of_sync_unknown_and_no_load( self, mock_patch_orch_thread, mock_global_prestage_validate, mock_initial_subcloud_validate, ): # Create fake subclouds and respective status # Subcloud1 will be prestaged load in sync fake_subcloud1 = self.create_subcloud( self.ctxt, "subcloud1", 1, is_managed=True, is_online=True ) self.update_subcloud_status( self.ctxt, fake_subcloud1.id, dccommon_consts.ENDPOINT_TYPE_LOAD, dccommon_consts.SYNC_STATUS_IN_SYNC, ) # Subcloud2 will be prestaged load is None fake_subcloud2 = self.create_subcloud( self.ctxt, "subcloud2", 1, is_managed=True, is_online=True ) self.update_subcloud_status( self.ctxt, fake_subcloud2.id, dccommon_consts.ENDPOINT_TYPE_LOAD, None ) # Subcloud3 will be prestaged load out of sync fake_subcloud3 = self.create_subcloud( self.ctxt, "subcloud3", 1, is_managed=True, is_online=True ) self.update_subcloud_status( self.ctxt, fake_subcloud3.id, dccommon_consts.ENDPOINT_TYPE_LOAD, dccommon_consts.SYNC_STATUS_OUT_OF_SYNC, ) # Subcloud4 will be prestaged sync unknown fake_subcloud4 = self.create_subcloud( self.ctxt, "subcloud4", 1, is_managed=True, is_online=True ) self.update_subcloud_status( self.ctxt, fake_subcloud4.id, dccommon_consts.ENDPOINT_TYPE_LOAD, dccommon_consts.SYNC_STATUS_UNKNOWN, ) mock_global_prestage_validate.return_value = None mock_initial_subcloud_validate.return_value = None data = copy.copy(FAKE_SW_PRESTAGE_DATA) fake_password = (base64.b64encode("testpass".encode("utf-8"))).decode( "ascii" ) data["sysadmin_password"] = fake_password um = sw_update_manager.SwUpdateManager() response = um.create_sw_update_strategy(self.ctxt, payload=data) # Verify strategy was created as expected using group values self.assertEqual(response["max-parallel-subclouds"], 2) self.assertEqual( response["subcloud-apply-type"], consts.SUBCLOUD_APPLY_TYPE_PARALLEL ) self.assertEqual(response["type"], consts.SW_UPDATE_TYPE_PRESTAGE) # Verify the strategy step list subcloud_ids = [1, 2, 3, 4] strategy_step_list = db_api.strategy_step_get_all(self.ctxt) for index, strategy_step in enumerate(strategy_step_list): self.assertEqual(subcloud_ids[index], strategy_step.subcloud_id) @mock.patch.object(prestage, "initial_subcloud_validate") @mock.patch.object(prestage, "_get_system_controller_upgrades") @mock.patch.object(sw_update_manager, "PatchOrchThread") def test_create_sw_prestage_strategy_no_password( self, mock_patch_orch_thread, mock_controller_upgrade, mock_initial_subcloud_validate, ): # Create fake subclouds and respective status fake_subcloud1 = self.create_subcloud( self.ctxt, "subcloud1", self.fake_group3.id, is_managed=True, is_online=True, ) self.update_subcloud_status( self.ctxt, fake_subcloud1.id, endpoint=dccommon_consts.ENDPOINT_TYPE_LOAD ) fake_subcloud2 = self.create_subcloud( self.ctxt, "subcloud2", self.fake_group3.id, is_managed=True, is_online=True, ) self.update_subcloud_status( self.ctxt, fake_subcloud2.id, endpoint=dccommon_consts.ENDPOINT_TYPE_LOAD ) mock_initial_subcloud_validate.return_value = None mock_controller_upgrade.return_value = list() data = copy.copy(FAKE_SW_PRESTAGE_DATA) data["sysadmin_password"] = "" data["subcloud_group"] = str(self.fake_group3.id) um = sw_update_manager.SwUpdateManager() self.assertRaises( exceptions.BadRequest, um.create_sw_update_strategy, self.ctxt, payload=data, ) @mock.patch.object(prestage, "_get_system_controller_upgrades") @mock.patch.object(sw_update_manager, "PatchOrchThread") def test_create_sw_prestage_strategy_backup_in_progress( self, mock_patch_orch_thread, mock_controller_upgrade ): mock_controller_upgrade.return_value = list() # Create fake subcloud and respective status (managed & online) fake_subcloud1 = self.create_subcloud( self.ctxt, "subcloud1", self.fake_group3.id, is_managed=True, is_online=True, ) self.update_subcloud_status(self.ctxt, fake_subcloud1.id) db_api.subcloud_update( self.ctx, fake_subcloud1.id, backup_status=consts.BACKUP_STATE_IN_PROGRESS, ) data = copy.copy(FAKE_SW_PRESTAGE_DATA) fake_password = (base64.b64encode("testpass".encode("utf-8"))).decode( "ascii" ) data["sysadmin_password"] = fake_password data["cloud_name"] = "subcloud1" um = sw_update_manager.SwUpdateManager() self.assertRaises( exceptions.BadRequest, um.create_sw_update_strategy, self.ctxt, payload=data, ) @mock.patch.object(sw_update_manager, "PatchOrchThread") def test_create_sw_update_strategy_cloud_name_not_exists( self, mock_patch_orch_thread ): # Create fake subclouds and respective status fake_subcloud1 = self.create_subcloud( self.ctxt, "subcloud1", self.fake_group3.id, is_managed=True, is_online=True, ) self.update_subcloud_status(self.ctxt, fake_subcloud1.id) data = copy.copy(FAKE_SW_UPDATE_DATA) # Create a strategy with a cloud_name that doesn't exist data["cloud_name"] = "subcloud2" um = sw_update_manager.SwUpdateManager() self.assertRaises( exceptions.BadRequest, um.create_sw_update_strategy, self.ctxt, payload=data, ) @mock.patch.object(sw_update_manager, "PatchOrchThread") def test_create_sw_update_strategy_parallel(self, mock_patch_orch_thread): # Create fake subclouds and respective status # Subcloud1 will be patched fake_subcloud1 = self.create_subcloud( self.ctxt, "subcloud1", 1, is_managed=True, is_online=True ) self.update_subcloud_status(self.ctxt, fake_subcloud1.id) # Subcloud2 will not be patched because not managed fake_subcloud2 = self.create_subcloud( self.ctxt, "subcloud2", 1, is_managed=False, is_online=True ) self.update_subcloud_status(self.ctxt, fake_subcloud2.id) # Subcloud3 will be patched fake_subcloud3 = self.create_subcloud( self.ctxt, "subcloud3", 1, is_managed=True, is_online=True ) self.update_subcloud_status(self.ctxt, fake_subcloud3.id) # Subcloud4 will not be patched because patching is in sync fake_subcloud4 = self.create_subcloud( self.ctxt, "subcloud4", 2, is_managed=True, is_online=True ) self.update_subcloud_status( self.ctxt, fake_subcloud4.id, None, dccommon_consts.SYNC_STATUS_IN_SYNC ) # Subcloud5 will be patched fake_subcloud5 = self.create_subcloud( self.ctxt, "subcloud5", 2, is_managed=True, is_online=True ) self.update_subcloud_status(self.ctxt, fake_subcloud5.id) # Subcloud6 will be patched fake_subcloud6 = self.create_subcloud( self.ctxt, "subcloud6", 3, is_managed=True, is_online=True ) self.update_subcloud_status(self.ctxt, fake_subcloud6.id) # Subcloud7 will be patched fake_subcloud7 = self.create_subcloud( self.ctxt, "subcloud7", 3, is_managed=True, is_online=True ) self.update_subcloud_status(self.ctxt, fake_subcloud7.id) um = sw_update_manager.SwUpdateManager() strategy_dict = um.create_sw_update_strategy( self.ctxt, payload=FAKE_SW_UPDATE_DATA ) # Assert that values passed through CLI are used instead of group values self.assertEqual(strategy_dict["max-parallel-subclouds"], 2) self.assertEqual( strategy_dict["subcloud-apply-type"], consts.SUBCLOUD_APPLY_TYPE_PARALLEL ) # Verify the strategy step list subcloud_ids = [1, 3, 5, 6, 7] strategy_step_list = db_api.strategy_step_get_all(self.ctxt) for index, strategy_step in enumerate(strategy_step_list): self.assertEqual(subcloud_ids[index], strategy_step.subcloud_id) @mock.patch.object(sw_update_manager, "PatchOrchThread") def test_create_sw_patching_subcloud_in_sync_out_of_sync( self, mock_patch_orch_thread ): # Subcloud 1 will be patched fake_subcloud1 = self.create_subcloud( self.ctxt, "subcloud1", self.fake_group3.id, is_managed=True, is_online=True, ) self.update_subcloud_status( self.ctxt, fake_subcloud1.id, dccommon_consts.ENDPOINT_TYPE_PATCHING, dccommon_consts.SYNC_STATUS_OUT_OF_SYNC, ) # Subcloud 2 will not be patched because it is offline fake_subcloud2 = self.create_subcloud( self.ctxt, "subcloud2", self.fake_group3.id, is_managed=True, is_online=False, ) self.update_subcloud_status( self.ctxt, fake_subcloud2.id, dccommon_consts.ENDPOINT_TYPE_PATCHING, dccommon_consts.SYNC_STATUS_OUT_OF_SYNC, ) # Subcloud 3 will be patched fake_subcloud3 = self.create_subcloud( self.ctxt, "subcloud3", self.fake_group3.id, is_managed=True, is_online=True, ) self.update_subcloud_status( self.ctxt, fake_subcloud3.id, dccommon_consts.ENDPOINT_TYPE_PATCHING, dccommon_consts.SYNC_STATUS_OUT_OF_SYNC, ) # Subcloud 4 will not be patched because it is in sync fake_subcloud4 = self.create_subcloud( self.ctxt, "subcloud4", self.fake_group3.id, is_managed=True, is_online=True, ) self.update_subcloud_status( self.ctxt, fake_subcloud4.id, dccommon_consts.ENDPOINT_TYPE_PATCHING, dccommon_consts.SYNC_STATUS_IN_SYNC, ) data = copy.copy(FAKE_SW_PATCH_DATA) data["type"] = consts.SW_UPDATE_TYPE_PATCH data["subcloud_group"] = str(self.fake_group3.id) um = sw_update_manager.SwUpdateManager() response = um.create_sw_update_strategy(self.ctxt, payload=data) # Verify strategy was created as expected using group values self.assertEqual(response["max-parallel-subclouds"], 2) self.assertEqual( response["subcloud-apply-type"], consts.SUBCLOUD_APPLY_TYPE_PARALLEL ) self.assertEqual(response["type"], consts.SW_UPDATE_TYPE_PATCH) # Verify the strategy step list subcloud_ids = [1, 3] strategy_step_list = db_api.strategy_step_get_all(self.ctxt) subcloud_id_processed = [] for strategy_step in strategy_step_list: subcloud_id_processed.append(strategy_step.subcloud_id) self.assertEqual(subcloud_ids, subcloud_id_processed) @mock.patch.object(cutils, "get_systemcontroller_installed_loads") @mock.patch.object(prestage, "initial_subcloud_validate") @mock.patch.object(prestage, "_get_system_controller_upgrades") @mock.patch.object(sw_update_manager, "PatchOrchThread") def test_create_sw_prestage_strategy_parallel( self, mock_patch_orch_thread, mock_controller_upgrade, mock_initial_subcloud_validate, mock_installed_loads, ): # Create fake subclouds and respective status # Subcloud1 will be prestaged self.create_subcloud( self.ctxt, "subcloud1", 1, is_managed=True, is_online=True ) # Subcloud2 will not be prestaged because not managed self.create_subcloud( self.ctxt, "subcloud2", 1, is_managed=False, is_online=True ) # Subcloud3 will be prestaged self.create_subcloud( self.ctxt, "subcloud3", 1, is_managed=True, is_online=True ) # Subcloud4 will not be prestaged because offline self.create_subcloud( self.ctxt, "subcloud4", 2, is_managed=True, is_online=False ) # Subcloud5 will be prestaged self.create_subcloud( self.ctxt, "subcloud5", 2, is_managed=True, is_online=True ) # Subcloud6 will be prestaged self.create_subcloud( self.ctxt, "subcloud6", 3, is_managed=True, is_online=True ) # Subcloud7 will be prestaged self.create_subcloud( self.ctxt, "subcloud7", 3, is_managed=True, is_online=True ) data = copy.copy(FAKE_SW_PRESTAGE_DATA) fake_password = (base64.b64encode("testpass".encode("utf-8"))).decode( "ascii" ) data["sysadmin_password"] = fake_password fake_release = "21.12" data[consts.PRESTAGE_REQUEST_RELEASE] = fake_release mock_installed_loads.return_value = [fake_release] um = sw_update_manager.SwUpdateManager() strategy_dict = um.create_sw_update_strategy(self.ctxt, payload=data) mock_initial_subcloud_validate.return_value = None mock_controller_upgrade.return_value = list() # Assert that values passed through CLI are used instead of group values self.assertEqual(strategy_dict["max-parallel-subclouds"], 2) self.assertEqual( strategy_dict["subcloud-apply-type"], consts.SUBCLOUD_APPLY_TYPE_PARALLEL ) self.assertEqual( fake_release, strategy_dict["extra-args"].get(consts.PRESTAGE_SOFTWARE_VERSION), ) # Verify the strategy step list subcloud_ids = [1, 3, 5, 6, 7] strategy_step_list = db_api.strategy_step_get_all(self.ctxt) subcloud_id_processed = [] for index, strategy_step in enumerate(strategy_step_list): subcloud_id_processed.append(strategy_step.subcloud_id) self.assertEqual(subcloud_ids, subcloud_id_processed) @mock.patch.object(sw_update_manager, "PatchOrchThread") def test_create_sw_update_strategy_serial(self, mock_patch_orch_thread): # Create fake subclouds and respective status # Subcloud1 will be patched fake_subcloud1 = self.create_subcloud( self.ctxt, "subcloud1", 1, is_managed=True, is_online=True ) self.update_subcloud_status(self.ctxt, fake_subcloud1.id) # Subcloud2 will not be patched because not managed fake_subcloud2 = self.create_subcloud( self.ctxt, "subcloud2", 1, is_managed=False, is_online=True ) self.update_subcloud_status(self.ctxt, fake_subcloud2.id) # Subcloud3 will be patched fake_subcloud3 = self.create_subcloud( self.ctxt, "subcloud3", 1, is_managed=True, is_online=True ) self.update_subcloud_status(self.ctxt, fake_subcloud3.id) # Subcloud4 will not be patched because patching is in sync fake_subcloud4 = self.create_subcloud( self.ctxt, "subcloud4", 2, is_managed=True, is_online=True ) self.update_subcloud_status( self.ctxt, fake_subcloud4.id, None, dccommon_consts.SYNC_STATUS_IN_SYNC ) # Subcloud5 will be patched fake_subcloud5 = self.create_subcloud( self.ctxt, "subcloud5", 2, is_managed=True, is_online=True ) self.update_subcloud_status(self.ctxt, fake_subcloud5.id) # Subcloud6 will be patched fake_subcloud6 = self.create_subcloud( self.ctxt, "subcloud6", 3, is_managed=True, is_online=True ) self.update_subcloud_status(self.ctxt, fake_subcloud6.id) # Subcloud7 will be patched fake_subcloud7 = self.create_subcloud( self.ctxt, "subcloud7", 3, is_managed=True, is_online=True ) self.update_subcloud_status(self.ctxt, fake_subcloud7.id) um = sw_update_manager.SwUpdateManager() data = copy.copy(FAKE_SW_UPDATE_DATA) data["subcloud-apply-type"] = consts.SUBCLOUD_APPLY_TYPE_SERIAL strategy_dict = um.create_sw_update_strategy(self.ctxt, payload=data) # Assert that values passed through CLI are used instead of group values self.assertEqual(strategy_dict["max-parallel-subclouds"], 1) self.assertEqual( strategy_dict["subcloud-apply-type"], consts.SUBCLOUD_APPLY_TYPE_SERIAL ) # Verify the strategy step list subcloud_ids = [1, 3, 5, 6, 7] strategy_step_list = db_api.strategy_step_get_all(self.ctxt) for index, strategy_step in enumerate(strategy_step_list): self.assertEqual(subcloud_ids[index], strategy_step.subcloud_id) @mock.patch.object(sw_update_manager, "PatchOrchThread") def test_create_sw_update_strategy_using_group_apply_type( self, mock_patch_orch_thread ): # Create fake subclouds and respective status # Subcloud1 will be patched fake_subcloud1 = self.create_subcloud( self.ctxt, "subcloud1", 1, is_managed=True, is_online=True ) self.update_subcloud_status(self.ctxt, fake_subcloud1.id) # Subcloud2 will not be patched because not managed fake_subcloud2 = self.create_subcloud( self.ctxt, "subcloud2", 1, is_managed=False, is_online=True ) self.update_subcloud_status(self.ctxt, fake_subcloud2.id) # Subcloud3 will be patched fake_subcloud3 = self.create_subcloud( self.ctxt, "subcloud3", 1, is_managed=True, is_online=True ) self.update_subcloud_status(self.ctxt, fake_subcloud3.id) # Subcloud4 will not be patched because patching is in sync fake_subcloud4 = self.create_subcloud( self.ctxt, "subcloud4", 2, is_managed=True, is_online=True ) self.update_subcloud_status( self.ctxt, fake_subcloud4.id, None, dccommon_consts.SYNC_STATUS_IN_SYNC ) # Subcloud5 will be patched fake_subcloud5 = self.create_subcloud( self.ctxt, "subcloud5", 2, is_managed=True, is_online=True ) self.update_subcloud_status(self.ctxt, fake_subcloud5.id) # Subcloud6 will be patched fake_subcloud6 = self.create_subcloud( self.ctxt, "subcloud6", 3, is_managed=True, is_online=True ) self.update_subcloud_status(self.ctxt, fake_subcloud6.id) # Subcloud7 will be patched fake_subcloud7 = self.create_subcloud( self.ctxt, "subcloud7", 3, is_managed=True, is_online=True ) self.update_subcloud_status(self.ctxt, fake_subcloud7.id) # Subcloud8 will be patched fake_subcloud8 = self.create_subcloud( self.ctxt, "subcloud8", 4, is_managed=True, is_online=True ) self.update_subcloud_status(self.ctxt, fake_subcloud8.id) # Subcloud9 will be patched fake_subcloud9 = self.create_subcloud( self.ctxt, "subcloud9", 4, is_managed=True, is_online=True ) self.update_subcloud_status(self.ctxt, fake_subcloud9.id) # Subcloud10 will be patched fake_subcloud10 = self.create_subcloud( self.ctxt, "subcloud10", 4, is_managed=True, is_online=True ) self.update_subcloud_status(self.ctxt, fake_subcloud10.id) # Subcloud11 will be patched fake_subcloud11 = self.create_subcloud( self.ctxt, "subcloud11", 5, is_managed=True, is_online=True ) self.update_subcloud_status(self.ctxt, fake_subcloud11.id) # Subcloud12 will be patched fake_subcloud12 = self.create_subcloud( self.ctxt, "subcloud12", 5, is_managed=True, is_online=True ) self.update_subcloud_status(self.ctxt, fake_subcloud12.id) # Subcloud13 will be patched fake_subcloud13 = self.create_subcloud( self.ctxt, "subcloud13", 5, is_managed=True, is_online=True ) self.update_subcloud_status(self.ctxt, fake_subcloud13.id) data = copy.copy(FAKE_SW_UPDATE_DATA) del data["subcloud-apply-type"] um = sw_update_manager.SwUpdateManager() strategy_dict = um.create_sw_update_strategy(self.ctxt, payload=data) # Assert that group values are being used for subcloud_apply_type self.assertEqual(strategy_dict["subcloud-apply-type"], None) # Assert that values passed through CLI are used instead of # group values for max_parallel_subclouds self.assertEqual(strategy_dict["max-parallel-subclouds"], 2) # Verify the strategy step list subcloud_ids = [1, 3, 5, 6, 7, 8, 9, 10, 11, 12, 13] strategy_step_list = db_api.strategy_step_get_all(self.ctxt) for index, strategy_step in enumerate(strategy_step_list): self.assertEqual(subcloud_ids[index], strategy_step.subcloud_id) @mock.patch.object(sw_update_manager, "PatchOrchThread") def test_create_sw_update_strategy_using_group_max_parallel( self, mock_patch_orch_thread ): # Create fake subclouds and respective status # Subcloud1 will be patched fake_subcloud1 = self.create_subcloud( self.ctxt, "subcloud1", 1, is_managed=True, is_online=True ) self.update_subcloud_status(self.ctxt, fake_subcloud1.id) # Subcloud2 will not be patched because not managed fake_subcloud2 = self.create_subcloud( self.ctxt, "subcloud2", 1, is_managed=False, is_online=True ) self.update_subcloud_status(self.ctxt, fake_subcloud2.id) # Subcloud3 will be patched fake_subcloud3 = self.create_subcloud( self.ctxt, "subcloud3", 1, is_managed=True, is_online=True ) self.update_subcloud_status(self.ctxt, fake_subcloud3.id) # Subcloud4 will not be patched because patching is in sync fake_subcloud4 = self.create_subcloud( self.ctxt, "subcloud4", 2, is_managed=True, is_online=True ) self.update_subcloud_status( self.ctxt, fake_subcloud4.id, None, dccommon_consts.SYNC_STATUS_IN_SYNC ) # Subcloud5 will be patched fake_subcloud5 = self.create_subcloud( self.ctxt, "subcloud5", 2, is_managed=True, is_online=True ) self.update_subcloud_status(self.ctxt, fake_subcloud5.id) # Subcloud6 will be patched fake_subcloud6 = self.create_subcloud( self.ctxt, "subcloud6", 3, is_managed=True, is_online=True ) self.update_subcloud_status(self.ctxt, fake_subcloud6.id) # Subcloud7 will be patched fake_subcloud7 = self.create_subcloud( self.ctxt, "subcloud7", 3, is_managed=True, is_online=True ) self.update_subcloud_status(self.ctxt, fake_subcloud7.id) # Subcloud8 will be patched fake_subcloud8 = self.create_subcloud( self.ctxt, "subcloud8", 4, is_managed=True, is_online=True ) self.update_subcloud_status(self.ctxt, fake_subcloud8.id) # Subcloud9 will be patched fake_subcloud9 = self.create_subcloud( self.ctxt, "subcloud9", 4, is_managed=True, is_online=True ) self.update_subcloud_status(self.ctxt, fake_subcloud9.id) # Subcloud10 will be patched fake_subcloud10 = self.create_subcloud( self.ctxt, "subcloud10", 4, is_managed=True, is_online=True ) self.update_subcloud_status(self.ctxt, fake_subcloud10.id) # Subcloud11 will be patched fake_subcloud11 = self.create_subcloud( self.ctxt, "subcloud11", 5, is_managed=True, is_online=True ) self.update_subcloud_status(self.ctxt, fake_subcloud11.id) # Subcloud12 will be patched fake_subcloud12 = self.create_subcloud( self.ctxt, "subcloud12", 5, is_managed=True, is_online=True ) self.update_subcloud_status(self.ctxt, fake_subcloud12.id) # Subcloud13 will be patched fake_subcloud13 = self.create_subcloud( self.ctxt, "subcloud13", 5, is_managed=True, is_online=True ) self.update_subcloud_status(self.ctxt, fake_subcloud13.id) data = copy.copy(FAKE_SW_UPDATE_DATA) del data["max-parallel-subclouds"] um = sw_update_manager.SwUpdateManager() strategy_dict = um.create_sw_update_strategy(self.ctxt, payload=data) # Assert that values passed through CLI are used instead of # group values for max_parallel_subclouds self.assertEqual( strategy_dict["subcloud-apply-type"], consts.SUBCLOUD_APPLY_TYPE_PARALLEL ) # Assert that group values are being used for subcloud_apply_type self.assertEqual( strategy_dict["max-parallel-subclouds"], consts.DEFAULT_SUBCLOUD_GROUP_MAX_PARALLEL_SUBCLOUDS, ) # Verify the strategy step list subcloud_ids = [1, 3, 5, 6, 7, 8, 9, 10, 11, 12, 13] strategy_step_list = db_api.strategy_step_get_all(self.ctxt) for index, strategy_step in enumerate(strategy_step_list): self.assertEqual(subcloud_ids[index], strategy_step.subcloud_id) @mock.patch.object(sw_update_manager, "PatchOrchThread") def test_create_sw_update_strategy_using_all_group_values( self, mock_patch_orch_thread ): # Create fake subclouds and respective status # Subcloud1 will be patched fake_subcloud1 = self.create_subcloud( self.ctxt, "subcloud1", 1, is_managed=True, is_online=True ) self.update_subcloud_status(self.ctxt, fake_subcloud1.id) # Subcloud2 will not be patched because not managed fake_subcloud2 = self.create_subcloud( self.ctxt, "subcloud2", 1, is_managed=False, is_online=True ) self.update_subcloud_status(self.ctxt, fake_subcloud2.id) # Subcloud3 will be patched fake_subcloud3 = self.create_subcloud( self.ctxt, "subcloud3", 1, is_managed=True, is_online=True ) self.update_subcloud_status(self.ctxt, fake_subcloud3.id) # Subcloud4 will not be patched because patching is in sync fake_subcloud4 = self.create_subcloud( self.ctxt, "subcloud4", 2, is_managed=True, is_online=True ) self.update_subcloud_status( self.ctxt, fake_subcloud4.id, None, dccommon_consts.SYNC_STATUS_IN_SYNC ) # Subcloud5 will be patched fake_subcloud5 = self.create_subcloud( self.ctxt, "subcloud5", 2, is_managed=True, is_online=True ) self.update_subcloud_status(self.ctxt, fake_subcloud5.id) # Subcloud6 will be patched fake_subcloud6 = self.create_subcloud( self.ctxt, "subcloud6", 3, is_managed=True, is_online=True ) self.update_subcloud_status(self.ctxt, fake_subcloud6.id) # Subcloud7 will be patched fake_subcloud7 = self.create_subcloud( self.ctxt, "subcloud7", 3, is_managed=True, is_online=True ) self.update_subcloud_status(self.ctxt, fake_subcloud7.id) # Subcloud8 will be patched fake_subcloud8 = self.create_subcloud( self.ctxt, "subcloud8", 4, is_managed=True, is_online=True ) self.update_subcloud_status(self.ctxt, fake_subcloud8.id) # Subcloud9 will be patched fake_subcloud9 = self.create_subcloud( self.ctxt, "subcloud9", 4, is_managed=True, is_online=True ) self.update_subcloud_status(self.ctxt, fake_subcloud9.id) # Subcloud10 will be patched fake_subcloud10 = self.create_subcloud( self.ctxt, "subcloud10", 4, is_managed=True, is_online=True ) self.update_subcloud_status(self.ctxt, fake_subcloud10.id) # Subcloud11 will be patched fake_subcloud11 = self.create_subcloud( self.ctxt, "subcloud11", 5, is_managed=True, is_online=True ) self.update_subcloud_status(self.ctxt, fake_subcloud11.id) # Subcloud12 will be patched fake_subcloud12 = self.create_subcloud( self.ctxt, "subcloud12", 5, is_managed=True, is_online=True ) self.update_subcloud_status(self.ctxt, fake_subcloud12.id) # Subcloud13 will be patched fake_subcloud13 = self.create_subcloud( self.ctxt, "subcloud13", 5, is_managed=True, is_online=True ) self.update_subcloud_status(self.ctxt, fake_subcloud13.id) data = copy.copy(FAKE_SW_UPDATE_DATA) del data["subcloud-apply-type"] del data["max-parallel-subclouds"] um = sw_update_manager.SwUpdateManager() strategy_dict = um.create_sw_update_strategy(self.ctxt, payload=data) # Assert that group values are being used self.assertEqual( strategy_dict["max-parallel-subclouds"], consts.DEFAULT_SUBCLOUD_GROUP_MAX_PARALLEL_SUBCLOUDS, ) self.assertEqual(strategy_dict["subcloud-apply-type"], None) # Verify the strategy step list subcloud_ids = [1, 3, 5, 6, 7, 8, 9, 10, 11, 12, 13] strategy_step_list = db_api.strategy_step_get_all(self.ctxt) for index, strategy_step in enumerate(strategy_step_list): self.assertEqual(subcloud_ids[index], strategy_step.subcloud_id) @mock.patch.object(sw_update_manager, "PatchOrchThread") def test_create_sw_update_strategy_unknown_sync_status( self, mock_patch_orch_thread ): # Create fake subclouds and respective status # Subcloud1 will be patched fake_subcloud1 = self.create_subcloud( self.ctxt, "subcloud1", 1, is_managed=True, is_online=True ) self.update_subcloud_status(self.ctxt, fake_subcloud1.id) # Subcloud2 will not be patched because not managed fake_subcloud2 = self.create_subcloud( self.ctxt, "subcloud2", 1, is_managed=False, is_online=True ) self.update_subcloud_status(self.ctxt, fake_subcloud2.id) # Subcloud3 will be patched fake_subcloud3 = self.create_subcloud( self.ctxt, "subcloud3", 1, is_managed=True, is_online=True ) self.update_subcloud_status(self.ctxt, fake_subcloud3.id) # Subcloud4 will not be patched because patching is not in sync fake_subcloud4 = self.create_subcloud( self.ctxt, "subcloud4", 2, is_managed=True, is_online=True ) self.update_subcloud_status( self.ctxt, fake_subcloud4.id, None, dccommon_consts.SYNC_STATUS_UNKNOWN ) um = sw_update_manager.SwUpdateManager() self.assertRaises( exceptions.BadRequest, um.create_sw_update_strategy, self.ctxt, payload=FAKE_SW_UPDATE_DATA, ) @mock.patch.object(prestage, "_get_prestage_subcloud_info") @mock.patch.object(prestage, "_get_system_controller_upgrades") @mock.patch.object(sw_update_manager, "PatchOrchThread") def test_create_sw_prestage_strategy_duplex( self, mock_patch_orch_thread, mock_controller_upgrade, mock_prestage_subcloud_info, ): # Create fake subclouds and respective status # A note on subcloud system mode = duplex checking: For this test case # it will be *included* in the strategy_step_list. It is not until the # strategy is applied that the subcloud is skipped. During # orchestration, we will see a raised PrestagePreCheckFailedException # from prestage.validate_prestage() during the PrestagePreCheckState # from the orchestration. That state is the first state executed by # the orchestrator. # # Therefore, subcloud1 will be included in the strategy but not be # prestaged because during the apply we find out it is a duplex self.create_subcloud( self.ctxt, "subcloud1", 1, is_managed=True, is_online=True ) data = copy.copy(FAKE_SW_PRESTAGE_DATA) fake_password = (base64.b64encode("testpass".encode("utf-8"))).decode( "ascii" ) data["sysadmin_password"] = fake_password mock_controller_upgrade.return_value = list() mock_prestage_subcloud_info.return_value = ( consts.SYSTEM_MODE_DUPLEX, health_report_no_mgmt_alarm, OAM_FLOATING_IP, ) um = sw_update_manager.SwUpdateManager() um.create_sw_update_strategy(self.ctxt, payload=data) strategy_step_list = db_api.strategy_step_get_all(self.ctxt) self.assertEqual(1, len(strategy_step_list)) @mock.patch.object(sw_update_manager, "PatchOrchThread") def test_create_sw_update_strategy_offline_subcloud_no_force( self, mock_patch_orch_thread ): # Create fake subclouds and respective status # Subcloud1 will not be included in the strategy as it's offline fake_subcloud1 = self.create_subcloud( self.ctxt, "subcloud1", 1, is_managed=True, is_online=False ) self.update_subcloud_status(self.ctxt, fake_subcloud1.id) # Subcloud2 will be included in the strategy as it's online fake_subcloud2 = self.create_subcloud( self.ctxt, "subcloud2", 1, is_managed=True, is_online=True ) self.update_subcloud_status(self.ctxt, fake_subcloud2.id) # Subcloud3 will be included in the strategy as it's online fake_subcloud3 = self.create_subcloud( self.ctxt, "subcloud3", 1, is_managed=True, is_online=True ) self.update_subcloud_status(self.ctxt, fake_subcloud3.id) # Subcloud4 will be included in the strategy as it's online fake_subcloud4 = self.create_subcloud( self.ctxt, "subcloud4", 1, is_managed=True, is_online=True ) self.update_subcloud_status(self.ctxt, fake_subcloud4.id) um = sw_update_manager.SwUpdateManager() data = copy.copy(FAKE_SW_UPDATE_DATA) data["max-parallel-subclouds"] = 10 strategy_dict = um.create_sw_update_strategy(self.ctxt, payload=data) # Assert that values passed through CLI are used instead of group values self.assertEqual(strategy_dict["max-parallel-subclouds"], 10) self.assertEqual( strategy_dict["subcloud-apply-type"], consts.SUBCLOUD_APPLY_TYPE_PARALLEL ) self.assertEqual(strategy_dict["type"], consts.SW_UPDATE_TYPE_PATCH) # Verify the strategy step list subcloud_ids = [2, 3, 4] strategy_step_list = db_api.strategy_step_get_all(self.ctxt) for index, strategy_step in enumerate(strategy_step_list): self.assertEqual(subcloud_ids[index], strategy_step.subcloud_id) @mock.patch.object(sw_update_manager, "PatchOrchThread") def test_create_sw_update_strategy_with_force_option( self, mock_patch_orch_thread ): # Subcloud 1 will be upgraded because force is true fake_subcloud1 = self.create_subcloud( self.ctxt, "subcloud1", self.fake_group3.id, is_managed=True, is_online=False, ) self.update_subcloud_status( self.ctxt, fake_subcloud1.id, dccommon_consts.ENDPOINT_TYPE_LOAD, dccommon_consts.SYNC_STATUS_OUT_OF_SYNC, ) # Subcloud 2 will be upgraded fake_subcloud2 = self.create_subcloud( self.ctxt, "subcloud2", self.fake_group3.id, is_managed=True, is_online=True, ) self.update_subcloud_status( self.ctxt, fake_subcloud2.id, dccommon_consts.ENDPOINT_TYPE_LOAD, dccommon_consts.SYNC_STATUS_OUT_OF_SYNC, ) # Subcloud 3 will not be upgraded because it is already load in-sync fake_subcloud3 = self.create_subcloud( self.ctxt, "subcloud3", self.fake_group3.id, is_managed=True, is_online=True, ) self.update_subcloud_status( self.ctxt, fake_subcloud3.id, dccommon_consts.ENDPOINT_TYPE_LOAD, dccommon_consts.SYNC_STATUS_IN_SYNC, ) data = copy.copy(FAKE_SW_UPDATE_DATA) data["type"] = consts.SW_UPDATE_TYPE_UPGRADE data["force"] = "true" data["subcloud_group"] = str(self.fake_group3.id) um = sw_update_manager.SwUpdateManager() strategy_dict = um.create_sw_update_strategy(self.ctxt, payload=data) # Assert that values passed through CLI are used instead of group values self.assertEqual( strategy_dict["subcloud-apply-type"], consts.SUBCLOUD_APPLY_TYPE_PARALLEL ) self.assertEqual(strategy_dict["type"], consts.SW_UPDATE_TYPE_UPGRADE) subcloud_ids = [1, 2] strategy_step_list = db_api.strategy_step_get_all(self.ctxt) for index, strategy_step in enumerate(strategy_step_list): self.assertEqual(subcloud_ids[index], strategy_step.subcloud_id) @mock.patch.object(sw_update_manager, "PatchOrchThread") def test_create_sw_update_strategy_without_force_option( self, mock_patch_orch_thread ): # Subcloud 1 will not be upgraded fake_subcloud1 = self.create_subcloud( self.ctxt, "subcloud1", self.fake_group3.id, is_managed=True, is_online=False, ) self.update_subcloud_status( self.ctxt, fake_subcloud1.id, dccommon_consts.ENDPOINT_TYPE_LOAD, dccommon_consts.SYNC_STATUS_OUT_OF_SYNC, ) # Subcloud 2 will be upgraded fake_subcloud2 = self.create_subcloud( self.ctxt, "subcloud2", self.fake_group3.id, is_managed=True, is_online=True, ) self.update_subcloud_status( self.ctxt, fake_subcloud2.id, dccommon_consts.ENDPOINT_TYPE_LOAD, dccommon_consts.SYNC_STATUS_OUT_OF_SYNC, ) # Subcloud 3 will not be upgraded because it is already load in-sync fake_subcloud3 = self.create_subcloud( self.ctxt, "subcloud3", self.fake_group3.id, is_managed=True, is_online=True, ) self.update_subcloud_status( self.ctxt, fake_subcloud3.id, dccommon_consts.ENDPOINT_TYPE_LOAD, dccommon_consts.SYNC_STATUS_IN_SYNC, ) data = copy.copy(FAKE_SW_UPDATE_DATA) data["type"] = consts.SW_UPDATE_TYPE_UPGRADE data["force"] = "false" data["subcloud_group"] = str(self.fake_group3.id) um = sw_update_manager.SwUpdateManager() strategy_dict = um.create_sw_update_strategy(self.ctxt, payload=data) # Assert that values passed through CLI are used instead of group values self.assertEqual( strategy_dict["subcloud-apply-type"], consts.SUBCLOUD_APPLY_TYPE_PARALLEL ) self.assertEqual(strategy_dict["type"], consts.SW_UPDATE_TYPE_UPGRADE) subcloud_ids = [2] strategy_step_list = db_api.strategy_step_get_all(self.ctxt) for index, strategy_step in enumerate(strategy_step_list): self.assertEqual(subcloud_ids[index], strategy_step.subcloud_id) @mock.patch.object(sw_update_manager, "PatchOrchThread") def test_create_sw_update_strategy_not_insync_offline_sc_with_force_upgrade( self, mock_patch_orch_thread ): # This test verifies the offline subcloud is added to the strategy # because force option is specified in the upgrade request. fake_subcloud1 = self.create_subcloud( self.ctxt, "subcloud1", 1, is_managed=True, is_online=False ) self.update_subcloud_status( self.ctxt, fake_subcloud1.id, dccommon_consts.ENDPOINT_TYPE_LOAD, dccommon_consts.SYNC_STATUS_UNKNOWN, ) um = sw_update_manager.SwUpdateManager() data = copy.copy(FAKE_SW_UPDATE_DATA) data["type"] = consts.SW_UPDATE_TYPE_UPGRADE data["force"] = "true" data["cloud_name"] = "subcloud1" strategy_dict = um.create_sw_update_strategy(self.ctxt, payload=data) # Assert that values passed through CLI are used instead of group values self.assertEqual( strategy_dict["subcloud-apply-type"], consts.SUBCLOUD_APPLY_TYPE_PARALLEL ) self.assertEqual(strategy_dict["type"], consts.SW_UPDATE_TYPE_UPGRADE) # Verify the strategy step list subcloud_ids = [1] strategy_step_list = db_api.strategy_step_get_all(self.ctxt) for index, strategy_step in enumerate(strategy_step_list): self.assertEqual(subcloud_ids[index], strategy_step.subcloud_id) @mock.patch.object(sw_update_manager, "PatchOrchThread") def test_create_sw_update_strategy_in_sync_offline_subcloud_with_force_upgrade( self, mock_patch_orch_thread ): # This test verifies that a bad request exception is raised even # though force option is specified in the request because the load sync # status of the offline subcloud is in-sync. fake_subcloud1 = self.create_subcloud( self.ctxt, "subcloud1", 1, is_managed=True, is_online=False ) self.update_subcloud_status( self.ctxt, fake_subcloud1.id, dccommon_consts.ENDPOINT_TYPE_LOAD, dccommon_consts.SYNC_STATUS_IN_SYNC, ) um = sw_update_manager.SwUpdateManager() data = copy.copy(FAKE_SW_UPDATE_DATA) data["type"] = consts.SW_UPDATE_TYPE_UPGRADE data["force"] = True data["cloud_name"] = "subcloud1" self.assertRaises( exceptions.BadRequest, um.create_sw_update_strategy, self.ctxt, payload=data, ) @mock.patch.object(sw_update_manager, "PatchOrchThread") def test_create_sw_update_strategy_online_subcloud_with_force_upgrade( self, mock_patch_orch_thread ): # This test verifies that the force option has no effect in # upgrade creation strategy if the subcloud is online. A bad request # exception will be raised if the subcloud load sync status is # unknown. fake_subcloud1 = self.create_subcloud( self.ctxt, "subcloud1", 1, is_managed=True, is_online=True ) self.update_subcloud_status( self.ctxt, fake_subcloud1.id, dccommon_consts.ENDPOINT_TYPE_LOAD, dccommon_consts.SYNC_STATUS_UNKNOWN, ) um = sw_update_manager.SwUpdateManager() data = copy.copy(FAKE_SW_UPDATE_DATA) data["type"] = consts.SW_UPDATE_TYPE_UPGRADE data["force"] = True data["cloud_name"] = "subcloud1" self.assertRaises( exceptions.BadRequest, um.create_sw_update_strategy, self.ctxt, payload=data, ) @mock.patch.object(sw_update_manager, "PatchOrchThread") def test_create_sw_update_strategy_offline_subcloud_with_force_patching( self, mock_patch_orch_thread ): # This test verifies that the force option has no effect in # patching creation strategy even though the subcloud is offline fake_subcloud1 = self.create_subcloud( self.ctxt, "subcloud1", 1, is_managed=True, is_online=False ) self.update_subcloud_status(self.ctxt, fake_subcloud1.id) um = sw_update_manager.SwUpdateManager() data = copy.copy(FAKE_SW_UPDATE_DATA) data["force"] = True data["cloud_name"] = "subcloud1" # No strategy step is created when all subclouds are offline, # should raise 'Bad strategy request: Strategy has no steps to apply' self.assertRaises( exceptions.BadRequest, um.create_sw_update_strategy, self.ctxt, payload=data, ) @mock.patch.object(sw_update_manager, "PatchOrchThread") def test_delete_sw_update_strategy(self, mock_patch_orch_thread): self.create_strategy( self.ctxt, consts.SW_UPDATE_TYPE_PATCH, consts.SW_UPDATE_STATE_INITIAL ) um = sw_update_manager.SwUpdateManager() deleted_strategy = um.delete_sw_update_strategy(self.ctxt) self.assertEqual(deleted_strategy["state"], consts.SW_UPDATE_STATE_DELETING) @mock.patch.object(sw_update_manager, "PatchOrchThread") def test_delete_sw_update_strategy_scoped(self, mock_patch_orch_thread): self.create_strategy( self.ctxt, consts.SW_UPDATE_TYPE_PATCH, consts.SW_UPDATE_STATE_INITIAL ) um = sw_update_manager.SwUpdateManager() deleted_strategy = um.delete_sw_update_strategy( self.ctxt, update_type=consts.SW_UPDATE_TYPE_PATCH ) self.assertEqual(deleted_strategy["state"], consts.SW_UPDATE_STATE_DELETING) @mock.patch.object(sw_update_manager, "PatchOrchThread") def test_delete_sw_update_strategy_bad_scope(self, mock_patch_orch_thread): self.create_strategy( self.ctxt, consts.SW_UPDATE_TYPE_PATCH, consts.SW_UPDATE_STATE_INITIAL ) um = sw_update_manager.SwUpdateManager() # the strategy is PATCH. The delete for UPGRADE should fail self.assertRaises( exceptions.NotFound, um.delete_sw_update_strategy, self.ctx, update_type=consts.SW_UPDATE_TYPE_UPGRADE, ) @mock.patch.object(sw_update_manager, "PatchOrchThread") def test_delete_sw_update_strategy_invalid_state(self, mock_patch_orch_thread): # Create fake strategy self.create_strategy( self.ctxt, consts.SW_UPDATE_TYPE_PATCH, consts.SW_UPDATE_STATE_APPLYING ) um = sw_update_manager.SwUpdateManager() self.assertRaises( exceptions.BadRequest, um.delete_sw_update_strategy, self.ctxt ) @mock.patch.object(sw_update_manager, "PatchOrchThread") def test_apply_sw_update_strategy(self, mock_patch_orch_thread): # Create fake strategy self.create_strategy( self.ctxt, consts.SW_UPDATE_TYPE_PATCH, consts.SW_UPDATE_STATE_INITIAL ) um = sw_update_manager.SwUpdateManager() updated_strategy = um.apply_sw_update_strategy(self.ctxt) self.assertEqual(updated_strategy["state"], consts.SW_UPDATE_STATE_APPLYING) @mock.patch.object(sw_update_manager, "PatchOrchThread") def test_apply_sw_update_strategy_invalid_state(self, mock_patch_orch_thread): # Create fake strategy self.create_strategy( self.ctxt, consts.SW_UPDATE_TYPE_PATCH, consts.SW_UPDATE_STATE_APPLYING ) um = sw_update_manager.SwUpdateManager() self.assertRaises( exceptions.BadRequest, um.apply_sw_update_strategy, self.ctxt ) @mock.patch.object(sw_update_manager, "PatchOrchThread") def test_abort_sw_update_strategy(self, mock_patch_orch_thread): # Create fake strategy self.create_strategy( self.ctxt, consts.SW_UPDATE_TYPE_PATCH, consts.SW_UPDATE_STATE_APPLYING ) um = sw_update_manager.SwUpdateManager() aborted_strategy = um.abort_sw_update_strategy(self.ctxt) self.assertEqual( aborted_strategy["state"], consts.SW_UPDATE_STATE_ABORT_REQUESTED ) @mock.patch.object(sw_update_manager, "PatchOrchThread") def test_abort_sw_update_strategy_invalid_state(self, mock_patch_orch_thread): # Create fake strategy self.create_strategy( self.ctxt, consts.SW_UPDATE_TYPE_PATCH, consts.SW_UPDATE_STATE_COMPLETE ) um = sw_update_manager.SwUpdateManager() self.assertRaises( exceptions.BadRequest, um.apply_sw_update_strategy, self.ctxt )