Merge "Improve unit test coverage for dcmanager's orchestrator/states/software"
This commit is contained in:
commit
28903f8bfd
|
@ -198,6 +198,11 @@ class DCManagerTestCase(base.BaseTestCase):
|
||||||
self.mock_sysinv_client = mock_patch.start()
|
self.mock_sysinv_client = mock_patch.start()
|
||||||
self.addCleanup(mock_patch.stop)
|
self.addCleanup(mock_patch.stop)
|
||||||
|
|
||||||
|
def _mock_read_from_cache(self, target):
|
||||||
|
mock_patch = mock.patch.object(target, '_read_from_cache')
|
||||||
|
self.mock_read_from_cache = mock_patch.start()
|
||||||
|
self.addCleanup(mock_patch.stop)
|
||||||
|
|
||||||
def _mock_get_network_address_pool(self):
|
def _mock_get_network_address_pool(self):
|
||||||
"""Mock phased subcloud deploy's get_network_address_pool"""
|
"""Mock phased subcloud deploy's get_network_address_pool"""
|
||||||
|
|
||||||
|
|
|
@ -3,38 +3,66 @@
|
||||||
#
|
#
|
||||||
# SPDX-License-Identifier: Apache-2.0
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
#
|
#
|
||||||
|
|
||||||
import mock
|
import mock
|
||||||
|
|
||||||
from dcmanager.common import consts
|
from dcmanager.common import consts
|
||||||
|
from dcmanager.orchestrator.states.base import BaseState
|
||||||
from dcmanager.orchestrator.states.software.finish_strategy import \
|
from dcmanager.orchestrator.states.software.finish_strategy import \
|
||||||
FinishStrategyState
|
FinishStrategyState
|
||||||
from dcmanager.tests.unit.orchestrator.states.software.test_base import \
|
from dcmanager.tests.unit.orchestrator.states.software.test_base import \
|
||||||
TestSoftwareOrchestrator
|
TestSoftwareOrchestrator
|
||||||
|
|
||||||
|
|
||||||
REGION_ONE_RELEASES = {"DC.1": {"sw_version": "20.12",
|
REGION_ONE_RELEASES = {
|
||||||
"state": "committed"},
|
"DC.1": {
|
||||||
"DC.2": {"sw_version": "20.12",
|
"sw_version": "20.12",
|
||||||
"state": "committed"},
|
"state": "committed"
|
||||||
"DC.3": {"sw_version": "20.12",
|
},
|
||||||
"state": "committed"},
|
"DC.2": {
|
||||||
"DC.8": {"sw_version": "20.12",
|
"sw_version": "20.12",
|
||||||
"state": "committed"}}
|
"state": "committed"
|
||||||
|
},
|
||||||
|
"DC.3": {
|
||||||
|
"sw_version": "20.12",
|
||||||
|
"state": "committed"
|
||||||
|
},
|
||||||
|
"DC.8": {
|
||||||
|
"sw_version": "20.12",
|
||||||
|
"state": "committed"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
SUBCLOUD_RELEASES = {"DC.1": {"sw_version": "20.12",
|
SUBCLOUD_RELEASES = {
|
||||||
"state": "committed"},
|
"DC.1": {
|
||||||
"DC.2": {"sw_version": "20.12",
|
"sw_version": "20.12",
|
||||||
"state": "committed"},
|
"state": "committed"
|
||||||
"DC.3": {"sw_version": "20.12",
|
},
|
||||||
"state": "deployed"},
|
"DC.2": {
|
||||||
"DC.9": {"sw_version": "20.12",
|
"sw_version": "20.12",
|
||||||
"state": "available"}}
|
"state": "committed"
|
||||||
|
},
|
||||||
|
"DC.3": {
|
||||||
|
"sw_version": "20.12",
|
||||||
|
"state": "deployed"
|
||||||
|
},
|
||||||
|
"DC.9": {
|
||||||
|
"sw_version": "20.12",
|
||||||
|
"state": "available"
|
||||||
|
},
|
||||||
|
"DC.10": {
|
||||||
|
"sw_version": "20.12",
|
||||||
|
"state": "deployed"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
class TestFinishStrategyState(TestSoftwareOrchestrator):
|
class TestFinishStrategyState(TestSoftwareOrchestrator):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
super().setUp()
|
super().setUp()
|
||||||
|
|
||||||
|
self._mock_read_from_cache(FinishStrategyState)
|
||||||
|
|
||||||
self.on_success_state = consts.STRATEGY_STATE_COMPLETE
|
self.on_success_state = consts.STRATEGY_STATE_COMPLETE
|
||||||
|
|
||||||
# Add the subcloud being processed by this unit test
|
# Add the subcloud being processed by this unit test
|
||||||
|
@ -51,10 +79,10 @@ class TestFinishStrategyState(TestSoftwareOrchestrator):
|
||||||
self.software_client.commit_patch = mock.MagicMock()
|
self.software_client.commit_patch = mock.MagicMock()
|
||||||
self._read_from_cache = mock.MagicMock()
|
self._read_from_cache = mock.MagicMock()
|
||||||
|
|
||||||
@mock.patch.object(FinishStrategyState, '_read_from_cache')
|
def test_finish_strategy_success(self):
|
||||||
def test_finish_strategy_success(self, mock_read_from_cache):
|
|
||||||
"""Test software finish strategy when the API call succeeds."""
|
"""Test software finish strategy when the API call succeeds."""
|
||||||
mock_read_from_cache.return_value = REGION_ONE_RELEASES
|
|
||||||
|
self.mock_read_from_cache.return_value = REGION_ONE_RELEASES
|
||||||
|
|
||||||
self.software_client.query.side_effect = [SUBCLOUD_RELEASES]
|
self.software_client.query.side_effect = [SUBCLOUD_RELEASES]
|
||||||
|
|
||||||
|
@ -71,10 +99,10 @@ class TestFinishStrategyState(TestSoftwareOrchestrator):
|
||||||
self.assert_step_updated(self.strategy_step.subcloud_id,
|
self.assert_step_updated(self.strategy_step.subcloud_id,
|
||||||
self.on_success_state)
|
self.on_success_state)
|
||||||
|
|
||||||
@mock.patch.object(FinishStrategyState, '_read_from_cache')
|
def test_finish_strategy_no_operation_required(self):
|
||||||
def test_finish_strategy_no_operation_required(self, mock_read_from_cache):
|
|
||||||
"""Test software finish strategy when no operation is required."""
|
"""Test software finish strategy when no operation is required."""
|
||||||
mock_read_from_cache.return_value = REGION_ONE_RELEASES
|
|
||||||
|
self.mock_read_from_cache.return_value = REGION_ONE_RELEASES
|
||||||
|
|
||||||
self.software_client.query.side_effect = [REGION_ONE_RELEASES]
|
self.software_client.query.side_effect = [REGION_ONE_RELEASES]
|
||||||
|
|
||||||
|
@ -88,3 +116,62 @@ class TestFinishStrategyState(TestSoftwareOrchestrator):
|
||||||
# On success, the state should transition to the next state
|
# On success, the state should transition to the next state
|
||||||
self.assert_step_updated(self.strategy_step.subcloud_id,
|
self.assert_step_updated(self.strategy_step.subcloud_id,
|
||||||
self.on_success_state)
|
self.on_success_state)
|
||||||
|
|
||||||
|
def test_finish_strategy_fails_when_query_exception(self):
|
||||||
|
"""Test finish strategy fails when software client query raises exception"""
|
||||||
|
|
||||||
|
self.mock_read_from_cache.return_value = REGION_ONE_RELEASES
|
||||||
|
|
||||||
|
self.software_client.query.side_effect = Exception()
|
||||||
|
|
||||||
|
self.worker.perform_state_action(self.strategy_step)
|
||||||
|
|
||||||
|
self.assert_step_updated(
|
||||||
|
self.strategy_step.subcloud_id, consts.STRATEGY_STATE_FAILED
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_finish_strategy_fails_when_delete_exception(self):
|
||||||
|
"""Test finish strategy fails when software client delete raises exception"""
|
||||||
|
|
||||||
|
self.mock_read_from_cache.return_value = REGION_ONE_RELEASES
|
||||||
|
|
||||||
|
self.software_client.query.side_effect = [SUBCLOUD_RELEASES]
|
||||||
|
self.software_client.delete.side_effect = Exception()
|
||||||
|
|
||||||
|
self.worker.perform_state_action(self.strategy_step)
|
||||||
|
|
||||||
|
self.assert_step_updated(
|
||||||
|
self.strategy_step.subcloud_id, consts.STRATEGY_STATE_FAILED
|
||||||
|
)
|
||||||
|
|
||||||
|
@mock.patch.object(BaseState, 'stopped')
|
||||||
|
def test_finish_strategy_fails_when_stopped(self, mock_base_stopped):
|
||||||
|
"""Test finish strategy fails when stopped"""
|
||||||
|
self.mock_read_from_cache.return_value = REGION_ONE_RELEASES
|
||||||
|
|
||||||
|
self.software_client.query.side_effect = [SUBCLOUD_RELEASES]
|
||||||
|
|
||||||
|
mock_base_stopped.return_value = True
|
||||||
|
|
||||||
|
self.worker.perform_state_action(self.strategy_step)
|
||||||
|
|
||||||
|
self.assert_step_updated(
|
||||||
|
self.strategy_step.subcloud_id, consts.STRATEGY_STATE_FAILED
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_finish_strategy_fails_when_commit_patch_exception(self):
|
||||||
|
"""Test finish strategy fails when software client commit_patch
|
||||||
|
|
||||||
|
raises exception
|
||||||
|
"""
|
||||||
|
|
||||||
|
self.mock_read_from_cache.return_value = REGION_ONE_RELEASES
|
||||||
|
|
||||||
|
self.software_client.query.side_effect = [SUBCLOUD_RELEASES]
|
||||||
|
self.software_client.commit_patch.side_effect = Exception()
|
||||||
|
|
||||||
|
self.worker.perform_state_action(self.strategy_step)
|
||||||
|
|
||||||
|
self.assert_step_updated(
|
||||||
|
self.strategy_step.subcloud_id, consts.STRATEGY_STATE_FAILED
|
||||||
|
)
|
||||||
|
|
|
@ -6,7 +6,9 @@
|
||||||
|
|
||||||
import mock
|
import mock
|
||||||
|
|
||||||
|
from dccommon import consts as dccommon_consts
|
||||||
from dcmanager.common import consts
|
from dcmanager.common import consts
|
||||||
|
from dcmanager.db import api as db_api
|
||||||
from dcmanager.tests.unit.orchestrator.states.software.test_base import \
|
from dcmanager.tests.unit.orchestrator.states.software.test_base import \
|
||||||
TestSoftwareOrchestrator
|
TestSoftwareOrchestrator
|
||||||
|
|
||||||
|
@ -15,6 +17,8 @@ MISSING_LICENSE_RESPONSE = {
|
||||||
"error": "License file not found. A license may not have been installed.",
|
"error": "License file not found. A license may not have been installed.",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
GENERIC_ERROR_RESPONSE = {"content": "", "error": "Invalid license"}
|
||||||
|
|
||||||
LICENSE_VALID_RESPONSE = {"content": "A valid license", "error": ""}
|
LICENSE_VALID_RESPONSE = {"content": "A valid license", "error": ""}
|
||||||
|
|
||||||
ALTERNATE_LICENSE_RESPONSE = {"content": "A different valid license", "error": ""}
|
ALTERNATE_LICENSE_RESPONSE = {"content": "A different valid license", "error": ""}
|
||||||
|
@ -69,7 +73,7 @@ class TestInstallLicenseState(TestSoftwareOrchestrator):
|
||||||
self.strategy_step.subcloud_id, consts.STRATEGY_STATE_FAILED
|
self.strategy_step.subcloud_id, consts.STRATEGY_STATE_FAILED
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_install_license_success(self):
|
def test_install_license_succeeds(self):
|
||||||
"""Test the install license step succeeds.
|
"""Test the install license step succeeds.
|
||||||
|
|
||||||
The license will be installed on the subcloud when system controller
|
The license will be installed on the subcloud when system controller
|
||||||
|
@ -147,8 +151,8 @@ class TestInstallLicenseState(TestSoftwareOrchestrator):
|
||||||
self.strategy_step.subcloud_id, self.on_success_state
|
self.strategy_step.subcloud_id, self.on_success_state
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_install_license_skip_when_no_sys_controller_lic(self):
|
def test_install_license_skips_with_sys_controller_without_license(self):
|
||||||
"""Test license install skipped when no license on system controller."""
|
"""Test license install skips when sys controller doesn't have a license"""
|
||||||
|
|
||||||
# Only makes one query: to system controller
|
# Only makes one query: to system controller
|
||||||
self.sysinv_client.get_license.return_value = MISSING_LICENSE_RESPONSE
|
self.sysinv_client.get_license.return_value = MISSING_LICENSE_RESPONSE
|
||||||
|
@ -163,3 +167,28 @@ class TestInstallLicenseState(TestSoftwareOrchestrator):
|
||||||
self.assert_step_updated(
|
self.assert_step_updated(
|
||||||
self.strategy_step.subcloud_id, self.on_success_state
|
self.strategy_step.subcloud_id, self.on_success_state
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def test_install_license_fails_with_generic_error_response(self):
|
||||||
|
"""Test license install fails with generic error response"""
|
||||||
|
|
||||||
|
# Only makes one query: to system controller
|
||||||
|
self.sysinv_client.get_license.return_value = GENERIC_ERROR_RESPONSE
|
||||||
|
|
||||||
|
# invoke the strategy state operation on the orch thread
|
||||||
|
self.worker.perform_state_action(self.strategy_step)
|
||||||
|
|
||||||
|
subcloud = db_api.subcloud_get(self.ctx, self.subcloud.id)
|
||||||
|
|
||||||
|
self.assertEqual(
|
||||||
|
subcloud.error_description, "An unexpected error occurred querying the "
|
||||||
|
f"license {dccommon_consts.SYSTEM_CONTROLLER_NAME}. "
|
||||||
|
f"Detail: {GENERIC_ERROR_RESPONSE['error']}"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Should skip install_license API call
|
||||||
|
self.sysinv_client.install_license.assert_not_called()
|
||||||
|
|
||||||
|
# Verify it successfully moves to the next step
|
||||||
|
self.assert_step_updated(
|
||||||
|
self.strategy_step.subcloud_id, consts.STRATEGY_STATE_FAILED
|
||||||
|
)
|
||||||
|
|
Loading…
Reference in New Issue