diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-18 05:52:22 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-18 05:52:22 +0000 |
commit | 38b7c80217c4e72b1d8988eb1e60bb6e77334114 (patch) | |
tree | 356e9fd3762877d07cde52d21e77070aeff7e789 /ansible_collections/dellemc/powerflex/tests | |
parent | Adding upstream version 7.7.0+dfsg. (diff) | |
download | ansible-38b7c80217c4e72b1d8988eb1e60bb6e77334114.tar.xz ansible-38b7c80217c4e72b1d8988eb1e60bb6e77334114.zip |
Adding upstream version 9.4.0+dfsg.upstream/9.4.0+dfsg
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'ansible_collections/dellemc/powerflex/tests')
33 files changed, 5253 insertions, 346 deletions
diff --git a/ansible_collections/dellemc/powerflex/tests/sanity/ignore-2.13.txt b/ansible_collections/dellemc/powerflex/tests/sanity/ignore-2.13.txt deleted file mode 100644 index c78903cdf..000000000 --- a/ansible_collections/dellemc/powerflex/tests/sanity/ignore-2.13.txt +++ /dev/null @@ -1,11 +0,0 @@ -plugins/modules/device.py validate-modules:missing-gplv3-license -plugins/modules/sdc.py validate-modules:missing-gplv3-license -plugins/modules/sds.py validate-modules:missing-gplv3-license -plugins/modules/snapshot.py validate-modules:missing-gplv3-license -plugins/modules/storagepool.py validate-modules:missing-gplv3-license -plugins/modules/volume.py validate-modules:missing-gplv3-license -plugins/modules/info.py validate-modules:missing-gplv3-license -plugins/modules/protection_domain.py validate-modules:missing-gplv3-license -plugins/modules/mdm_cluster.py validate-modules:missing-gplv3-license -plugins/modules/replication_consistency_group.py validate-modules:missing-gplv3-license -plugins/modules/replication_pair.py validate-modules:missing-gplv3-license diff --git a/ansible_collections/dellemc/powerflex/tests/sanity/ignore-2.14.txt b/ansible_collections/dellemc/powerflex/tests/sanity/ignore-2.14.txt index c78903cdf..cb6ef4675 100644 --- a/ansible_collections/dellemc/powerflex/tests/sanity/ignore-2.14.txt +++ b/ansible_collections/dellemc/powerflex/tests/sanity/ignore-2.14.txt @@ -1,3 +1,18 @@ +plugins/modules/sds.py import-2.7 +plugins/modules/sds.py import-3.5 +plugins/modules/sds.py compile-2.7 +plugins/modules/sds.py compile-3.5 +plugins/modules/info.py import-2.7 +plugins/modules/info.py import-3.5 +plugins/modules/info.py compile-2.7 +plugins/modules/fault_set.py import-2.7 +plugins/modules/fault_set.py import-3.5 +plugins/modules/fault_set.py compile-2.7 +plugins/modules/fault_set.py compile-3.5 +plugins/module_utils/storage/dell/libraries/configuration.py import-2.7 +plugins/module_utils/storage/dell/libraries/configuration.py import-3.5 +plugins/module_utils/storage/dell/libraries/configuration.py compile-2.7 +plugins/module_utils/storage/dell/libraries/configuration.py compile-3.5 plugins/modules/device.py validate-modules:missing-gplv3-license plugins/modules/sdc.py validate-modules:missing-gplv3-license plugins/modules/sds.py validate-modules:missing-gplv3-license @@ -9,3 +24,19 @@ plugins/modules/protection_domain.py validate-modules:missing-gplv3-license plugins/modules/mdm_cluster.py validate-modules:missing-gplv3-license plugins/modules/replication_consistency_group.py validate-modules:missing-gplv3-license plugins/modules/replication_pair.py validate-modules:missing-gplv3-license +plugins/modules/snapshot_policy.py validate-modules:missing-gplv3-license +plugins/modules/fault_set.py validate-modules:missing-gplv3-license +plugins/modules/snapshot_policy.py compile-2.7 +plugins/modules/snapshot_policy.py compile-3.5 +plugins/modules/snapshot_policy.py import-2.7 +plugins/modules/snapshot_policy.py import-3.5 +plugins/modules/sdc.py import-2.7 +plugins/modules/sdc.py import-3.5 +plugins/modules/sdc.py compile-2.7 +plugins/modules/sdc.py compile-3.5 +tests/unit/plugins/module_utils/mock_device_api.py compile-2.7 +tests/unit/plugins/module_utils/mock_device_api.py compile-3.5 +plugins/modules/replication_consistency_group.py import-2.7 +plugins/modules/replication_consistency_group.py import-3.5 +plugins/modules/replication_consistency_group.py compile-2.7 +plugins/modules/replication_consistency_group.py compile-3.5 diff --git a/ansible_collections/dellemc/powerflex/tests/sanity/ignore-2.15.txt b/ansible_collections/dellemc/powerflex/tests/sanity/ignore-2.15.txt new file mode 100644 index 000000000..cb6ef4675 --- /dev/null +++ b/ansible_collections/dellemc/powerflex/tests/sanity/ignore-2.15.txt @@ -0,0 +1,42 @@ +plugins/modules/sds.py import-2.7 +plugins/modules/sds.py import-3.5 +plugins/modules/sds.py compile-2.7 +plugins/modules/sds.py compile-3.5 +plugins/modules/info.py import-2.7 +plugins/modules/info.py import-3.5 +plugins/modules/info.py compile-2.7 +plugins/modules/fault_set.py import-2.7 +plugins/modules/fault_set.py import-3.5 +plugins/modules/fault_set.py compile-2.7 +plugins/modules/fault_set.py compile-3.5 +plugins/module_utils/storage/dell/libraries/configuration.py import-2.7 +plugins/module_utils/storage/dell/libraries/configuration.py import-3.5 +plugins/module_utils/storage/dell/libraries/configuration.py compile-2.7 +plugins/module_utils/storage/dell/libraries/configuration.py compile-3.5 +plugins/modules/device.py validate-modules:missing-gplv3-license +plugins/modules/sdc.py validate-modules:missing-gplv3-license +plugins/modules/sds.py validate-modules:missing-gplv3-license +plugins/modules/snapshot.py validate-modules:missing-gplv3-license +plugins/modules/storagepool.py validate-modules:missing-gplv3-license +plugins/modules/volume.py validate-modules:missing-gplv3-license +plugins/modules/info.py validate-modules:missing-gplv3-license +plugins/modules/protection_domain.py validate-modules:missing-gplv3-license +plugins/modules/mdm_cluster.py validate-modules:missing-gplv3-license +plugins/modules/replication_consistency_group.py validate-modules:missing-gplv3-license +plugins/modules/replication_pair.py validate-modules:missing-gplv3-license +plugins/modules/snapshot_policy.py validate-modules:missing-gplv3-license +plugins/modules/fault_set.py validate-modules:missing-gplv3-license +plugins/modules/snapshot_policy.py compile-2.7 +plugins/modules/snapshot_policy.py compile-3.5 +plugins/modules/snapshot_policy.py import-2.7 +plugins/modules/snapshot_policy.py import-3.5 +plugins/modules/sdc.py import-2.7 +plugins/modules/sdc.py import-3.5 +plugins/modules/sdc.py compile-2.7 +plugins/modules/sdc.py compile-3.5 +tests/unit/plugins/module_utils/mock_device_api.py compile-2.7 +tests/unit/plugins/module_utils/mock_device_api.py compile-3.5 +plugins/modules/replication_consistency_group.py import-2.7 +plugins/modules/replication_consistency_group.py import-3.5 +plugins/modules/replication_consistency_group.py compile-2.7 +plugins/modules/replication_consistency_group.py compile-3.5 diff --git a/ansible_collections/dellemc/powerflex/tests/sanity/ignore-2.16.txt b/ansible_collections/dellemc/powerflex/tests/sanity/ignore-2.16.txt new file mode 100644 index 000000000..531796f6c --- /dev/null +++ b/ansible_collections/dellemc/powerflex/tests/sanity/ignore-2.16.txt @@ -0,0 +1,28 @@ +plugins/modules/sds.py import-2.7 +plugins/modules/sds.py compile-2.7 +plugins/module_utils/storage/dell/libraries/configuration.py import-2.7 +plugins/module_utils/storage/dell/libraries/configuration.py compile-2.7 +plugins/modules/device.py validate-modules:missing-gplv3-license +plugins/modules/sdc.py validate-modules:missing-gplv3-license +plugins/modules/sds.py validate-modules:missing-gplv3-license +plugins/modules/snapshot.py validate-modules:missing-gplv3-license +plugins/modules/storagepool.py validate-modules:missing-gplv3-license +plugins/modules/volume.py validate-modules:missing-gplv3-license +plugins/modules/info.py validate-modules:missing-gplv3-license +plugins/modules/protection_domain.py validate-modules:missing-gplv3-license +plugins/modules/mdm_cluster.py validate-modules:missing-gplv3-license +plugins/modules/replication_consistency_group.py validate-modules:missing-gplv3-license +plugins/modules/replication_pair.py validate-modules:missing-gplv3-license +plugins/modules/snapshot_policy.py validate-modules:missing-gplv3-license +plugins/modules/fault_set.py validate-modules:missing-gplv3-license +plugins/modules/snapshot_policy.py compile-2.7 +plugins/modules/snapshot_policy.py import-2.7 +plugins/modules/sdc.py import-2.7 +plugins/modules/sdc.py compile-2.7 +plugins/modules/fault_set.py import-2.7 +plugins/modules/fault_set.py compile-2.7 +tests/unit/plugins/module_utils/mock_device_api.py compile-2.7 +plugins/modules/replication_consistency_group.py import-2.7 +plugins/modules/replication_consistency_group.py compile-2.7 +plugins/modules/info.py compile-2.7 +plugins/modules/info.py import-2.7 diff --git a/ansible_collections/dellemc/powerflex/tests/sanity/ignore-2.12.txt b/ansible_collections/dellemc/powerflex/tests/sanity/ignore-2.17.txt index c78903cdf..54067647b 100644 --- a/ansible_collections/dellemc/powerflex/tests/sanity/ignore-2.12.txt +++ b/ansible_collections/dellemc/powerflex/tests/sanity/ignore-2.17.txt @@ -9,3 +9,5 @@ plugins/modules/protection_domain.py validate-modules:missing-gplv3-license plugins/modules/mdm_cluster.py validate-modules:missing-gplv3-license plugins/modules/replication_consistency_group.py validate-modules:missing-gplv3-license plugins/modules/replication_pair.py validate-modules:missing-gplv3-license +plugins/modules/snapshot_policy.py validate-modules:missing-gplv3-license +plugins/modules/fault_set.py validate-modules:missing-gplv3-license diff --git a/ansible_collections/dellemc/powerflex/tests/unit/plugins/module_utils/libraries/__init__.py b/ansible_collections/dellemc/powerflex/tests/unit/plugins/module_utils/libraries/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ansible_collections/dellemc/powerflex/tests/unit/plugins/module_utils/libraries/__init__.py diff --git a/ansible_collections/dellemc/powerflex/tests/unit/plugins/module_utils/libraries/fail_json.py b/ansible_collections/dellemc/powerflex/tests/unit/plugins/module_utils/libraries/fail_json.py new file mode 100644 index 000000000..d270326b9 --- /dev/null +++ b/ansible_collections/dellemc/powerflex/tests/unit/plugins/module_utils/libraries/fail_json.py @@ -0,0 +1,21 @@ +# Copyright: (c) 2024, Dell Technologies + +# Apache License version 2.0 (see MODULE-LICENSE or http://www.apache.org/licenses/LICENSE-2.0.txt) + +"""Mock fail json for PowerFlex Test modules""" + +from __future__ import (absolute_import, division, print_function) + +__metaclass__ = type + + +class FailJsonException(Exception): + def __init__(self, *args): + if args: + self.message = args[0] + else: + self.message = None + + +def fail_json(msg, **kwargs): + raise FailJsonException(msg) diff --git a/ansible_collections/dellemc/powerflex/tests/unit/plugins/module_utils/libraries/initial_mock.py b/ansible_collections/dellemc/powerflex/tests/unit/plugins/module_utils/libraries/initial_mock.py new file mode 100644 index 000000000..7409ab4c2 --- /dev/null +++ b/ansible_collections/dellemc/powerflex/tests/unit/plugins/module_utils/libraries/initial_mock.py @@ -0,0 +1,17 @@ +# Copyright: (c) 2024, Dell Technologies + +# Apache License version 2.0 (see MODULE-LICENSE or http://www.apache.org/licenses/LICENSE-2.0.txt) + +from __future__ import (absolute_import, division, print_function) + +__metaclass__ = type +from ansible_collections.dellemc.powerflex.plugins.module_utils.storage.dell \ + import utils +from mock.mock import MagicMock + +utils.get_logger = MagicMock() +utils.get_powerflex_gateway_host_connection = MagicMock() +utils.PowerFlexClient = MagicMock() + +from ansible.module_utils import basic +basic.AnsibleModule = MagicMock() diff --git a/ansible_collections/dellemc/powerflex/tests/unit/plugins/module_utils/libraries/powerflex_unit_base.py b/ansible_collections/dellemc/powerflex/tests/unit/plugins/module_utils/libraries/powerflex_unit_base.py new file mode 100644 index 000000000..0c06b0cd5 --- /dev/null +++ b/ansible_collections/dellemc/powerflex/tests/unit/plugins/module_utils/libraries/powerflex_unit_base.py @@ -0,0 +1,40 @@ +# Copyright: (c) 2024, Dell Technologies + +# Apache License version 2.0 (see MODULE-LICENSE or http://www.apache.org/licenses/LICENSE-2.0.txt) + +from __future__ import (absolute_import, division, print_function) + +__metaclass__ = type +import pytest +# pylint: disable=unused-import +from ansible_collections.dellemc.powerflex.tests.unit.plugins.module_utils.libraries import initial_mock +from mock.mock import MagicMock +from ansible_collections.dellemc.powerflex.tests.unit.plugins.module_utils.libraries. \ + fail_json import FailJsonException, fail_json + + +class PowerFlexUnitBase: + + '''Powerflex Unit Test Base Class''' + + @pytest.fixture + def powerflex_module_mock(self, mocker, module_object): + powerflex_module_mock = module_object() + powerflex_module_mock.module = MagicMock() + powerflex_module_mock.module.fail_json = fail_json + powerflex_module_mock.module.check_mode = False + return powerflex_module_mock + + def capture_fail_json_call(self, error_msg, module_mock, module_handler=None, invoke_perform_module=False): + try: + if not invoke_perform_module: + module_handler().handle(module_mock, module_mock.module.params) + else: + module_mock.perform_module_operation() + except FailJsonException as fj_object: + if error_msg not in fj_object.message: + raise AssertionError(fj_object.message) + + def set_module_params(self, module_mock, get_module_args, params): + get_module_args.update(params) + module_mock.module.params = get_module_args diff --git a/ansible_collections/dellemc/powerflex/tests/unit/plugins/module_utils/mock_device_api.py b/ansible_collections/dellemc/powerflex/tests/unit/plugins/module_utils/mock_device_api.py new file mode 100644 index 000000000..2a2cf8756 --- /dev/null +++ b/ansible_collections/dellemc/powerflex/tests/unit/plugins/module_utils/mock_device_api.py @@ -0,0 +1,146 @@ +# Copyright: (c) 2023, Dell Technologies + +# Apache License version 2.0 (see MODULE-LICENSE or http://www.apache.org/licenses/LICENSE-2.0.txt) + +""" +Mock Api response for Unit tests of Device module on Dell Technologies (Dell) PowerFlex +""" + +from __future__ import (absolute_import, division, print_function) + +__metaclass__ = type + + +class MockDeviceApi: + MODULE_UTILS_PATH = "ansible_collections.dellemc.powerflex.plugins.module_utils.storage.dell.utils" + + DEVICE_NAME_1 = "ansible_device_1" + DEVICE_ID_1 = "3fef781c00080000" + PD_ID_1 = "4eeb305100000001" + PD_NAME_1 = "domain1" + SDS_ID_1 = "6af03fc500000008" + SDS_NAME_1 = "ansible_sds_1" + SP_ID_1 = "7644c68600000008" + SP_NAME_1 = "ansible_sp_1" + PATH_1 = "/dev/sdb" + + DEVICE_COMMON_ARGS = { + "hostname": "**.***.**.***", + "current_pathname": None, + "device_id": None, + "device_name": None, + "sds_name": None, + "sds_id": None, + "storage_pool_name": None, + "storage_pool_id": None, + "acceleration_pool_name": None, + "acceleration_pool_id": None, + "protection_domain_name": None, + "protection_domain_id": None, + "external_acceleration_type": None, + "media_type": None, + "state": None + } + + DEVICE_GET_LIST = [ + { + "accelerationPoolId": SP_ID_1, + "accelerationPoolName": SP_NAME_1, + "autoDetectMediaType": "Unknown", + "capacityLimitInKb": 124718080, + "deviceCurrentPathName": PATH_1, + "deviceOriginalPathName": PATH_1, + "externalAccelerationType": "ReadAndWrite", + "fglNvdimmWriteCacheSize": 16, + "id": DEVICE_ID_1, + "mediaType": "HDD", + "name": DEVICE_NAME_1, + "protectionDomainId": PD_ID_1, + "protectionDomainName": PD_NAME_1, + "sdsId": SDS_ID_1, + "sdsName": SDS_NAME_1, + "spSdsId": "bfe791ff00080000", + "storagePoolId": SP_ID_1, + "storagePoolName": SP_NAME_1 + } + ] + SDS_DETAILS_1 = [ + { + "name": SDS_NAME_1, + "id": SDS_ID_1 + } + ] + PD_DETAILS_1 = [ + { + "name": PD_NAME_1, + "id": PD_ID_1 + } + ] + SP_DETAILS_1 = [ + { + "name": SP_NAME_1, + "protectionDomainId": PD_ID_1, + "id": SP_ID_1 + } + ] + AP_DETAILS_1 = [ + { + "name": SP_NAME_1, + "protectionDomainId": PD_ID_1, + "id": SP_ID_1 + } + ] + + @staticmethod + def get_device_exception_response(response_type): + if response_type == 'get_dev_without_SDS': + return "sds_name or sds_id is mandatory along with device_name. Please enter a valid value" + elif response_type == 'get_device_details_without_path': + return "sds_name or sds_id is mandatory along with current_pathname. Please enter a valid value" + elif response_type == 'get_device_exception': + return "Failed to get the device with error" + elif response_type == 'create_id_exception': + return "Addition of device is allowed using device_name only, device_id given." + elif response_type == 'empty_path': + return "Please enter a valid value for current_pathname" + elif response_type == 'empty_device_name': + return "Please enter a valid value for device_name." + elif response_type == 'empty_sds': + return "Please enter a valid value for " + elif response_type == 'empty_dev_id': + return "Please provide valid device_id value to identify a device" + elif response_type == 'space_in_name': + return "current_pathname or device_name is mandatory along with sds" + elif response_type == 'with_required_params': + return "Please specify a valid parameter combination to identify a device" + + @staticmethod + def get_device_exception_response1(response_type): + if response_type == 'modify_exception': + return "Modification of device attributes is currently not supported by Ansible modules." + elif response_type == 'delete_exception': + return f"Remove device '{MockDeviceApi.DEVICE_ID_1}' operation failed with error" + elif response_type == 'sds_exception': + return f"Unable to find the SDS with '{MockDeviceApi.SDS_NAME_1}'. Please enter a valid SDS name/id." + elif response_type == 'pd_exception': + return f"Unable to find the protection domain with " \ + f"'{MockDeviceApi.PD_NAME_1}'. Please enter a valid " \ + f"protection domain name/id" + elif response_type == 'sp_exception': + return f"Unable to find the storage pool with " \ + f"'{MockDeviceApi.SP_NAME_1}'. Please enter a valid " \ + f"storage pool name/id." + elif response_type == 'ap_exception': + return f"Unable to find the acceleration pool with " \ + f"'{MockDeviceApi.SP_NAME_1}'. Please enter a valid " \ + f"acceleration pool name/id." + elif response_type == 'add_exception': + return "Adding device ansible_device_1 operation failed with error" + elif response_type == 'add_dev_name_exception': + return "Please provide valid device_name value for adding a device" + elif response_type == 'add_dev_path_exception': + return "Current pathname of device is a mandatory parameter for adding a device. Please enter a valid value" + elif response_type == 'ext_type_exception': + return "Storage Pool ID/name or Acceleration Pool ID/name is mandatory along with external_acceleration_type." + elif response_type == 'add_without_pd': + return "Protection domain name/id is required to uniquely identify" diff --git a/ansible_collections/dellemc/powerflex/tests/unit/plugins/module_utils/mock_fail_json.py b/ansible_collections/dellemc/powerflex/tests/unit/plugins/module_utils/mock_fail_json.py new file mode 100644 index 000000000..8e20402c0 --- /dev/null +++ b/ansible_collections/dellemc/powerflex/tests/unit/plugins/module_utils/mock_fail_json.py @@ -0,0 +1,21 @@ +# Copyright: (c) 2023, Dell Technologies + +# Apache License version 2.0 (see MODULE-LICENSE or http://www.apache.org/licenses/LICENSE-2.0.txt) + +"""Mock fail json for PowerFlex Test modules""" + +from __future__ import (absolute_import, division, print_function) + +__metaclass__ = type + + +class FailJsonException(Exception): + def __init__(self, *args): + if args: + self.message = args[0] + else: + self.message = None + + +def fail_json(msg): + raise FailJsonException(msg) diff --git a/ansible_collections/dellemc/powerflex/tests/unit/plugins/module_utils/mock_fault_set_api.py b/ansible_collections/dellemc/powerflex/tests/unit/plugins/module_utils/mock_fault_set_api.py new file mode 100644 index 000000000..1072888a2 --- /dev/null +++ b/ansible_collections/dellemc/powerflex/tests/unit/plugins/module_utils/mock_fault_set_api.py @@ -0,0 +1,69 @@ +# Copyright: (c) 2024, Dell Technologies + +# Apache License version 2.0 (see MODULE-LICENSE or http://www.apache.org/licenses/LICENSE-2.0.txt) + +""" +Mock Api response for Unit tests of fault set module on Dell Technologies (Dell) PowerFlex +""" + +from __future__ import (absolute_import, division, print_function) + +__metaclass__ = type + + +class MockFaultSetApi: + FAULT_SET_COMMON_ARGS = { + "hostname": "**.***.**.***", + "protection_domain_name": None, + "protection_domain_id": None, + "fault_set_name": None, + "fault_set_id": None, + "fault_set_new_name": None, + "state": None + } + + FAULT_SET_GET_LIST = [ + { + "protectionDomainId": "7bd6457000000000", + "name": "fault_set_name_1", + "id": "fault_set_id_1", + "links": [] + } + ] + + PROTECTION_DOMAIN = { + "protectiondomain": [ + { + "id": "7bd6457000000000", + "name": "test_pd_1", + "protectionDomainState": "Active", + "overallIoNetworkThrottlingInKbps": 20480, + "rebalanceNetworkThrottlingInKbps": 10240, + "rebuildNetworkThrottlingInKbps": 10240, + "vtreeMigrationNetworkThrottlingInKbps": 10240, + "rfcacheEnabled": "false", + "rfcacheMaxIoSizeKb": 128, + "rfcacheOpertionalMode": "None", + "rfcachePageSizeKb": 64, + "storagePools": [ + { + "id": "8d1cba1700000000", + "name": "pool1" + } + ] + } + ] + } + + RESPONSE_EXEC_DICT = { + 'delete_fault_set_exception': "Removing Fault Set fault_set_id_1 failed with error", + 'rename_fault_set_exception': "Failed to rename the fault set instance", + 'create_fault_set_exception': "Create fault set test_fs_1 operation failed", + 'get_fault_set_exception': "Failed to get the Fault Set", + 'create_fault_set_wo_pd_exception': "Provide protection_domain_id/protection_domain_name with fault_set_name.", + 'create_fault_set_empty_name_exception': "Provide valid value for name for the creation/modification of the fault set." + } + + @staticmethod + def get_fault_set_exception_response(response_type): + return MockFaultSetApi.RESPONSE_EXEC_DICT.get(response_type, "") diff --git a/ansible_collections/dellemc/powerflex/tests/unit/plugins/module_utils/mock_info_api.py b/ansible_collections/dellemc/powerflex/tests/unit/plugins/module_utils/mock_info_api.py index e2ef01fe7..20de1c1c9 100644 --- a/ansible_collections/dellemc/powerflex/tests/unit/plugins/module_utils/mock_info_api.py +++ b/ansible_collections/dellemc/powerflex/tests/unit/plugins/module_utils/mock_info_api.py @@ -1,4 +1,4 @@ -# Copyright: (c) 2022, Dell Technologies +# Copyright: (c) 2024, Dell Technologies # Apache License version 2.0 (see MODULE-LICENSE or http://www.apache.org/licenses/LICENSE-2.0.txt) @@ -13,12 +13,16 @@ from ansible_collections.dellemc.powerflex.tests.unit.plugins.module_utils.mock_ import MockReplicationConsistencyGroupApi from ansible_collections.dellemc.powerflex.tests.unit.plugins.module_utils.mock_replication_pair_api \ import MockReplicationPairApi +from ansible_collections.dellemc.powerflex.tests.unit.plugins.module_utils.mock_snapshot_policy_api \ + import MockSnapshotPolicyApi +from ansible_collections.dellemc.powerflex.tests.unit.plugins.module_utils.mock_fault_set_api import MockFaultSetApi __metaclass__ = type class MockInfoApi: + MODULE_UTILS_PATH = "ansible_collections.dellemc.powerflex.plugins.module_utils.storage.dell.utils" INFO_COMMON_ARGS = { "hostname": "**.***.**.***", "gather_subset": [], @@ -219,6 +223,12 @@ class MockInfoApi: 'test_vol_id_1': MockVolumeApi.VOLUME_STATISTICS } + INFO_SNAPSHOT_POLICY_GET_LIST = MockSnapshotPolicyApi.SNAPSHOT_POLICY_GET_LIST + + INFO_SNAPSHOT_POLICY_STATISTICS = { + 'test_snap_pol_id_1': MockSnapshotPolicyApi.SNAPSHOT_POLICY_STATISTICS + } + INFO_STORAGE_POOL_GET_LIST = MockStoragePoolApi.STORAGE_POOL_GET_LIST INFO_STORAGE_POOL_STATISTICS = { @@ -228,13 +238,90 @@ class MockInfoApi: RCG_LIST = MockReplicationConsistencyGroupApi.get_rcg_details() PAIR_LIST = MockReplicationPairApi.get_pair_details() + INFO_GET_FAULT_SET_LIST = MockFaultSetApi.FAULT_SET_GET_LIST + + INFO_SDC_GET_LIST = [ + { + "id": "07335d3d00000006", + "name": "sdc_1" + }, + { + "id": "07335d3c00000005", + "name": "sdc_2" + }, + { + "id": "0733844a00000003", + "name": "sdc_3" + } + ] + + INFO_SDC_FILTER_LIST = [ + { + "id": "07335d3d00000006", + "name": "sdc_1" + } + ] + + INFO_SDS_GET_LIST = [ + { + "id": "8f3bb0cc00000002", + "name": "node0" + }, + { + "id": "8f3bb0ce00000000", + "name": "node1" + }, + { + "id": "8f3bb15300000001", + "name": "node22" + } + ] + INFO_GET_PD_LIST = [ + { + "id": "9300e90900000001", + "name": "domain2" + }, + { + "id": "9300c1f900000000", + "name": "domain1" + } + ] + INFO_GET_DEVICE_LIST = [ + { + "id": "b6efa59900000000", + "name": "device230" + }, + { + "id": "b6efa5fa00020000", + "name": "device_node0" + }, + { + "id": "b7f3a60900010000", + "name": "device22" + } + ] + + RESPONSE_EXEC_DICT = { + 'volume_get_details': "Get volumes list from powerflex array failed with error", + 'snapshot_policy_get_details': "Get snapshot policies list from powerflex array failed with error ", + 'sp_get_details': "Get storage pool list from powerflex array failed with error ", + 'rcg_get_details': "Get replication consistency group list from powerflex array failed with error ", + 'replication_pair_get_details': "Get replication pair list from powerflex array failed with error ", + 'fault_set_get_details': "Get fault set list from powerflex array failed with error", + 'sdc_get_details': "Get SDC list from powerflex array failed with error", + 'sds_get_details': "Get SDS list from powerflex array failed with error", + 'pd_get_details': "Get protection domain list from powerflex array failed with error", + 'device_get_details': "Get device list from powerflex array failed with error", + 'get_sds_details_filter_invalid': "Filter should have all keys: 'filter_key, filter_operator, filter_value'", + 'get_sds_details_filter_empty': "Filter keys: '['filter_key', 'filter_operator', 'filter_value']' cannot be None", + 'invalid_filter_operator_exception': "Given filter operator 'does_not_contain' is not supported.", + 'api_exception': "Get API details from Powerflex array failed with error", + 'system_exception': "Get array details from Powerflex array failed with error", + 'managed_device_get_error': "Get managed devices from PowerFlex Manager failed with error", + 'service_template_get_error': "Get service templates from PowerFlex Manager failed with error", + 'deployment_get_error': "Get deployments from PowerFlex Manager failed with error" + } + @staticmethod def get_exception_response(response_type): - if response_type == 'volume_get_details': - return "Get volumes list from powerflex array failed with error " - elif response_type == 'sp_get_details': - return "Get storage pool list from powerflex array failed with error " - elif response_type == 'rcg_get_details': - return "Get replication consistency group list from powerflex array failed with error " - elif response_type == 'replication_pair_get_details': - return "Get replication pair list from powerflex array failed with error " + return MockInfoApi.RESPONSE_EXEC_DICT.get(response_type, "") diff --git a/ansible_collections/dellemc/powerflex/tests/unit/plugins/module_utils/mock_protection_domain_api.py b/ansible_collections/dellemc/powerflex/tests/unit/plugins/module_utils/mock_protection_domain_api.py index 60452ecda..bab9a832c 100644 --- a/ansible_collections/dellemc/powerflex/tests/unit/plugins/module_utils/mock_protection_domain_api.py +++ b/ansible_collections/dellemc/powerflex/tests/unit/plugins/module_utils/mock_protection_domain_api.py @@ -1,4 +1,4 @@ -# Copyright: (c) 2022, Dell Technologies +# Copyright: (c) 2024, Dell Technologies # Apache License version 2.0 (see MODULE-LICENSE or http://www.apache.org/licenses/LICENSE-2.0.txt) @@ -15,54 +15,86 @@ class MockProtectionDomainApi: MODULE_PATH = 'ansible_collections.dellemc.powerflex.plugins.modules.protection_domain.PowerFlexProtectionDomain' MODULE_UTILS_PATH = 'ansible_collections.dellemc.powerflex.plugins.module_utils.storage.dell.utils' - PROTECTION_DOMAIN = { - "protectiondomain": [ - { - "id": "7bd6457000000000", - "name": "test_domain", - "protectionDomainState": "Active", - "overallIoNetworkThrottlingInKbps": 20480, - "rebalanceNetworkThrottlingInKbps": 10240, - "rebuildNetworkThrottlingInKbps": 10240, - "vtreeMigrationNetworkThrottlingInKbps": 10240, - "rfcacheEnabled": "false", - "rfcacheMaxIoSizeKb": 128, - "rfcacheOpertionalMode": "None", - "rfcachePageSizeKb": 64, - "storagePools": [ - { - "id": "8d1cba1700000000", - "name": "pool1" - } - ] - } - ] - } - STORAGE_POOL = { - "storagepool": [ - { - "protectionDomainId": "7bd6457000000000", - "rebuildEnabled": True, - "mediaType": "HDD", - "name": "pool1", - "id": "8d1cba1700000000" - } - ] + PD_COMMON_ARGS = { + 'hostname': '**.***.**.***', + 'protection_domain_id': None, + 'protection_domain_name': None, + 'protection_domain_new_name': None, + 'is_active': None, + 'network_limits': None, + 'rf_cache_limits': None, + 'state': 'present' } + PD_NAME = 'test_domain' + PD_NEW_NAME = 'test_domain_new' + PD_ID = '7bd6457000000000' - @staticmethod - def modify_pd_with_failed_msg(protection_domain_name): - return "Failed to update the rf cache limits of protection domain " + protection_domain_name + " with error " + PROTECTION_DOMAIN = [ + { + "id": "7bd6457000000000", + "name": "test_domain", + "protectionDomainState": "Active", + "overallIoNetworkThrottlingInKbps": 20480, + "rebalanceNetworkThrottlingInKbps": 10240, + "rebuildNetworkThrottlingInKbps": 10240, + "vtreeMigrationNetworkThrottlingInKbps": 10240, + "rfcacheEnabled": "false", + "rfcacheMaxIoSizeKb": 128, + "rfcacheOpertionalMode": "None", + "rfcachePageSizeKb": 64, + "storagePools": [ + { + "id": "8d1cba1700000000", + "name": "pool1" + } + ] + } + ] - @staticmethod - def delete_pd_failed_msg(protection_domain_id): - return "Delete protection domain '" + protection_domain_id + "' operation failed with error ''" + PROTECTION_DOMAIN_1 = [ + { + "id": "7bd6457000000000", + "name": "test_domain", + "protectionDomainState": "Inactive", + "overallIoNetworkThrottlingInKbps": 20480, + "rebalanceNetworkThrottlingInKbps": 10240, + "rebuildNetworkThrottlingInKbps": 10240, + "vtreeMigrationNetworkThrottlingInKbps": 10240, + "rfcacheEnabled": "false", + "rfcacheMaxIoSizeKb": 128, + "rfcacheOpertionalMode": "None", + "rfcachePageSizeKb": 64, + "storagePools": [ + { + "id": "8d1cba1700000000", + "name": "pool1" + } + ] + } + ] - @staticmethod - def rename_pd_failed_msg(protection_domain_name): - return "Failed to update the protection domain " + protection_domain_name + " with error " + STORAGE_POOL = [ + { + "protectionDomainId": "7bd6457000000000", + "rebuildEnabled": True, + "mediaType": "HDD", + "name": "pool1", + "id": "8d1cba1700000000" + } + ] @staticmethod - def version_pd_failed_msg(): - return "Getting PyPowerFlex SDK version, failed with Error The 'PyPowerFlex' distribution was " \ - "not found and is required by the application" + def get_failed_msgs(response_type): + error_msg = { + 'get_pd_failed_msg': "Failed to get the protection domain ", + 'empty_pd_msg': "Please provide the valid protection_domain_name", + 'overall_limit_msg': "Overall limit cannot be negative. Provide a valid value", + 'new_name_in_create': "protection_domain_new_name/protection_domain_id are not supported during creation of protection domain", + 'create_pd_exception': "operation failed with error", + 'rename_pd_exception': "Failed to update the protection domain 7bd6457000000000 with error", + 'modify_network_limits_exception': "Failed to update the network limits of protection domain", + 'rf_cache_limits_exception': "Failed to update the rf cache limits of protection domain", + 'delete_pd_exception': "Delete protection domain '7bd6457000000000' operation failed with error ''", + 'get_sp_exception': "Failed to get the storage pools present in protection domain", + } + return error_msg.get(response_type) diff --git a/ansible_collections/dellemc/powerflex/tests/unit/plugins/module_utils/mock_replication_consistency_group_api.py b/ansible_collections/dellemc/powerflex/tests/unit/plugins/module_utils/mock_replication_consistency_group_api.py index 6671fd875..0867024f5 100644 --- a/ansible_collections/dellemc/powerflex/tests/unit/plugins/module_utils/mock_replication_consistency_group_api.py +++ b/ansible_collections/dellemc/powerflex/tests/unit/plugins/module_utils/mock_replication_consistency_group_api.py @@ -23,6 +23,7 @@ class MockReplicationConsistencyGroupApi: "verifycert": None, "port": None, "protection_domain_name": None, "protection_domain_id": None}, "target_volume_access_mode": None, "is_consistent": None, + "rcg_state": None, "force": None, "state": None } RCG_ID = "aadc17d500000000" diff --git a/ansible_collections/dellemc/powerflex/tests/unit/plugins/module_utils/mock_replication_pair_api.py b/ansible_collections/dellemc/powerflex/tests/unit/plugins/module_utils/mock_replication_pair_api.py index f621db47e..5ac7010cf 100644 --- a/ansible_collections/dellemc/powerflex/tests/unit/plugins/module_utils/mock_replication_pair_api.py +++ b/ansible_collections/dellemc/powerflex/tests/unit/plugins/module_utils/mock_replication_pair_api.py @@ -1,4 +1,4 @@ -# Copyright: (c) 2023, Dell Technologies +# Copyright: (c) 2024, Dell Technologies # Apache License version 2.0 (see MODULE-LICENSE or http://www.apache.org/licenses/LICENSE-2.0.txt) @@ -48,3 +48,12 @@ class MockReplicationPairApi: def get_volume_details(): return [{"id": "0001", "name": "volume1"}] + + @staticmethod + def get_error_message(response_type): + error_msg = {"get_rcg_exception": "Failed to get the replication consistency group 12 with error ", + "get_rcg_id_name_error": "Specify either rcg_id or rcg_name to create replication pair", + "get_pause_error": "Specify either pair_id or pair_name to perform pause or resume of initial copy", + "get_pause_or_resume_error": "Specify a valid pair_name or pair_id to perform pause or resume", + "get_volume_exception": "Failed to retrieve volume"} + return error_msg[response_type] diff --git a/ansible_collections/dellemc/powerflex/tests/unit/plugins/module_utils/mock_sdc_api.py b/ansible_collections/dellemc/powerflex/tests/unit/plugins/module_utils/mock_sdc_api.py new file mode 100644 index 000000000..ba65bc67f --- /dev/null +++ b/ansible_collections/dellemc/powerflex/tests/unit/plugins/module_utils/mock_sdc_api.py @@ -0,0 +1,64 @@ +# Copyright: (c) 2024, Dell Technologies + +# Apache License version 2.0 (see MODULE-LICENSE or http://www.apache.org/licenses/LICENSE-2.0.txt) + +""" +Mock Api response for Unit tests of SDC module on Dell Technologies (Dell) PowerFlex +""" + +from __future__ import (absolute_import, division, print_function) + +__metaclass__ = type + + +class MockSdcApi: + MODULE_UTILS_PATH = "ansible_collections.dellemc.powerflex.plugins.module_utils.storage.dell.utils" + COMMON_ARGS = { + "sdc_id": None, + "sdc_ip": None, + "sdc_name": None, "sdc_new_name": None, + "performance_profile": None, + "state": None + } + SDC_ID = "07335d3d00000006" + + @staticmethod + def get_sdc_details(): + return [{ + "id": "07335d3d00000006", + "installedSoftwareVersionInfo": "R3_6.0.0", + "kernelBuildNumber": None, + "kernelVersion": "3.10.0", + "mapped_volumes": [], + "mdmConnectionState": "Disconnected", + "memoryAllocationFailure": None, + "name": "LGLAP203", + "osType": "Linux", + "peerMdmId": None, + "perfProfile": "HighPerformance", + "sdcApproved": True, + "sdcApprovedIps": None, + "sdcGuid": "F8ECB844-23B8-4629-92BB-B6E49A1744CB", + "sdcIp": "N/A", + "sdcIps": None, + "sdcType": "AppSdc", + "sdrId": None, + "socketAllocationFailure": None, + "softwareVersionInfo": "R3_6.0.0", + "systemId": "4a54a8ba6df0690f", + "versionInfo": "R3_6.0.0" + }] + + RESPONSE_EXEC_DICT = { + 'get_sdc_details_empty_sdc_id_exception': "Please provide valid sdc_id", + 'get_sdc_details_with_exception': "Failed to get the SDC 07335d3d00000006 with error", + 'get_sdc_details_mapped_volumes_with_exception': "Failed to get the volumes mapped to SDC", + 'modify_sdc_throws_exception': "Modifying performance profile of SDC 07335d3d00000006 failed with error", + 'rename_sdc_empty_new_name_exception': "Provide valid SDC name to rename to.", + 'rename_sdc_throws_exception': "Failed to rename SDC", + 'remove_sdc_throws_exception': "Removing SDC 07335d3d00000006 failed with error" + } + + @staticmethod + def get_sdc_exception_response(response_type): + return MockSdcApi.RESPONSE_EXEC_DICT.get(response_type, "") diff --git a/ansible_collections/dellemc/powerflex/tests/unit/plugins/module_utils/mock_sds_api.py b/ansible_collections/dellemc/powerflex/tests/unit/plugins/module_utils/mock_sds_api.py new file mode 100644 index 000000000..60b5f95e9 --- /dev/null +++ b/ansible_collections/dellemc/powerflex/tests/unit/plugins/module_utils/mock_sds_api.py @@ -0,0 +1,147 @@ +# Copyright: (c) 2024, Dell Technologies + +# Apache License version 2.0 (see MODULE-LICENSE or http://www.apache.org/licenses/LICENSE-2.0.txt) + +""" +Mock Api response for Unit tests of SDS module on Dell Technologies (Dell) PowerFlex +""" + +from __future__ import (absolute_import, division, print_function) + +__metaclass__ = type + + +class MockSDSApi: + SDS_COMMON_ARGS = { + "hostname": "**.***.**.***", + "sds_name": "test_node0", + "sds_id": None, + "sds_new_name": None, + "sds_ip_list": None, + "sds_ip_state": None, + "rfcache_enabled": None, + "rmcache_enabled": None, + "rmcache_size": None, + "performance_profile": None, + "protection_domain_name": None, + "protection_domain_id": None, + "fault_set_name": None, + "fault_set_id": None, + "fault_set_new_name": None, + "state": None + } + + SDS_GET_LIST = [ + { + "authenticationError": "None", + "certificateInfo": None, + "configuredDrlMode": "Volatile", + "drlMode": "Volatile", + "faultSetId": "test_id_1", + "fglMetadataCacheSize": 0, + "fglMetadataCacheState": "Disabled", + "fglNumConcurrentWrites": 1000, + "id": "8f3bb0cc00000002", + "ipList": [ + { + "ip": "10.47.xxx.xxx", + "role": "all" + }, + { + "ip": "10.46.xxx.xxx", + "role": "sdcOnly" + } + ], + "lastUpgradeTime": 0, + "links": [], + "maintenanceState": "NoMaintenance", + "maintenanceType": "NoMaintenance", + "mdmConnectionState": "Connected", + "membershipState": "Joined", + "name": "test_node0", + "numOfIoBuffers": None, + "numRestarts": 2, + "onVmWare": True, + "perfProfile": "HighPerformance", + "port": 7072, + "protectionDomainId": "9300c1f900000000", + "protectionDomainName": "test_domain", + "raidControllers": None, + "rfcacheEnabled": True, + "rfcacheErrorApiVersionMismatch": False, + "rfcacheErrorDeviceDoesNotExist": False, + "rfcacheErrorInconsistentCacheConfiguration": False, + "rfcacheErrorInconsistentSourceConfiguration": False, + "rfcacheErrorInvalidDriverPath": False, + "rfcacheErrorLowResources": False, + "rmcacheEnabled": True, + "rmcacheFrozen": False, + "rmcacheMemoryAllocationState": "AllocationPending", + "rmcacheSizeInKb": 131072, + "rmcacheSizeInMb": 128, + "sdsConfigurationFailure": None, + "sdsDecoupled": None, + "sdsReceiveBufferAllocationFailures": None, + "sdsState": "Normal", + "softwareVersionInfo": "R3_6.0.0" + } + ] + + PROTECTION_DOMAIN = { + "protectiondomain": [ + { + "id": "7bd6457000000000", + "name": "test_domain", + "protectionDomainState": "Active", + "overallIoNetworkThrottlingInKbps": 20480, + "rebalanceNetworkThrottlingInKbps": 10240, + "rebuildNetworkThrottlingInKbps": 10240, + "vtreeMigrationNetworkThrottlingInKbps": 10240, + "rfcacheEnabled": "false", + "rfcacheMaxIoSizeKb": 128, + "rfcacheOpertionalMode": "None", + "rfcachePageSizeKb": 64, + "storagePools": [ + { + "id": "8d1cba1700000000", + "name": "pool1" + } + ] + } + ] + } + + FAULT_SET_GET_LIST = [ + { + "protectionDomainId": "test_domain", + "name": "fault_set_name", + "id": "fault_set_id", + "links": [] + } + ] + + RESPONSE_EXEC_DICT = { + "delete_sds_exception": "Delete SDS '8f3bb0cc00000002' operation failed with error", + "rename_sds_exception": "Failed to update the SDS", + "create_sds_exception": "Create SDS test_node0 operation failed with error", + "get_sds_exception": "Failed to get the SDS", + "rmcache_size_exception": "RM cache size can be set only when RM cache is enabled", + "create_sds_wo_sds_name": "Please provide valid sds_name value for creation of SDS.", + "create_sds_wo_pd": "Protection Domain is a mandatory parameter", + "create_sds_wo_sds_ip_list": "Please provide valid sds_ip_list values for " + + "creation of SDS.", + "create_sds_incorrect_sds_ip_state": "Incorrect IP state given for creation of SDS.", + "create_sds_sds_id": "Creation of SDS is allowed using sds_name " + + "only, sds_id given.", + "create_sds_sds_new_name": "sds_new_name parameter is not supported " + + "during creation of a SDS. Try renaming the " + + "SDS after the creation.", + "rename_sds_empty_exception": "Provide valid value for name for the creation/modification of the SDS.", + "add_ip_exception": "Add IP to SDS '8f3bb0cc00000002' operation failed with error ", + "remove_ip_exception": "Remove IP from SDS '8f3bb0cc00000002' operation failed with error ", + "set_ip_role_exception": "Update role of IP for SDS '8f3bb0cc00000002' operation failed" + } + + @staticmethod + def get_sds_exception_response(response_type): + return MockSDSApi.RESPONSE_EXEC_DICT.get(response_type, "") diff --git a/ansible_collections/dellemc/powerflex/tests/unit/plugins/module_utils/mock_snapshot_policy_api.py b/ansible_collections/dellemc/powerflex/tests/unit/plugins/module_utils/mock_snapshot_policy_api.py new file mode 100644 index 000000000..35dabb9fd --- /dev/null +++ b/ansible_collections/dellemc/powerflex/tests/unit/plugins/module_utils/mock_snapshot_policy_api.py @@ -0,0 +1,186 @@ +# Copyright: (c) 2023, Dell Technologies + +# Apache License version 2.0 (see MODULE-LICENSE or http://www.apache.org/licenses/LICENSE-2.0.txt) + +""" +Mock Api response for Unit tests of snapshot policy module on Dell Technologies (Dell) PowerFlex +""" + +from __future__ import (absolute_import, division, print_function) + +__metaclass__ = type + + +class MockSnapshotPolicyApi: + SNAPSHOT_POLICY_COMMON_ARGS = { + "hostname": "**.***.**.***", + "snapshot_policy_name": None, + "snapshot_policy_id": None, + "new_name": None, + "access_mode": None, + "secure_snapshots": None, + "auto_snapshot_creation_cadence": { + "time": None, + "unit": "Minute"}, + "num_of_retained_snapshots_per_level": None, + "source_volume": None, + "source_volume_state": None, + "pause": None, + "state": None + } + + SNAPSHOT_POLICY_GET_LIST = [ + { + "autoSnapshotCreationCadenceInMin": 120, + "id": "15ae842500000004", + "lastAutoSnapshotCreationFailureReason": "NR", + "lastAutoSnapshotFailureInFirstLevel": False, + "maxVTreeAutoSnapshots": 40, + "name": "Ansible_snap_policy_1", + "nextAutoSnapshotCreationTime": 1683617581, + "numOfAutoSnapshots": 0, + "numOfCreationFailures": 0, + "numOfExpiredButLockedSnapshots": 0, + "numOfLockedSnapshots": 0, + "numOfRetainedSnapshotsPerLevel": [ + 40 + ], + "numOfSourceVolumes": 0, + "secureSnapshots": False, + "snapshotAccessMode": "ReadWrite", + "snapshotPolicyState": "Active", + "systemId": "0e7a082862fedf0f", + "timeOfLastAutoSnapshot": 0, + "timeOfLastAutoSnapshotCreationFailure": 0 + } + ] + + SNAPSHOT_POLICY_2_GET_LIST = [ + { + "autoSnapshotCreationCadenceInMin": 120, + "id": "15ae842500000005", + "lastAutoSnapshotCreationFailureReason": "NR", + "lastAutoSnapshotFailureInFirstLevel": False, + "maxVTreeAutoSnapshots": 40, + "name": "testing_2", + "nextAutoSnapshotCreationTime": 1683617581, + "numOfAutoSnapshots": 0, + "numOfCreationFailures": 0, + "numOfExpiredButLockedSnapshots": 0, + "numOfLockedSnapshots": 0, + "numOfRetainedSnapshotsPerLevel": [ + 40 + ], + "numOfSourceVolumes": 1, + "secureSnapshots": False, + "snapshotAccessMode": "ReadWrite", + "snapshotPolicyState": "Paused", + "systemId": "0e7a082862fedf0f", + "timeOfLastAutoSnapshot": 0, + "timeOfLastAutoSnapshotCreationFailure": 0 + } + ] + + VOLUME_GET_LIST = [ + { + 'storagePoolId': 'test_pool_id_1', + 'dataLayout': 'MediumGranularity', + 'vtreeId': 'vtree_id_1', + 'sizeInKb': 8388608, + 'snplIdOfAutoSnapshot': None, + 'volumeType': 'ThinProvisioned', + 'consistencyGroupId': None, + 'ancestorVolumeId': None, + 'notGenuineSnapshot': False, + 'accessModeLimit': 'ReadWrite', + 'secureSnapshotExpTime': 0, + 'useRmcache': False, + 'managedBy': 'ScaleIO', + 'lockedAutoSnapshot': False, + 'lockedAutoSnapshotMarkedForRemoval': False, + 'autoSnapshotGroupId': None, + 'compressionMethod': 'Invalid', + 'pairIds': None, + 'timeStampIsAccurate': False, + 'mappedSdcInfo': None, + 'originalExpiryTime': 0, + 'retentionLevels': [ + ], + 'snplIdOfSourceVolume': None, + 'volumeReplicationState': 'UnmarkedForReplication', + 'replicationJournalVolume': False, + 'replicationTimeStamp': 0, + 'creationTime': 1655878090, + 'name': 'source_volume_name', + 'id': 'source_volume_id' + } + ] + + VOLUME_2_GET_LIST = [ + { + 'storagePoolId': 'test_pool_id_1', + 'dataLayout': 'MediumGranularity', + 'vtreeId': 'vtree_id_1', + 'sizeInKb': 8388608, + 'snplIdOfAutoSnapshot': None, + 'volumeType': 'ThinProvisioned', + 'consistencyGroupId': None, + 'ancestorVolumeId': None, + 'notGenuineSnapshot': False, + 'accessModeLimit': 'ReadWrite', + 'secureSnapshotExpTime': 0, + 'useRmcache': False, + 'managedBy': 'ScaleIO', + 'lockedAutoSnapshot': False, + 'lockedAutoSnapshotMarkedForRemoval': False, + 'autoSnapshotGroupId': None, + 'compressionMethod': 'Invalid', + 'pairIds': None, + 'timeStampIsAccurate': False, + 'mappedSdcInfo': None, + 'originalExpiryTime': 0, + 'retentionLevels': [ + ], + 'snplIdOfSourceVolume': "15ae842500000005", + 'volumeReplicationState': 'UnmarkedForReplication', + 'replicationJournalVolume': False, + 'replicationTimeStamp': 0, + 'creationTime': 1655878090, + 'name': 'source_volume_name_2', + 'id': 'source_volume_id_2' + } + ] + + SNAPSHOT_POLICY_STATISTICS = { + "autoSnapshotVolIds": [], + "expiredButLockedSnapshotsIds": [], + "numOfAutoSnapshots": 0, + "numOfExpiredButLockedSnapshots": 0, + "numOfSrcVols": 0, + "srcVolIds": [] + } + + @staticmethod + def get_snapshot_policy_exception_response(response_type): + if response_type == 'get_vol_details_exception': + return "Failed to get the volume source_volume_id_2 with error " + elif response_type == 'get_snapshot_policy_details_exception': + return "Failed to get the snapshot policy with error " + elif response_type == 'create_exception': + return "Creation of snapshot policy failed with error " + elif response_type == 'create_id_exception': + return "Creation of snapshot policy is allowed using snapshot_policy_name only, snapshot_policy_id given." + elif response_type == 'delete_exception': + return "Deletion of snapshot policy 15ae842500000004 failed with error " + elif response_type == 'modify_exception': + return "Failed to update the snapshot policy 15ae842500000004 with error " + elif response_type == 'source_volume_exception': + return "Failed to manage the source volume source_volume_id with error " + elif response_type == 'add_source_volume_wo_vol': + return "Either id or name of source volume needs to be passed with state of source volume" + elif response_type == 'add_source_volume_vol_id_name': + return "id and name of source volume are mutually exclusive" + elif response_type == 'add_non_existing_source_volume': + return "Failed to get the volume non_existing_source_volume_name with error Volume with identifier non_existing_source_volume_name not found" + elif response_type == 'pause_exception': + return "Failed to pause/resume 15ae842500000004 with error" diff --git a/ansible_collections/dellemc/powerflex/tests/unit/plugins/module_utils/mock_storagepool_api.py b/ansible_collections/dellemc/powerflex/tests/unit/plugins/module_utils/mock_storagepool_api.py index 0246b9dd4..87af1d6eb 100644 --- a/ansible_collections/dellemc/powerflex/tests/unit/plugins/module_utils/mock_storagepool_api.py +++ b/ansible_collections/dellemc/powerflex/tests/unit/plugins/module_utils/mock_storagepool_api.py @@ -1,4 +1,4 @@ -# Copyright: (c) 2022, Dell Technologies +# Copyright: (c) 2024, Dell Technologies # Apache License version 2.0 (see MODULE-LICENSE or http://www.apache.org/licenses/LICENSE-2.0.txt) @@ -28,6 +28,7 @@ class MockStoragePoolApi: STORAGE_POOL_GET_LIST = [ { 'protectionDomainId': '4eeb304600000000', + 'protectionDomainName': 'test_pd', 'rebuildEnabled': True, 'dataLayout': 'MediumGranularity', 'persistentChecksumState': 'Protected', @@ -95,6 +96,149 @@ class MockStoragePoolApi: } ] + STORAGE_POOL_GET_MULTI_LIST = [ + { + 'protectionDomainId': '4eeb304600000000', + 'protectionDomainName': 'test_pd', + 'rebuildEnabled': True, + 'dataLayout': 'MediumGranularity', + 'persistentChecksumState': 'Protected', + 'addressSpaceUsage': 'Normal', + 'externalAccelerationType': 'None', + 'rebalanceEnabled': True, + 'sparePercentage': 10, + 'rmcacheWriteHandlingMode': 'Cached', + 'checksumEnabled': False, + 'useRfcache': False, + 'compressionMethod': 'Invalid', + 'fragmentationEnabled': True, + 'numOfParallelRebuildRebalanceJobsPerDevice': 2, + 'capacityAlertHighThreshold': 80, + 'capacityAlertCriticalThreshold': 90, + 'capacityUsageState': 'Normal', + 'capacityUsageType': 'NetCapacity', + 'addressSpaceUsageType': 'DeviceCapacityLimit', + 'bgScannerCompareErrorAction': 'ReportAndFix', + 'bgScannerReadErrorAction': 'ReportAndFix', + 'fglExtraCapacity': None, + 'fglOverProvisioningFactor': None, + 'fglWriteAtomicitySize': None, + 'fglMaxCompressionRatio': None, + 'fglPerfProfile': None, + 'replicationCapacityMaxRatio': 0, + 'persistentChecksumEnabled': True, + 'persistentChecksumBuilderLimitKb': 3072, + 'persistentChecksumValidateOnRead': False, + 'useRmcache': False, + 'fglAccpId': None, + 'rebuildIoPriorityPolicy': 'limitNumOfConcurrentIos', + 'rebalanceIoPriorityPolicy': 'favorAppIos', + 'vtreeMigrationIoPriorityPolicy': 'favorAppIos', + 'protectedMaintenanceModeIoPriorityPolicy': 'limitNumOfConcurrentIos', + 'rebuildIoPriorityNumOfConcurrentIosPerDevice': 1, + 'rebalanceIoPriorityNumOfConcurrentIosPerDevice': 1, + 'vtreeMigrationIoPriorityNumOfConcurrentIosPerDevice': 1, + 'protectedMaintenanceModeIoPriorityNumOfConcurrentIosPerDevice': 1, + 'rebuildIoPriorityBwLimitPerDeviceInKbps': 10240, + 'rebalanceIoPriorityBwLimitPerDeviceInKbps': 10240, + 'vtreeMigrationIoPriorityBwLimitPerDeviceInKbps': 10240, + 'protectedMaintenanceModeIoPriorityBwLimitPerDeviceInKbps': 10240, + 'rebuildIoPriorityAppIopsPerDeviceThreshold': None, + 'rebalanceIoPriorityAppIopsPerDeviceThreshold': None, + 'vtreeMigrationIoPriorityAppIopsPerDeviceThreshold': None, + 'protectedMaintenanceModeIoPriorityAppIopsPerDeviceThreshold': None, + 'rebuildIoPriorityAppBwPerDeviceThresholdInKbps': None, + 'rebalanceIoPriorityAppBwPerDeviceThresholdInKbps': None, + 'vtreeMigrationIoPriorityAppBwPerDeviceThresholdInKbps': None, + 'protectedMaintenanceModeIoPriorityAppBwPerDeviceThresholdInKbps': None, + 'rebuildIoPriorityQuietPeriodInMsec': None, + 'rebalanceIoPriorityQuietPeriodInMsec': None, + 'vtreeMigrationIoPriorityQuietPeriodInMsec': None, + 'protectedMaintenanceModeIoPriorityQuietPeriodInMsec': None, + 'zeroPaddingEnabled': True, + 'backgroundScannerMode': 'DataComparison', + 'backgroundScannerBWLimitKBps': 3072, + 'fglMetadataSizeXx100': None, + 'fglNvdimmWriteCacheSizeInMb': None, + 'fglNvdimmMetadataAmortizationX100': None, + 'mediaType': 'HDD', + 'name': 'test_pool', + 'id': 'test_pool_id_1' + }, + { + 'protectionDomainId': '4eeb304600000002', + 'protectionDomainName': 'test_pd_1', + 'rebuildEnabled': True, + 'dataLayout': 'MediumGranularity', + 'persistentChecksumState': 'Protected', + 'addressSpaceUsage': 'Normal', + 'externalAccelerationType': 'None', + 'rebalanceEnabled': True, + 'sparePercentage': 10, + 'rmcacheWriteHandlingMode': 'Cached', + 'checksumEnabled': False, + 'useRfcache': False, + 'compressionMethod': 'Invalid', + 'fragmentationEnabled': True, + 'numOfParallelRebuildRebalanceJobsPerDevice': 2, + 'capacityAlertHighThreshold': 80, + 'capacityAlertCriticalThreshold': 90, + 'capacityUsageState': 'Normal', + 'capacityUsageType': 'NetCapacity', + 'addressSpaceUsageType': 'DeviceCapacityLimit', + 'bgScannerCompareErrorAction': 'ReportAndFix', + 'bgScannerReadErrorAction': 'ReportAndFix', + 'fglExtraCapacity': None, + 'fglOverProvisioningFactor': None, + 'fglWriteAtomicitySize': None, + 'fglMaxCompressionRatio': None, + 'fglPerfProfile': None, + 'replicationCapacityMaxRatio': 0, + 'persistentChecksumEnabled': True, + 'persistentChecksumBuilderLimitKb': 3072, + 'persistentChecksumValidateOnRead': False, + 'useRmcache': False, + 'fglAccpId': None, + 'rebuildIoPriorityPolicy': 'limitNumOfConcurrentIos', + 'rebalanceIoPriorityPolicy': 'favorAppIos', + 'vtreeMigrationIoPriorityPolicy': 'favorAppIos', + 'protectedMaintenanceModeIoPriorityPolicy': 'limitNumOfConcurrentIos', + 'rebuildIoPriorityNumOfConcurrentIosPerDevice': 1, + 'rebalanceIoPriorityNumOfConcurrentIosPerDevice': 1, + 'vtreeMigrationIoPriorityNumOfConcurrentIosPerDevice': 1, + 'protectedMaintenanceModeIoPriorityNumOfConcurrentIosPerDevice': 1, + 'rebuildIoPriorityBwLimitPerDeviceInKbps': 10240, + 'rebalanceIoPriorityBwLimitPerDeviceInKbps': 10240, + 'vtreeMigrationIoPriorityBwLimitPerDeviceInKbps': 10240, + 'protectedMaintenanceModeIoPriorityBwLimitPerDeviceInKbps': 10240, + 'rebuildIoPriorityAppIopsPerDeviceThreshold': None, + 'rebalanceIoPriorityAppIopsPerDeviceThreshold': None, + 'vtreeMigrationIoPriorityAppIopsPerDeviceThreshold': None, + 'protectedMaintenanceModeIoPriorityAppIopsPerDeviceThreshold': None, + 'rebuildIoPriorityAppBwPerDeviceThresholdInKbps': None, + 'rebalanceIoPriorityAppBwPerDeviceThresholdInKbps': None, + 'vtreeMigrationIoPriorityAppBwPerDeviceThresholdInKbps': None, + 'protectedMaintenanceModeIoPriorityAppBwPerDeviceThresholdInKbps': None, + 'rebuildIoPriorityQuietPeriodInMsec': None, + 'rebalanceIoPriorityQuietPeriodInMsec': None, + 'vtreeMigrationIoPriorityQuietPeriodInMsec': None, + 'protectedMaintenanceModeIoPriorityQuietPeriodInMsec': None, + 'zeroPaddingEnabled': True, + 'backgroundScannerMode': 'DataComparison', + 'backgroundScannerBWLimitKBps': 3072, + 'fglMetadataSizeXx100': None, + 'fglNvdimmWriteCacheSizeInMb': None, + 'fglNvdimmMetadataAmortizationX100': None, + 'mediaType': 'HDD', + 'name': 'test_pool', + 'id': 'test_pool_id_2' + } + ] + + PROTECTION_DETAILS = [{"pd_id": "4eeb304600000000", "pd_name": "test_pd"}] + + PROTECTION_DETAILS_1 = [{"id": "4eeb304600000001", "name": "test_pd_name"}] + STORAGE_POOL_STATISTICS = { 'backgroundScanFixedReadErrorCount': 0, 'pendingMovingOutBckRebuildJobs': 0, @@ -461,7 +605,23 @@ class MockStoragePoolApi: 'numOfIncomingVtreeMigrations': 1 } + RESPONSE_EXEC_DICT = { + "get_details": "Failed to get the storage pool test_pool with error ", + "invalid_pd_id": "Entered protection domain id does not match with the storage pool's protection domain id.", + "get_pd_exception": "Failed to get the protection domain 4eeb304600000001 with error", + "create_storage_pool": "Failed to create the storage pool", + "rename_storage_pool": "Failed to update the storage pool", + "create_pool_id": "storage_pool_name is missing & name required to create a storage pool", + "get_pd_non_exist": "Unable to find the protection domain", + "get_multi_details": "More than one storage pool found", + "create_wo_pd": "Please provide protection domain details", + "create_transitional": "TRANSITIONAL media type is not supported during creation.", + "create_pool_name_empty": "Empty or white spaced string provided in storage_pool_name.", + "create_pool_new_name": "storage_pool_new_name is passed during creation.", + "rename_storage_pool_empty": "Empty/White spaced name is not allowed during renaming of a storage pool.", + "delete_storage_pool": "Deleting storage pool is not supported through ansible module." + } + @staticmethod def get_exception_response(response_type): - if response_type == 'get_details': - return "Failed to get the storage pool test_pool with error " + return MockStoragePoolApi.RESPONSE_EXEC_DICT.get(response_type, "") diff --git a/ansible_collections/dellemc/powerflex/tests/unit/plugins/module_utils/mock_volume_api.py b/ansible_collections/dellemc/powerflex/tests/unit/plugins/module_utils/mock_volume_api.py index b05cc84d3..8c264940f 100644 --- a/ansible_collections/dellemc/powerflex/tests/unit/plugins/module_utils/mock_volume_api.py +++ b/ansible_collections/dellemc/powerflex/tests/unit/plugins/module_utils/mock_volume_api.py @@ -1,4 +1,4 @@ -# Copyright: (c) 2022, Dell Technologies +# Copyright: (c) 2024, Dell Technologies # Apache License version 2.0 (see MODULE-LICENSE or http://www.apache.org/licenses/LICENSE-2.0.txt) @@ -61,7 +61,9 @@ class MockVolumeApi: 'originalExpiryTime': 0, 'retentionLevels': [ ], - 'snplIdOfSourceVolume': None, + 'snplIdOfSourceVolume': "snplIdOfSourceVolume", + 'snapshotPolicyId': 'snapshotPolicyId', + 'snapshotPolicyName': 'snapshotPolicyName', 'volumeReplicationState': 'UnmarkedForReplication', 'replicationJournalVolume': False, 'replicationTimeStamp': 0, @@ -542,7 +544,61 @@ class MockVolumeApi: 'numOfIncomingVtreeMigrations': 0 } + SDC_RESPONSE = [ + { + 'id': 'abdfe71b00030001', + } + ] + + SDC_RESPONSE_EMPTY = [] + + GET_STORAGE_POOL = { + 'dataLayout': 'MediumGranularity' + } + + GET_STORAGE_POOL_FINE = { + 'dataLayout': 'FineGranularity', + } + + PROTECTION_DETAILS = [{"pd_id": "pd_id", "pd_name": "pd_name"}] + + GET_ID = {"id": "e0d8f6c900000000"} + PROTECTION_DETAILS_MULTI = [ + {"pd_id": "pd_id", "pd_name": "pd_name"}, + {"pd_id": "pd_id", "pd_name": "pd_name"}, + ] + + RESPONSE_EXEC_DICT = { + 'get_details': "Failed to get the volume test_id_1 with error ", + 'get_sds': "Failed to get the SDC sdc_name with error ", + 'create_vol_name': "Please provide valid volume name.", + 'create_vol_size': "Size is a mandatory parameter", + 'create_vol_ctype': "compression_type for volume can only be", + 'create_vol_exc': "Create volume vol_name operation failed with error", + 'modify_access': "Modify access mode of SDC operation failed", + 'modify_limits': "Modify bandwidth/iops limits of SDC", + 'delete_volume': "Delete volume vol_id operation failed with", + 'val_params_err1': "sdc_id, sdc_ip and sdc_name are mutually exclusive", + 'val_params_err2': "cap_unit can be specified along with size only", + 'val_params_err3': "To remove/detach snapshot policy, please provide", + 'val_params_err4': "delete_snapshots can be specified only when the state", + 'modify_volume_exp': "Failed to update the volume", + 'to_modify_err1': "To remove/detach a snapshot policy, provide the ", + 'snap_pol_id_err': "Entered snapshot policy id does not ", + 'snap_pol_name_err': "Entered snapshot policy name does not ", + 'pd_id_err': "Entered protection domain id does not ", + 'pool_id_err': "Entered storage pool id does ", + 'pd_name_err': "Entered protection domain name does ", + 'pool_name_err': "Entered storage pool name does ", + 'get_pd_exception': "Failed to get the protection domain ", + 'get_sp_exception': "Failed to get the snapshot policy ", + 'get_spool_error1': "More than one storage pool found with", + 'get_spool_error2': "Failed to get the storage pool", + 'map_vol_exception': "Mapping volume name to SDC sdc_id1 failed with error", + 'unmap': "Unmap SDC sdc_id from volume vol_id failed with error", + 'perform_error1': "vol_new_name parameter is not supported during creation of a volume" + } + @staticmethod def get_exception_response(response_type): - if response_type == 'get_details': - return "Failed to get the volume test_id_1 with error " + return MockVolumeApi.RESPONSE_EXEC_DICT.get(response_type, "") diff --git a/ansible_collections/dellemc/powerflex/tests/unit/plugins/modules/test_device.py b/ansible_collections/dellemc/powerflex/tests/unit/plugins/modules/test_device.py new file mode 100644 index 000000000..c18204339 --- /dev/null +++ b/ansible_collections/dellemc/powerflex/tests/unit/plugins/modules/test_device.py @@ -0,0 +1,471 @@ +# Copyright: (c) 2023, Dell Technologies + +# Apache License version 2.0 (see MODULE-LICENSE or http://www.apache.org/licenses/LICENSE-2.0.txt) + +"""Unit Tests for Device module on PowerFlex""" + +from __future__ import (absolute_import, division, print_function) + +__metaclass__ = type + +import pytest +from mock.mock import MagicMock +from ansible_collections.dellemc.powerflex.tests.unit.plugins.module_utils.mock_device_api import MockDeviceApi +from ansible_collections.dellemc.powerflex.tests.unit.plugins.module_utils.mock_api_exception \ + import MockApiException +from ansible_collections.dellemc.powerflex.plugins.module_utils.storage.dell \ + import utils +from ansible_collections.dellemc.powerflex.tests.unit.plugins.module_utils.mock_fail_json \ + import FailJsonException, fail_json + +utils.get_logger = MagicMock() +utils.get_powerflex_gateway_host_connection = MagicMock() +utils.PowerFlexClient = MagicMock() + +from ansible.module_utils import basic +basic.AnsibleModule = MagicMock() +from ansible_collections.dellemc.powerflex.plugins.modules.device import PowerFlexDevice + + +class TestPowerflexDevice(): + + get_module_args = MockDeviceApi.DEVICE_COMMON_ARGS + + @pytest.fixture + def device_module_mock(self, mocker): + mocker.patch( + MockDeviceApi.MODULE_UTILS_PATH + '.PowerFlexClient', + new=MockApiException) + device_module_mock = PowerFlexDevice() + device_module_mock.module.fail_json = fail_json + return device_module_mock + + def capture_fail_json_call(self, error_msg, device_module_mock): + try: + device_module_mock.perform_module_operation() + except FailJsonException as fj_object: + assert error_msg in fj_object.message + + def test_get_device_detail_using_dev_name_sds_id(self, device_module_mock): + self.get_module_args.update({ + "device_name": MockDeviceApi.DEVICE_NAME_1, + "sds_id": MockDeviceApi.SDS_ID_1, + "state": "present" + }) + device_module_mock.module.params = self.get_module_args + device_module_mock.powerflex_conn.device.get = MagicMock( + return_value=MockDeviceApi.DEVICE_GET_LIST) + device_module_mock.perform_module_operation() + device_module_mock.powerflex_conn.device.get.assert_called() + + def test_get_device_detail_using_path_sds_name(self, device_module_mock): + self.get_module_args.update({ + "current_pathname": MockDeviceApi.PATH_1, + "sds_name": MockDeviceApi.SDS_NAME_1, + "state": "present" + }) + device_module_mock.module.params = self.get_module_args + device_module_mock.powerflex_conn.sds.get = MagicMock( + return_value=MockDeviceApi.SDS_DETAILS_1) + device_module_mock.powerflex_conn.device.get = MagicMock( + return_value=MockDeviceApi.DEVICE_GET_LIST) + device_module_mock.perform_module_operation() + device_module_mock.powerflex_conn.device.get.assert_called() + + def test_get_device_detail_using_dev_id(self, device_module_mock): + self.get_module_args.update({ + "device_id": MockDeviceApi.DEVICE_ID_1, + "state": "present" + }) + device_module_mock.module.params = self.get_module_args + device_module_mock.powerflex_conn.device.get = MagicMock( + return_value=MockDeviceApi.DEVICE_GET_LIST) + device_module_mock.perform_module_operation() + device_module_mock.powerflex_conn.device.get.assert_called() + + def test_get_device_without_sds(self, device_module_mock): + self.get_module_args.update({ + "device_name": MockDeviceApi.DEVICE_NAME_1, + "state": "present" + }) + device_module_mock.module.params = self.get_module_args + device_module_mock.powerflex_conn.device.get = MagicMock( + return_value=None) + self.capture_fail_json_call(MockDeviceApi.get_device_exception_response( + 'get_dev_without_SDS'), device_module_mock) + + def test_get_device_without_sds_with_path(self, device_module_mock): + self.get_module_args.update({ + "current_pathname": MockDeviceApi.PATH_1, + "state": "present" + }) + device_module_mock.module.params = self.get_module_args + device_module_mock.powerflex_conn.device.get = MagicMock( + return_value=None) + self.capture_fail_json_call(MockDeviceApi.get_device_exception_response( + 'get_device_details_without_path'), device_module_mock) + + def test_get_device_exception(self, device_module_mock): + self.get_module_args.update({ + "device_id": MockDeviceApi.DEVICE_ID_1, + "state": "present" + }) + device_module_mock.module.params = self.get_module_args + device_module_mock.powerflex_conn.device.get = MagicMock( + side_effect=MockApiException) + self.capture_fail_json_call(MockDeviceApi.get_device_exception_response( + 'get_device_exception'), device_module_mock) + + def test_create_device_with_id(self, device_module_mock): + self.get_module_args.update({ + "device_id": MockDeviceApi.DEVICE_ID_1, + "state": "present" + }) + device_module_mock.module.params = self.get_module_args + device_module_mock.powerflex_conn.device.get = MagicMock( + return_value=[]) + self.capture_fail_json_call(MockDeviceApi.get_device_exception_response( + 'create_id_exception'), device_module_mock) + + def test_get_device_with_empty_path(self, device_module_mock): + self.get_module_args.update({ + "current_pathname": "", + "sds_id": MockDeviceApi.SDS_ID_1, + "state": "present" + }) + device_module_mock.module.params = self.get_module_args + self.capture_fail_json_call(MockDeviceApi.get_device_exception_response( + 'empty_path'), device_module_mock) + + def test_get_device_with_empty_name(self, device_module_mock): + self.get_module_args.update({ + "device_name": "", + "sds_id": MockDeviceApi.SDS_ID_1, + "state": "present" + }) + device_module_mock.module.params = self.get_module_args + self.capture_fail_json_call(MockDeviceApi.get_device_exception_response( + 'empty_device_name'), device_module_mock) + + def test_get_device_with_empty_sds_id(self, device_module_mock): + self.get_module_args.update({ + "device_id": MockDeviceApi.DEVICE_ID_1, + "sds_id": "", + "state": "present" + }) + device_module_mock.module.params = self.get_module_args + self.capture_fail_json_call(MockDeviceApi.get_device_exception_response( + 'empty_sds'), device_module_mock) + + def test_get_device_with_empty_sds_name(self, device_module_mock): + self.get_module_args.update({ + "device_id": MockDeviceApi.DEVICE_ID_1, + "sds_name": "", + "state": "present" + }) + device_module_mock.module.params = self.get_module_args + self.capture_fail_json_call(MockDeviceApi.get_device_exception_response( + 'empty_sds'), device_module_mock) + + def test_get_device_with_empty_dev_id(self, device_module_mock): + self.get_module_args.update({ + "device_id": "", + "state": "present" + }) + device_module_mock.module.params = self.get_module_args + self.capture_fail_json_call(MockDeviceApi.get_device_exception_response( + 'empty_dev_id'), device_module_mock) + + def test_get_device_with_space_in_name(self, device_module_mock): + self.get_module_args.update({ + "device_name": " ", + "sds_id": MockDeviceApi.SDS_ID_1, + "state": "present" + }) + device_module_mock.module.params = self.get_module_args + self.capture_fail_json_call(MockDeviceApi.get_device_exception_response( + 'space_in_name'), device_module_mock) + + def test_get_device_with_space_in_name_with_sds_name(self, device_module_mock): + self.get_module_args.update({ + "device_name": " ", + "sds_name": MockDeviceApi.SDS_NAME_1, + "state": "present" + }) + device_module_mock.module.params = self.get_module_args + self.capture_fail_json_call(MockDeviceApi.get_device_exception_response( + 'space_in_name'), device_module_mock) + + def test_get_device_without_required_params(self, device_module_mock): + self.get_module_args.update({ + "state": "present" + }) + device_module_mock.module.params = self.get_module_args + self.capture_fail_json_call(MockDeviceApi.get_device_exception_response( + 'with_required_params'), device_module_mock) + + def test_modify_device_exception(self, device_module_mock): + self.get_module_args.update({ + "current_pathname": MockDeviceApi.PATH_1, + "sds_name": MockDeviceApi.SDS_NAME_1, + "media_type": "SSD", + "state": "present" + }) + device_module_mock.module.params = self.get_module_args + device_module_mock.powerflex_conn.sds.get = MagicMock( + return_value=MockDeviceApi.SDS_DETAILS_1) + device_module_mock.powerflex_conn.device.get = MagicMock( + return_value=MockDeviceApi.DEVICE_GET_LIST) + self.capture_fail_json_call(MockDeviceApi.get_device_exception_response1( + 'modify_exception'), device_module_mock) + + def test_delete_device(self, device_module_mock): + self.get_module_args.update({ + "current_pathname": MockDeviceApi.PATH_1, + "sds_name": MockDeviceApi.SDS_NAME_1, + "state": "absent" + }) + device_module_mock.module.params = self.get_module_args + device_module_mock.powerflex_conn.sds.get = MagicMock( + return_value=MockDeviceApi.SDS_DETAILS_1) + device_module_mock.powerflex_conn.device.get = MagicMock( + return_value=MockDeviceApi.DEVICE_GET_LIST) + device_module_mock.perform_module_operation() + device_module_mock.powerflex_conn.device.delete.assert_called() + + def test_delete_device_exception(self, device_module_mock): + self.get_module_args.update({ + "current_pathname": MockDeviceApi.PATH_1, + "sds_name": MockDeviceApi.SDS_NAME_1, + "state": "absent" + }) + device_module_mock.module.params = self.get_module_args + device_module_mock.powerflex_conn.sds.get = MagicMock( + return_value=MockDeviceApi.SDS_DETAILS_1) + device_module_mock.powerflex_conn.device.get = MagicMock( + return_value=MockDeviceApi.DEVICE_GET_LIST) + device_module_mock.powerflex_conn.device.delete = MagicMock( + side_effect=MockApiException) + self.capture_fail_json_call(MockDeviceApi.get_device_exception_response1( + 'delete_exception'), device_module_mock) + device_module_mock.powerflex_conn.device.delete.assert_called() + + def test_get_sds_exception(self, device_module_mock): + self.get_module_args.update({ + "current_pathname": MockDeviceApi.PATH_1, + "sds_name": MockDeviceApi.SDS_NAME_1, + "state": "absent" + }) + device_module_mock.module.params = self.get_module_args + device_module_mock.powerflex_conn.sds.get = MagicMock( + return_value=False) + self.capture_fail_json_call(MockDeviceApi.get_device_exception_response1( + 'sds_exception'), device_module_mock) + + def test_get_pd_exception(self, device_module_mock): + self.get_module_args.update({ + "current_pathname": MockDeviceApi.PATH_1, + "sds_name": MockDeviceApi.SDS_NAME_1, + "device_name": MockDeviceApi.DEVICE_NAME_1, + "protection_domain_name": MockDeviceApi.PD_NAME_1, + "media_type": "HDD", + "external_acceleration_type": "ReadAndWrite", + "storage_pool_name": MockDeviceApi.SP_NAME_1, + "force": False, + "state": "present" + }) + device_module_mock.module.params = self.get_module_args + device_module_mock.powerflex_conn.sds.get = MagicMock( + return_value=MockDeviceApi.SDS_DETAILS_1) + device_module_mock.powerflex_conn.device.get = MagicMock( + return_value=[]) + device_module_mock.powerflex_conn.protection_domain.get = MagicMock( + return_value=False) + self.capture_fail_json_call(MockDeviceApi.get_device_exception_response1( + 'pd_exception'), device_module_mock) + + def test_get_sp_exception(self, device_module_mock): + self.get_module_args.update({ + "current_pathname": MockDeviceApi.PATH_1, + "sds_name": MockDeviceApi.SDS_NAME_1, + "device_name": MockDeviceApi.DEVICE_NAME_1, + "protection_domain_name": MockDeviceApi.PD_NAME_1, + "media_type": "HDD", + "external_acceleration_type": "ReadAndWrite", + "storage_pool_name": MockDeviceApi.SP_NAME_1, + "force": False, + "state": "present" + }) + device_module_mock.module.params = self.get_module_args + device_module_mock.powerflex_conn.sds.get = MagicMock( + return_value=MockDeviceApi.SDS_DETAILS_1) + device_module_mock.powerflex_conn.device.get = MagicMock( + return_value=[]) + device_module_mock.powerflex_conn.protection_domain.get = MagicMock( + return_value=MockDeviceApi.PD_DETAILS_1) + device_module_mock.powerflex_conn.storage_pool.get = MagicMock( + return_value=False) + self.capture_fail_json_call(MockDeviceApi.get_device_exception_response1( + 'sp_exception'), device_module_mock) + + def test_get_acc_pool_exception(self, device_module_mock): + self.get_module_args.update({ + "current_pathname": MockDeviceApi.PATH_1, + "sds_name": MockDeviceApi.SDS_NAME_1, + "device_name": MockDeviceApi.DEVICE_NAME_1, + "protection_domain_name": MockDeviceApi.PD_NAME_1, + "media_type": "HDD", + "external_acceleration_type": "ReadAndWrite", + "acceleration_pool_name": MockDeviceApi.SP_NAME_1, + "force": False, + "state": "present" + }) + device_module_mock.module.params = self.get_module_args + device_module_mock.powerflex_conn.sds.get = MagicMock( + return_value=MockDeviceApi.SDS_DETAILS_1) + device_module_mock.powerflex_conn.device.get = MagicMock( + return_value=[]) + device_module_mock.powerflex_conn.protection_domain.get = MagicMock( + return_value=MockDeviceApi.PD_DETAILS_1) + device_module_mock.powerflex_conn.acceleration_pool.get = MagicMock( + return_value=False) + self.capture_fail_json_call(MockDeviceApi.get_device_exception_response1( + 'ap_exception'), device_module_mock) + + def test_add_device_exception(self, device_module_mock): + self.get_module_args.update({ + "current_pathname": MockDeviceApi.PATH_1, + "sds_name": MockDeviceApi.SDS_NAME_1, + "device_name": MockDeviceApi.DEVICE_NAME_1, + "protection_domain_name": MockDeviceApi.PD_NAME_1, + "media_type": "HDD", + "external_acceleration_type": "ReadAndWrite", + "storage_pool_name": MockDeviceApi.SP_NAME_1, + "force": False, + "state": "present" + }) + device_module_mock.module.params = self.get_module_args + device_module_mock.powerflex_conn.sds.get = MagicMock( + return_value=MockDeviceApi.SDS_DETAILS_1) + device_module_mock.powerflex_conn.device.get = MagicMock( + return_value=[]) + device_module_mock.powerflex_conn.protection_domain.get = MagicMock( + return_value=MockDeviceApi.PD_DETAILS_1) + device_module_mock.powerflex_conn.storage_pool.get = MagicMock( + return_value=MockDeviceApi.SP_DETAILS_1) + device_module_mock.powerflex_conn.device.create = MagicMock( + side_effect=MockApiException) + self.capture_fail_json_call(MockDeviceApi.get_device_exception_response1( + 'add_exception'), device_module_mock) + device_module_mock.powerflex_conn.device.create.assert_called() + + def test_add_device_name_exception(self, device_module_mock): + self.get_module_args.update({ + "current_pathname": MockDeviceApi.PATH_1, + "sds_name": MockDeviceApi.SDS_NAME_1, + "device_name": " ", + "protection_domain_name": MockDeviceApi.PD_NAME_1, + "media_type": "HDD", + "external_acceleration_type": "ReadAndWrite", + "storage_pool_name": MockDeviceApi.SP_NAME_1, + "force": False, + "state": "present" + }) + device_module_mock.module.params = self.get_module_args + device_module_mock.powerflex_conn.sds.get = MagicMock( + return_value=MockDeviceApi.SDS_DETAILS_1) + device_module_mock.powerflex_conn.device.get = MagicMock( + return_value=[]) + device_module_mock.powerflex_conn.protection_domain.get = MagicMock( + return_value=MockDeviceApi.PD_DETAILS_1) + device_module_mock.powerflex_conn.storage_pool.get = MagicMock( + return_value=MockDeviceApi.SP_DETAILS_1) + self.capture_fail_json_call(MockDeviceApi.get_device_exception_response1( + 'add_dev_name_exception'), device_module_mock) + + def test_add_device_path_exception(self, device_module_mock): + self.get_module_args.update({ + "current_pathname": " ", + "sds_name": MockDeviceApi.SDS_NAME_1, + "device_name": MockDeviceApi.DEVICE_NAME_1, + "protection_domain_name": MockDeviceApi.PD_NAME_1, + "media_type": "HDD", + "external_acceleration_type": "ReadAndWrite", + "storage_pool_name": MockDeviceApi.SP_NAME_1, + "force": False, + "state": "present" + }) + device_module_mock.module.params = self.get_module_args + device_module_mock.powerflex_conn.sds.get = MagicMock( + return_value=MockDeviceApi.SDS_DETAILS_1) + device_module_mock.powerflex_conn.device.get = MagicMock( + return_value=[]) + device_module_mock.powerflex_conn.protection_domain.get = MagicMock( + return_value=MockDeviceApi.PD_DETAILS_1) + device_module_mock.powerflex_conn.storage_pool.get = MagicMock( + return_value=MockDeviceApi.SP_DETAILS_1) + self.capture_fail_json_call(MockDeviceApi.get_device_exception_response1( + 'add_dev_path_exception'), device_module_mock) + + def test_add_device_ext_acc_type_exception(self, device_module_mock): + self.get_module_args.update({ + "current_pathname": " ", + "sds_name": MockDeviceApi.SDS_NAME_1, + "device_name": MockDeviceApi.DEVICE_NAME_1, + "protection_domain_name": MockDeviceApi.PD_NAME_1, + "media_type": "HDD", + "external_acceleration_type": "ReadAndWrite", + "force": False, + "state": "present" + }) + device_module_mock.module.params = self.get_module_args + device_module_mock.powerflex_conn.sds.get = MagicMock( + return_value=MockDeviceApi.SDS_DETAILS_1) + device_module_mock.powerflex_conn.device.get = MagicMock( + return_value=[]) + device_module_mock.powerflex_conn.protection_domain.get = MagicMock( + return_value=MockDeviceApi.PD_DETAILS_1) + self.capture_fail_json_call(MockDeviceApi.get_device_exception_response1( + 'ext_type_exception'), device_module_mock) + + def test_add_device_without_pd_exception(self, device_module_mock): + self.get_module_args.update({ + "current_pathname": MockDeviceApi.PATH_1, + "sds_name": MockDeviceApi.SDS_NAME_1, + "device_name": MockDeviceApi.DEVICE_NAME_1, + "media_type": "HDD", + "external_acceleration_type": "ReadAndWrite", + "storage_pool_name": MockDeviceApi.SP_NAME_1, + "force": False, + "state": "present" + }) + device_module_mock.module.params = self.get_module_args + device_module_mock.powerflex_conn.sds.get = MagicMock( + return_value=MockDeviceApi.SDS_DETAILS_1) + device_module_mock.powerflex_conn.device.get = MagicMock( + return_value=[]) + device_module_mock.powerflex_conn.storage_pool.get = MagicMock( + return_value=MockDeviceApi.SP_DETAILS_1) + self.capture_fail_json_call(MockDeviceApi.get_device_exception_response1( + 'add_without_pd'), device_module_mock) + + def test_add_device_without_pd_exception_for_acc_pool(self, device_module_mock): + self.get_module_args.update({ + "current_pathname": MockDeviceApi.PATH_1, + "sds_name": MockDeviceApi.SDS_NAME_1, + "device_name": MockDeviceApi.DEVICE_NAME_1, + "media_type": "HDD", + "external_acceleration_type": "ReadAndWrite", + "acceleration_pool_name": MockDeviceApi.SP_NAME_1, + "force": False, + "state": "present" + }) + device_module_mock.module.params = self.get_module_args + device_module_mock.powerflex_conn.sds.get = MagicMock( + return_value=MockDeviceApi.SDS_DETAILS_1) + device_module_mock.powerflex_conn.device.get = MagicMock( + return_value=[]) + device_module_mock.powerflex_conn.acceleration_pool.get = MagicMock( + return_value=MockDeviceApi.SP_DETAILS_1) + self.capture_fail_json_call(MockDeviceApi.get_device_exception_response1( + 'add_without_pd'), device_module_mock) diff --git a/ansible_collections/dellemc/powerflex/tests/unit/plugins/modules/test_fault_set.py b/ansible_collections/dellemc/powerflex/tests/unit/plugins/modules/test_fault_set.py new file mode 100644 index 000000000..ea2aa1104 --- /dev/null +++ b/ansible_collections/dellemc/powerflex/tests/unit/plugins/modules/test_fault_set.py @@ -0,0 +1,215 @@ +# Copyright: (c) 2024, Dell Technologies + +# Apache License version 2.0 (see MODULE-LICENSE or http://www.apache.org/licenses/LICENSE-2.0.txt) + +"""Unit Tests for fault set module on PowerFlex""" + +from __future__ import (absolute_import, division, print_function) + +__metaclass__ = type + +import pytest +# pylint: disable=unused-import +from ansible_collections.dellemc.powerflex.tests.unit.plugins.module_utils.libraries import initial_mock +from mock.mock import MagicMock +from ansible_collections.dellemc.powerflex.tests.unit.plugins.module_utils.mock_fault_set_api import MockFaultSetApi +from ansible_collections.dellemc.powerflex.plugins.modules.fault_set import \ + FaultSetHandler +from ansible_collections.dellemc.powerflex.tests.unit.plugins.module_utils.mock_api_exception \ + import MockApiException +from ansible_collections.dellemc.powerflex.tests.unit.plugins.module_utils.libraries.powerflex_unit_base \ + import PowerFlexUnitBase + +from ansible_collections.dellemc.powerflex.plugins.modules.fault_set import PowerFlexFaultSet + + +class TestPowerflexFaultSet(PowerFlexUnitBase): + + get_module_args = MockFaultSetApi.FAULT_SET_COMMON_ARGS + + @pytest.fixture + def module_object(self): + return PowerFlexFaultSet + + def test_get_fault_set_response(self, powerflex_module_mock): + self.set_module_params( + powerflex_module_mock, + self.get_module_args, + { + 'fault_set_id': 'fault_set_id_1', + 'state': "present" + }) + powerflex_module_mock.powerflex_conn.fault_set.get = MagicMock( + return_value=MockFaultSetApi.FAULT_SET_GET_LIST) + pd_resp = MockFaultSetApi.PROTECTION_DOMAIN + powerflex_module_mock.powerflex_conn.protection_domain.get = MagicMock( + return_value=pd_resp['protectiondomain']) + FaultSetHandler().handle( + powerflex_module_mock, powerflex_module_mock.module.params) + powerflex_module_mock.powerflex_conn.fault_set.get.assert_called() + + def test_get_fault_set_name_response(self, powerflex_module_mock): + self.set_module_params( + powerflex_module_mock, + self.get_module_args, + { + 'fault_set_name': 'fault_set_name_1', + 'protection_domain_id': 'test_pd_id_1', + 'state': "present" + }) + pd_resp = MockFaultSetApi.PROTECTION_DOMAIN + powerflex_module_mock.powerflex_conn.protection_domain.get = MagicMock( + return_value=pd_resp['protectiondomain']) + powerflex_module_mock.powerflex_conn.fault_set.get = MagicMock( + return_value=MockFaultSetApi.FAULT_SET_GET_LIST) + FaultSetHandler().handle( + powerflex_module_mock, powerflex_module_mock.module.params) + powerflex_module_mock.powerflex_conn.fault_set.get.assert_called() + + def test_create_fault_set_response(self, powerflex_module_mock): + self.set_module_params( + powerflex_module_mock, + self.get_module_args, + { + "fault_set_name": "test_fs_1", + "protection_domain_name": "test_pd_1", + "state": "present" + }) + pd_resp = MockFaultSetApi.PROTECTION_DOMAIN + powerflex_module_mock.powerflex_conn.protection_domain.get = MagicMock( + return_value=pd_resp['protectiondomain']) + powerflex_module_mock.get_fault_set = MagicMock( + return_value=None) + FaultSetHandler().handle( + powerflex_module_mock, powerflex_module_mock.module.params) + powerflex_module_mock.powerflex_conn.fault_set.create.assert_called() + + def test_create_fault_set_wo_pd_exception(self, powerflex_module_mock): + self.set_module_params( + powerflex_module_mock, + self.get_module_args, + { + "fault_set_name": "test_fs_1", + "state": "present" + }) + powerflex_module_mock.get_fault_set = MagicMock( + return_value=None) + self.capture_fail_json_call( + MockFaultSetApi.get_fault_set_exception_response( + 'create_fault_set_wo_pd_exception'), powerflex_module_mock, FaultSetHandler) + + def test_create_fault_set_empty_name_exception(self, powerflex_module_mock): + self.set_module_params( + powerflex_module_mock, + self.get_module_args, + { + "fault_set_name": " ", + "protection_domain_name": "test_pd_1", + "state": "present" + }) + pd_resp = MockFaultSetApi.PROTECTION_DOMAIN + powerflex_module_mock.powerflex_conn.protection_domain.get = MagicMock( + return_value=pd_resp['protectiondomain']) + powerflex_module_mock.get_fault_set = MagicMock( + return_value=None) + self.capture_fail_json_call( + MockFaultSetApi.get_fault_set_exception_response( + 'create_fault_set_empty_name_exception'), powerflex_module_mock, FaultSetHandler) + + def test_create_fault_set_exception(self, powerflex_module_mock): + self.set_module_params( + powerflex_module_mock, + self.get_module_args, + { + "fault_set_name": "test_fs_1", + "protection_domain_name": "test_pd_1", + "state": "present" + }) + pd_resp = MockFaultSetApi.PROTECTION_DOMAIN + powerflex_module_mock.powerflex_conn.protection_domain.get = MagicMock( + return_value=pd_resp['protectiondomain']) + powerflex_module_mock.get_fault_set = MagicMock( + return_value=None) + powerflex_module_mock.powerflex_conn.fault_set.create = MagicMock( + side_effect=MockApiException) + self.capture_fail_json_call( + MockFaultSetApi.get_fault_set_exception_response( + 'create_fault_set_exception'), powerflex_module_mock, FaultSetHandler) + + def test_rename_fault_set_response(self, powerflex_module_mock): + self.set_module_params( + powerflex_module_mock, + self.get_module_args, + { + "fault_set_name": 'fault_set_name_1', + "protection_domain_name": "test_pd_1", + "fault_set_new_name": "fs_new_name", + "state": "present" + }) + pd_resp = MockFaultSetApi.PROTECTION_DOMAIN + powerflex_module_mock.powerflex_conn.protection_domain.get = MagicMock( + return_value=pd_resp['protectiondomain']) + powerflex_module_mock.get_fault_set = MagicMock( + return_value=MockFaultSetApi.FAULT_SET_GET_LIST[0]) + FaultSetHandler().handle( + powerflex_module_mock, powerflex_module_mock.module.params) + powerflex_module_mock.powerflex_conn.fault_set.rename.assert_called() + + def test_rename_fault_set_exception(self, powerflex_module_mock): + self.set_module_params( + powerflex_module_mock, + self.get_module_args, + { + "fault_set_name": 'fault_set_name_1', + "protection_domain_name": "test_pd_1", + "fault_set_new_name": "fs_new_name", + "state": "present" + }) + pd_resp = MockFaultSetApi.PROTECTION_DOMAIN + powerflex_module_mock.powerflex_conn.protection_domain.get = MagicMock( + return_value=pd_resp['protectiondomain']) + powerflex_module_mock.get_fault_set = MagicMock( + return_value=MockFaultSetApi.FAULT_SET_GET_LIST[0]) + powerflex_module_mock.powerflex_conn.fault_set.rename = MagicMock( + side_effect=MockApiException) + self.capture_fail_json_call( + MockFaultSetApi.get_fault_set_exception_response( + 'rename_fault_set_exception'), powerflex_module_mock, FaultSetHandler) + + def test_delete_fault_set_response(self, powerflex_module_mock): + self.set_module_params( + powerflex_module_mock, + self.get_module_args, + { + "fault_set_name": 'test_fs_1', + "protection_domain_name": "test_pd_1", + "state": "absent" + }) + pd_resp = MockFaultSetApi.PROTECTION_DOMAIN + powerflex_module_mock.powerflex_conn.protection_domain.get = MagicMock( + return_value=pd_resp['protectiondomain']) + powerflex_module_mock.get_fault_set = MagicMock( + return_value=MockFaultSetApi.FAULT_SET_GET_LIST[0]) + FaultSetHandler().handle( + powerflex_module_mock, powerflex_module_mock.module.params) + powerflex_module_mock.powerflex_conn.fault_set.delete.assert_called() + + def test_delete_fault_set_exception(self, powerflex_module_mock): + self.set_module_params( + powerflex_module_mock, + self.get_module_args, + { + "fault_set_name": 'test_fs_1', + "protection_domain_name": "test_pd_1", + "state": "absent" + }) + pd_resp = MockFaultSetApi.PROTECTION_DOMAIN + powerflex_module_mock.powerflex_conn.protection_domain.get = MagicMock( + return_value=pd_resp['protectiondomain']) + powerflex_module_mock.get_fault_set = MagicMock( + return_value=MockFaultSetApi.FAULT_SET_GET_LIST[0]) + powerflex_module_mock.powerflex_conn.fault_set.delete = MagicMock( + side_effect=MockApiException) + self.capture_fail_json_call( + MockFaultSetApi.get_fault_set_exception_response( + 'delete_fault_set_exception'), powerflex_module_mock, FaultSetHandler) diff --git a/ansible_collections/dellemc/powerflex/tests/unit/plugins/modules/test_info.py b/ansible_collections/dellemc/powerflex/tests/unit/plugins/modules/test_info.py index 2bd0ff158..ce5091212 100644 --- a/ansible_collections/dellemc/powerflex/tests/unit/plugins/modules/test_info.py +++ b/ansible_collections/dellemc/powerflex/tests/unit/plugins/modules/test_info.py @@ -1,4 +1,4 @@ -# Copyright: (c) 2022, Dell Technologies +# Copyright: (c) 2024, Dell Technologies # Apache License version 2.0 (see MODULE-LICENSE or http://www.apache.org/licenses/LICENSE-2.0.txt) @@ -9,14 +9,17 @@ from __future__ import (absolute_import, division, print_function) __metaclass__ = type import pytest +# pylint: disable=unused-import +from ansible_collections.dellemc.powerflex.tests.unit.plugins.module_utils.libraries import initial_mock from mock.mock import MagicMock from ansible_collections.dellemc.powerflex.tests.unit.plugins.module_utils.mock_info_api import MockInfoApi -from ansible_collections.dellemc.powerflex.tests.unit.plugins.module_utils.mock_sdk_response \ - import MockSDKResponse +from ansible_collections.dellemc.powerflex.tests.unit.plugins.module_utils.mock_fault_set_api import MockFaultSetApi from ansible_collections.dellemc.powerflex.tests.unit.plugins.module_utils.mock_api_exception \ import MockApiException from ansible_collections.dellemc.powerflex.plugins.module_utils.storage.dell \ import utils +from ansible_collections.dellemc.powerflex.tests.unit.plugins.module_utils.mock_fail_json \ + import FailJsonException, fail_json utils.get_logger = MagicMock() utils.get_powerflex_gateway_host_connection = MagicMock() @@ -25,6 +28,7 @@ utils.PowerFlexClient = MagicMock() from ansible.module_utils import basic basic.AnsibleModule = MagicMock() from ansible_collections.dellemc.powerflex.plugins.modules.info import PowerFlexInfo +INVALID_SORT_MSG = 'messageCode=PARSE002 displayMessage=An invalid column name: invalid is entered in the sort list' class TestPowerflexInfo(): @@ -33,6 +37,9 @@ class TestPowerflexInfo(): @pytest.fixture def info_module_mock(self, mocker): + mocker.patch( + MockInfoApi.MODULE_UTILS_PATH + '.PowerFlexClient', + new=MockApiException) info_module_mock = PowerFlexInfo() info_module_mock.module.check_mode = False info_module_mock.powerflex_conn.system.api_version = MagicMock( @@ -41,8 +48,15 @@ class TestPowerflexInfo(): info_module_mock.powerflex_conn.system.get = MagicMock( return_value=MockInfoApi.INFO_ARRAY_DETAILS ) + info_module_mock.module.fail_json = fail_json return info_module_mock + def capture_fail_json_call(self, error_msg, info_module_mock): + try: + info_module_mock.perform_module_operation() + except FailJsonException as fj_object: + assert error_msg in fj_object.message + def test_get_volume_details(self, info_module_mock): self.get_module_args.update({ "gather_subset": ['vol'] @@ -60,6 +74,23 @@ class TestPowerflexInfo(): info_module_mock.powerflex_conn.volume.get.assert_called() info_module_mock.powerflex_conn.utility.get_statistics_for_all_volumes.assert_called() + def test_get_volume_details_filter(self, info_module_mock): + self.get_module_args.update({ + "gather_subset": ['vol'], + "filters": [{ + "filter_key": "storagePoolId", + "filter_operator": "equal", + "filter_value": "test_pool_id_1" + }] + }) + info_module_mock.module.params = self.get_module_args + vol_resp = MockInfoApi.INFO_VOLUME_GET_LIST + info_module_mock.powerflex_conn.volume.get = MagicMock( + return_value=vol_resp + ) + info_module_mock.perform_module_operation() + info_module_mock.powerflex_conn.volume.get.assert_called() + def test_get_volume_details_with_exception(self, info_module_mock): self.get_module_args.update({ "gather_subset": ['vol'] @@ -72,8 +103,8 @@ class TestPowerflexInfo(): info_module_mock.powerflex_conn.utility.get_statistics_for_all_volumes = MagicMock( side_effect=MockApiException ) - info_module_mock.perform_module_operation() - assert MockInfoApi.get_exception_response('volume_get_details') in info_module_mock.module.fail_json.call_args[1]['msg'] + self.capture_fail_json_call(MockInfoApi.get_exception_response( + 'volume_get_details'), info_module_mock) def test_get_sp_details(self, info_module_mock): self.get_module_args.update({ @@ -92,6 +123,23 @@ class TestPowerflexInfo(): info_module_mock.powerflex_conn.storage_pool.get.assert_called() info_module_mock.powerflex_conn.utility.get_statistics_for_all_storagepools.assert_called() + def test_get_sp_details_filter(self, info_module_mock): + self.get_module_args.update({ + "gather_subset": ['storage_pool'], + "filters": [{ + "filter_key": "id", + "filter_operator": "equal", + "filter_value": "test_pool_id_1" + }] + }) + info_module_mock.module.params = self.get_module_args + storage_pool_resp = MockInfoApi.INFO_STORAGE_POOL_GET_LIST + info_module_mock.powerflex_conn.storage_pool.get = MagicMock( + return_value=storage_pool_resp + ) + info_module_mock.perform_module_operation() + info_module_mock.powerflex_conn.storage_pool.get.assert_called() + def test_get_sp_details_with_exception(self, info_module_mock): self.get_module_args.update({ "gather_subset": ['storage_pool'] @@ -104,8 +152,8 @@ class TestPowerflexInfo(): info_module_mock.powerflex_conn.utility.get_statistics_for_all_storagepools = MagicMock( side_effect=MockApiException ) - info_module_mock.perform_module_operation() - assert MockInfoApi.get_exception_response('sp_get_details') in info_module_mock.module.fail_json.call_args[1]['msg'] + self.capture_fail_json_call(MockInfoApi.get_exception_response( + 'sp_get_details'), info_module_mock) def test_get_rcg_details(self, info_module_mock): self.get_module_args.update({ @@ -118,6 +166,22 @@ class TestPowerflexInfo(): info_module_mock.perform_module_operation() info_module_mock.powerflex_conn.replication_consistency_group.get.assert_called() + def test_get_rcg_filter_details(self, info_module_mock): + self.get_module_args.update({ + "gather_subset": ['rcg'], + "filters": [{ + "filter_key": "id", + "filter_operator": "equal", + "filter_value": "aadc17d500000000" + }] + }) + info_module_mock.module.params = self.get_module_args + rcg_resp = MockInfoApi.RCG_LIST + info_module_mock.powerflex_conn.replication_consistency_group.get = MagicMock( + return_value=rcg_resp) + info_module_mock.perform_module_operation() + info_module_mock.powerflex_conn.replication_consistency_group.get.assert_called() + def test_get_rcg_details_throws_exception(self, info_module_mock): self.get_module_args.update({ "gather_subset": ['rcg'] @@ -126,8 +190,8 @@ class TestPowerflexInfo(): info_module_mock.powerflex_conn.replication_consistency_group.get = MagicMock( side_effect=MockApiException ) - info_module_mock.perform_module_operation() - assert MockInfoApi.get_exception_response('rcg_get_details') in info_module_mock.module.fail_json.call_args[1]['msg'] + self.capture_fail_json_call(MockInfoApi.get_exception_response( + 'rcg_get_details'), info_module_mock) def test_get_replication_pair_details(self, info_module_mock): self.get_module_args.update({ @@ -147,5 +211,396 @@ class TestPowerflexInfo(): info_module_mock.powerflex_conn.replication_pair.get = MagicMock( side_effect=MockApiException ) + self.capture_fail_json_call(MockInfoApi.get_exception_response( + 'replication_pair_get_details'), info_module_mock) + + def test_get_snapshot_policy_details(self, info_module_mock): + self.get_module_args.update({ + "gather_subset": ['snapshot_policy'] + }) + info_module_mock.module.params = self.get_module_args + snapshot_policy_resp = MockInfoApi.INFO_SNAPSHOT_POLICY_GET_LIST + info_module_mock.powerflex_conn.snapshot_policy.get = MagicMock( + return_value=snapshot_policy_resp + ) + snapshot_policy_stat_resp = MockInfoApi.INFO_SNAPSHOT_POLICY_STATISTICS + info_module_mock.powerflex_conn.utility.get_statistics_for_all_snapshot_policies = MagicMock( + return_value=snapshot_policy_stat_resp + ) + info_module_mock.perform_module_operation() + info_module_mock.powerflex_conn.snapshot_policy.get.assert_called() + info_module_mock.powerflex_conn.utility.get_statistics_for_all_snapshot_policies.assert_called() + + def test_get_snapshot_policy_details_with_exception(self, info_module_mock): + self.get_module_args.update({ + "gather_subset": ['snapshot_policy'] + }) + info_module_mock.module.params = self.get_module_args + snapshot_policy_resp = MockInfoApi.INFO_SNAPSHOT_POLICY_GET_LIST + info_module_mock.powerflex_conn.snapshot_policy.get = MagicMock( + return_value=snapshot_policy_resp + ) + info_module_mock.powerflex_conn.utility.get_statistics_for_all_snapshot_policies = MagicMock( + side_effect=MockApiException + ) + self.capture_fail_json_call(MockInfoApi.get_exception_response( + 'snapshot_policy_get_details'), info_module_mock) + + def test_get_sdc_details(self, info_module_mock): + self.get_module_args.update({ + "gather_subset": ['sdc'] + }) + info_module_mock.module.params = self.get_module_args + sdc_resp = MockInfoApi.INFO_SDC_FILTER_LIST + info_module_mock.powerflex_conn.sdc.get = MagicMock( + return_value=sdc_resp + ) + info_module_mock.perform_module_operation() + info_module_mock.powerflex_conn.sdc.get.assert_called() + + def test_get_sdc_details_filter(self, info_module_mock): + self.get_module_args.update({ + "gather_subset": ['sdc'], + "filters": [{ + "filter_key": "name", + "filter_operator": "equal", + "filter_value": "sdc_1" + }] + }) + info_module_mock.module.params = self.get_module_args + sdc_resp = MockInfoApi.INFO_SDC_FILTER_LIST + info_module_mock.powerflex_conn.sdc.create = MagicMock( + return_value=sdc_resp + ) + info_module_mock.perform_module_operation() + info_module_mock.powerflex_conn.sdc.get.assert_called() + + def test_get_sdc_details_exception(self, info_module_mock): + self.get_module_args.update({ + "gather_subset": ['sdc'] + }) + info_module_mock.module.params = self.get_module_args + info_module_mock.powerflex_conn.sdc.get = MagicMock( + side_effect=MockApiException + ) + self.capture_fail_json_call(MockInfoApi.get_exception_response( + 'sdc_get_details'), info_module_mock) + + def test_get_sds_details(self, info_module_mock): + self.get_module_args.update({ + "gather_subset": ['sds'] + }) + info_module_mock.module.params = self.get_module_args + sds_resp = MockInfoApi.INFO_SDS_GET_LIST + info_module_mock.powerflex_conn.sds.get = MagicMock( + return_value=sds_resp + ) + info_module_mock.perform_module_operation() + info_module_mock.powerflex_conn.sds.get.assert_called() + + def test_get_sds_filter_details(self, info_module_mock): + self.get_module_args.update({ + "gather_subset": ['sds'], + "filters": [ + { + "filter_key": "name", + "filter_operator": "equal", + "filter_value": "node0", + }, + { + "filter_key": "name", + "filter_operator": "equal", + "filter_value": "node1", + }, + { + "filter_key": "id", + "filter_operator": "equal", + "filter_value": "8f3bb15300000001", + } + ] + }) + info_module_mock.module.params = self.get_module_args + sds_resp = MockInfoApi.INFO_SDS_GET_LIST + info_module_mock.powerflex_conn.sds.get = MagicMock( + return_value=sds_resp + ) + info_module_mock.perform_module_operation() + info_module_mock.powerflex_conn.sds.get.assert_called() + + def test_get_sds_details_filter_invalid(self, info_module_mock): + self.get_module_args.update({ + "gather_subset": ['sds'], + "filters": [{ + "filter_key": "name", + "filter_op": "equal", + "filter_value": "LGLAP203", + }] + }) + info_module_mock.module.params = self.get_module_args + self.capture_fail_json_call(MockInfoApi.get_exception_response( + 'get_sds_details_filter_invalid'), info_module_mock) + + def test_get_sds_details_filter_empty(self, info_module_mock): + self.get_module_args.update({ + "gather_subset": ['sds'], + "filters": [{ + "filter_key": "name", + "filter_operator": None, + "filter_value": "LGLAP203", + }] + }) + info_module_mock.module.params = self.get_module_args + self.capture_fail_json_call(MockInfoApi.get_exception_response( + 'get_sds_details_filter_empty'), info_module_mock) + + def test_get_sds_details_exception(self, info_module_mock): + self.get_module_args.update({ + "gather_subset": ['sds'] + }) + info_module_mock.module.params = self.get_module_args + sds_resp = MockInfoApi.INFO_SDS_GET_LIST + info_module_mock.powerflex_conn.sds.get = MagicMock( + side_effect=MockApiException + ) + self.capture_fail_json_call(MockInfoApi.get_exception_response( + 'sds_get_details'), info_module_mock) + + def test_get_pd_details(self, info_module_mock): + self.get_module_args.update({ + "gather_subset": ['protection_domain'] + }) + info_module_mock.module.params = self.get_module_args + pd_resp = MockInfoApi.INFO_GET_PD_LIST + info_module_mock.powerflex_conn.protection_domain.get = MagicMock( + return_value=pd_resp + ) + info_module_mock.perform_module_operation() + info_module_mock.powerflex_conn.protection_domain.get.assert_called() + + def test_get_pd_filter_details(self, info_module_mock): + self.get_module_args.update({ + "gather_subset": ['protection_domain'], + "filters": [{ + "filter_key": "name", + "filter_operator": "equal", + "filter_value": "domain1", + }] + }) + info_module_mock.module.params = self.get_module_args + pd_resp = MockInfoApi.INFO_GET_PD_LIST + info_module_mock.powerflex_conn.protection_domain.get = MagicMock( + return_value=pd_resp + ) + info_module_mock.perform_module_operation() + info_module_mock.powerflex_conn.protection_domain.get.assert_called() + + def test_get_pd_details_exception(self, info_module_mock): + self.get_module_args.update({ + "gather_subset": ['protection_domain'] + }) + info_module_mock.module.params = self.get_module_args + pd_resp = MockInfoApi.INFO_GET_PD_LIST + info_module_mock.powerflex_conn.protection_domain.get = MagicMock( + side_effect=MockApiException + ) + self.capture_fail_json_call(MockInfoApi.get_exception_response( + 'pd_get_details'), info_module_mock) + + def test_get_device_details(self, info_module_mock): + self.get_module_args.update({ + "gather_subset": ['device'] + }) + info_module_mock.module.params = self.get_module_args + device_resp = MockInfoApi.INFO_GET_DEVICE_LIST + info_module_mock.powerflex_conn.device.get = MagicMock( + return_value=device_resp + ) + info_module_mock.perform_module_operation() + info_module_mock.powerflex_conn.device.get.assert_called() + + def test_get_device_filter_details(self, info_module_mock): + self.get_module_args.update({ + "gather_subset": ['device'], + "filters": [{ + "filter_key": "name", + "filter_operator": "equal", + "filter_value": "device230", + }] + }) + info_module_mock.module.params = self.get_module_args + device_resp = MockInfoApi.INFO_GET_DEVICE_LIST + info_module_mock.powerflex_conn.device.get = MagicMock( + return_value=device_resp + ) + info_module_mock.perform_module_operation() + info_module_mock.powerflex_conn.device.get.assert_called() + + def test_get_device_details_exception(self, info_module_mock): + self.get_module_args.update({ + "gather_subset": ['device'] + }) + info_module_mock.module.params = self.get_module_args + device_resp = MockInfoApi.INFO_GET_DEVICE_LIST + info_module_mock.powerflex_conn.device.get = MagicMock( + side_effect=MockApiException + ) + self.capture_fail_json_call(MockInfoApi.get_exception_response( + 'device_get_details'), info_module_mock) + + def test_get_fault_set_details(self, info_module_mock): + self.get_module_args.update({ + "gather_subset": ['fault_set'] + }) + info_module_mock.module.params = self.get_module_args + pd_resp = MockFaultSetApi.PROTECTION_DOMAIN + info_module_mock.powerflex_conn.protection_domain.get = MagicMock( + return_value=pd_resp['protectiondomain']) + fault_set_resp = MockInfoApi.INFO_GET_FAULT_SET_LIST + info_module_mock.powerflex_conn.fault_set.get = MagicMock( + return_value=fault_set_resp + ) + info_module_mock.perform_module_operation() + info_module_mock.powerflex_conn.fault_set.get.assert_called() + + def test_get_fault_set_details_exception(self, info_module_mock): + self.get_module_args.update({ + "gather_subset": ['fault_set'] + }) + info_module_mock.module.params = self.get_module_args + fault_set_resp = MockInfoApi.INFO_GET_FAULT_SET_LIST + info_module_mock.powerflex_conn.fault_set.get = MagicMock( + side_effect=MockApiException + ) + self.capture_fail_json_call(MockInfoApi.get_exception_response( + 'fault_set_get_details'), info_module_mock) + + def test_get_fault_set_details_invalid_filter_operator_exception(self, info_module_mock): + self.get_module_args.update({ + "gather_subset": ['fault_set'], + "filters": [{ + "filter_key": "name", + "filter_operator": "does_not_contain", + "filter_value": "LGLAP203", + }] + }) + info_module_mock.module.params = self.get_module_args + fault_set_resp = MockInfoApi.INFO_GET_FAULT_SET_LIST + self.capture_fail_json_call(MockInfoApi.get_exception_response( + 'invalid_filter_operator_exception'), info_module_mock) + + def test_get_fault_set_details_api_exception(self, info_module_mock): + self.get_module_args.update({ + "gather_subset": ['fault_set'] + }) + info_module_mock.module.params = self.get_module_args + fault_set_resp = MockInfoApi.INFO_GET_FAULT_SET_LIST + info_module_mock.powerflex_conn.system.api_version = MagicMock( + side_effect=MockApiException + ) + self.capture_fail_json_call(MockInfoApi.get_exception_response( + 'api_exception'), info_module_mock) + + def test_get_fault_set_details_system_exception(self, info_module_mock): + self.get_module_args.update({ + "gather_subset": ['fault_set'] + }) + info_module_mock.module.params = self.get_module_args + fault_set_resp = MockInfoApi.INFO_GET_FAULT_SET_LIST + info_module_mock.powerflex_conn.system.get = MagicMock( + side_effect=MockApiException + ) + self.capture_fail_json_call(MockInfoApi.get_exception_response( + 'system_exception'), info_module_mock) + + def test_get_managed_device_details(self, info_module_mock): + self.get_module_args.update({ + "gather_subset": ['managed_device'] + }) + info_module_mock.module.params = self.get_module_args + info_module_mock.powerflex_conn.managed_device.get = MagicMock() + info_module_mock.perform_module_operation() + info_module_mock.powerflex_conn.managed_device.get.assert_called() + + def test_get_managed_device_details_throws_exception(self, info_module_mock): + self.get_module_args.update({ + "gather_subset": ['managed_device'] + }) + info_module_mock.module.params = self.get_module_args + info_module_mock.powerflex_conn.managed_device.get = MagicMock( + side_effect=MockApiException + ) + self.capture_fail_json_call(MockInfoApi.get_exception_response( + 'managed_device_get_error'), info_module_mock) + + def test_get_service_template_details(self, info_module_mock): + self.get_module_args.update({ + "gather_subset": ['service_template'] + }) + info_module_mock.module.params = self.get_module_args + info_module_mock.powerflex_conn.service_template.get = MagicMock() + info_module_mock.perform_module_operation() + info_module_mock.powerflex_conn.service_template.get.assert_called() + + def test_get_service_template_details_throws_exception(self, info_module_mock): + self.get_module_args.update({ + "gather_subset": ['service_template'] + }) + info_module_mock.module.params = self.get_module_args + info_module_mock.powerflex_conn.service_template.get = MagicMock( + side_effect=MockApiException + ) + self.capture_fail_json_call(MockInfoApi.get_exception_response( + 'service_template_get_error'), info_module_mock) + + def test_get_deployment_details(self, info_module_mock): + self.get_module_args.update({ + "gather_subset": ['deployment'], + "limit": 20 + }) + info_module_mock.module.params = self.get_module_args + info_module_mock.powerflex_conn.deployment.get = MagicMock() + info_module_mock.perform_module_operation() + info_module_mock.powerflex_conn.deployment.get.assert_called() + + def test_get_deployment_details_throws_exception(self, info_module_mock): + self.get_module_args.update({ + "gather_subset": ['deployment'] + }) + info_module_mock.module.params = self.get_module_args + info_module_mock.powerflex_conn.deployment.get = MagicMock( + side_effect=MockApiException + ) + self.capture_fail_json_call(MockInfoApi.get_exception_response( + 'deployment_get_error'), info_module_mock) + + def test_get_deployment_details_throws_exception_invalid_sort(self, info_module_mock): + self.get_module_args.update({ + "gather_subset": ['deployment'], + "sort": 'invalid' + }) + info_module_mock.module.params = self.get_module_args + info_module_mock.powerflex_conn.deployment.get = MagicMock( + side_effect=MockApiException(INVALID_SORT_MSG) + ) + info_module_mock.perform_module_operation() + assert info_module_mock.get_deployments_list() == [] + + def test_get_with_multiple_gather_subset(self, info_module_mock): + self.get_module_args.update({ + "gather_subset": ['deployment', 'service_template'], + "sort": 'name', "filters": [{"filter_key": "name", "filter_operator": "equal", "filter_value": "rack"}], + }) + info_module_mock.module.params = self.get_module_args + info_module_mock.perform_module_operation() + assert info_module_mock.populate_filter_list() == [] + assert info_module_mock.get_param_value('sort') is None + + def test_get_with_invalid_offset_and_limit_for_subset(self, info_module_mock): + self.get_module_args.update({ + "gather_subset": ['deployment'], + "limit": -1, "offset": -1 + }) + info_module_mock.module.params = self.get_module_args info_module_mock.perform_module_operation() - assert MockInfoApi.get_exception_response('replication_pair_get_details') in info_module_mock.module.fail_json.call_args[1]['msg'] + assert info_module_mock.get_param_value('limit') is None + assert info_module_mock.get_param_value('offset') is None diff --git a/ansible_collections/dellemc/powerflex/tests/unit/plugins/modules/test_protection_domain.py b/ansible_collections/dellemc/powerflex/tests/unit/plugins/modules/test_protection_domain.py index ced9fc7f7..fa1dfe641 100644 --- a/ansible_collections/dellemc/powerflex/tests/unit/plugins/modules/test_protection_domain.py +++ b/ansible_collections/dellemc/powerflex/tests/unit/plugins/modules/test_protection_domain.py @@ -1,4 +1,4 @@ -# Copyright: (c) 2022, Dell Technologies +# Copyright: (c) 2024, Dell Technologies # Apache License version 2.0 (see MODULE-LICENSE or http://www.apache.org/licenses/LICENSE-2.0.txt) @@ -8,229 +8,327 @@ from __future__ import (absolute_import, division, print_function) __metaclass__ = type import pytest +# pylint: disable=unused-import +from ansible_collections.dellemc.powerflex.tests.unit.plugins.module_utils.libraries import initial_mock from mock.mock import MagicMock -from ansible_collections.dellemc.powerflex.tests.unit.plugins.module_utils.mock_protection_domain_api import MockProtectionDomainApi -from ansible_collections.dellemc.powerflex.tests.unit.plugins.module_utils.mock_sdk_response \ - import MockSDKResponse +from ansible_collections.dellemc.powerflex.tests.unit.plugins.module_utils.mock_protection_domain_api \ + import MockProtectionDomainApi from ansible_collections.dellemc.powerflex.tests.unit.plugins.module_utils.mock_api_exception \ import MockApiException -from ansible_collections.dellemc.powerflex.plugins.module_utils.storage.dell \ - import utils - -utils.get_logger = MagicMock() -utils.get_powerflex_gateway_host_connection = MagicMock() -utils.PowerFlexClient = MagicMock() -from ansible.module_utils import basic -basic.AnsibleModule = MagicMock() -from ansible_collections.dellemc.powerflex.plugins.modules.protection_domain import PowerFlexProtectionDomain - - -class TestPowerflexProtectionDomain(): - - get_module_args = { - 'hostname': '**.***.**.***', - 'protection_domain_id': '7bd6457000000000', - 'protection_domain_name': None, - 'protection_domain_new_name': None, - 'is_active': True, - 'network_limits': { - 'rebuild_limit': 10240, - 'rebalance_limit': 10240, - 'vtree_migration_limit': 10240, - 'overall_limit': 20480, - 'bandwidth_unit': 'KBps', - }, - 'rf_cache_limits': { - 'is_enabled': None, - 'page_size': 4, - 'max_io_limit': 16, - 'pass_through_mode': 'None' - }, - 'state': 'present' - } +from ansible_collections.dellemc.powerflex.tests.unit.plugins.module_utils.libraries.powerflex_unit_base \ + import PowerFlexUnitBase +from ansible_collections.dellemc.powerflex.plugins.modules.protection_domain \ + import PowerFlexProtectionDomain + + +class TestPowerflexProtectionDomain(PowerFlexUnitBase): + + get_module_args = MockProtectionDomainApi.PD_COMMON_ARGS @pytest.fixture - def protection_domain_module_mock(self, mocker): - mocker.patch(MockProtectionDomainApi.MODULE_UTILS_PATH + '.PowerFlexClient', new=MockApiException) - protection_domain_module_mock = PowerFlexProtectionDomain() - return protection_domain_module_mock - - def test_get_protection_domain_response(self, protection_domain_module_mock): - protection_domain_module_mock.module.params = self.get_module_args - pd_resp = MockSDKResponse(MockProtectionDomainApi.PROTECTION_DOMAIN) - protection_domain_module_mock.powerflex_conn.protection_domain.get = MagicMock( - return_value=pd_resp.__dict__['data']['protectiondomain'] - ) - protection_domain_module_mock.perform_module_operation() - protection_domain_module_mock.powerflex_conn.protection_domain.get.assert_called() - - def test_create_protection_domain(self, protection_domain_module_mock): - self.get_module_args.update({ - "protection_domain_name": "test_domain", - "state": "present" - }) - protection_domain_module_mock.module.params = self.get_module_args - pd_resp = MockSDKResponse(MockProtectionDomainApi.PROTECTION_DOMAIN) - protection_domain_module_mock.get_protection_domain = MagicMock( - return_value=pd_resp.__dict__['data']['protectiondomain'][0] - ) - protection_domain_module_mock.powerflex_conn.protection_domain.create = MagicMock(return_values=None) - protection_domain_module_mock.perform_module_operation() - assert (self.get_module_args['protection_domain_name'] == - protection_domain_module_mock.module.exit_json.call_args[1]["protection_domain_details"]['name']) - assert protection_domain_module_mock.module.exit_json.call_args[1]['changed'] is True - - def test_modify_protection_domain(self, protection_domain_module_mock): - self.get_module_args.update({ - 'network_limits': { - 'rebuild_limit': 10, - 'rebalance_limit': 10, - 'vtree_migration_limit': 11, - 'overall_limit': 21, - 'bandwidth_unit': 'GBps', - } - }) - protection_domain_module_mock.module.params = self.get_module_args - pd_resp = MockSDKResponse(MockProtectionDomainApi.PROTECTION_DOMAIN) - sp_resp = MockSDKResponse(MockProtectionDomainApi.STORAGE_POOL) - protection_domain_module_mock.powerflex_conn.protection_domain.get = MagicMock( - return_value=pd_resp.__dict__['data']['protectiondomain'] - ) - protection_domain_module_mock.powerflex_conn.protection_domain.get_storage_pools = MagicMock( - return_value=sp_resp.__dict__['data']['storagepool'] - ) - protection_domain_module_mock.perform_module_operation() - protection_domain_module_mock.powerflex_conn.protection_domain.network_limits.assert_called() - - def test_rename_protection_domain(self, protection_domain_module_mock): - self.get_module_args.update({ - 'protection_domain_new_name': 'new_test_domain' - }) - protection_domain_module_mock.module.params = self.get_module_args - pd_resp = MockSDKResponse(MockProtectionDomainApi.PROTECTION_DOMAIN) - protection_domain_module_mock.powerflex_conn.protection_domain.get = MagicMock( - return_value=pd_resp.__dict__['data']['protectiondomain'] - ) - protection_domain_module_mock.perform_module_operation() - protection_domain_module_mock.powerflex_conn.protection_domain.rename.assert_called() - - def test_inactivate_protection_domain(self, protection_domain_module_mock): - self.get_module_args.update({ - 'is_active': False - }) - protection_domain_module_mock.module.params = self.get_module_args - pd_resp = MockSDKResponse(MockProtectionDomainApi.PROTECTION_DOMAIN) - protection_domain_module_mock.powerflex_conn.protection_domain.get = MagicMock( - return_value=pd_resp.__dict__['data']['protectiondomain'] - ) - protection_domain_module_mock.perform_module_operation() - protection_domain_module_mock.powerflex_conn.protection_domain. \ - inactivate.assert_called() - - def test_activate_protection_domain(self, protection_domain_module_mock): - self.get_module_args.update({ - 'is_active': True - }) - protection_domain_module_mock.module.params = self.get_module_args - pd_resp = MockSDKResponse(MockProtectionDomainApi.PROTECTION_DOMAIN) - protection_domain_module_mock.powerflex_conn.protection_domain.get = MagicMock( - return_value=pd_resp.__dict__['data']['protectiondomain'] - ) - protection_domain_module_mock.powerflex_conn.protection_domain.activate = MagicMock(return_value=None) - protection_domain_module_mock.perform_module_operation() - assert protection_domain_module_mock.module.exit_json.call_args[1]['changed'] is True - - def test_delete_protection_domain(self, protection_domain_module_mock): - self.get_module_args.update({ - 'protection_domain_name': 'new_test_domain', - 'state': 'absent' - }) - protection_domain_module_mock.module.params = self.get_module_args - protection_domain_module_mock.get_protection_domain = MagicMock(return_values=None) - protection_domain_module_mock.perform_module_operation() - assert protection_domain_module_mock.module.exit_json.call_args[1]['changed'] is True - - def test_delete_protection_domain_throws_exception(self, protection_domain_module_mock): - self.get_module_args.update({ - 'protection_domain_id': '7bd6457000000000', - 'state': 'absent' - }) - protection_domain_module_mock.module.params = self.get_module_args - pd_resp = MockSDKResponse(MockProtectionDomainApi.PROTECTION_DOMAIN) - protection_domain_module_mock.powerflex_conn.protection_domain.get = MagicMock( - return_value=pd_resp.__dict__['data']['protectiondomain'] - ) - protection_domain_module_mock.powerflex_conn.protection_domain.delete = MagicMock( - side_effect=utils.PowerFlexClient) - protection_domain_module_mock.perform_module_operation() - assert MockProtectionDomainApi.delete_pd_failed_msg(self.get_module_args['protection_domain_id']) in \ - protection_domain_module_mock.module.fail_json.call_args[1]['msg'] - - def test_get_with_404_exception(self, protection_domain_module_mock): - MockProtectionDomainApi.status = 404 - self.get_module_args.update({ - "protection_domain_name": "test_domain1" - }) - protection_domain_module_mock.module.params = self.get_module_args - pd_resp = MockSDKResponse(MockProtectionDomainApi.PROTECTION_DOMAIN) - protection_domain_module_mock.powerflex_conn.protection_domain.get = MagicMock( - return_value=pd_resp.__dict__['data']['protectiondomain'] - ) - protection_domain_module_mock.powerflex_conn.protection_domain.create = MagicMock( - side_effect=utils.PowerFlexClient) - protection_domain_module_mock.perform_module_operation() - assert protection_domain_module_mock.module.exit_json.call_args[1]['changed'] is True - - def test_modify_protection_domain_throws_exception(self, protection_domain_module_mock): - self.get_module_args.update({ - "protection_domain_id": "7bd6457000000000", - 'rf_cache_limits': { - 'is_enabled': True, - 'page_size': 64, - 'max_io_limit': 128, - 'pass_through_mode': 'invalid_Read' - } - }) - protection_domain_module_mock.module.params = self.get_module_args - pd_resp = MockSDKResponse(MockProtectionDomainApi.PROTECTION_DOMAIN) - protection_domain_module_mock.powerflex_conn.protection_domain.get = MagicMock( - return_value=pd_resp.__dict__['data']['protectiondomain'] - ) - protection_domain_module_mock.powerflex_conn.protection_domain.set_rfcache_enabled = MagicMock( - side_effect=utils.PowerFlexClient) - protection_domain_module_mock.perform_module_operation() - assert MockProtectionDomainApi.modify_pd_with_failed_msg(self.get_module_args['protection_domain_id']) in \ - protection_domain_module_mock.module.fail_json.call_args[1]['msg'] - - def test_rename_protection_domain_invalid_value(self, protection_domain_module_mock): - self.get_module_args.update({ - "protection_domain_name": "test_domain", - "protection_domain_new_name": " test domain", - }) - protection_domain_module_mock.module.params = self.get_module_args - pd_resp = MockSDKResponse(MockProtectionDomainApi.PROTECTION_DOMAIN) - protection_domain_module_mock.powerflex_conn.protection_domain.get = MagicMock( - return_value=pd_resp.__dict__['data']['protectiondomain'] - ) - protection_domain_module_mock.powerflex_conn.protection_domain.rename = MagicMock( - side_effect=utils.PowerFlexClient) - protection_domain_module_mock.perform_module_operation() - assert MockProtectionDomainApi.rename_pd_failed_msg(self.get_module_args['protection_domain_id']) in \ - protection_domain_module_mock.module.fail_json.call_args[1]['msg'] - - def test_create_protection_domain_invalid_param(self, protection_domain_module_mock): - self.get_module_args.update({ - "protection_domain_name": "test_domain1", - "protection_domain_new_name": "new_domain", - "state": "present" - }) - protection_domain_module_mock.module.params = self.get_module_args - pd_resp = MockSDKResponse(MockProtectionDomainApi.PROTECTION_DOMAIN) - protection_domain_module_mock.powerflex_conn.protection_domain.get = MagicMock( - return_value=pd_resp.__dict__['data']['protectiondomain'] - ) - protection_domain_module_mock.powerflex_conn.protection_domain.create = MagicMock( - side_effect=utils.PowerFlexClient) - protection_domain_module_mock.perform_module_operation() - assert MockProtectionDomainApi.version_pd_failed_msg() in \ - protection_domain_module_mock.module.fail_json.call_args[1]['msg'] + def module_object(self): + return PowerFlexProtectionDomain + + @pytest.mark.parametrize("params", [ + {'protection_domain_id': MockProtectionDomainApi.PD_ID}, + {"protection_domain_name": MockProtectionDomainApi.PD_NAME}, + ]) + def test_get_protection_domain_response(self, powerflex_module_mock, params): + self.set_module_params( + powerflex_module_mock, + self.get_module_args, + { + 'protection_domain_id': params.get('protection_domain_id', None), + 'protection_domain_name': params.get('protection_domain_name', None) + }) + powerflex_module_mock.powerflex_conn.protection_domain.get = MagicMock( + return_value=MockProtectionDomainApi.PROTECTION_DOMAIN + ) + powerflex_module_mock.perform_module_operation() + powerflex_module_mock.powerflex_conn.protection_domain.get.assert_called() + + def test_get_pd_exception(self, powerflex_module_mock): + self.set_module_params( + powerflex_module_mock, + self.get_module_args, + { + 'protection_domain_id': MockProtectionDomainApi.PD_ID, + }) + powerflex_module_mock.powerflex_conn.protection_domain.get = MagicMock( + return_value=MockApiException()) + self.capture_fail_json_call( + MockProtectionDomainApi.get_failed_msgs('get_pd_failed_msg'), + powerflex_module_mock, invoke_perform_module=True) + + def test_validate_input_empty_params(self, powerflex_module_mock): + self.set_module_params( + powerflex_module_mock, + self.get_module_args, + { + 'protection_domain_name': '' + }) + powerflex_module_mock.powerflex_conn.protection_domain.get = MagicMock( + return_value=MockProtectionDomainApi.PROTECTION_DOMAIN + ) + self.capture_fail_json_call( + MockProtectionDomainApi.get_failed_msgs('empty_pd_msg'), + powerflex_module_mock, invoke_perform_module=True) + + def test_validate_network_limits_params(self, powerflex_module_mock): + self.set_module_params( + powerflex_module_mock, + self.get_module_args, + { + 'protection_domain_name': MockProtectionDomainApi.PD_NAME, + 'network_limits': {'overall_limit': -199, + 'bandwidth_unit': 'MBps'} + }) + powerflex_module_mock.powerflex_conn.protection_domain.get = MagicMock( + return_value=MockProtectionDomainApi.PROTECTION_DOMAIN + ) + self.capture_fail_json_call( + MockProtectionDomainApi.get_failed_msgs('overall_limit_msg'), + powerflex_module_mock, invoke_perform_module=True) + + def test_create_pd_new_name_exception(self, powerflex_module_mock): + self.set_module_params( + powerflex_module_mock, + self.get_module_args, + { + 'protection_domain_name': MockProtectionDomainApi.PD_NAME, + 'protection_domain_new_name': MockProtectionDomainApi.PD_NAME + }) + powerflex_module_mock.powerflex_conn.protection_domain.get = MagicMock( + return_value=[] + ) + self.capture_fail_json_call( + MockProtectionDomainApi.get_failed_msgs('new_name_in_create'), + powerflex_module_mock, invoke_perform_module=True) + + def test_create_pd_(self, powerflex_module_mock): + self.set_module_params( + powerflex_module_mock, + self.get_module_args, + { + 'protection_domain_name': MockProtectionDomainApi.PD_NAME + }) + powerflex_module_mock.powerflex_conn.protection_domain.get = MagicMock( + return_value=[] + ) + powerflex_module_mock.powerflex_conn.protection_domain.create = MagicMock( + return_value=None + ) + resp = powerflex_module_mock.create_protection_domain("protection_domain_name") + assert resp is True + powerflex_module_mock.powerflex_conn.protection_domain.create.assert_called() + + def test_rename_pd(self, powerflex_module_mock): + self.set_module_params( + powerflex_module_mock, + self.get_module_args, + { + 'protection_domain_name': MockProtectionDomainApi.PD_NAME, + 'protection_domain_new_name': MockProtectionDomainApi.PD_NEW_NAME, + }) + powerflex_module_mock.powerflex_conn.protection_domain.get = MagicMock( + return_value=MockProtectionDomainApi.PROTECTION_DOMAIN) + powerflex_module_mock.perform_module_operation() + powerflex_module_mock.powerflex_conn.protection_domain.rename.assert_called() + + def test_rename_pd_execption(self, powerflex_module_mock): + self.set_module_params( + powerflex_module_mock, + self.get_module_args, + { + 'protection_domain_name': MockProtectionDomainApi.PD_NAME, + 'protection_domain_new_name': MockProtectionDomainApi.PD_NEW_NAME, + }) + powerflex_module_mock.powerflex_conn.protection_domain.get = MagicMock( + return_value=MockProtectionDomainApi.PROTECTION_DOMAIN) + powerflex_module_mock.powerflex_conn.protection_domain.rename = MagicMock( + side_effect=MockApiException + ) + self.capture_fail_json_call( + MockProtectionDomainApi.get_failed_msgs('rename_pd_exception'), + powerflex_module_mock, invoke_perform_module=True + ) + + def test_inactivate_pd(self, powerflex_module_mock): + self.set_module_params( + powerflex_module_mock, + self.get_module_args, + { + 'protection_domain_name': MockProtectionDomainApi.PD_NAME, + 'is_active': False + }) + powerflex_module_mock.powerflex_conn.protection_domain.get = MagicMock( + return_value=MockProtectionDomainApi.PROTECTION_DOMAIN) + powerflex_module_mock.perform_module_operation() + powerflex_module_mock.powerflex_conn.protection_domain.inactivate.assert_called() + + def test_activate_pd(self, powerflex_module_mock): + self.set_module_params( + powerflex_module_mock, + self.get_module_args, + { + 'protection_domain_id': MockProtectionDomainApi.PD_ID, + 'is_active': True + }) + powerflex_module_mock.powerflex_conn.protection_domain.get = MagicMock( + return_value=MockProtectionDomainApi.PROTECTION_DOMAIN_1) + powerflex_module_mock.perform_module_operation() + powerflex_module_mock.powerflex_conn.protection_domain.activate.assert_called() + + def test_create_pd_exception(self, powerflex_module_mock): + self.set_module_params( + powerflex_module_mock, + self.get_module_args, + { + 'protection_domain_name': MockProtectionDomainApi.PD_NAME, + 'is_active': False + }) + powerflex_module_mock.powerflex_conn.protection_domain.get = MagicMock( + return_value=[] + ) + powerflex_module_mock.powerflex_conn.protection_domain.create = MagicMock( + side_effect=MockApiException + ) + self.capture_fail_json_call( + MockProtectionDomainApi.get_failed_msgs('create_pd_exception'), + powerflex_module_mock, invoke_perform_module=True + ) + + def test_modify_network_limits(self, powerflex_module_mock): + self.set_module_params( + powerflex_module_mock, + self.get_module_args, + { + 'protection_domain_name': MockProtectionDomainApi.PD_NAME, + 'network_limits': {'overall_limit': 15, + 'bandwidth_unit': 'GBps', + 'rebalance_limit': 10, + 'rebuild_limit': 10, + 'vtree_migration_limit': 10} + }) + powerflex_module_mock.powerflex_conn.protection_domain.get = MagicMock( + return_value=MockProtectionDomainApi.PROTECTION_DOMAIN) + powerflex_module_mock.perform_module_operation() + powerflex_module_mock.powerflex_conn.protection_domain.network_limits.assert_called() + + def test_modify_network_limits_exception(self, powerflex_module_mock): + self.set_module_params( + powerflex_module_mock, + self.get_module_args, + { + 'protection_domain_id': MockProtectionDomainApi.PD_ID, + 'network_limits': {'overall_limit': 15, + 'bandwidth_unit': 'GBps', + 'rebalance_limit': 10, + 'rebuild_limit': 10, + 'vtree_migration_limit': 10} + }) + powerflex_module_mock.powerflex_conn.protection_domain.get = MagicMock( + return_value=MockProtectionDomainApi.PROTECTION_DOMAIN) + powerflex_module_mock.powerflex_conn.protection_domain.network_limits = MagicMock( + side_effect=MockApiException + ) + self.capture_fail_json_call( + MockProtectionDomainApi.get_failed_msgs('modify_network_limits_exception'), + powerflex_module_mock, invoke_perform_module=True + ) + + def test_enable_rf_cache(self, powerflex_module_mock): + self.set_module_params( + powerflex_module_mock, + self.get_module_args, + { + 'protection_domain_name': MockProtectionDomainApi.PD_NAME, + 'rf_cache_limits': {'is_enabled': True, + 'page_size': 10, + 'max_io_limit': 10, + 'pass_through_mode': 'Read'} + }) + powerflex_module_mock.powerflex_conn.protection_domain.get = MagicMock( + return_value=MockProtectionDomainApi.PROTECTION_DOMAIN) + powerflex_module_mock.perform_module_operation() + powerflex_module_mock.powerflex_conn.protection_domain.set_rfcache_enabled.assert_called() + powerflex_module_mock.powerflex_conn.protection_domain.rfcache_parameters.assert_called() + + def test_modify_rf_cache_params_exception(self, powerflex_module_mock): + self.set_module_params( + powerflex_module_mock, + self.get_module_args, + { + 'protection_domain_name': MockProtectionDomainApi.PD_NAME, + 'rf_cache_limits': {'is_enabled': True, 'page_size': 10, + 'max_io_limit': 10, 'pass_through_mode': 'Read'} + }) + powerflex_module_mock.powerflex_conn.protection_domain.get = MagicMock( + return_value=MockProtectionDomainApi.PROTECTION_DOMAIN) + powerflex_module_mock.powerflex_conn.protection_domain.set_rfcache_enabled = MagicMock( + side_effect=MockApiException) + self.capture_fail_json_call( + MockProtectionDomainApi.get_failed_msgs('rf_cache_limits_exception'), + powerflex_module_mock, invoke_perform_module=True + ) + + def test_delete_pd(self, powerflex_module_mock): + self.set_module_params( + powerflex_module_mock, + self.get_module_args, + { + 'protection_domain_name': MockProtectionDomainApi.PD_NAME, + 'state': 'absent' + }) + powerflex_module_mock.powerflex_conn.protection_domain.get = MagicMock( + return_value=MockProtectionDomainApi.PROTECTION_DOMAIN) + powerflex_module_mock.perform_module_operation() + powerflex_module_mock.powerflex_conn.protection_domain.delete.assert_called() + + def test_delete_pd_exception(self, powerflex_module_mock): + self.set_module_params( + powerflex_module_mock, + self.get_module_args, + { + 'protection_domain_id': MockProtectionDomainApi.PD_ID, + 'state': 'absent' + }) + powerflex_module_mock.powerflex_conn.protection_domain.get = MagicMock( + return_value=MockProtectionDomainApi.PROTECTION_DOMAIN) + powerflex_module_mock.powerflex_conn.protection_domain.delete = MagicMock( + side_effect=MockApiException + ) + self.capture_fail_json_call( + MockProtectionDomainApi.get_failed_msgs('delete_pd_exception'), + powerflex_module_mock, invoke_perform_module=True + ) + + def test_get_sp(self, powerflex_module_mock): + self.set_module_params( + powerflex_module_mock, + self.get_module_args, + { + 'protection_domain_name': MockProtectionDomainApi.PD_NAME + }) + powerflex_module_mock.powerflex_conn.protection_domain.get = MagicMock( + return_value=MockProtectionDomainApi.PROTECTION_DOMAIN) + powerflex_module_mock.powerflex_conn.protection_domain.get_storage_pools = MagicMock( + return_value=MockProtectionDomainApi.STORAGE_POOL) + powerflex_module_mock.perform_module_operation() + powerflex_module_mock.powerflex_conn.protection_domain.get_storage_pools.assert_called() + + def test_get_sp_exception(self, powerflex_module_mock): + self.set_module_params( + powerflex_module_mock, + self.get_module_args, + { + 'protection_domain_id': MockProtectionDomainApi.PD_ID + }) + powerflex_module_mock.powerflex_conn.protection_domain.get = MagicMock( + return_value=MockProtectionDomainApi.PROTECTION_DOMAIN) + powerflex_module_mock.powerflex_conn.protection_domain.get_storage_pools = MagicMock( + side_effect=MockApiException + ) + self.capture_fail_json_call( + MockProtectionDomainApi.get_failed_msgs('get_sp_exception'), + powerflex_module_mock, invoke_perform_module=True) diff --git a/ansible_collections/dellemc/powerflex/tests/unit/plugins/modules/test_replication_consistency_group.py b/ansible_collections/dellemc/powerflex/tests/unit/plugins/modules/test_replication_consistency_group.py index 334de8942..e1074f282 100644 --- a/ansible_collections/dellemc/powerflex/tests/unit/plugins/modules/test_replication_consistency_group.py +++ b/ansible_collections/dellemc/powerflex/tests/unit/plugins/modules/test_replication_consistency_group.py @@ -5,7 +5,6 @@ """Unit Tests for replication consistency group module on PowerFlex""" from __future__ import (absolute_import, division, print_function) -from unittest.mock import Mock __metaclass__ = type @@ -170,7 +169,7 @@ class TestPowerflexReplicationConsistencyGroup(): replication_consistency_group_module_mock.module.fail_json.call_args[1]['msg'] def test_pause_rcg(self, replication_consistency_group_module_mock): - self.get_module_args.update({"rcg_name": "test_rcg", "pause": True, + self.get_module_args.update({"rcg_name": "test_rcg", "rcg_state": 'pause', "pause_mode": "StopDataTransfer", "state": "present"}) replication_consistency_group_module_mock.module.params = self.get_module_args replication_consistency_group_module_mock.powerflex_conn.replication_consistency_group.get = MagicMock( @@ -180,7 +179,7 @@ class TestPowerflexReplicationConsistencyGroup(): replication_consistency_group_module_mock.powerflex_conn.replication_consistency_group.pause.assert_called() def test_pause_rcg_throws_exception(self, replication_consistency_group_module_mock): - self.get_module_args.update({"rcg_name": "test_rcg", "pause": True, + self.get_module_args.update({"rcg_name": "test_rcg", "rcg_state": 'pause', "pause_mode": "StopDataTransfer", "state": "present"}) replication_consistency_group_module_mock.module.params = self.get_module_args replication_consistency_group_module_mock.powerflex_conn.replication_consistency_group.get = MagicMock( @@ -193,7 +192,7 @@ class TestPowerflexReplicationConsistencyGroup(): replication_consistency_group_module_mock.module.fail_json.call_args[1]['msg'] def test_resume_rcg(self, replication_consistency_group_module_mock): - self.get_module_args.update({"rcg_name": "test_rcg", "pause": False, "state": "present"}) + self.get_module_args.update({"rcg_name": "test_rcg", "rcg_state": 'resume', "state": "present"}) replication_consistency_group_module_mock.module.params = self.get_module_args replication_consistency_group_module_mock.powerflex_conn.replication_consistency_group.get = MagicMock( return_value=MockReplicationConsistencyGroupApi.get_rcg_details(pause_mode="StopDataTransfer")) @@ -201,7 +200,7 @@ class TestPowerflexReplicationConsistencyGroup(): replication_consistency_group_module_mock.powerflex_conn.replication_consistency_group.resume.assert_called() def test_resume_rcg_throws_exception(self, replication_consistency_group_module_mock): - self.get_module_args.update({"rcg_name": "test_rcg", "pause": False, "state": "present"}) + self.get_module_args.update({"rcg_name": "test_rcg", "rcg_state": 'resume', "state": "present"}) replication_consistency_group_module_mock.module.params = self.get_module_args replication_consistency_group_module_mock.powerflex_conn.replication_consistency_group.get = MagicMock( return_value=MockReplicationConsistencyGroupApi.get_rcg_details(pause_mode="StopDataTransfer")) @@ -213,7 +212,7 @@ class TestPowerflexReplicationConsistencyGroup(): replication_consistency_group_module_mock.module.fail_json.call_args[1]['msg'] def test_freeze_rcg(self, replication_consistency_group_module_mock): - self.get_module_args.update({"rcg_name": "test_rcg", "freeze": True, "state": "present"}) + self.get_module_args.update({"rcg_name": "test_rcg", "rcg_state": 'freeze', "state": "present"}) replication_consistency_group_module_mock.module.params = self.get_module_args replication_consistency_group_module_mock.powerflex_conn.replication_consistency_group.get = MagicMock( return_value=MockReplicationConsistencyGroupApi.get_rcg_details()) @@ -221,7 +220,7 @@ class TestPowerflexReplicationConsistencyGroup(): replication_consistency_group_module_mock.powerflex_conn.replication_consistency_group.freeze.assert_called() def test_freeze_rcg_throws_exception(self, replication_consistency_group_module_mock): - self.get_module_args.update({"rcg_name": "test_rcg", "freeze": True, "state": "present"}) + self.get_module_args.update({"rcg_name": "test_rcg", "rcg_state": 'freeze', "state": "present"}) replication_consistency_group_module_mock.module.params = self.get_module_args replication_consistency_group_module_mock.powerflex_conn.replication_consistency_group.get = MagicMock( return_value=MockReplicationConsistencyGroupApi.get_rcg_details()) @@ -233,7 +232,7 @@ class TestPowerflexReplicationConsistencyGroup(): replication_consistency_group_module_mock.module.fail_json.call_args[1]['msg'] def test_unfreeze_rcg(self, replication_consistency_group_module_mock): - self.get_module_args.update({"rcg_name": "test_rcg", "freeze": False, "state": "present"}) + self.get_module_args.update({"rcg_name": "test_rcg", "rcg_state": 'unfreeze', "state": "present"}) replication_consistency_group_module_mock.module.params = self.get_module_args replication_consistency_group_module_mock.powerflex_conn.replication_consistency_group.get = MagicMock( return_value=MockReplicationConsistencyGroupApi.get_rcg_details(freeze_state="Frozen") @@ -242,7 +241,7 @@ class TestPowerflexReplicationConsistencyGroup(): replication_consistency_group_module_mock.powerflex_conn.replication_consistency_group.unfreeze.assert_called() def test_unfreeze_rcg_throws_exception(self, replication_consistency_group_module_mock): - self.get_module_args.update({"rcg_name": "test_rcg", "freeze": False, "state": "present"}) + self.get_module_args.update({"rcg_name": "test_rcg", "rcg_state": 'unfreeze', "state": "present"}) replication_consistency_group_module_mock.module.params = self.get_module_args replication_consistency_group_module_mock.powerflex_conn.replication_consistency_group.get = MagicMock( return_value=MockReplicationConsistencyGroupApi.get_rcg_details(freeze_state="Frozen")) @@ -311,7 +310,7 @@ class TestPowerflexReplicationConsistencyGroup(): replication_consistency_group_module_mock.module.fail_json.call_args[1]['msg'] def test_pause_rcg_without_pause_mode(self, replication_consistency_group_module_mock): - self.get_module_args.update({"rcg_name": "test_rcg", "pause": True, "state": "present"}) + self.get_module_args.update({"rcg_name": "test_rcg", "rcg_state": 'pause', "state": "present"}) replication_consistency_group_module_mock.module.params = self.get_module_args replication_consistency_group_module_mock.powerflex_conn.replication_consistency_group.get = MagicMock( return_value=MockReplicationConsistencyGroupApi.get_rcg_details()) @@ -340,5 +339,109 @@ class TestPowerflexReplicationConsistencyGroup(): return_value=MockReplicationConsistencyGroupApi.get_rcg_details()) replication_consistency_group_module_mock.powerflex_conn.protection_domain.get = MagicMock(return_value=[{"name": "pd_id"}]) replication_consistency_group_module_mock.perform_module_operation() - assert "Specify pause as True to pause replication consistency group" in \ + assert "Specify rcg_state as 'pause' to pause replication consistency group" in \ + replication_consistency_group_module_mock.module.fail_json.call_args[1]['msg'] + + def test_failover_rcg(self, replication_consistency_group_module_mock): + self.get_module_args.update({"rcg_name": "test_rcg", "rcg_state": "failover", "state": "present"}) + replication_consistency_group_module_mock.module.params = self.get_module_args + replication_consistency_group_module_mock.powerflex_conn.replication_consistency_group.get = MagicMock( + return_value=MockReplicationConsistencyGroupApi.get_rcg_details()) + replication_consistency_group_module_mock.perform_module_operation() + replication_consistency_group_module_mock.powerflex_conn.replication_consistency_group.failover.assert_called() + + def test_failover_rcg_throws_exception(self, replication_consistency_group_module_mock): + self.get_module_args.update({"rcg_name": "test_rcg", "rcg_state": "failover", "state": "present"}) + replication_consistency_group_module_mock.module.params = self.get_module_args + replication_consistency_group_module_mock.powerflex_conn.replication_consistency_group.get = MagicMock( + return_value=MockReplicationConsistencyGroupApi.get_rcg_details()) + replication_consistency_group_module_mock.powerflex_conn.replication_consistency_group.failover = \ + MagicMock(side_effect=MockApiException) + replication_consistency_group_module_mock.perform_module_operation() + assert "Failover replication consistency group " + MockReplicationConsistencyGroupApi.RCG_ID \ + + MockReplicationConsistencyGroupApi.FAIL_MSG in \ + replication_consistency_group_module_mock.module.fail_json.call_args[1]['msg'] + + def test_restore_rcg(self, replication_consistency_group_module_mock): + self.get_module_args.update({"rcg_name": "test_rcg", "rcg_state": "restore", "state": "present"}) + replication_consistency_group_module_mock.module.params = self.get_module_args + rcg_details = MockReplicationConsistencyGroupApi.get_rcg_details() + rcg_details[0]['failoverType'] = 'Failover' + replication_consistency_group_module_mock.powerflex_conn.replication_consistency_group.get = MagicMock(return_value=rcg_details) + replication_consistency_group_module_mock.perform_module_operation() + replication_consistency_group_module_mock.powerflex_conn.replication_consistency_group.restore.assert_called() + + def test_restore_rcg_throws_exception(self, replication_consistency_group_module_mock): + self.get_module_args.update({"rcg_name": "test_rcg", "rcg_state": "restore", "state": "present"}) + replication_consistency_group_module_mock.module.params = self.get_module_args + rcg_details = MockReplicationConsistencyGroupApi.get_rcg_details() + rcg_details[0]['failoverType'] = 'Failover' + replication_consistency_group_module_mock.powerflex_conn.replication_consistency_group.get = MagicMock(return_value=rcg_details) + replication_consistency_group_module_mock.powerflex_conn.replication_consistency_group.restore = \ + MagicMock(side_effect=MockApiException) + replication_consistency_group_module_mock.perform_module_operation() + assert "Restore replication consistency group " + MockReplicationConsistencyGroupApi.RCG_ID \ + + MockReplicationConsistencyGroupApi.FAIL_MSG in \ + replication_consistency_group_module_mock.module.fail_json.call_args[1]['msg'] + + def test_reverse_rcg(self, replication_consistency_group_module_mock): + self.get_module_args.update({"rcg_name": "test_rcg", "rcg_state": "reverse", "state": "present"}) + replication_consistency_group_module_mock.module.params = self.get_module_args + rcg_details = MockReplicationConsistencyGroupApi.get_rcg_details() + rcg_details[0]['failoverType'] = 'Failover' + replication_consistency_group_module_mock.powerflex_conn.replication_consistency_group.get = MagicMock(return_value=rcg_details) + replication_consistency_group_module_mock.perform_module_operation() + replication_consistency_group_module_mock.powerflex_conn.replication_consistency_group.reverse.assert_called() + + def test_reverse_rcg_throws_exception(self, replication_consistency_group_module_mock): + self.get_module_args.update({"rcg_name": "test_rcg", "rcg_state": "reverse", "state": "present"}) + replication_consistency_group_module_mock.module.params = self.get_module_args + rcg_details = MockReplicationConsistencyGroupApi.get_rcg_details() + rcg_details[0]['failoverType'] = 'Failover' + replication_consistency_group_module_mock.powerflex_conn.replication_consistency_group.get = MagicMock(return_value=rcg_details) + replication_consistency_group_module_mock.powerflex_conn.replication_consistency_group.reverse = \ + MagicMock(side_effect=MockApiException) + replication_consistency_group_module_mock.perform_module_operation() + assert "Reverse replication consistency group " + MockReplicationConsistencyGroupApi.RCG_ID \ + + MockReplicationConsistencyGroupApi.FAIL_MSG in \ + replication_consistency_group_module_mock.module.fail_json.call_args[1]['msg'] + + def test_switchover_rcg(self, replication_consistency_group_module_mock): + self.get_module_args.update({"rcg_name": "test_rcg", "rcg_state": "switchover", "state": "present"}) + replication_consistency_group_module_mock.module.params = self.get_module_args + replication_consistency_group_module_mock.powerflex_conn.replication_consistency_group.get = MagicMock( + return_value=MockReplicationConsistencyGroupApi.get_rcg_details()) + replication_consistency_group_module_mock.perform_module_operation() + replication_consistency_group_module_mock.powerflex_conn.replication_consistency_group.switchover.assert_called() + + def test_switchover_rcg_throws_exception(self, replication_consistency_group_module_mock): + self.get_module_args.update({"rcg_name": "test_rcg", "rcg_state": "switchover", "state": "present"}) + replication_consistency_group_module_mock.module.params = self.get_module_args + replication_consistency_group_module_mock.powerflex_conn.replication_consistency_group.get = MagicMock( + return_value=MockReplicationConsistencyGroupApi.get_rcg_details()) + replication_consistency_group_module_mock.powerflex_conn.replication_consistency_group.switchover = \ + MagicMock(side_effect=MockApiException) + replication_consistency_group_module_mock.perform_module_operation() + assert "Switchover replication consistency group " + MockReplicationConsistencyGroupApi.RCG_ID \ + + MockReplicationConsistencyGroupApi.FAIL_MSG in \ + replication_consistency_group_module_mock.module.fail_json.call_args[1]['msg'] + + def test_sync_rcg(self, replication_consistency_group_module_mock): + self.get_module_args.update({"rcg_name": "test_rcg", "rcg_state": 'sync', "state": "present"}) + replication_consistency_group_module_mock.module.params = self.get_module_args + replication_consistency_group_module_mock.powerflex_conn.replication_consistency_group.get = MagicMock( + return_value=MockReplicationConsistencyGroupApi.get_rcg_details()) + replication_consistency_group_module_mock.perform_module_operation() + replication_consistency_group_module_mock.powerflex_conn.replication_consistency_group.sync.assert_called() + + def test_sync_rcg_throws_exception(self, replication_consistency_group_module_mock): + self.get_module_args.update({"rcg_name": "test_rcg", "rcg_state": 'sync', "state": "present"}) + replication_consistency_group_module_mock.module.params = self.get_module_args + replication_consistency_group_module_mock.powerflex_conn.replication_consistency_group.get = MagicMock( + return_value=MockReplicationConsistencyGroupApi.get_rcg_details()) + replication_consistency_group_module_mock.powerflex_conn.replication_consistency_group.sync = \ + MagicMock(side_effect=MockApiException) + replication_consistency_group_module_mock.perform_module_operation() + assert "Synchronization of replication consistency group " + MockReplicationConsistencyGroupApi.RCG_ID \ + + MockReplicationConsistencyGroupApi.FAIL_MSG in \ replication_consistency_group_module_mock.module.fail_json.call_args[1]['msg'] diff --git a/ansible_collections/dellemc/powerflex/tests/unit/plugins/modules/test_replication_pair.py b/ansible_collections/dellemc/powerflex/tests/unit/plugins/modules/test_replication_pair.py index 81787de8f..e3c9cf18d 100644 --- a/ansible_collections/dellemc/powerflex/tests/unit/plugins/modules/test_replication_pair.py +++ b/ansible_collections/dellemc/powerflex/tests/unit/plugins/modules/test_replication_pair.py @@ -1,11 +1,10 @@ -# Copyright: (c) 2023, Dell Technologies +# Copyright: (c) 2024, Dell Technologies # Apache License version 2.0 (see MODULE-LICENSE or http://www.apache.org/licenses/LICENSE-2.0.txt) """Unit Tests for replication pair module on PowerFlex""" from __future__ import (absolute_import, division, print_function) -from unittest.mock import Mock __metaclass__ = type @@ -16,6 +15,8 @@ from ansible_collections.dellemc.powerflex.tests.unit.plugins.module_utils.mock_ import MockApiException from ansible_collections.dellemc.powerflex.plugins.module_utils.storage.dell \ import utils +from ansible_collections.dellemc.powerflex.tests.unit.plugins.module_utils.mock_fail_json \ + import FailJsonException, fail_json utils.get_logger = MagicMock() utils.get_powerflex_gateway_host_connection = MagicMock() @@ -26,7 +27,7 @@ basic.AnsibleModule = MagicMock() from ansible_collections.dellemc.powerflex.plugins.modules.replication_pair import PowerFlexReplicationPair -class TestPowerflexReplicationPair(): +class TestPowerflexReplicationPair: get_module_args = MockReplicationPairApi.REPLICATION_PAIR_COMMON_ARGS @@ -37,6 +38,19 @@ class TestPowerflexReplicationPair(): replication_pair_module_mock.module.check_mode = False return replication_pair_module_mock + @pytest.fixture + def replication_pair_mock(self): + replication_pair_mock = PowerFlexReplicationPair() + replication_pair_mock.module.check_mode = False + replication_pair_mock.module.fail_json = fail_json + return replication_pair_mock + + def capture_fail_json_call(self, error_msg, device_module_mock): + try: + device_module_mock.perform_module_operation() + except FailJsonException as fj_object: + assert error_msg in fj_object.message + def test_get_pair_details(self, replication_pair_module_mock): self.get_module_args.update({ "pair_name": "test_pair", @@ -235,3 +249,115 @@ class TestPowerflexReplicationPair(): replication_pair_module_mock.perform_module_operation() assert "Failed to get the replication pairs for replication consistency group" in \ replication_pair_module_mock.module.fail_json.call_args[1]['msg'] + + def test_rcg_application_pairs(self, replication_pair_module_mock): + self.get_module_args.update({ + "rcg_name": "test_rcg", + "pairs": [{"source_volume_name": "src_vol", "target_volume_name": "dest_vol", "source_volume_id": None, + "target_volume_id": None, "copy_type": "OnlineCopy", "name": "test_pair"}], + "state": "present" + }) + replication_pair_module_mock.module.params = self.get_module_args + replication_pair_module_mock.powerflex_conn.volume.get = MagicMock( + return_value=MockReplicationPairApi.get_volume_details() + ) + replication_pair_module_mock.get_rcg = MagicMock(return_value={"id": 123, "name": "test_rcg"}) + replication_pair_module_mock.powerflex_conn.replication_consistency_group.get_replication_pairs = MagicMock( + return_value=MockReplicationPairApi.get_pair_details()) + replication_pair_module_mock.perform_module_operation() + replication_pair_module_mock.powerflex_conn.replication_pair.add.assert_called() + + def test_get_rcg_name(self, replication_pair_mock): + self.get_module_args.update({ + "rcg_name": "test_rcg", + "pairs": [{"source_volume_name": "src_vol", "target_volume_name": "dest_vol", "source_volume_id": None, + "target_volume_id": None, "copy_type": "OnlineCopy", "name": "test_pair"}], + "state": "present" + }) + replication_pair_mock.module.params = self.get_module_args + replication_pair_mock.powerflex_conn.replication_consistency_group = MagicMock( + return_value=[{"name": "test_rcg", "id": 12}] + ) + replication_pair_mock.powerflex_conn.replication_consistency_group.get_replication_pairs = MagicMock( + return_value=MockReplicationPairApi.get_pair_details()) + replication_pair_mock.create_replication_pairs = MagicMock(return_value=True) + replication_pair_mock.get_rcg_replication_pairs = MagicMock(return_value={}) + replication_pair_mock.get_replication_pair = MagicMock(return_value=None) + replication_pair_mock.validate_pause_or_resume = MagicMock(return_value=True) + replication_pair_mock.perform_module_operation() + assert replication_pair_mock.module.exit_json.call_args[1]['changed'] is True + + def test_get_rcg_id(self, replication_pair_mock): + self.get_module_args.update({ + "rcg_id": 12, + "pairs": [{"source_volume_name": "src_vol", "target_volume_name": "dest_vol", "source_volume_id": None, + "target_volume_id": None, "copy_type": "OnlineCopy", "name": "test_pair"}], + "state": "present" + }) + replication_pair_mock.module.params = self.get_module_args + replication_pair_mock.powerflex_conn.replication_consistency_group = MagicMock( + return_value=[{"name": "test_rcg", "id": 12}] + ) + replication_pair_mock.powerflex_conn.replication_consistency_group.get_replication_pairs = MagicMock( + return_value=MockReplicationPairApi.get_pair_details()) + replication_pair_mock.create_replication_pairs = MagicMock(return_value=True) + replication_pair_mock.get_rcg_replication_pairs = MagicMock(return_value={}) + replication_pair_mock.get_replication_pair = MagicMock(return_value=None) + replication_pair_mock.validate_pause_or_resume = MagicMock(return_value=True) + replication_pair_mock.perform_module_operation() + assert replication_pair_mock.module.exit_json.call_args[1]['changed'] is True + + def test_get_rcg_exception(self, replication_pair_mock): + self.get_module_args.update({ + "rcg_id": 12, + "pairs": [{"source_volume_name": "src_vol", "target_volume_name": "dest_vol", "source_volume_id": None, + "target_volume_id": None, "copy_type": "OnlineCopy", "name": "test_pair"}], + "state": "present" + }) + replication_pair_mock.module.params = self.get_module_args + replication_pair_mock.powerflex_conn.replication_consistency_group.get = MagicMock(side_effect=MockApiException) + replication_pair_mock.create_replication_pairs = MagicMock(return_value=None) + self.capture_fail_json_call(MockReplicationPairApi.get_error_message('get_rcg_exception'), + replication_pair_mock) + + def test_input_validation(self, replication_pair_mock): + self.get_module_args.update({ + "rcg_id": None, "rcg_name": None, + "pairs": [{"source_volume_name": "src_vol", "target_volume_name": "dest_vol", "source_volume_id": None, + "target_volume_id": None, "copy_type": "OnlineCopy", "name": "test_pair"}], + "state": "present" + }) + replication_pair_mock.module.params = self.get_module_args + replication_pair_mock.validate_pairs = MagicMock(return_value=None) + self.capture_fail_json_call(MockReplicationPairApi.get_error_message('get_rcg_id_name_error'), + replication_pair_mock) + self.get_module_args.update({ + "rcg_name": "test_rcg", "pairs": None, "state": "present", "pause": False, + "pair_id": None, "pair_name": None + }) + replication_pair_mock.module.params = self.get_module_args + self.capture_fail_json_call(MockReplicationPairApi.get_error_message('get_pause_error'), + replication_pair_mock) + self.get_module_args.update({ + "rcg_name": "test_rcg", "pairs": None, "state": "present", "pause": False, + "pair_id": None, "pair_name": None + }) + replication_pair_mock.module.params = self.get_module_args + replication_pair_mock.validate_input = MagicMock(return_value=None) + self.capture_fail_json_call(MockReplicationPairApi.get_error_message('get_pause_or_resume_error'), + replication_pair_mock) + + def test_get_volume_exception(self, replication_pair_mock): + self.get_module_args.update({ + "rcg_name": "test_rcg", "pairs": None, "state": "present", "pause": False, + "pair_id": None, "pair_name": "test_pair" + }) + replication_pair_mock.module.params = self.get_module_args + replication_pair_mock.validate_input = MagicMock(return_value=None) + replication_pair_mock.powerflex_conn.replication_consistency_group.get_replication_pairs = MagicMock( + return_value=MockReplicationPairApi.get_pair_details()) + replication_pair_mock.powerflex_conn.volume.get = MagicMock( + side_effect=MockApiException + ) + self.capture_fail_json_call(MockReplicationPairApi.get_error_message('get_volume_exception'), + replication_pair_mock) diff --git a/ansible_collections/dellemc/powerflex/tests/unit/plugins/modules/test_sdc.py b/ansible_collections/dellemc/powerflex/tests/unit/plugins/modules/test_sdc.py new file mode 100644 index 000000000..dbf9ac1e0 --- /dev/null +++ b/ansible_collections/dellemc/powerflex/tests/unit/plugins/modules/test_sdc.py @@ -0,0 +1,192 @@ +# Copyright: (c) 2024, Dell Technologies + +# Apache License version 2.0 (see MODULE-LICENSE or http://www.apache.org/licenses/LICENSE-2.0.txt) + +"""Unit Tests for SDC module on PowerFlex""" + +from __future__ import (absolute_import, division, print_function) + +__metaclass__ = type + +import pytest +# pylint: disable=unused-import +from ansible_collections.dellemc.powerflex.tests.unit.plugins.module_utils.libraries import initial_mock +from mock.mock import MagicMock +from ansible_collections.dellemc.powerflex.tests.unit.plugins.module_utils.mock_sdc_api import MockSdcApi +from ansible_collections.dellemc.powerflex.tests.unit.plugins.module_utils.mock_api_exception \ + import MockApiException +from ansible_collections.dellemc.powerflex.plugins.module_utils.storage.dell \ + import utils +from ansible_collections.dellemc.powerflex.tests.unit.plugins.module_utils.mock_fail_json \ + import FailJsonException, fail_json + +utils.get_logger = MagicMock() +utils.get_powerflex_gateway_host_connection = MagicMock() +utils.PowerFlexClient = MagicMock() + +from ansible.module_utils import basic +basic.AnsibleModule = MagicMock() +from ansible_collections.dellemc.powerflex.plugins.modules.sdc import PowerFlexSdc + + +class TestPowerflexSdc(): + + get_module_args = MockSdcApi.COMMON_ARGS + + @pytest.fixture + def sdc_module_mock(self, mocker): + mocker.patch( + MockSdcApi.MODULE_UTILS_PATH + '.PowerFlexClient', + new=MockApiException) + sdc_module_mock = PowerFlexSdc() + sdc_module_mock.module.check_mode = False + sdc_module_mock.module.fail_json = fail_json + return sdc_module_mock + + def capture_fail_json_call(self, error_msg, sdc_module_mock): + try: + sdc_module_mock.perform_module_operation() + except FailJsonException as fj_object: + assert error_msg in fj_object.message + + def test_get_sdc_details(self, sdc_module_mock): + self.get_module_args.update({ + "sdc_name": "test_sdc", + "state": "present" + }) + sdc_module_mock.module.params = self.get_module_args + sdc_module_mock.powerflex_conn.sdc.get = MagicMock( + return_value=MockSdcApi.get_sdc_details() + ) + sdc_module_mock.perform_module_operation() + sdc_module_mock.powerflex_conn.sdc.get.assert_called() + + def test_get_sdc_details_empty_sdc_id_exception(self, sdc_module_mock): + self.get_module_args.update({ + "sdc_id": " ", + "state": "present" + }) + sdc_module_mock.module.params = self.get_module_args + self.capture_fail_json_call(MockSdcApi.get_sdc_exception_response( + 'get_sdc_details_empty_sdc_id_exception'), sdc_module_mock) + + def test_get_sdc_details_with_exception(self, sdc_module_mock): + self.get_module_args.update({ + "sdc_id": MockSdcApi.SDC_ID, + "state": "present" + }) + sdc_module_mock.module.params = self.get_module_args + sdc_module_mock.powerflex_conn.sdc.get = MagicMock( + side_effect=MockApiException) + self.capture_fail_json_call(MockSdcApi.get_sdc_exception_response( + 'get_sdc_details_with_exception'), sdc_module_mock) + + def test_get_sdc_details_mapped_volumes_with_exception(self, sdc_module_mock): + self.get_module_args.update({ + "sdc_id": MockSdcApi.SDC_ID, + "state": "present" + }) + sdc_module_mock.module.params = self.get_module_args + sdc_module_mock.powerflex_conn.sdc.get = MagicMock( + return_value=MockSdcApi.get_sdc_details() + ) + sdc_module_mock.powerflex_conn.sdc.get_mapped_volumes = MagicMock( + side_effect=MockApiException) + self.capture_fail_json_call(MockSdcApi.get_sdc_exception_response( + 'get_sdc_details_mapped_volumes_with_exception'), sdc_module_mock) + + def test_modify_sdc(self, sdc_module_mock): + self.get_module_args.update({ + "sdc_name": "test_sdc", + "performance_profile": "Compact", + "state": "present" + }) + sdc_module_mock.module.params = self.get_module_args + sdc_module_mock.powerflex_conn.sdc.get = MagicMock( + return_value=MockSdcApi.get_sdc_details() + ) + sdc_module_mock.powerflex_conn.sdc.set_performance_profile = MagicMock(return_value=True) + sdc_module_mock.perform_module_operation() + sdc_module_mock.powerflex_conn.sdc.set_performance_profile.assert_called() + + def test_modify_sdc_throws_exception(self, sdc_module_mock): + self.get_module_args.update({ + "sdc_name": "test_sdc", + "performance_profile": "Compact", + "state": "present" + }) + sdc_module_mock.module.params = self.get_module_args + sdc_module_mock.powerflex_conn.sdc.get = MagicMock( + return_value=MockSdcApi.get_sdc_details() + ) + sdc_module_mock.powerflex_conn.sdc.set_performance_profile = MagicMock( + side_effect=MockApiException) + self.capture_fail_json_call(MockSdcApi.get_sdc_exception_response( + 'modify_sdc_throws_exception'), sdc_module_mock) + + def test_rename_sdc(self, sdc_module_mock): + self.get_module_args.update({ + "sdc_name": "test_sdc", + "sdc_new_name": "test_sdc_renamed", + "state": "present" + }) + sdc_module_mock.module.params = self.get_module_args + sdc_module_mock.powerflex_conn.sdc.get = MagicMock( + return_value=MockSdcApi.get_sdc_details() + ) + sdc_module_mock.powerflex_conn.sdc.rename = MagicMock(return_value=True) + sdc_module_mock.perform_module_operation() + sdc_module_mock.powerflex_conn.sdc.rename.assert_called() + + def test_rename_sdc_empty_new_name_exception(self, sdc_module_mock): + self.get_module_args.update({ + "sdc_name": "test_sdc", + "sdc_new_name": " ", + "state": "present" + }) + sdc_module_mock.module.params = self.get_module_args + sdc_module_mock.powerflex_conn.sdc.get = MagicMock( + return_value=MockSdcApi.get_sdc_details() + ) + self.capture_fail_json_call(MockSdcApi.get_sdc_exception_response( + 'rename_sdc_empty_new_name_exception'), sdc_module_mock) + + def test_rename_sdc_throws_exception(self, sdc_module_mock): + self.get_module_args.update({ + "sdc_name": "test_sdc", + "sdc_new_name": "test_sdc_renamed", + "state": "present" + }) + sdc_module_mock.module.params = self.get_module_args + sdc_module_mock.powerflex_conn.sdc.get = MagicMock( + return_value=MockSdcApi.get_sdc_details() + ) + sdc_module_mock.powerflex_conn.sdc.rename = MagicMock(side_effect=MockApiException) + self.capture_fail_json_call(MockSdcApi.get_sdc_exception_response( + 'rename_sdc_throws_exception'), sdc_module_mock) + + def test_remove_sdc(self, sdc_module_mock): + self.get_module_args.update({ + "sdc_name": "test_sdc", + "state": "absent" + }) + sdc_module_mock.module.params = self.get_module_args + sdc_module_mock.powerflex_conn.sdc.get = MagicMock( + return_value=MockSdcApi.get_sdc_details() + ) + sdc_module_mock.powerflex_conn.sdc.delete = MagicMock(return_value=True) + sdc_module_mock.perform_module_operation() + sdc_module_mock.powerflex_conn.sdc.delete.assert_called() + + def test_remove_sdc_throws_exception(self, sdc_module_mock): + self.get_module_args.update({ + "sdc_ip": "1.1.1.1", + "state": "absent" + }) + sdc_module_mock.module.params = self.get_module_args + sdc_module_mock.powerflex_conn.sdc.get = MagicMock( + return_value=MockSdcApi.get_sdc_details() + ) + sdc_module_mock.powerflex_conn.sdc.delete = MagicMock(side_effect=MockApiException) + self.capture_fail_json_call(MockSdcApi.get_sdc_exception_response( + 'remove_sdc_throws_exception'), sdc_module_mock) diff --git a/ansible_collections/dellemc/powerflex/tests/unit/plugins/modules/test_sds.py b/ansible_collections/dellemc/powerflex/tests/unit/plugins/modules/test_sds.py new file mode 100644 index 000000000..30e9a589f --- /dev/null +++ b/ansible_collections/dellemc/powerflex/tests/unit/plugins/modules/test_sds.py @@ -0,0 +1,630 @@ +# Copyright: (c) 2024, Dell Technologies + +# Apache License version 2.0 (see MODULE-LICENSE or http://www.apache.org/licenses/LICENSE-2.0.txt) + +"""Unit Tests for SDS module on PowerFlex""" + +from __future__ import (absolute_import, division, print_function) + +__metaclass__ = type + +import pytest +# pylint: disable=unused-import +from ansible_collections.dellemc.powerflex.tests.unit.plugins.module_utils.libraries import initial_mock +from mock.mock import MagicMock +from ansible_collections.dellemc.powerflex.tests.unit.plugins.module_utils.mock_sds_api import MockSDSApi +from ansible_collections.dellemc.powerflex.plugins.modules.sds import \ + SDSHandler +from ansible_collections.dellemc.powerflex.tests.unit.plugins.module_utils.mock_sdk_response \ + import MockSDKResponse +from ansible_collections.dellemc.powerflex.tests.unit.plugins.module_utils.mock_api_exception \ + import MockApiException +from ansible_collections.dellemc.powerflex.tests.unit.plugins.module_utils.libraries.powerflex_unit_base \ + import PowerFlexUnitBase + +from ansible_collections.dellemc.powerflex.plugins.modules.sds import PowerFlexSDS + + +class TestPowerflexSDS(PowerFlexUnitBase): + + get_module_args = MockSDSApi.SDS_COMMON_ARGS + ip1 = "10.47.xxx.xxx" + ip2 = "10.46.xxx.xxx" + + @pytest.fixture + def module_object(self): + return PowerFlexSDS + + def test_get_sds_response(self, powerflex_module_mock): + self.set_module_params( + powerflex_module_mock, + self.get_module_args, + { + 'sds_id': '8f3bb0cc00000002', + 'state': 'present' + }) + powerflex_module_mock.powerflex_conn.sds.get = MagicMock( + return_value=MockSDSApi.SDS_GET_LIST) + pd_resp = MockSDKResponse(MockSDSApi.PROTECTION_DOMAIN) + powerflex_module_mock.powerflex_conn.protection_domain.get = MagicMock( + return_value=pd_resp.__dict__['data']['protectiondomain']) + fs_resp = MockSDSApi.FAULT_SET_GET_LIST + powerflex_module_mock.powerflex_conn.fault_set.get = MagicMock( + return_value=fs_resp) + SDSHandler().handle( + powerflex_module_mock, powerflex_module_mock.module.params) + powerflex_module_mock.powerflex_conn.sds.get.assert_called() + + def test_get_sds_name_response(self, powerflex_module_mock): + self.set_module_params( + powerflex_module_mock, + self.get_module_args, + { + 'state': "present" + }) + powerflex_module_mock.powerflex_conn.sds.get = MagicMock( + return_value=MockSDSApi.SDS_GET_LIST) + pd_resp = MockSDKResponse(MockSDSApi.PROTECTION_DOMAIN) + powerflex_module_mock.powerflex_conn.protection_domain.get = MagicMock( + return_value=pd_resp.__dict__['data']['protectiondomain']) + fs_resp = MockSDSApi.FAULT_SET_GET_LIST + powerflex_module_mock.powerflex_conn.fault_set.get = MagicMock( + return_value=fs_resp) + SDSHandler().handle( + powerflex_module_mock, powerflex_module_mock.module.params) + powerflex_module_mock.powerflex_conn.sds.get.assert_called() + + def test_get_sds_exception(self, powerflex_module_mock): + self.set_module_params( + powerflex_module_mock, + self.get_module_args, + { + 'state': "present" + }) + powerflex_module_mock.powerflex_conn.sds.get = MagicMock( + side_effect=MockApiException) + self.capture_fail_json_call( + MockSDSApi.get_sds_exception_response( + 'get_sds_exception'), powerflex_module_mock, SDSHandler) + + def test_create_sds_response(self, powerflex_module_mock): + self.set_module_params( + powerflex_module_mock, + self.get_module_args, + { + 'protection_domain_name': 'test_domain', + 'fault_set_name': 'fault_set_name', + 'sds_ip_list': + [ + { + 'ip': self.ip1, + 'role': "all" + } + ], + 'sds_ip_state': 'present-in-sds', + 'rmcache_enabled': True, + 'rmcache_size': 128, + 'performance_profile': "HighPerformance", + 'state': "present" + }) + pd_resp = MockSDKResponse(MockSDSApi.PROTECTION_DOMAIN) + powerflex_module_mock.powerflex_conn.protection_domain.get = MagicMock( + return_value=pd_resp.__dict__['data']['protectiondomain']) + fs_resp = MockSDSApi.FAULT_SET_GET_LIST + powerflex_module_mock.powerflex_conn.fault_set.get = MagicMock( + return_value=fs_resp) + powerflex_module_mock.get_sds_details = MagicMock( + return_value=None) + SDSHandler().handle( + powerflex_module_mock, powerflex_module_mock.module.params) + powerflex_module_mock.powerflex_conn.sds.create.assert_called() + + def test_create_sds_exception(self, powerflex_module_mock): + self.set_module_params( + powerflex_module_mock, + self.get_module_args, + { + 'protection_domain_name': 'test_domain', + 'fault_set_name': 'fault_set_name', + 'sds_ip_list': + [ + { + 'ip': self.ip1, + 'role': "all" + } + ], + 'sds_ip_state': 'present-in-sds', + 'rmcache_enabled': True, + 'rmcache_size': 128, + 'performance_profile': "HighPerformance", + 'state': "present" + }) + pd_resp = MockSDKResponse(MockSDSApi.PROTECTION_DOMAIN) + powerflex_module_mock.powerflex_conn.protection_domain.get = MagicMock( + return_value=pd_resp.__dict__['data']['protectiondomain']) + powerflex_module_mock.get_fault_set = MagicMock( + return_value=MockSDSApi.FAULT_SET_GET_LIST[0]) + powerflex_module_mock.powerflex_conn.sds.create = MagicMock( + side_effect=MockApiException) + self.capture_fail_json_call( + MockSDSApi.get_sds_exception_response( + 'create_sds_exception'), powerflex_module_mock, SDSHandler) + + def test_rename_sds_response(self, powerflex_module_mock): + self.set_module_params( + powerflex_module_mock, + self.get_module_args, + { + "sds_new_name": "node0_new", + "state": "present" + }) + powerflex_module_mock.get_sds_details = MagicMock( + return_value=MockSDSApi.SDS_GET_LIST[0]) + SDSHandler().handle( + powerflex_module_mock, powerflex_module_mock.module.params) + powerflex_module_mock.powerflex_conn.sds.rename.assert_called() + + def test_modify_rfcache_enabled_response(self, powerflex_module_mock): + self.set_module_params( + powerflex_module_mock, + self.get_module_args, + { + "rfcache_enabled": False, + "state": "present" + }) + powerflex_module_mock.get_sds_details = MagicMock( + return_value=MockSDSApi.SDS_GET_LIST[0]) + SDSHandler().handle( + powerflex_module_mock, powerflex_module_mock.module.params) + powerflex_module_mock.powerflex_conn.sds.set_rfcache_enabled.assert_called() + + def test_modify_rmcache_enabled_response(self, powerflex_module_mock): + self.set_module_params( + powerflex_module_mock, + self.get_module_args, + { + "rmcache_enabled": False, + "state": "present" + }) + powerflex_module_mock.get_sds_details = MagicMock( + return_value=MockSDSApi.SDS_GET_LIST[0]) + SDSHandler().handle( + powerflex_module_mock, powerflex_module_mock.module.params) + powerflex_module_mock.powerflex_conn.sds.set_rmcache_enabled.assert_called() + + def test_modify_rmcache_size_response(self, powerflex_module_mock): + self.set_module_params( + powerflex_module_mock, + self.get_module_args, + { + "rmcache_size": 256, + "state": "present" + }) + powerflex_module_mock.get_sds_details = MagicMock( + return_value=MockSDSApi.SDS_GET_LIST[0]) + SDSHandler().handle( + powerflex_module_mock, powerflex_module_mock.module.params) + powerflex_module_mock.powerflex_conn.sds.set_rmcache_size.assert_called() + + def test_rmcache_size_exception(self, powerflex_module_mock): + self.set_module_params( + powerflex_module_mock, + self.get_module_args, + { + 'protection_domain_name': 'test_domain', + 'fault_set_name': 'fault_set_name', + 'sds_ip_list': + [ + { + 'ip': self.ip1, + 'role': "all" + } + ], + 'sds_ip_state': 'present-in-sds', + 'rmcache_enabled': False, + 'rmcache_size': 128, + 'state': "present" + }) + pd_resp = MockSDKResponse(MockSDSApi.PROTECTION_DOMAIN) + powerflex_module_mock.powerflex_conn.protection_domain.get = MagicMock( + return_value=pd_resp.__dict__['data']['protectiondomain']) + fs_resp = MockSDSApi.FAULT_SET_GET_LIST + powerflex_module_mock.powerflex_conn.fault_set.get = MagicMock( + return_value=fs_resp) + self.capture_fail_json_call( + MockSDSApi.get_sds_exception_response( + 'rmcache_size_exception'), powerflex_module_mock, SDSHandler) + + def test_create_sds_wo_sds_name_exception(self, powerflex_module_mock): + self.set_module_params( + powerflex_module_mock, + self.get_module_args, + { + 'sds_name': None, + 'protection_domain_name': 'test_domain', + 'fault_set_name': 'fault_set_name', + 'sds_ip_list': + [ + { + 'ip': self.ip1, + 'role': "all" + } + ], + 'sds_ip_state': 'present-in-sds', + 'rmcache_enabled': True, + 'rmcache_size': 128, + 'state': "present" + }) + pd_resp = MockSDKResponse(MockSDSApi.PROTECTION_DOMAIN) + powerflex_module_mock.powerflex_conn.protection_domain.get = MagicMock( + return_value=pd_resp.__dict__['data']['protectiondomain']) + fs_resp = MockSDSApi.FAULT_SET_GET_LIST + powerflex_module_mock.powerflex_conn.fault_set.get = MagicMock( + return_value=fs_resp) + self.capture_fail_json_call( + MockSDSApi.get_sds_exception_response( + 'create_sds_wo_sds_name'), powerflex_module_mock, SDSHandler) + + def test_create_sds_wo_pd_exception(self, powerflex_module_mock): + self.set_module_params( + powerflex_module_mock, + self.get_module_args, + { + 'fault_set_name': 'fault_set_name', + 'sds_ip_list': + [ + { + 'ip': self.ip1, + 'role': "all" + } + ], + 'sds_ip_state': 'present-in-sds', + 'rmcache_enabled': True, + 'rmcache_size': 128, + 'state': "present" + }) + powerflex_module_mock.powerflex_conn.protection_domain.get = MagicMock( + return_value=None) + fs_resp = MockSDSApi.FAULT_SET_GET_LIST + powerflex_module_mock.powerflex_conn.fault_set.get = MagicMock( + return_value=fs_resp) + self.capture_fail_json_call( + MockSDSApi.get_sds_exception_response( + 'create_sds_wo_pd'), powerflex_module_mock, SDSHandler) + + def test_create_sds_wo_sds_ip_list_exception(self, powerflex_module_mock): + self.set_module_params( + powerflex_module_mock, + self.get_module_args, + { + 'protection_domain_name': 'test_domain', + 'fault_set_name': 'fault_set_name', + 'sds_ip_list': [], + 'sds_ip_state': 'present-in-sds', + 'rmcache_enabled': True, + 'rmcache_size': 128, + 'state': "present" + }) + pd_resp = MockSDKResponse(MockSDSApi.PROTECTION_DOMAIN) + powerflex_module_mock.powerflex_conn.protection_domain.get = MagicMock( + return_value=pd_resp.__dict__['data']['protectiondomain']) + fs_resp = MockSDSApi.FAULT_SET_GET_LIST + powerflex_module_mock.powerflex_conn.fault_set.get = MagicMock( + return_value=fs_resp) + self.capture_fail_json_call( + MockSDSApi.get_sds_exception_response( + 'create_sds_wo_sds_ip_list'), powerflex_module_mock, SDSHandler) + + def test_create_sds_incorrect_sds_ip_state_exception(self, powerflex_module_mock): + self.set_module_params( + powerflex_module_mock, + self.get_module_args, + { + 'protection_domain_name': 'test_domain', + 'fault_set_name': 'fault_set_name', + 'sds_ip_list': + [ + { + 'ip': self.ip1, + 'role': "all" + } + ], + 'sds_ip_state': 'absent-in-sds', + 'rmcache_enabled': True, + 'rmcache_size': 128, + 'state': "present" + }) + pd_resp = MockSDKResponse(MockSDSApi.PROTECTION_DOMAIN) + powerflex_module_mock.powerflex_conn.protection_domain.get = MagicMock( + return_value=pd_resp.__dict__['data']['protectiondomain']) + fs_resp = MockSDSApi.FAULT_SET_GET_LIST + powerflex_module_mock.powerflex_conn.fault_set.get = MagicMock( + return_value=fs_resp) + self.capture_fail_json_call( + MockSDSApi.get_sds_exception_response( + 'create_sds_incorrect_sds_ip_state'), powerflex_module_mock, SDSHandler) + + def test_create_sds_sds_id_exception(self, powerflex_module_mock): + self.set_module_params( + powerflex_module_mock, + self.get_module_args, + { + 'sds_id': "sds_id_1", + 'protection_domain_name': 'test_domain', + 'fault_set_name': 'fault_set_name', + 'sds_ip_list': + [ + { + 'ip': self.ip1, + 'role': "all" + } + ], + 'sds_ip_state': 'present-in-sds', + 'rmcache_enabled': True, + 'rmcache_size': 128, + 'state': "present" + }) + pd_resp = MockSDKResponse(MockSDSApi.PROTECTION_DOMAIN) + powerflex_module_mock.powerflex_conn.protection_domain.get = MagicMock( + return_value=pd_resp.__dict__['data']['protectiondomain']) + fs_resp = MockSDSApi.FAULT_SET_GET_LIST + powerflex_module_mock.powerflex_conn.fault_set.get = MagicMock( + return_value=fs_resp) + self.capture_fail_json_call( + MockSDSApi.get_sds_exception_response( + 'create_sds_sds_id'), powerflex_module_mock, SDSHandler) + + def test_create_sds_sds_new_name_exception(self, powerflex_module_mock): + self.set_module_params( + powerflex_module_mock, + self.get_module_args, + { + 'sds_new_name': "sds_new_name", + 'protection_domain_name': 'test_domain', + 'fault_set_name': 'fault_set_name', + 'sds_ip_list': + [ + { + 'ip': self.ip1, + 'role': "all" + } + ], + 'sds_ip_state': 'present-in-sds', + 'rmcache_enabled': True, + 'rmcache_size': 128, + 'state': "present" + }) + pd_resp = MockSDKResponse(MockSDSApi.PROTECTION_DOMAIN) + powerflex_module_mock.powerflex_conn.protection_domain.get = MagicMock( + return_value=pd_resp.__dict__['data']['protectiondomain']) + fs_resp = MockSDSApi.FAULT_SET_GET_LIST + powerflex_module_mock.powerflex_conn.fault_set.get = MagicMock( + return_value=fs_resp) + self.capture_fail_json_call( + MockSDSApi.get_sds_exception_response( + 'create_sds_sds_new_name'), powerflex_module_mock, SDSHandler) + + def test_modify_performance_profile_response(self, powerflex_module_mock): + self.set_module_params( + powerflex_module_mock, + self.get_module_args, + { + "performance_profile": "Compact", + "state": "present" + }) + powerflex_module_mock.get_sds_details = MagicMock( + return_value=MockSDSApi.SDS_GET_LIST[0]) + SDSHandler().handle( + powerflex_module_mock, powerflex_module_mock.module.params) + powerflex_module_mock.powerflex_conn.sds.set_performance_parameters.assert_called() + + def test_rename_sds_exception(self, powerflex_module_mock): + self.set_module_params( + powerflex_module_mock, + self.get_module_args, + { + "sds_new_name": "node0_new", + "state": "present" + }) + powerflex_module_mock.get_sds_details = MagicMock( + return_value=MockSDSApi.SDS_GET_LIST[0]) + powerflex_module_mock.powerflex_conn.sds.rename = MagicMock( + side_effect=MockApiException) + self.capture_fail_json_call( + MockSDSApi.get_sds_exception_response( + 'rename_sds_exception'), powerflex_module_mock, SDSHandler) + + def test_rename_sds_empty_exception(self, powerflex_module_mock): + self.set_module_params( + powerflex_module_mock, + self.get_module_args, + { + "sds_new_name": "", + "state": "present" + }) + powerflex_module_mock.get_sds_details = MagicMock( + return_value=MockSDSApi.SDS_GET_LIST[0]) + self.capture_fail_json_call( + MockSDSApi.get_sds_exception_response( + 'rename_sds_empty_exception'), powerflex_module_mock, SDSHandler) + + def test_update_role_response(self, powerflex_module_mock): + self.set_module_params( + powerflex_module_mock, + self.get_module_args, + { + "sds_ip_list": + [ + { + "ip": self.ip2, + "role": "all" + } + ], + "sds_ip_state": "present-in-sds", + "state": "present" + }) + powerflex_module_mock.get_sds_details = MagicMock( + return_value=MockSDSApi.SDS_GET_LIST[0]) + SDSHandler().handle( + powerflex_module_mock, powerflex_module_mock.module.params) + powerflex_module_mock.powerflex_conn.sds.set_ip_role.assert_called() + + def test_update_role_exception(self, powerflex_module_mock): + self.set_module_params( + powerflex_module_mock, + self.get_module_args, + { + "sds_ip_list": + [ + { + "ip": self.ip2, + "role": "all" + } + ], + "sds_ip_state": "present-in-sds", + "state": "present" + }) + powerflex_module_mock.get_sds_details = MagicMock( + return_value=MockSDSApi.SDS_GET_LIST[0]) + powerflex_module_mock.powerflex_conn.sds.set_ip_role = MagicMock( + side_effect=MockApiException) + self.capture_fail_json_call( + MockSDSApi.get_sds_exception_response( + 'set_ip_role_exception'), powerflex_module_mock, SDSHandler) + + def test_add_ip_response(self, powerflex_module_mock): + self.set_module_params( + powerflex_module_mock, + self.get_module_args, + { + "sds_ip_list": + [ + { + "ip": "10.xx.xx.xx", + "role": "all" + } + ], + "sds_ip_state": "present-in-sds", + "state": "present" + }) + powerflex_module_mock.get_sds_details = MagicMock( + return_value=MockSDSApi.SDS_GET_LIST[0]) + SDSHandler().handle( + powerflex_module_mock, powerflex_module_mock.module.params) + powerflex_module_mock.powerflex_conn.sds.add_ip.assert_called() + + def test_add_ip_exception(self, powerflex_module_mock): + self.set_module_params( + powerflex_module_mock, + self.get_module_args, + { + "sds_ip_list": + [ + { + "ip": "10.xx.xx.xx", + "role": "all" + } + ], + "sds_ip_state": "present-in-sds", + "state": "present" + }) + powerflex_module_mock.get_sds_details = MagicMock( + return_value=MockSDSApi.SDS_GET_LIST[0]) + powerflex_module_mock.powerflex_conn.sds.add_ip = MagicMock( + side_effect=MockApiException) + self.capture_fail_json_call( + MockSDSApi.get_sds_exception_response( + 'add_ip_exception'), powerflex_module_mock, SDSHandler) + + def test_remove_ip_response(self, powerflex_module_mock): + self.set_module_params( + powerflex_module_mock, + self.get_module_args, + { + "sds_ip_list": + [ + { + "ip": self.ip2, + "role": "sdcOnly" + } + ], + "sds_ip_state": "absent-in-sds", + "state": "present" + }) + powerflex_module_mock.get_sds_details = MagicMock( + return_value=MockSDSApi.SDS_GET_LIST[0]) + SDSHandler().handle( + powerflex_module_mock, powerflex_module_mock.module.params) + powerflex_module_mock.powerflex_conn.sds.remove_ip.assert_called() + + def test_remove_ip_idempotent(self, powerflex_module_mock): + self.set_module_params( + powerflex_module_mock, + self.get_module_args, + { + "sds_ip_list": + [ + { + "ip": "10.45.xxx.xxx", + "role": "sdcOnly" + } + ], + "sds_ip_state": "absent-in-sds", + "state": "present" + }) + powerflex_module_mock.get_sds_details = MagicMock( + return_value=MockSDSApi.SDS_GET_LIST[0]) + SDSHandler().handle( + powerflex_module_mock, powerflex_module_mock.module.params) + powerflex_module_mock.powerflex_conn.sds.get.assert_called() + + def test_remove_ip_exception(self, powerflex_module_mock): + self.set_module_params( + powerflex_module_mock, + self.get_module_args, + { + "sds_ip_list": + [ + { + "ip": self.ip2, + "role": "sdcOnly" + } + ], + "sds_ip_state": "absent-in-sds", + "state": "present" + }) + powerflex_module_mock.get_sds_details = MagicMock( + return_value=MockSDSApi.SDS_GET_LIST[0]) + powerflex_module_mock.powerflex_conn.sds.remove_ip = MagicMock( + side_effect=MockApiException) + self.capture_fail_json_call( + MockSDSApi.get_sds_exception_response( + 'remove_ip_exception'), powerflex_module_mock, SDSHandler) + + def test_delete_sds_response(self, powerflex_module_mock): + self.set_module_params( + powerflex_module_mock, + self.get_module_args, + { + 'state': 'absent' + }) + powerflex_module_mock.get_sds_details = MagicMock( + return_value=MockSDSApi.SDS_GET_LIST[0]) + SDSHandler().handle( + powerflex_module_mock, powerflex_module_mock.module.params) + powerflex_module_mock.powerflex_conn.sds.delete.assert_called() + + def test_delete_fault_set_exception(self, powerflex_module_mock): + self.set_module_params( + powerflex_module_mock, + self.get_module_args, + { + 'state': 'absent' + }) + powerflex_module_mock.get_sds_details = MagicMock( + return_value=MockSDSApi.SDS_GET_LIST[0]) + powerflex_module_mock.powerflex_conn.sds.delete = MagicMock( + side_effect=MockApiException) + self.capture_fail_json_call( + MockSDSApi.get_sds_exception_response( + 'delete_sds_exception'), powerflex_module_mock, SDSHandler) diff --git a/ansible_collections/dellemc/powerflex/tests/unit/plugins/modules/test_snapshot_policy.py b/ansible_collections/dellemc/powerflex/tests/unit/plugins/modules/test_snapshot_policy.py new file mode 100644 index 000000000..275b7b007 --- /dev/null +++ b/ansible_collections/dellemc/powerflex/tests/unit/plugins/modules/test_snapshot_policy.py @@ -0,0 +1,502 @@ +# Copyright: (c) 2023, Dell Technologies + +# Apache License version 2.0 (see MODULE-LICENSE or http://www.apache.org/licenses/LICENSE-2.0.txt) + +"""Unit Tests for snapshot policy module on PowerFlex""" + +from __future__ import (absolute_import, division, print_function) + +__metaclass__ = type + +import pytest +from mock.mock import MagicMock +from ansible_collections.dellemc.powerflex.tests.unit.plugins.module_utils.mock_snapshot_policy_api import MockSnapshotPolicyApi +from ansible_collections.dellemc.powerflex.tests.unit.plugins.module_utils.mock_api_exception \ + import MockApiException +from ansible_collections.dellemc.powerflex.plugins.module_utils.storage.dell \ + import utils +from ansible_collections.dellemc.powerflex.tests.unit.plugins.module_utils.mock_fail_json \ + import FailJsonException, fail_json + +utils.get_logger = MagicMock() +utils.get_powerflex_gateway_host_connection = MagicMock() +utils.PowerFlexClient = MagicMock() + +from ansible.module_utils import basic +basic.AnsibleModule = MagicMock() +from ansible_collections.dellemc.powerflex.plugins.modules.snapshot_policy import PowerFlexSnapshotPolicy, SnapshotPolicyHandler + + +class TestPowerflexSnapshotPolicy(): + + get_module_args = MockSnapshotPolicyApi.SNAPSHOT_POLICY_COMMON_ARGS + + @pytest.fixture + def snapshot_policy_module_mock(self, mocker): + snapshot_policy_module_mock = PowerFlexSnapshotPolicy() + snapshot_policy_module_mock.module.check_mode = False + snapshot_policy_module_mock.module.fail_json = fail_json + return snapshot_policy_module_mock + + def capture_fail_json_call(self, error_msg, snapshot_policy_module_mock): + try: + SnapshotPolicyHandler().handle(snapshot_policy_module_mock, snapshot_policy_module_mock.module.params) + except FailJsonException as fj_object: + assert error_msg == fj_object.message + + def test_get_snapshot_policy_detail_using_name(self, snapshot_policy_module_mock): + self.get_module_args.update({ + "snapshot_policy_name": "testing", + "state": "present" + }) + snapshot_policy_module_mock.module.params = self.get_module_args + snapshot_policy_module_mock.powerflex_conn.snapshot_policy.get = MagicMock( + return_value=MockSnapshotPolicyApi.SNAPSHOT_POLICY_GET_LIST + ) + snapshot_policy_module_mock.powerflex_conn.snapshot_policy.get_statistics = MagicMock( + return_value=MockSnapshotPolicyApi.SNAPSHOT_POLICY_STATISTICS + ) + SnapshotPolicyHandler().handle(snapshot_policy_module_mock, snapshot_policy_module_mock.module.params) + snapshot_policy_module_mock.powerflex_conn.snapshot_policy.get.assert_called() + snapshot_policy_module_mock.powerflex_conn.snapshot_policy.get_statistics.assert_called() + + def test_get_snapshot_policy_detail_using_id(self, snapshot_policy_module_mock): + self.get_module_args.update({ + "snapshot_policy_id": "testing", + "state": "present" + }) + snapshot_policy_module_mock.module.params = self.get_module_args + snapshot_policy_module_mock.powerflex_conn.snapshot_policy.get = MagicMock( + return_value=MockSnapshotPolicyApi.SNAPSHOT_POLICY_GET_LIST + ) + snapshot_policy_module_mock.powerflex_conn.snapshot_policy.get_statistics = MagicMock( + return_value=MockSnapshotPolicyApi.SNAPSHOT_POLICY_STATISTICS + ) + SnapshotPolicyHandler().handle(snapshot_policy_module_mock, snapshot_policy_module_mock.module.params) + snapshot_policy_module_mock.powerflex_conn.snapshot_policy.get.assert_called() + snapshot_policy_module_mock.powerflex_conn.snapshot_policy.get_statistics.assert_called() + + def test_get_snapshot_policy_details_with_exception(self, snapshot_policy_module_mock): + self.get_module_args.update({ + "snapshot_policy_name": "testing", + "state": "present" + }) + snapshot_policy_module_mock.module.params = self.get_module_args + snapshot_policy_module_mock.powerflex_conn.snapshot_policy.get = MagicMock( + return_value=MockSnapshotPolicyApi.SNAPSHOT_POLICY_GET_LIST + ) + snapshot_policy_module_mock.powerflex_conn.snapshot_policy.get_statistics = MagicMock( + side_effect=MockApiException + ) + self.capture_fail_json_call(MockSnapshotPolicyApi.get_snapshot_policy_exception_response('get_snapshot_policy_details_exception'), + snapshot_policy_module_mock) + + def test_create_snapshot_policy_using_name(self, snapshot_policy_module_mock): + self.get_module_args.update({ + "snapshot_policy_name": "testing", + "access_mode": "ReadOnly", + "secure_snapshots": True, + "auto_snapshot_creation_cadence": { + "time": 1, + "unit": "Hour"}, + "num_of_retained_snapshots_per_level": [20], + "state": "present" + }) + snapshot_policy_module_mock.module.params = self.get_module_args + snapshot_policy_module_mock.powerflex_conn.snapshot_policy.get = MagicMock( + return_value=None + ) + SnapshotPolicyHandler().handle(snapshot_policy_module_mock, snapshot_policy_module_mock.module.params) + snapshot_policy_module_mock.powerflex_conn.snapshot_policy.create.assert_called() + + def test_create_snapshot_policy_exception(self, snapshot_policy_module_mock): + self.get_module_args.update({ + "snapshot_policy_name": "testing", + "access_mode": "ReadOnly", + "secure_snapshots": True, + "auto_snapshot_creation_cadence": { + "time": 1, + "unit": "Hour"}, + "num_of_retained_snapshots_per_level": [20], + "state": "present" + }) + snapshot_policy_module_mock.module.params = self.get_module_args + snapshot_policy_module_mock.powerflex_conn.snapshot_policy.get = MagicMock( + return_value=None + ) + snapshot_policy_module_mock.powerflex_conn.snapshot_policy.create = MagicMock( + side_effect=MockApiException + ) + self.capture_fail_json_call(MockSnapshotPolicyApi.get_snapshot_policy_exception_response('create_exception'), + snapshot_policy_module_mock) + + def test_create_snapshot_policy_with_id_exception(self, snapshot_policy_module_mock): + self.get_module_args.update({ + "snapshot_policy_id": "testing", + "access_mode": "ReadOnly", + "secure_snapshots": True, + "auto_snapshot_creation_cadence": { + "time": 1, + "unit": "Hour"}, + "num_of_retained_snapshots_per_level": [20], + "state": "present" + }) + snapshot_policy_module_mock.module.params = self.get_module_args + snapshot_policy_module_mock.powerflex_conn.snapshot_policy.get = MagicMock( + return_value=None + ) + self.capture_fail_json_call(MockSnapshotPolicyApi.get_snapshot_policy_exception_response('create_id_exception'), + snapshot_policy_module_mock) + + def test_delete_snapshot_policy(self, snapshot_policy_module_mock): + self.get_module_args.update({ + "snapshot_policy_name": "testing", + "state": "absent" + }) + snapshot_policy_module_mock.module.params = self.get_module_args + snapshot_policy_module_mock.powerflex_conn.snapshot_policy.get = MagicMock( + return_value=MockSnapshotPolicyApi.SNAPSHOT_POLICY_GET_LIST + ) + SnapshotPolicyHandler().handle(snapshot_policy_module_mock, snapshot_policy_module_mock.module.params) + snapshot_policy_module_mock.powerflex_conn.snapshot_policy.delete.assert_called() + + def test_delete_snapshot_policy_exception(self, snapshot_policy_module_mock): + self.get_module_args.update({ + "snapshot_policy_name": "testing", + "state": "absent" + }) + snapshot_policy_module_mock.module.params = self.get_module_args + snapshot_policy_module_mock.powerflex_conn.snapshot_policy.get = MagicMock( + return_value=MockSnapshotPolicyApi.SNAPSHOT_POLICY_GET_LIST + ) + snapshot_policy_module_mock.powerflex_conn.snapshot_policy.delete = MagicMock( + side_effect=MockApiException + ) + self.capture_fail_json_call(MockSnapshotPolicyApi.get_snapshot_policy_exception_response('delete_exception'), + snapshot_policy_module_mock) + + def test_modify_snapshot_policy(self, snapshot_policy_module_mock): + self.get_module_args.update({ + "snapshot_policy_name": "testing", + "auto_snapshot_creation_cadence": { + "time": 20, + "unit": "Minute"}, + "num_of_retained_snapshots_per_level": [30], + "state": "present" + }) + snapshot_policy_module_mock.module.params = self.get_module_args + snapshot_policy_module_mock.powerflex_conn.snapshot_policy.get = MagicMock( + return_value=MockSnapshotPolicyApi.SNAPSHOT_POLICY_GET_LIST + ) + snapshot_policy_module_mock.powerflex_conn.snapshot_policy.get_statistics = MagicMock( + return_value=MockSnapshotPolicyApi.SNAPSHOT_POLICY_STATISTICS + ) + SnapshotPolicyHandler().handle(snapshot_policy_module_mock, snapshot_policy_module_mock.module.params) + snapshot_policy_module_mock.powerflex_conn.snapshot_policy.modify.assert_called() + + def test_modify_snapshot_policy_wo_snapshot_rule(self, snapshot_policy_module_mock): + self.get_module_args.update({ + "snapshot_policy_name": "testing", + "num_of_retained_snapshots_per_level": [30], + "state": "present" + }) + snapshot_policy_module_mock.module.params = self.get_module_args + snapshot_policy_module_mock.powerflex_conn.snapshot_policy.get = MagicMock( + return_value=MockSnapshotPolicyApi.SNAPSHOT_POLICY_GET_LIST + ) + snapshot_policy_module_mock.powerflex_conn.snapshot_policy.get_statistics = MagicMock( + return_value=MockSnapshotPolicyApi.SNAPSHOT_POLICY_STATISTICS + ) + SnapshotPolicyHandler().handle(snapshot_policy_module_mock, snapshot_policy_module_mock.module.params) + snapshot_policy_module_mock.powerflex_conn.snapshot_policy.modify.assert_called() + + def test_modify_snapshot_policy_wo_retention_rule(self, snapshot_policy_module_mock): + self.get_module_args.update({ + "snapshot_policy_name": "testing", + "auto_snapshot_creation_cadence": { + "time": 20, + "unit": "Minute"}, + "state": "present" + }) + snapshot_policy_module_mock.module.params = self.get_module_args + snapshot_policy_module_mock.powerflex_conn.snapshot_policy.get = MagicMock( + return_value=MockSnapshotPolicyApi.SNAPSHOT_POLICY_GET_LIST + ) + snapshot_policy_module_mock.powerflex_conn.snapshot_policy.get_statistics = MagicMock( + return_value=MockSnapshotPolicyApi.SNAPSHOT_POLICY_STATISTICS + ) + SnapshotPolicyHandler().handle(snapshot_policy_module_mock, snapshot_policy_module_mock.module.params) + snapshot_policy_module_mock.powerflex_conn.snapshot_policy.modify.assert_called() + + def test_modify_snapshot_policy_exception(self, snapshot_policy_module_mock): + self.get_module_args.update({ + "snapshot_policy_name": "testing", + "auto_snapshot_creation_cadence": { + "time": 20, + "unit": "Minute"}, + "num_of_retained_snapshots_per_level": [30], + "state": "present" + }) + snapshot_policy_module_mock.module.params = self.get_module_args + snapshot_policy_module_mock.powerflex_conn.snapshot_policy.get = MagicMock( + return_value=MockSnapshotPolicyApi.SNAPSHOT_POLICY_GET_LIST + ) + snapshot_policy_module_mock.powerflex_conn.snapshot_policy.get_statistics = MagicMock( + return_value=MockSnapshotPolicyApi.SNAPSHOT_POLICY_STATISTICS + ) + snapshot_policy_module_mock.powerflex_conn.snapshot_policy.modify = MagicMock( + side_effect=MockApiException + ) + self.capture_fail_json_call(MockSnapshotPolicyApi.get_snapshot_policy_exception_response('modify_exception'), + snapshot_policy_module_mock) + + def test_rename_snapshot_policy(self, snapshot_policy_module_mock): + self.get_module_args.update({ + "snapshot_policy_name": "testing", + "new_name": "testing_new", + "state": "present" + }) + snapshot_policy_module_mock.module.params = self.get_module_args + snapshot_policy_module_mock.powerflex_conn.snapshot_policy.get = MagicMock( + return_value=MockSnapshotPolicyApi.SNAPSHOT_POLICY_GET_LIST + ) + snapshot_policy_module_mock.powerflex_conn.snapshot_policy.get_statistics = MagicMock( + return_value=MockSnapshotPolicyApi.SNAPSHOT_POLICY_STATISTICS + ) + SnapshotPolicyHandler().handle(snapshot_policy_module_mock, snapshot_policy_module_mock.module.params) + snapshot_policy_module_mock.powerflex_conn.snapshot_policy.rename.assert_called() + + def test_rename_snapshot_policy_exception(self, snapshot_policy_module_mock): + self.get_module_args.update({ + "snapshot_policy_name": "testing", + "new_name": "testing_new", + "state": "present" + }) + snapshot_policy_module_mock.module.params = self.get_module_args + snapshot_policy_module_mock.powerflex_conn.snapshot_policy.get = MagicMock( + return_value=MockSnapshotPolicyApi.SNAPSHOT_POLICY_GET_LIST + ) + snapshot_policy_module_mock.powerflex_conn.snapshot_policy.get_statistics = MagicMock( + return_value=MockSnapshotPolicyApi.SNAPSHOT_POLICY_STATISTICS + ) + snapshot_policy_module_mock.powerflex_conn.snapshot_policy.rename = MagicMock( + side_effect=MockApiException + ) + self.capture_fail_json_call(MockSnapshotPolicyApi.get_snapshot_policy_exception_response('modify_exception'), + snapshot_policy_module_mock) + + def test_add_source_volume(self, snapshot_policy_module_mock): + self.get_module_args.update({ + "snapshot_policy_name": "testing", + "source_volume": [{ + "name": "source_volume_name", + "id": None, + "auto_snap_removal_action": None, + "detach_locked_auto_snapshots": None, + "state": "present"}], + "state": "present" + }) + snapshot_policy_module_mock.module.params = self.get_module_args + snapshot_policy_module_mock.powerflex_conn.snapshot_policy.get = MagicMock( + return_value=MockSnapshotPolicyApi.SNAPSHOT_POLICY_GET_LIST + ) + snapshot_policy_module_mock.powerflex_conn.snapshot_policy.get_statistics = MagicMock( + return_value=MockSnapshotPolicyApi.SNAPSHOT_POLICY_STATISTICS + ) + snapshot_policy_module_mock.powerflex_conn.volume.get = MagicMock( + return_value=MockSnapshotPolicyApi.VOLUME_GET_LIST + ) + SnapshotPolicyHandler().handle(snapshot_policy_module_mock, snapshot_policy_module_mock.module.params) + snapshot_policy_module_mock.powerflex_conn.snapshot_policy.add_source_volume.assert_called() + + def test_add_non_existing_volume_exception(self, snapshot_policy_module_mock): + self.get_module_args.update({ + "snapshot_policy_name": "testing", + "source_volume": [{ + "name": "non_existing_source_volume_name", + "id": None, + "auto_snap_removal_action": None, + "detach_locked_auto_snapshots": None}], + "source_volume_state": "present", + "state": "present" + }) + snapshot_policy_module_mock.module.params = self.get_module_args + snapshot_policy_module_mock.powerflex_conn.snapshot_policy.get = MagicMock( + return_value=MockSnapshotPolicyApi.SNAPSHOT_POLICY_GET_LIST + ) + snapshot_policy_module_mock.powerflex_conn.snapshot_policy.get_statistics = MagicMock( + return_value=MockSnapshotPolicyApi.SNAPSHOT_POLICY_STATISTICS + ) + snapshot_policy_module_mock.powerflex_conn.volume.get = MagicMock( + return_value=[] + ) + self.capture_fail_json_call(MockSnapshotPolicyApi.get_snapshot_policy_exception_response('add_non_existing_source_volume'), + snapshot_policy_module_mock) + + def test_add_source_volume_wo_id_or_name_exception(self, snapshot_policy_module_mock): + self.get_module_args.update({ + "snapshot_policy_name": "testing", + "source_volume": [{ + "name": None, + "id": None, + "auto_snap_removal_action": None, + "detach_locked_auto_snapshots": None, + "state": "present"}], + "state": "present" + }) + snapshot_policy_module_mock.module.params = self.get_module_args + snapshot_policy_module_mock.powerflex_conn.snapshot_policy.get = MagicMock( + return_value=MockSnapshotPolicyApi.SNAPSHOT_POLICY_GET_LIST + ) + snapshot_policy_module_mock.powerflex_conn.snapshot_policy.get_statistics = MagicMock( + return_value=MockSnapshotPolicyApi.SNAPSHOT_POLICY_STATISTICS + ) + self.capture_fail_json_call(MockSnapshotPolicyApi.get_snapshot_policy_exception_response('add_source_volume_wo_vol'), + snapshot_policy_module_mock) + + def test_add_source_volume_wo_id_and_name_exception(self, snapshot_policy_module_mock): + self.get_module_args.update({ + "snapshot_policy_name": "testing", + "source_volume": [{ + "name": "source_volume_name", + "id": "source_volume_id", + "auto_snap_removal_action": None, + "detach_locked_auto_snapshots": None, + "state": "present"}], + "state": "present" + }) + snapshot_policy_module_mock.module.params = self.get_module_args + snapshot_policy_module_mock.powerflex_conn.snapshot_policy.get = MagicMock( + return_value=MockSnapshotPolicyApi.SNAPSHOT_POLICY_GET_LIST + ) + snapshot_policy_module_mock.powerflex_conn.snapshot_policy.get_statistics = MagicMock( + return_value=MockSnapshotPolicyApi.SNAPSHOT_POLICY_STATISTICS + ) + self.capture_fail_json_call(MockSnapshotPolicyApi.get_snapshot_policy_exception_response('add_source_volume_vol_id_name'), + snapshot_policy_module_mock) + + def test_add_source_volume_exception(self, snapshot_policy_module_mock): + self.get_module_args.update({ + "snapshot_policy_name": "testing", + "source_volume": [{ + "name": "source_volume_name", + "id": None, + "auto_snap_removal_action": None, + "detach_locked_auto_snapshots": None, + "state": "present"}], + "state": "present" + }) + snapshot_policy_module_mock.module.params = self.get_module_args + snapshot_policy_module_mock.powerflex_conn.snapshot_policy.get = MagicMock( + return_value=MockSnapshotPolicyApi.SNAPSHOT_POLICY_GET_LIST + ) + snapshot_policy_module_mock.powerflex_conn.snapshot_policy.get_statistics = MagicMock( + return_value=MockSnapshotPolicyApi.SNAPSHOT_POLICY_STATISTICS + ) + snapshot_policy_module_mock.powerflex_conn.volume.get = MagicMock( + return_value=MockSnapshotPolicyApi.VOLUME_GET_LIST + ) + snapshot_policy_module_mock.powerflex_conn.snapshot_policy.add_source_volume = MagicMock( + side_effect=MockApiException + ) + self.capture_fail_json_call(MockSnapshotPolicyApi.get_snapshot_policy_exception_response('source_volume_exception'), + snapshot_policy_module_mock) + + def test_remove_source_volume(self, snapshot_policy_module_mock): + self.get_module_args.update({ + "snapshot_policy_name": "testing_2", + "source_volume": [{ + "id": "source_volume_id_2", + "name": None, + "auto_snap_removal_action": 'Remove', + "detach_locked_auto_snapshots": None, + "state": "absent"}], + "state": "present" + }) + snapshot_policy_module_mock.module.params = self.get_module_args + snapshot_policy_module_mock.powerflex_conn.snapshot_policy.get = MagicMock( + return_value=MockSnapshotPolicyApi.SNAPSHOT_POLICY_2_GET_LIST + ) + snapshot_policy_module_mock.powerflex_conn.snapshot_policy.get_statistics = MagicMock( + return_value=MockSnapshotPolicyApi.SNAPSHOT_POLICY_STATISTICS + ) + snapshot_policy_module_mock.powerflex_conn.volume.get = MagicMock( + return_value=MockSnapshotPolicyApi.VOLUME_2_GET_LIST + ) + SnapshotPolicyHandler().handle(snapshot_policy_module_mock, snapshot_policy_module_mock.module.params) + snapshot_policy_module_mock.powerflex_conn.snapshot_policy.remove_source_volume.assert_called() + + def test_pause_snapshot_policy(self, snapshot_policy_module_mock): + self.get_module_args.update({ + "snapshot_policy_name": "testing_2", + "pause": True, + "state": "present" + }) + snapshot_policy_module_mock.module.params = self.get_module_args + snapshot_policy_module_mock.powerflex_conn.snapshot_policy.get = MagicMock( + return_value=MockSnapshotPolicyApi.SNAPSHOT_POLICY_GET_LIST + ) + snapshot_policy_module_mock.powerflex_conn.snapshot_policy.get_statistics = MagicMock( + return_value=MockSnapshotPolicyApi.SNAPSHOT_POLICY_STATISTICS + ) + SnapshotPolicyHandler().handle(snapshot_policy_module_mock, snapshot_policy_module_mock.module.params) + snapshot_policy_module_mock.powerflex_conn.snapshot_policy.pause.assert_called() + + def test_resume_snapshot_policy(self, snapshot_policy_module_mock): + self.get_module_args.update({ + "snapshot_policy_name": "testing_2", + "pause": False, + "state": "present" + }) + snapshot_policy_module_mock.module.params = self.get_module_args + snapshot_policy_module_mock.powerflex_conn.snapshot_policy.get = MagicMock( + return_value=MockSnapshotPolicyApi.SNAPSHOT_POLICY_2_GET_LIST + ) + snapshot_policy_module_mock.powerflex_conn.snapshot_policy.get_statistics = MagicMock( + return_value=MockSnapshotPolicyApi.SNAPSHOT_POLICY_STATISTICS + ) + SnapshotPolicyHandler().handle(snapshot_policy_module_mock, snapshot_policy_module_mock.module.params) + snapshot_policy_module_mock.powerflex_conn.snapshot_policy.resume.assert_called() + + def test_pause_snapshot_policy_exception(self, snapshot_policy_module_mock): + self.get_module_args.update({ + "snapshot_policy_name": "testing_2", + "pause": True, + "state": "present" + }) + snapshot_policy_module_mock.module.params = self.get_module_args + snapshot_policy_module_mock.powerflex_conn.snapshot_policy.get = MagicMock( + return_value=MockSnapshotPolicyApi.SNAPSHOT_POLICY_2_GET_LIST + ) + snapshot_policy_module_mock.powerflex_conn.snapshot_policy.get_statistics = MagicMock( + return_value=MockSnapshotPolicyApi.SNAPSHOT_POLICY_STATISTICS + ) + snapshot_policy_module_mock.powerflex_conn.volume.pause = MagicMock( + side_effect=MockApiException + ) + self.capture_fail_json_call(MockSnapshotPolicyApi.get_snapshot_policy_exception_response('pause_exception'), + snapshot_policy_module_mock) + + def test_remove_source_volume_exception(self, snapshot_policy_module_mock): + self.get_module_args.update({ + "snapshot_policy_name": "testing_2", + "source_volume": [{ + "id": "source_volume_id_2", + "name": None, + "auto_snap_removal_action": 'Remove', + "detach_locked_auto_snapshots": None, + "state": "absent"}], + "state": "present" + }) + snapshot_policy_module_mock.module.params = self.get_module_args + snapshot_policy_module_mock.powerflex_conn.snapshot_policy.get = MagicMock( + return_value=MockSnapshotPolicyApi.SNAPSHOT_POLICY_2_GET_LIST + ) + snapshot_policy_module_mock.powerflex_conn.snapshot_policy.get_statistics = MagicMock( + return_value=MockSnapshotPolicyApi.SNAPSHOT_POLICY_STATISTICS + ) + snapshot_policy_module_mock.powerflex_conn.volume.get = MagicMock( + side_effect=MockApiException + ) + self.capture_fail_json_call(MockSnapshotPolicyApi.get_snapshot_policy_exception_response('get_vol_details_exception'), + snapshot_policy_module_mock) diff --git a/ansible_collections/dellemc/powerflex/tests/unit/plugins/modules/test_storagepool.py b/ansible_collections/dellemc/powerflex/tests/unit/plugins/modules/test_storagepool.py index a2c463f66..6780ed7ad 100644 --- a/ansible_collections/dellemc/powerflex/tests/unit/plugins/modules/test_storagepool.py +++ b/ansible_collections/dellemc/powerflex/tests/unit/plugins/modules/test_storagepool.py @@ -1,4 +1,4 @@ -# Copyright: (c) 2022, Dell Technologies +# Copyright: (c) 2024, Dell Technologies # Apache License version 2.0 (see MODULE-LICENSE or http://www.apache.org/licenses/LICENSE-2.0.txt) @@ -10,13 +10,15 @@ __metaclass__ = type import pytest from mock.mock import MagicMock +# pylint: disable=unused-import +from ansible_collections.dellemc.powerflex.tests.unit.plugins.module_utils.libraries import initial_mock from ansible_collections.dellemc.powerflex.tests.unit.plugins.module_utils.mock_storagepool_api import MockStoragePoolApi -from ansible_collections.dellemc.powerflex.tests.unit.plugins.module_utils.mock_sdk_response \ - import MockSDKResponse from ansible_collections.dellemc.powerflex.tests.unit.plugins.module_utils.mock_api_exception \ import MockApiException from ansible_collections.dellemc.powerflex.plugins.module_utils.storage.dell \ import utils +from ansible_collections.dellemc.powerflex.tests.unit.plugins.module_utils.libraries.powerflex_unit_base \ + import PowerFlexUnitBase utils.get_logger = MagicMock() utils.get_powerflex_gateway_host_connection = MagicMock() @@ -27,46 +29,355 @@ basic.AnsibleModule = MagicMock() from ansible_collections.dellemc.powerflex.plugins.modules.storagepool import PowerFlexStoragePool -class TestPowerflexStoragePool(): +class TestPowerflexStoragePool(PowerFlexUnitBase): get_module_args = MockStoragePoolApi.STORAGE_POOL_COMMON_ARGS @pytest.fixture - def storagepool_module_mock(self, mocker): - storagepool_module_mock = PowerFlexStoragePool() - storagepool_module_mock.module.check_mode = False - return storagepool_module_mock + def module_object(self): + return PowerFlexStoragePool - def test_get_storagepool_details(self, storagepool_module_mock): + def test_get_storagepool_details(self, powerflex_module_mock): self.get_module_args.update({ "storage_pool_name": "test_pool", "state": "present" }) - storagepool_module_mock.module.params = self.get_module_args + powerflex_module_mock.module.params = self.get_module_args storagepool_resp = MockStoragePoolApi.STORAGE_POOL_GET_LIST - storagepool_module_mock.powerflex_conn.storage_pool.get = MagicMock( + powerflex_module_mock.powerflex_conn.storage_pool.get = MagicMock( return_value=storagepool_resp ) storagepool_statistics_resp = MockStoragePoolApi.STORAGE_POOL_STATISTICS - storagepool_module_mock.powerflex_conn.storage_pool.get_statistics = MagicMock( + powerflex_module_mock.powerflex_conn.storage_pool.get_statistics = MagicMock( return_value=storagepool_statistics_resp ) - storagepool_module_mock.perform_module_operation() - storagepool_module_mock.powerflex_conn.storage_pool.get.assert_called() - storagepool_module_mock.powerflex_conn.storage_pool.get_statistics.assert_called() + powerflex_module_mock.perform_module_operation() + powerflex_module_mock.powerflex_conn.storage_pool.get.assert_called() + powerflex_module_mock.powerflex_conn.storage_pool.get_statistics.assert_called() - def test_get_storagepool_details_with_exception(self, storagepool_module_mock): + def test_get_storagepool_details_multi(self, powerflex_module_mock): + self.get_module_args.update({ + "storage_pool_name": "test_pool", + "state": "present" + }) + powerflex_module_mock.module.params = self.get_module_args + storagepool_resp = MockStoragePoolApi.STORAGE_POOL_GET_MULTI_LIST + powerflex_module_mock.powerflex_conn.storage_pool.get = MagicMock( + return_value=storagepool_resp + ) + storagepool_statistics_resp = MockStoragePoolApi.STORAGE_POOL_STATISTICS + powerflex_module_mock.powerflex_conn.storage_pool.get_statistics = MagicMock( + return_value=storagepool_statistics_resp + ) + self.capture_fail_json_call( + MockStoragePoolApi.get_exception_response('get_multi_details'), + powerflex_module_mock, invoke_perform_module=True) + + def test_get_storagepool_details_with_exception(self, powerflex_module_mock): self.get_module_args.update({ "storage_pool_name": "test_pool" }) - storagepool_module_mock.module.params = self.get_module_args + powerflex_module_mock.module.params = self.get_module_args storagepool_resp = MockStoragePoolApi.STORAGE_POOL_GET_LIST - storagepool_module_mock.powerflex_conn.storage_pool.get = MagicMock( + powerflex_module_mock.powerflex_conn.storage_pool.get = MagicMock( return_value=storagepool_resp ) - storagepool_module_mock.powerflex_conn.storage_pool.get_statistics = MagicMock( + powerflex_module_mock.powerflex_conn.storage_pool.get_statistics = MagicMock( side_effect=MockApiException ) - storagepool_module_mock.create_storage_pool = MagicMock(return_value=None) - storagepool_module_mock.perform_module_operation() - assert MockStoragePoolApi.get_exception_response('get_details') in storagepool_module_mock.module.fail_json.call_args[1]['msg'] + powerflex_module_mock.create_storage_pool = MagicMock(return_value=None) + self.capture_fail_json_call( + MockStoragePoolApi.get_exception_response('get_details'), + powerflex_module_mock, invoke_perform_module=True) + + @pytest.mark.parametrize("params", [ + {"pd_id": "4eeb304600000000"}, + {"pd_name": "test"}, + ]) + def test_get_protection_domain(self, powerflex_module_mock, params): + pd_id = params.get("pd_id", None) + pd_name = params.get("pd_name", None) + powerflex_module_mock.powerflex_conn.protection_domain.get = MagicMock( + return_value=MockStoragePoolApi.PROTECTION_DETAILS + ) + pd_details = powerflex_module_mock.get_protection_domain(pd_name, pd_id) + assert MockStoragePoolApi.PROTECTION_DETAILS[0] == pd_details + + def test_get_protection_domain_exception(self, powerflex_module_mock): + self.set_module_params( + powerflex_module_mock, + self.get_module_args, + { + "storage_pool_name": "test_pool", + "protection_domain_id": "4eeb304600000001", + "state": "present" + }) + powerflex_module_mock.powerflex_conn.protection_domain.get = MagicMock( + side_effect=MockApiException) + self.capture_fail_json_call( + MockStoragePoolApi.get_exception_response('get_pd_exception'), + powerflex_module_mock, invoke_perform_module=True) + + def test_get_protection_domain_non_exist(self, powerflex_module_mock): + self.set_module_params( + powerflex_module_mock, + self.get_module_args, + { + "storage_pool_name": "test_pool", + "protection_domain_id": "4eeb304600000001", + "state": "present" + }) + powerflex_module_mock.powerflex_conn.protection_domain.get = MagicMock( + return_value=None) + self.capture_fail_json_call( + MockStoragePoolApi.get_exception_response('get_pd_non_exist'), + powerflex_module_mock, invoke_perform_module=True) + + def test_get_storagepool_details_with_invalid_pd_id(self, powerflex_module_mock): + self.get_module_args.update({ + "storage_pool_name": "test_pool", + "protection_domain_id": "4eeb304600000001" + }) + powerflex_module_mock.module.params = self.get_module_args + storagepool_resp = MockStoragePoolApi.STORAGE_POOL_GET_LIST + powerflex_module_mock.powerflex_conn.storage_pool.get = MagicMock( + return_value=storagepool_resp + ) + storagepool_statistics_resp = MockStoragePoolApi.STORAGE_POOL_STATISTICS + powerflex_module_mock.powerflex_conn.storage_pool.get_statistics = MagicMock( + return_value=storagepool_statistics_resp + ) + powerflex_module_mock.powerflex_conn.protection_domain.get = MagicMock( + return_value=MockStoragePoolApi.PROTECTION_DETAILS_1 + ) + self.capture_fail_json_call( + MockStoragePoolApi.get_exception_response('invalid_pd_id'), + powerflex_module_mock, invoke_perform_module=True) + + def test_create_storagepool_response(self, powerflex_module_mock): + self.get_module_args.update({ + "storage_pool_name": "test_pool", + "protection_domain_name": "test_pd_name", + "media_type": "HDD", + "state": "present" + }) + powerflex_module_mock.module.params = self.get_module_args + powerflex_module_mock.powerflex_conn.protection_domain.get = MagicMock( + return_value=MockStoragePoolApi.PROTECTION_DETAILS_1) + powerflex_module_mock.powerflex_conn.storage_pool.get = MagicMock( + return_value=[] + ) + powerflex_module_mock.powerflex_conn.storage_pool.get_statistics = MagicMock( + return_value=[] + ) + powerflex_module_mock.powerflex_conn.storage_pool.create = MagicMock( + return_value=None + ) + resp = powerflex_module_mock.create_storage_pool(pool_name="test_pool", + pd_id=MockStoragePoolApi.PROTECTION_DETAILS_1[0]['id'], + media_type="HDD") + assert resp is True + powerflex_module_mock.powerflex_conn.storage_pool.create.assert_called() + + def test_create_storagepool_only_pool_id(self, powerflex_module_mock): + self.get_module_args.update({ + "storage_pool_id": "test_pool_id", + "protection_domain_name": "test_pd_name", + "media_type": "HDD", + "state": "present" + }) + powerflex_module_mock.module.params = self.get_module_args + powerflex_module_mock.powerflex_conn.protection_domain.get = MagicMock( + return_value=MockStoragePoolApi.PROTECTION_DETAILS_1) + powerflex_module_mock.powerflex_conn.storage_pool.get = MagicMock( + return_value=[] + ) + powerflex_module_mock.powerflex_conn.storage_pool.get_statistics = MagicMock( + return_value=[] + ) + self.capture_fail_json_call( + MockStoragePoolApi.get_exception_response('create_pool_id'), + powerflex_module_mock, invoke_perform_module=True) + + def test_create_storagepool_new_name(self, powerflex_module_mock): + self.get_module_args.update({ + "storage_pool_name": "test_pool", + "storage_pool_new_name": "pool_new_name", + "protection_domain_name": "test_pd_name", + "media_type": "HDD", + "state": "present" + }) + powerflex_module_mock.module.params = self.get_module_args + powerflex_module_mock.powerflex_conn.protection_domain.get = MagicMock( + return_value=MockStoragePoolApi.PROTECTION_DETAILS_1) + powerflex_module_mock.powerflex_conn.storage_pool.get = MagicMock( + return_value=[] + ) + powerflex_module_mock.powerflex_conn.storage_pool.get_statistics = MagicMock( + return_value=[] + ) + self.capture_fail_json_call( + MockStoragePoolApi.get_exception_response('create_pool_new_name'), + powerflex_module_mock, invoke_perform_module=True) + + def test_create_storagepool_empty_name(self, powerflex_module_mock): + self.get_module_args.update({ + "storage_pool_name": " ", + "protection_domain_name": "test_pd_name", + "media_type": "HDD", + "state": "present" + }) + powerflex_module_mock.module.params = self.get_module_args + powerflex_module_mock.powerflex_conn.protection_domain.get = MagicMock( + return_value=MockStoragePoolApi.PROTECTION_DETAILS_1) + self.capture_fail_json_call( + MockStoragePoolApi.get_exception_response('create_pool_name_empty'), + powerflex_module_mock, invoke_perform_module=True) + + def test_create_storagepool_wo_pd(self, powerflex_module_mock): + self.get_module_args.update({ + "storage_pool_name": "test_pool", + "media_type": "HDD", + "state": "present" + }) + powerflex_module_mock.module.params = self.get_module_args + powerflex_module_mock.powerflex_conn.protection_domain.get = MagicMock( + return_value=None) + powerflex_module_mock.powerflex_conn.storage_pool.get = MagicMock( + return_value=[] + ) + powerflex_module_mock.powerflex_conn.storage_pool.get_statistics = MagicMock( + return_value=[] + ) + self.capture_fail_json_call( + MockStoragePoolApi.get_exception_response('create_wo_pd'), + powerflex_module_mock, invoke_perform_module=True) + + def test_create_storagepool_transitional_exception(self, powerflex_module_mock): + self.get_module_args.update({ + "storage_pool_name": "test_pool", + "protection_domain_name": "test_pd_name", + "media_type": "TRANSITIONAL", + "state": "present" + }) + powerflex_module_mock.module.params = self.get_module_args + powerflex_module_mock.powerflex_conn.protection_domain.get = MagicMock( + return_value=MockStoragePoolApi.PROTECTION_DETAILS_1) + powerflex_module_mock.powerflex_conn.storage_pool.get = MagicMock( + return_value=[] + ) + powerflex_module_mock.powerflex_conn.storage_pool.get_statistics = MagicMock( + return_value=[] + ) + powerflex_module_mock.powerflex_conn.storage_pool.create = MagicMock( + return_value=None + ) + self.capture_fail_json_call( + MockStoragePoolApi.get_exception_response('create_transitional'), + powerflex_module_mock, invoke_perform_module=True) + + def test_create_storagepool_exception(self, powerflex_module_mock): + self.get_module_args.update({ + "storage_pool_name": "test_pool", + "protection_domain_name": "test_pd_name", + "media_type": "HDD", + "state": "present" + }) + powerflex_module_mock.module.params = self.get_module_args + powerflex_module_mock.powerflex_conn.protection_domain.get = MagicMock( + return_value=MockStoragePoolApi.PROTECTION_DETAILS_1) + powerflex_module_mock.powerflex_conn.storage_pool.get = MagicMock( + return_value=[] + ) + powerflex_module_mock.powerflex_conn.storage_pool.get_statistics = MagicMock( + return_value=[] + ) + powerflex_module_mock.powerflex_conn.storage_pool.create = MagicMock( + side_effect=MockApiException + ) + self.capture_fail_json_call( + MockStoragePoolApi.get_exception_response('create_storage_pool'), + powerflex_module_mock, invoke_perform_module=True) + + def test_modify_storagepool_details(self, powerflex_module_mock): + self.get_module_args.update({ + "storage_pool_name": "test_pool", + "storage_pool_new_name": "new_ansible_pool", + "use_rfcache": True, + "use_rmcache": True, + "media_type": "TRANSITIONAL", + "state": "present" + }) + powerflex_module_mock.module.params = self.get_module_args + storagepool_resp = MockStoragePoolApi.STORAGE_POOL_GET_LIST + powerflex_module_mock.powerflex_conn.storage_pool.get = MagicMock( + return_value=storagepool_resp + ) + storagepool_statistics_resp = MockStoragePoolApi.STORAGE_POOL_STATISTICS + powerflex_module_mock.powerflex_conn.storage_pool.get_statistics = MagicMock( + return_value=storagepool_statistics_resp + ) + powerflex_module_mock.perform_module_operation() + powerflex_module_mock.powerflex_conn.storage_pool.rename.assert_called() + powerflex_module_mock.powerflex_conn.storage_pool.set_use_rmcache.assert_called() + powerflex_module_mock.powerflex_conn.storage_pool.set_use_rfcache.assert_called() + powerflex_module_mock.powerflex_conn.storage_pool.set_media_type.assert_called() + + def test_rename_storagepool_exception(self, powerflex_module_mock): + self.get_module_args.update({ + "storage_pool_name": "test_pool", + "storage_pool_new_name": "new_ansible_pool", + "state": "present" + }) + powerflex_module_mock.module.params = self.get_module_args + storagepool_resp = MockStoragePoolApi.STORAGE_POOL_GET_LIST + powerflex_module_mock.powerflex_conn.storage_pool.get = MagicMock( + return_value=storagepool_resp + ) + storagepool_statistics_resp = MockStoragePoolApi.STORAGE_POOL_STATISTICS + powerflex_module_mock.powerflex_conn.storage_pool.get_statistics = MagicMock( + return_value=storagepool_statistics_resp + ) + powerflex_module_mock.powerflex_conn.storage_pool.rename = MagicMock( + side_effect=MockApiException + ) + self.capture_fail_json_call( + MockStoragePoolApi.get_exception_response('rename_storage_pool'), + powerflex_module_mock, invoke_perform_module=True) + + def test_rename_storagepool_empty_exception(self, powerflex_module_mock): + self.get_module_args.update({ + "storage_pool_name": "test_pool", + "storage_pool_new_name": " ", + "state": "present" + }) + powerflex_module_mock.module.params = self.get_module_args + storagepool_resp = MockStoragePoolApi.STORAGE_POOL_GET_LIST + powerflex_module_mock.powerflex_conn.storage_pool.get = MagicMock( + return_value=storagepool_resp + ) + storagepool_statistics_resp = MockStoragePoolApi.STORAGE_POOL_STATISTICS + powerflex_module_mock.powerflex_conn.storage_pool.get_statistics = MagicMock( + return_value=storagepool_statistics_resp + ) + self.capture_fail_json_call( + MockStoragePoolApi.get_exception_response('rename_storage_pool_empty'), + powerflex_module_mock, invoke_perform_module=True) + + def test_delete_storagepool_exception(self, powerflex_module_mock): + self.get_module_args.update({ + "storage_pool_name": "test_pool", + "state": "absent" + }) + powerflex_module_mock.module.params = self.get_module_args + storagepool_resp = MockStoragePoolApi.STORAGE_POOL_GET_LIST + powerflex_module_mock.powerflex_conn.storage_pool.get = MagicMock( + return_value=storagepool_resp + ) + storagepool_statistics_resp = MockStoragePoolApi.STORAGE_POOL_STATISTICS + powerflex_module_mock.powerflex_conn.storage_pool.get_statistics = MagicMock( + return_value=storagepool_statistics_resp + ) + self.capture_fail_json_call( + MockStoragePoolApi.get_exception_response('delete_storage_pool'), + powerflex_module_mock, invoke_perform_module=True) diff --git a/ansible_collections/dellemc/powerflex/tests/unit/plugins/modules/test_volume.py b/ansible_collections/dellemc/powerflex/tests/unit/plugins/modules/test_volume.py index 53cdcfc0d..e36c13695 100644 --- a/ansible_collections/dellemc/powerflex/tests/unit/plugins/modules/test_volume.py +++ b/ansible_collections/dellemc/powerflex/tests/unit/plugins/modules/test_volume.py @@ -1,4 +1,4 @@ -# Copyright: (c) 2022, Dell Technologies +# Copyright: (c) 2024, Dell Technologies # Apache License version 2.0 (see MODULE-LICENSE or http://www.apache.org/licenses/LICENSE-2.0.txt) @@ -11,8 +11,6 @@ __metaclass__ = type import pytest from mock.mock import MagicMock from ansible_collections.dellemc.powerflex.tests.unit.plugins.module_utils.mock_volume_api import MockVolumeApi -from ansible_collections.dellemc.powerflex.tests.unit.plugins.module_utils.mock_sdk_response \ - import MockSDKResponse from ansible_collections.dellemc.powerflex.tests.unit.plugins.module_utils.mock_api_exception \ import MockApiException from ansible_collections.dellemc.powerflex.plugins.module_utils.storage.dell \ @@ -78,4 +76,662 @@ class TestPowerflexVolume(): ) volume_module_mock.create_volume = MagicMock(return_value=None) volume_module_mock.perform_module_operation() - assert MockVolumeApi.get_exception_response('get_details') in volume_module_mock.module.fail_json.call_args[1]['msg'] + assert MockVolumeApi.get_exception_response( + 'get_details') in volume_module_mock.module.fail_json.call_args[1]['msg'] + + @pytest.mark.parametrize("params", [ + {"pd_id": "123"}, + {"pd_name": "test"}, + ]) + def test_get_protection_domain(self, volume_module_mock, params): + pd_id = params.get("pd_id", None) + pd_name = params.get("pd_name", None) + volume_module_mock.powerflex_conn.protection_domain.get = MagicMock( + return_value=MockVolumeApi.PROTECTION_DETAILS + ) + pd_details = volume_module_mock.get_protection_domain(pd_name, pd_id) + assert MockVolumeApi.PROTECTION_DETAILS[0] == pd_details + + def test_get_protection_domain_exeception(self, volume_module_mock): + pd_id = "pd_id" + pd_name = "pd_name" + volume_module_mock.powerflex_conn.protection_domain.get = MagicMock( + return_value=None + ) + volume_module_mock.get_protection_domain(pd_name, pd_id) + assert MockVolumeApi.get_exception_response( + 'get_pd_exception') in volume_module_mock.module.fail_json.call_args[1]['msg'] + + @pytest.mark.parametrize("params", [ + {"pol_id": "123"}, + {"pol_name": "test"}, + ]) + def test_get_snapshot_policy(self, volume_module_mock, params): + pol_id = params.get("pol_id", None) + pol_name = params.get("pol_name", None) + volume_module_mock.powerflex_conn.snapshot_policy.get = MagicMock( + return_value=MockVolumeApi.PROTECTION_DETAILS + ) + snap_pol_details = volume_module_mock.get_snapshot_policy( + pol_id, pol_name) + assert MockVolumeApi.PROTECTION_DETAILS[0] == snap_pol_details + + def test_get_snapshot_policy_exception(self, volume_module_mock): + pol_id = "pol_id" + pol_name = "pol_name" + volume_module_mock.powerflex_conn.snapshot_policy.get = MagicMock( + return_value=None + ) + volume_module_mock.get_snapshot_policy(pol_id, pol_name) + assert MockVolumeApi.get_exception_response( + 'get_sp_exception') in volume_module_mock.module.fail_json.call_args[1]['msg'] + + @pytest.mark.parametrize("params", [ + {"pol_id": "123", "prot_id": "123"}, + ]) + def test_get_storage_pool(self, volume_module_mock, params): + pol_id = params.get("pol_id", None) + pol_name = params.get("pol_name", None) + prot_id = params.get("prot_id", None) + + def mock_get(filter_fields): + if filter_fields.get("protectionDomainId", None): + return MockVolumeApi.PROTECTION_DETAILS + else: + return MockVolumeApi.PROTECTION_DETAILS_MULTI + + volume_module_mock.powerflex_conn.storage_pool.get = MagicMock( + side_effect=mock_get + ) + sp_details = volume_module_mock.get_storage_pool( + pol_id, pol_name, prot_id) + assert MockVolumeApi.PROTECTION_DETAILS[0] == sp_details + + @pytest.mark.parametrize("params", [ + {"pol_id": "123", "assert_msg": "get_spool_error1"}, + {"pol_name": "123", "prot_id": "123", "assert_msg": "get_spool_error2"}, + ]) + def test_get_storage_pool_error(self, volume_module_mock, params): + pol_id = params.get("pol_id", None) + pol_name = params.get("pol_name", None) + prot_id = params.get("prot_id", None) + + def mock_get(filter_fields): + if filter_fields.get("protectionDomainId", None): + return None + else: + return MockVolumeApi.PROTECTION_DETAILS_MULTI + + volume_module_mock.powerflex_conn.storage_pool.get = MagicMock( + side_effect=mock_get + ) + volume_module_mock.get_storage_pool(pol_id, pol_name, prot_id) + assert MockVolumeApi.get_exception_response(params.get( + "assert_msg")) in volume_module_mock.module.fail_json.call_args[1]['msg'] + + @pytest.mark.parametrize("params", [ + {"vol_name": "123", "assert_msg": "get_spool_error1"} + ]) + def test_get_volume(self, volume_module_mock, params): + vol_name = params.get("vol_name", None) + vol_id = params.get("vol_id", None) + volume_module_mock.powerflex_conn.volume.get = MagicMock( + return_value=MockVolumeApi.VOLUME_GET_LIST + ) + volume_module_mock.get_snapshot_policy = MagicMock( + return_value={"name": "snapshotPolicyName"} + ) + volume_details = volume_module_mock.get_volume(vol_name, vol_id) + assert volume_details["snapshotPolicyId"] == MockVolumeApi.VOLUME_GET_LIST[0]["snapshotPolicyId"] + assert volume_details["snapshotPolicyName"] == MockVolumeApi.VOLUME_GET_LIST[0]["snapshotPolicyName"] + + def test_get_volume_exeception(self, volume_module_mock): + volume_module_mock.powerflex_conn.volume.get = MagicMock( + side_effect=MockApiException + ) + volume_module_mock.get_snapshot_policy = MagicMock( + return_value={"name": "snapshotPolicyName"} + ) + volume_module_mock.get_volume("test_id_1", "test_id_1") + assert MockVolumeApi.get_exception_response( + "get_details") in volume_module_mock.module.fail_json.call_args[1]['msg'] + + @pytest.mark.parametrize("params", [ + {"sdc_id": "sdc_id"}, + {"sdc_ip": "sdc_ip"}, + ]) + def test_get_sdc_id(self, volume_module_mock, params): + sdc_name = params.get("sdc_name", None) + sdc_ip = params.get("sdc_ip", None) + sdc_id = params.get("sdc_id", None) + volume_module_mock.powerflex_conn.sdc.get = MagicMock( + return_value=MockVolumeApi.SDC_RESPONSE + ) + sdc_details = volume_module_mock.get_sdc_id(sdc_name, sdc_ip, sdc_id) + assert MockVolumeApi.SDC_RESPONSE[0]['id'] == sdc_details + + def test_get_sdc_id_error(self, volume_module_mock): + sdc_name = "sdc_name" + sdc_ip = "sdc_ip" + sdc_id = "sdc_id" + volume_module_mock.powerflex_conn.sdc.get = MagicMock( + return_value=[] + ) + volume_module_mock.get_sdc_id(sdc_name, sdc_ip, sdc_id) + assert MockVolumeApi.get_exception_response( + "get_sds") in volume_module_mock.module.fail_json.call_args[1]['msg'] + + def test_create_volume_error_vol_name(self, volume_module_mock): + volume_module_mock.create_volume("", "pool_id", 1024) + assert MockVolumeApi.get_exception_response( + "create_vol_name") in volume_module_mock.module.fail_json.call_args[1]['msg'] + + def test_create_volume_error_comp_type(self, volume_module_mock): + volume_module_mock.get_storage_pool = MagicMock( + return_value=MockVolumeApi.GET_STORAGE_POOL + ) + volume_module_mock.create_volume( + "vol_name", "pool_id", 1024, comp_type="comp_type") + assert MockVolumeApi.get_exception_response( + "create_vol_ctype") in volume_module_mock.module.fail_json.call_args[1]['msg'] + + def test_create_volume_error_size(self, volume_module_mock): + volume_module_mock.get_storage_pool = MagicMock( + return_value=MockVolumeApi.GET_STORAGE_POOL + ) + volume_module_mock.create_volume("vol_name", "pool_id", None) + assert MockVolumeApi.get_exception_response( + "create_vol_size") in volume_module_mock.module.fail_json.call_args[1]['msg'] + + def test_create_volume_exception(self, volume_module_mock): + volume_module_mock.powerflex_conn.volume.create = MagicMock( + side_effect=MockApiException + ) + volume_module_mock.create_volume("vol_name", None, 1024) + assert MockVolumeApi.get_exception_response( + "create_vol_exc") in volume_module_mock.module.fail_json.call_args[1]['msg'] + + def test_create_volume(self, volume_module_mock): + volume_module_mock.powerflex_conn.volume.create = MagicMock( + return_value=None + ) + ret = volume_module_mock.create_volume("vol_name", None, 1024) + assert ret is True + + def test_modify_access_mode_true(self, volume_module_mock): + access_mode_list = [{"accessMode": "READ_ONLY", "sdc_id": "sdc_id"}] + volume_module_mock.powerflex_conn.volume.set_access_mode_for_sdc = MagicMock( + return_value=None + ) + ret = volume_module_mock.modify_access_mode( + "vol_name", access_mode_list) + assert ret is True + + def test_modify_access_mode_false(self, volume_module_mock): + access_mode_list = [{"accessMode": None, "sdc_id": "sdc_id"}] + volume_module_mock.powerflex_conn.volume.set_access_mode_for_sdc = MagicMock( + return_value=None + ) + ret = volume_module_mock.modify_access_mode( + "vol_name", access_mode_list) + assert ret is False + + def test_modify_access_mode_exception(self, volume_module_mock): + access_mode_list = [{"accessMode": "READ_ONLY", "sdc_id": "sdc_id"}] + volume_module_mock.powerflex_conn.volume.set_access_mode_for_sdc = MagicMock( + side_effect=MockApiException + ) + volume_module_mock.modify_access_mode("vol_name", access_mode_list) + assert MockVolumeApi.get_exception_response( + "modify_access") in volume_module_mock.module.fail_json.call_args[1]['msg'] + + def test_modify_limits_true(self, volume_module_mock): + payload = {"sdc_id": "sdc_id", + "bandwidth_limit": 1024, "iops_limit": 1024} + volume_module_mock.powerflex_conn.volume.set_mapped_sdc_limits = MagicMock( + return_value=None + ) + ret = volume_module_mock.modify_limits(payload) + assert ret is True + + def test_modify_limits_false(self, volume_module_mock): + payload = {"sdc_id": "sdc_id", + "bandwidth_limit": None, "iops_limit": None} + volume_module_mock.powerflex_conn.volume.set_mapped_sdc_limits = MagicMock( + return_value=None + ) + ret = volume_module_mock.modify_limits(payload) + assert ret is False + + def test_modify_limits_exception(self, volume_module_mock): + payload = {"sdc_id": "sdc_id", + "bandwidth_limit": 1024, "iops_limit": 1024} + volume_module_mock.powerflex_conn.volume.set_mapped_sdc_limits = MagicMock( + side_effect=MockApiException + ) + volume_module_mock.modify_limits(payload) + assert MockVolumeApi.get_exception_response( + "modify_limits") in volume_module_mock.module.fail_json.call_args[1]['msg'] + + def test_delete_volume_true(self, volume_module_mock): + volume_module_mock.powerflex_conn.volume.delete = MagicMock( + side_effect=None + ) + ret = volume_module_mock.delete_volume("vol_id", "remove_mode") + assert ret is True + + def test_delete_volume_exception(self, volume_module_mock): + volume_module_mock.powerflex_conn.volume.delete = MagicMock( + side_effect=MockApiException + ) + volume_module_mock.delete_volume("vol_id", "remove_mode") + assert MockVolumeApi.get_exception_response( + "delete_volume") in volume_module_mock.module.fail_json.call_args[1]['msg'] + + def test_unmap_volume_from_sdc_true(self, volume_module_mock): + volume = {"mappedSdcInfo": [{"sdcId": "sdc_id"}], "id": "vol_id"} + sdc = [{"sdc_name": "sdc_name"}] + volume_module_mock.get_sdc_id = MagicMock( + return_value="sdc_id" + ) + volume_module_mock.powerflex_conn.volume.remove_mapped_sdc = MagicMock( + return_value=None + ) + ret = volume_module_mock.unmap_volume_from_sdc(volume, sdc) + assert ret is True + + def test_unmap_volume_from_sdc_false(self, volume_module_mock): + volume = {"mappedSdcInfo": [{"sdcId": "sdc_id"}], "id": "vol_id"} + sdc = [{"sdc_ip": "sdc_ip"}] + volume_module_mock.get_sdc_id = MagicMock( + return_value="sdc_id1" + ) + volume_module_mock.powerflex_conn.volume.remove_mapped_sdc = MagicMock( + return_value=None + ) + ret = volume_module_mock.unmap_volume_from_sdc(volume, sdc) + assert ret is False + + def test_unmap_volume_from_sdc_exception(self, volume_module_mock): + volume = {"mappedSdcInfo": [{"sdcId": "sdc_id"}], "id": "vol_id"} + sdc = [{"sdc_id": "sdc_id"}] + volume_module_mock.get_sdc_id = MagicMock( + return_value="sdc_id" + ) + volume_module_mock.powerflex_conn.volume.remove_mapped_sdc = MagicMock( + side_effect=MockApiException + ) + volume_module_mock.unmap_volume_from_sdc(volume, sdc) + assert MockVolumeApi.get_exception_response( + "unmap") in volume_module_mock.module.fail_json.call_args[1]['msg'] + + def test_map_volume_to_sdc_name(self, volume_module_mock): + volume = { + "mappedSdcInfo": [ + {"sdcId": "sdc_id", "accessMode": "READ_WRITE", + "limitIops": 1024, "limitBwInMbps": 1024}], + "id": "vol_id", + } + sdc = [{"sdc_name": "sdc_name", "access_mode": "READ_ONLY", + "iops_limit": 2048, "bandwidth_limit": 2048}] + volume_module_mock.get_sdc_id = MagicMock( + return_value="sdc_id" + ) + ret, sdc_modify_list1, sdc_modify_list2 = volume_module_mock.map_volume_to_sdc( + volume, sdc) + assert ret is False + assert sdc_modify_list1[0]["sdc_id"] == "sdc_id" + assert sdc_modify_list2[0]["sdc_id"] == "sdc_id" + + def test_map_volume_to_sdc_ip(self, volume_module_mock): + volume = { + "mappedSdcInfo": [ + {"sdcId": "sdc_id", "accessMode": "READ_WRITE", + "limitIops": 1024, "limitBwInMbps": 1024}], + "id": "vol_id", + } + sdc = [{"sdc_ip": "sdc_ip", "access_mode": "READ_ONLY", + "iops_limit": 2048, "bandwidth_limit": 2048}] + volume_module_mock.get_sdc_id = MagicMock( + return_value="sdc_id1" + ) + volume_module_mock.powerflex_conn.volume.add_mapped_sdc = MagicMock( + return_value=None + ) + volume_module_mock.powerflex_conn.volume.set_mapped_sdc_limits = MagicMock( + return_value=None + ) + ret, sdc_modify_list1, sdc_modify_list2 = volume_module_mock.map_volume_to_sdc( + volume, sdc) + assert ret is True + assert sdc_modify_list1 == [] + assert sdc_modify_list2 == [] + + def test_map_volume_to_sdc_id(self, volume_module_mock): + volume = { + "mappedSdcInfo": [ + {"sdcId": "sdc_id", "accessMode": "READ_WRITE", + "limitIops": 1024, "limitBwInMbps": 1024}], + "id": "vol_id", + } + sdc = [{"sdc_id": "sdc_id", "access_mode": "READ_ONLY"}] + volume_module_mock.get_sdc_id = MagicMock( + return_value="sdc_id1" + ) + volume_module_mock.powerflex_conn.volume.add_mapped_sdc = MagicMock( + return_value=None + ) + volume_module_mock.powerflex_conn.volume.set_mapped_sdc_limits = MagicMock( + return_value=None + ) + ret, sdc_modify_list1, sdc_modify_list2 = volume_module_mock.map_volume_to_sdc( + volume, sdc) + assert ret is True + assert sdc_modify_list1 == [] + assert sdc_modify_list2 == [] + + def test_map_volume_to_sdc_exception(self, volume_module_mock): + volume = { + "mappedSdcInfo": [ + {"sdcId": "sdc_id", "accessMode": "READ_WRITE", + "limitIops": 1024, "limitBwInMbps": 1024}], + "id": "vol_id", + "name": "name" + } + sdc = [{"sdc_id": "sdc_id", "access_mode": "READ_ONLY", + "iops_limit": 2048, "bandwidth_limit": 2048}] + volume_module_mock.get_sdc_id = MagicMock( + return_value="sdc_id1" + ) + volume_module_mock.powerflex_conn.volume.add_mapped_sdc = MagicMock( + return_value=None + ) + volume_module_mock.powerflex_conn.volume.set_mapped_sdc_limits = MagicMock( + side_effect=MockApiException + ) + volume_module_mock.map_volume_to_sdc(volume, sdc) + assert MockVolumeApi.get_exception_response( + "map_vol_exception") in volume_module_mock.module.fail_json.call_args[1]['msg'] + + @pytest.mark.parametrize('params', [ + {"sdc": [{"sdc_id": "sdc_id", "sdc_name": "sdc_name", "sdc_ip": "sdc_ip"}], + "assert_msg": "val_params_err1"}, + {"cap_unit": "GB", "size": None, "assert_msg": "val_params_err2"}, + {"asrt": "asrt", "assert_msg": "val_params_err3"}, + {"state": "present", "del_snaps": "del_snaps", + "assert_msg": "val_params_err4"}, + ]) + def test_validate_parameters(self, volume_module_mock, params): + self.get_module_args.update({ + "sdc": params.get("sdc", None), + "cap_unit": params.get("cap_unit", None), + "size": params.get("size", None), + }) + volume_module_mock.module.params = self.get_module_args + asrt = params.get("asrt", None) + pol_id = params.get("pol_id", None) + pol_name = params.get("pol_name", None) + del_snaps = params.get("del_snaps", None) + state = params.get("state", None) + assert_msg = params.get("assert_msg", None) + volume_module_mock.validate_parameters( + asrt, pol_id, pol_name, del_snaps, state) + assert MockVolumeApi.get_exception_response( + assert_msg) in volume_module_mock.module.fail_json.call_args[1]['msg'] + + @pytest.mark.parametrize('params', [ + {"modify_dict": { + "auto_snap_remove_type": "remove", + "snap_pol_id": "vol_id", + "new_name": "new_name", + "new_size": "new_size", + "use_rmcache": "use_rmcache", + "comp_type": "comp_type" + }, "vol_id": "vol_id"}, + {"modify_dict": { + "snap_pol_id": "vol_id" + }, "vol_id": "vol_id"}, + ]) + def test_modify_volume(self, volume_module_mock, params): + vol_id = params.get("vol_id", None) + modify_dict = params.get("modify_dict", None) + volume_module_mock.get_sdc_id = MagicMock( + return_value="sdc_id1" + ) + volume_module_mock.powerflex_conn.snapshot_policy.remove_source_volume = MagicMock( + return_value=None + ) + volume_module_mock.powerflex_conn.snapshot_policy.add_source_volume = MagicMock( + return_value=None + ) + volume_module_mock.powerflex_conn.snapshot_policy.rename = MagicMock( + return_value=None + ) + volume_module_mock.powerflex_conn.snapshot_policy.extend = MagicMock( + return_value=None + ) + volume_module_mock.powerflex_conn.snapshot_policy.set_use_rmcache = MagicMock( + return_value=None + ) + volume_module_mock.powerflex_conn.snapshot_policy.set_compression_method = MagicMock( + return_value=None + ) + ret = volume_module_mock.modify_volume(vol_id, modify_dict) + assert ret is True + + def test_modify_volume_execption(self, volume_module_mock): + vol_id = "vol_id" + modify_dict = {"snap_pol_id": "vol_id"} + volume_module_mock.powerflex_conn.snapshot_policy.add_source_volume = MagicMock( + side_effect=MockApiException + ) + volume_module_mock.modify_volume(vol_id, modify_dict) + assert MockVolumeApi.get_exception_response( + "modify_volume_exp") in volume_module_mock.module.fail_json.call_args[1]['msg'] + + def test_to_modify(self, volume_module_mock): + vol_details = { + "storagePoolId": "sdc_id", + "compressionMethod": "tar", + "useRmcache": True, + "sizeInKb": 2048, + "name": "name", + "snplIdOfSourceVolume": "snplIdOfSourceVolume" + } + new_size = 1024 + use_rmcache = False + comp_type = "zip" + new_name = "new_name" + snap_pol_id = "" + asrt = "asrt" + volume_module_mock.get_storage_pool = MagicMock( + return_value=MockVolumeApi.GET_STORAGE_POOL_FINE + ) + modify_dict = volume_module_mock.to_modify(vol_details, new_size, use_rmcache, comp_type, + new_name, snap_pol_id, + asrt) + assert modify_dict["snap_pol_id"] == "snplIdOfSourceVolume" + + def test_to_modify_comp_type_error(self, volume_module_mock): + vol_details = { + "storagePoolId": "sdc_id", + "compressionMethod": "tar", + "useRmcache": True, + "sizeInKb": 2048, + "name": "name", + "snplIdOfSourceVolume": None + } + new_size = 1024 + use_rmcache = False + comp_type = "zip" + new_name = "new_name" + snap_pol_id = "snap_pol_id" + asrt = None + volume_module_mock.get_storage_pool = MagicMock( + return_value=MockVolumeApi.GET_STORAGE_POOL + ) + volume_module_mock.to_modify(vol_details, new_size, use_rmcache, comp_type, + new_name, snap_pol_id, + asrt) + assert MockVolumeApi.get_exception_response( + "create_vol_ctype") in volume_module_mock.module.fail_json.call_args[1]['msg'] + + def test_to_modify_new_name_error(self, volume_module_mock): + vol_details = { + "storagePoolId": "sdc_id", + "compressionMethod": "tar", + "useRmcache": True, + "sizeInKb": 2048, + "name": "name", + "snplIdOfSourceVolume": "snplIdOfSourceVolume" + } + new_size = None + use_rmcache = None + comp_type = None + new_name = "" + snap_pol_id = "snap_pol_id" + asrt = None + volume_module_mock.get_storage_pool = MagicMock( + return_value=MockVolumeApi.GET_STORAGE_POOL_FINE + ) + volume_module_mock.to_modify(vol_details, new_size, use_rmcache, comp_type, + new_name, snap_pol_id, + asrt) + assert MockVolumeApi.get_exception_response( + "create_vol_name") in volume_module_mock.module.fail_json.call_args[1]['msg'] + + def test_to_modify_remove_error(self, volume_module_mock): + vol_details = { + "storagePoolId": "sdc_id", + "compressionMethod": "tar", + "useRmcache": True, + "sizeInKb": 2048, + "name": "name", + "snplIdOfSourceVolume": "snplIdOfSourceVolume" + } + new_size = None + use_rmcache = None + comp_type = None + new_name = None + snap_pol_id = "snap_pol_id" + asrt = "asrt" + volume_module_mock.get_storage_pool = MagicMock( + return_value=MockVolumeApi.GET_STORAGE_POOL_FINE + ) + volume_module_mock.to_modify(vol_details, new_size, use_rmcache, comp_type, + new_name, snap_pol_id, + asrt) + assert MockVolumeApi.get_exception_response( + "to_modify_err1") in volume_module_mock.module.fail_json.call_args[1]['msg'] + + @pytest.mark.parametrize('params', [ + {"snap_pol_id": "snap_pol_id", "assert_msg": "snap_pol_id_err"}, + {"snap_pol_name": "snap_pol_id", "assert_msg": "snap_pol_name_err"}, + {"pd_id": "pd_id", "assert_msg": "pd_id_err"}, + {"pool_id": "pool_id", "assert_msg": "pool_id_err"}, + {"pd_name": "pd_name", "assert_msg": "pd_name_err"}, + {"pool_name": "pool_name", "assert_msg": "pool_name_err"} + ]) + def test_verify_params(self, volume_module_mock, params): + vol_details = { + "snapshotPolicyId": "snapshotPolicyId", + "snapshotPolicyName": "snapshotPolicyName", + "protectionDomainId": "protectionDomainId", + "storagePoolId": "storagePoolId", + "protectionDomainName": "protectionDomainName", + "storagePoolName": "storagePoolName", + } + snap_pol_name = params.get("snap_pol_name", None) + snap_pol_id = params.get("snap_pol_id", None) + pd_name = params.get("pd_name", None) + pd_id = params.get("pd_id", None) + pool_name = params.get("pool_name", None) + pool_id = params.get("pool_id", None) + assert_msg = params.get("assert_msg", None) + volume_module_mock.verify_params(vol_details, snap_pol_name, snap_pol_id, pd_name, + pd_id, pool_name, pool_id) + assert MockVolumeApi.get_exception_response( + assert_msg) in volume_module_mock.module.fail_json.call_args[1]['msg'] + + def test_perform_module_operation_delete(self, volume_module_mock): + self.get_module_args.update({ + "compression_type": "tar", + "vol_type": "vol_type", + "auto_snap_remove_type": "asrt", + "size": 20, + "protection_domain_name": "protection_domain_name", + "storage_pool_name": "storage_pool_name", + "snapshot_policy_name": "snapshot_policy_name", + "vol_name": "vol_name", + "state": "absent", + "delete_snapshots": True + }) + volume_module_mock.module.params = self.get_module_args + volume_module_mock.validate_parameters = MagicMock( + return_value=None + ) + volume_module_mock.get_protection_domain = MagicMock( + return_value=MockVolumeApi.GET_ID + ) + volume_module_mock.get_storage_pool = MagicMock( + return_value=MockVolumeApi.GET_ID + ) + volume_module_mock.get_snapshot_policy = MagicMock( + return_value=MockVolumeApi.GET_ID + ) + volume_module_mock.get_volume = MagicMock( + return_value=MockVolumeApi.GET_ID + ) + volume_module_mock.delete_volume = MagicMock( + return_value=True + ) + volume_module_mock.verify_params = MagicMock( + return_value=None + ) + volume_module_mock.perform_module_operation() + assert volume_module_mock.module.exit_json.call_args[1]['changed'] is True + assert volume_module_mock.module.exit_json.call_args[1]['volume_details'] == { + } + + def test_perform_module_operation_create_fail(self, volume_module_mock): + self.get_module_args.update({ + "compression_type": "tar", + "vol_type": "vol_type", + "auto_snap_remove_type": "asrt", + "size": 1, + "protection_domain_name": "protection_domain_name", + "storage_pool_name": "storage_pool_name", + "snapshot_policy_name": "", + "snapshot_policy_id": "", + "vol_name": "vol_name", + "state": "present", + "delete_snapshots": True, + "cap_unit": "TB", + "vol_id": "vol_id", + "vol_new_name": "vol_new_name", + }) + volume_module_mock.module.params = self.get_module_args + volume_module_mock.validate_parameters = MagicMock( + return_value=None + ) + volume_module_mock.get_protection_domain = MagicMock( + return_value=MockVolumeApi.GET_ID + ) + volume_module_mock.get_storage_pool = MagicMock( + return_value=MockVolumeApi.GET_ID + ) + volume_module_mock.get_snapshot_policy = MagicMock( + return_value=MockVolumeApi.GET_ID + ) + volume_module_mock.get_volume = MagicMock( + return_value=None + ) + volume_module_mock.verify_params = MagicMock( + return_value=None + ) + volume_module_mock.create_volume = MagicMock( + return_value=False + ) + volume_module_mock.perform_module_operation() + assert MockVolumeApi.get_exception_response( + "perform_error1") in volume_module_mock.module.fail_json.call_args[1]['msg'] diff --git a/ansible_collections/dellemc/powerflex/tests/requirements.txt b/ansible_collections/dellemc/powerflex/tests/unit/requirements.txt index 3541acd15..3541acd15 100644 --- a/ansible_collections/dellemc/powerflex/tests/requirements.txt +++ b/ansible_collections/dellemc/powerflex/tests/unit/requirements.txt |