diff --git a/distributedcloud/dcmanager/tests/unit/orchestrator/states/software/cache/__init__.py b/distributedcloud/dcmanager/tests/unit/orchestrator/states/software/cache/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/distributedcloud/dcmanager/tests/unit/orchestrator/states/software/cache/test_shared_cache_repository.py b/distributedcloud/dcmanager/tests/unit/orchestrator/states/software/cache/test_shared_cache_repository.py new file mode 100644 index 000000000..777cf9b5b --- /dev/null +++ b/distributedcloud/dcmanager/tests/unit/orchestrator/states/software/cache/test_shared_cache_repository.py @@ -0,0 +1,134 @@ +# Copyright (c) 2024 Wind River Systems, Inc. +# +# SPDX-License-Identifier: Apache-2.0 +# + +import copy +import mock + +from keystoneauth1 import exceptions as keystone_exceptions + +from dcmanager.common import consts +from dcmanager.common.exceptions import InvalidParameterValue +from dcmanager.orchestrator.states.software.cache.cache_specifications import \ + REGION_ONE_LICENSE_CACHE_TYPE +from dcmanager.orchestrator.states.software.cache.cache_specifications import \ + REGION_ONE_RELEASE_USM_CACHE_TYPE +from dcmanager.orchestrator.states.software.cache.cache_specifications import \ + REGION_ONE_SYSTEM_INFO_CACHE_TYPE +from dcmanager.orchestrator.states.software.cache import clients +from dcmanager.orchestrator.states.software.cache.shared_cache_repository import \ + SharedCacheRepository +from dcmanager.tests import base + +SOFTWARE_CLIENT_QUERY_RETURN = { + "stx_23.09.0": { + "sw_version": "23.09.0", + "state": "available", + "reboot_required": "N", + }, + "stx_23.09.1": { + "sw_version": "23.09.1", + "state": "available", + "reboot_required": "N", + }, + "stx_23.09.2": { + "sw_version": "23.09.2", + "state": "unavailable", + "reboot_required": "N", + } +} + + +class TestSharedCacheRepository(base.DCManagerTestCase): + def setUp(self): + """Initializes the shared cache repository""" + + super().setUp() + + self._mock_openstack_driver(clients) + self._mock_sysinv_client(clients) + self._mock_software_client() + + self.shared_cache_repository = SharedCacheRepository( + operation_type=consts.SW_UPDATE_TYPE_SOFTWARE + ) + self.shared_cache_repository.initialize_caches() + + self.software_client().query.return_value = SOFTWARE_CLIENT_QUERY_RETURN + + def _mock_software_client(self): + mock_patch = mock.patch.object(clients, 'SoftwareClient') + self.software_client = mock_patch.start() + self.addCleanup(mock_patch.stop) + + def test_read_succeeds_with_license_cache_type(self): + """Test read cache succeeds when using the REGION_ONE_LICENSE_CACHE_TYPE""" + + self.mock_sysinv_client().get_license.return_value = 'fake license' + + response = self.shared_cache_repository.read(REGION_ONE_LICENSE_CACHE_TYPE) + + self.assertEqual(response, 'fake license') + + def test_read_succeeds_with_system_info_cache_type(self): + """Test read cache succeeds when using REGION_ONE_SYSTEM_INFO_CACHE_TYPE""" + + self.mock_sysinv_client().get_system.return_value = 'fake system info' + + response = \ + self.shared_cache_repository.read(REGION_ONE_SYSTEM_INFO_CACHE_TYPE) + + self.assertEqual(response, 'fake system info') + + def test_read_succeeds_with_release_usm_cache_type(self): + """Test read cache succeeds when using REGION_ONE_RELEASE_USM_CACHE_TYPE""" + + response = \ + self.shared_cache_repository.read(REGION_ONE_RELEASE_USM_CACHE_TYPE) + + self.assertEqual(response, SOFTWARE_CLIENT_QUERY_RETURN) + + def test_read_fails_with_invalid_cache_type(self): + """Test read cache fails when using an invalid cache type""" + + self.assertRaises( + InvalidParameterValue, + self.shared_cache_repository.read, + 'fake parameter' + ) + + def test_read_fails_when_openstack_driver_raises_exception(self): + """Test read cache fails when the OpenStackDriver raises an Exception""" + + self.mock_openstack_driver.side_effect = \ + keystone_exceptions.ConnectFailure() + + self.assertRaises( + keystone_exceptions.ConnectFailure, + self.shared_cache_repository.read, + REGION_ONE_RELEASE_USM_CACHE_TYPE + ) + + def test_read_succeeds_with_filter_params(self): + """Test read cache succeeds when filter_params is sent""" + + response = self.shared_cache_repository.read( + REGION_ONE_RELEASE_USM_CACHE_TYPE, + state='available' + ) + + expected_response = copy.copy(SOFTWARE_CLIENT_QUERY_RETURN) + del expected_response["stx_23.09.2"] + + self.assertEqual(response, expected_response) + + def test_read_fails_with_invalid_filter_params(self): + """Test read cache succeeds when and invalid filter_params is sent""" + + self.assertRaises( + InvalidParameterValue, + self.shared_cache_repository.read, + REGION_ONE_RELEASE_USM_CACHE_TYPE, + invalid='available' + ) diff --git a/distributedcloud/dcmanager/tests/unit/orchestrator/states/software/cache/test_shared_client_cache.py b/distributedcloud/dcmanager/tests/unit/orchestrator/states/software/cache/test_shared_client_cache.py new file mode 100644 index 000000000..a201a4729 --- /dev/null +++ b/distributedcloud/dcmanager/tests/unit/orchestrator/states/software/cache/test_shared_client_cache.py @@ -0,0 +1,110 @@ +# Copyright (c) 2024 Wind River Systems, Inc. +# +# SPDX-License-Identifier: Apache-2.0 +# + +import socket + +import mock + +from dcmanager.orchestrator.states.software.cache.cache_specifications import \ + CacheSpecification +from dcmanager.orchestrator.states.software.cache.cache_specifications import \ + REGION_ONE_LICENSE_CACHE_SPECIFICATION +from dcmanager.orchestrator.states.software.cache.cache_specifications import \ + REGION_ONE_LICENSE_CACHE_TYPE +from dcmanager.orchestrator.states.software.cache import clients +from dcmanager.orchestrator.states.software.cache.shared_client_cache import \ + SharedClientCache +from dcmanager.tests import base + +SOFTWARE_CLIENT_QUERY_RETURN = { + "stx_23.09.0": { + "sw_version": "23.09.0", + "state": "available", + "reboot_required": "N", + }, + "stx_23.09.1": { + "sw_version": "23.09.1", + "state": "available", + "reboot_required": "N", + } +} + + +class TestSharedClientCache(base.DCManagerTestCase): + def setUp(self): + """Initializes the shared client cache""" + + super().setUp() + + self._mock_openstack_driver(clients) + self._mock_sysinv_client(clients) + + def test_read_succeeds_when_cache_data_is_stored(self): + """Test read cache succeeds when the data is cached after the first request + + In the second request, instead of reacquiring the data using get_system, + the previously stored information should be returned + """ + + shared_client_cache = SharedClientCache( + REGION_ONE_LICENSE_CACHE_TYPE, REGION_ONE_LICENSE_CACHE_SPECIFICATION + ) + + self.mock_sysinv_client().get_license.return_value = 'fake license' + + self.assertIsNone(shared_client_cache._cache) + + response = shared_client_cache.read() + + self.assertEqual(response, 'fake license') + self.mock_sysinv_client().get_license.assert_called_once() + self.assertIsNotNone(shared_client_cache._cache) + + response = shared_client_cache.read() + + self.assertEqual(response, 'fake license') + self.mock_sysinv_client().get_license.assert_called_once() + + def test_read_fails_when_client_lock_is_writer_and_cache_is_not_stored(self): + """Test read cache fails with writer client lock and without cache stored""" + + shared_client_cache = SharedClientCache( + REGION_ONE_LICENSE_CACHE_TYPE, REGION_ONE_LICENSE_CACHE_SPECIFICATION + ) + + with shared_client_cache._client_lock.write_lock(): + self.assertRaises(RuntimeError, shared_client_cache.read) + + def test_read_succeeds_without_retry_on_exception(self): + """Test read cache succeeds without retry on exception""" + + cache_specification = CacheSpecification( + lambda: clients.get_sysinv_client().get_license(), + retry_on_exception=False + ) + self.shared_client_cache = SharedClientCache( + REGION_ONE_LICENSE_CACHE_TYPE, cache_specification + ) + + self.mock_sysinv_client().get_license.return_value = 'fake license' + + response = self.shared_client_cache.read() + + self.assertEqual(response, 'fake license') + self.mock_sysinv_client().get_license.assert_called_once() + + def test_read_fails_with_retry_on_exception(self): + """Test read cache fails with retry on exception""" + + fetch_implementation = mock.MagicMock(side_effect=socket.timeout) + + shared_client_cache = SharedClientCache( + REGION_ONE_LICENSE_CACHE_TYPE, CacheSpecification(fetch_implementation) + ) + + self.assertRaises(socket.timeout, shared_client_cache.read) + self.assertEqual( + fetch_implementation.call_count, shared_client_cache._max_attempts + )