summaryrefslogtreecommitdiffstats
path: root/ansible_collections/dellemc/powerflex
diff options
context:
space:
mode:
Diffstat (limited to 'ansible_collections/dellemc/powerflex')
-rw-r--r--ansible_collections/dellemc/powerflex/.ansible-lint2
-rw-r--r--ansible_collections/dellemc/powerflex/.ansible-lint-ignore27
-rw-r--r--ansible_collections/dellemc/powerflex/.github/CODEOWNERS3
-rw-r--r--ansible_collections/dellemc/powerflex/.github/workflows/ansible-test.yml128
-rw-r--r--ansible_collections/dellemc/powerflex/.gitignore3
-rw-r--r--ansible_collections/dellemc/powerflex/CHANGELOG.rst73
-rw-r--r--ansible_collections/dellemc/powerflex/FILES.json2551
-rw-r--r--ansible_collections/dellemc/powerflex/MANIFEST.json13
-rw-r--r--ansible_collections/dellemc/powerflex/README.md54
-rw-r--r--ansible_collections/dellemc/powerflex/ansible.cfg484
-rw-r--r--ansible_collections/dellemc/powerflex/changelogs/.plugin-cache.yaml9
-rw-r--r--ansible_collections/dellemc/powerflex/changelogs/changelog.yaml161
-rw-r--r--ansible_collections/dellemc/powerflex/docs/CONTRIBUTING.md10
-rw-r--r--ansible_collections/dellemc/powerflex/docs/INSTALLATION.md6
-rw-r--r--ansible_collections/dellemc/powerflex/docs/ISSUE_TRIAGE.md6
-rw-r--r--ansible_collections/dellemc/powerflex/docs/MAINTAINER_GUIDE.md4
-rw-r--r--ansible_collections/dellemc/powerflex/docs/Release Notes.md30
-rw-r--r--ansible_collections/dellemc/powerflex/docs/SECURITY.md4
-rw-r--r--ansible_collections/dellemc/powerflex/docs/modules/device.rst60
-rw-r--r--ansible_collections/dellemc/powerflex/docs/modules/fault_set.rst215
-rw-r--r--ansible_collections/dellemc/powerflex/docs/modules/info.rst209
-rw-r--r--ansible_collections/dellemc/powerflex/docs/modules/mdm_cluster.rst14
-rw-r--r--ansible_collections/dellemc/powerflex/docs/modules/protection_domain.rst6
-rw-r--r--ansible_collections/dellemc/powerflex/docs/modules/replication_consistency_group.rst100
-rw-r--r--ansible_collections/dellemc/powerflex/docs/modules/replication_pair.rst23
-rw-r--r--ansible_collections/dellemc/powerflex/docs/modules/sdc.rst31
-rw-r--r--ansible_collections/dellemc/powerflex/docs/modules/sds.rst26
-rw-r--r--ansible_collections/dellemc/powerflex/docs/modules/snapshot.rst28
-rw-r--r--ansible_collections/dellemc/powerflex/docs/modules/snapshot_policy.rst414
-rw-r--r--ansible_collections/dellemc/powerflex/docs/modules/storagepool.rst11
-rw-r--r--ansible_collections/dellemc/powerflex/docs/modules/volume.rst24
-rw-r--r--ansible_collections/dellemc/powerflex/meta/runtime.yml2
-rw-r--r--ansible_collections/dellemc/powerflex/playbooks/modules/device.yml90
-rw-r--r--ansible_collections/dellemc/powerflex/playbooks/modules/fault_set.yml67
-rw-r--r--ansible_collections/dellemc/powerflex/playbooks/modules/info.yml80
-rw-r--r--ansible_collections/dellemc/powerflex/playbooks/modules/mdm_cluster.yml148
-rw-r--r--ansible_collections/dellemc/powerflex/playbooks/modules/protection_domain.yml124
-rw-r--r--ansible_collections/dellemc/powerflex/playbooks/modules/replication_consistency_group.yml234
-rw-r--r--ansible_collections/dellemc/powerflex/playbooks/modules/replication_pair.yml82
-rw-r--r--ansible_collections/dellemc/powerflex/playbooks/modules/sdc.yml64
-rw-r--r--ansible_collections/dellemc/powerflex/playbooks/modules/sds.yml149
-rw-r--r--ansible_collections/dellemc/powerflex/playbooks/modules/snapshot.yml130
-rw-r--r--ansible_collections/dellemc/powerflex/playbooks/modules/snapshot_policy.yml215
-rw-r--r--ansible_collections/dellemc/powerflex/playbooks/modules/storagepool.yml61
-rw-r--r--ansible_collections/dellemc/powerflex/playbooks/modules/volume.yml133
-rw-r--r--ansible_collections/dellemc/powerflex/playbooks/roles/group_vars/all51
-rw-r--r--ansible_collections/dellemc/powerflex/playbooks/roles/host_vars/node211
-rw-r--r--ansible_collections/dellemc/powerflex/playbooks/roles/inventory41
-rw-r--r--ansible_collections/dellemc/powerflex/playbooks/roles/site.yml64
-rw-r--r--ansible_collections/dellemc/powerflex/playbooks/roles/site_powerflex45.yml59
-rw-r--r--ansible_collections/dellemc/powerflex/playbooks/roles/uninstall_powerflex.yml64
-rw-r--r--ansible_collections/dellemc/powerflex/playbooks/roles/uninstall_powerflex45.yml61
-rw-r--r--ansible_collections/dellemc/powerflex/playbooks/roles/vars_files/connection.yml8
-rw-r--r--ansible_collections/dellemc/powerflex/plugins/doc_fragments/powerflex.py10
-rw-r--r--ansible_collections/dellemc/powerflex/plugins/module_utils/storage/dell/libraries/__init__.py0
-rw-r--r--ansible_collections/dellemc/powerflex/plugins/module_utils/storage/dell/libraries/configuration.py121
-rw-r--r--ansible_collections/dellemc/powerflex/plugins/module_utils/storage/dell/libraries/powerflex_base.py45
-rw-r--r--ansible_collections/dellemc/powerflex/plugins/module_utils/storage/dell/utils.py33
-rw-r--r--ansible_collections/dellemc/powerflex/plugins/modules/device.py85
-rw-r--r--ansible_collections/dellemc/powerflex/plugins/modules/fault_set.py380
-rw-r--r--ansible_collections/dellemc/powerflex/plugins/modules/info.py818
-rw-r--r--ansible_collections/dellemc/powerflex/plugins/modules/mdm_cluster.py42
-rw-r--r--ansible_collections/dellemc/powerflex/plugins/modules/protection_domain.py8
-rw-r--r--ansible_collections/dellemc/powerflex/plugins/modules/replication_consistency_group.py252
-rw-r--r--ansible_collections/dellemc/powerflex/plugins/modules/replication_pair.py21
-rw-r--r--ansible_collections/dellemc/powerflex/plugins/modules/sdc.py114
-rw-r--r--ansible_collections/dellemc/powerflex/plugins/modules/sds.py708
-rw-r--r--ansible_collections/dellemc/powerflex/plugins/modules/snapshot.py22
-rw-r--r--ansible_collections/dellemc/powerflex/plugins/modules/snapshot_policy.py828
-rw-r--r--ansible_collections/dellemc/powerflex/plugins/modules/storagepool.py5
-rw-r--r--ansible_collections/dellemc/powerflex/plugins/modules/volume.py18
-rw-r--r--ansible_collections/dellemc/powerflex/requirements.txt4
-rw-r--r--ansible_collections/dellemc/powerflex/roles/README.md117
-rw-r--r--ansible_collections/dellemc/powerflex/roles/molecule.yml25
-rw-r--r--ansible_collections/dellemc/powerflex/roles/powerflex_activemq/README.md155
-rw-r--r--ansible_collections/dellemc/powerflex/roles/powerflex_activemq/defaults/main.yml6
-rw-r--r--ansible_collections/dellemc/powerflex/roles/powerflex_activemq/meta/argument_specs.yml48
-rw-r--r--ansible_collections/dellemc/powerflex/roles/powerflex_activemq/meta/main.yml27
-rw-r--r--ansible_collections/dellemc/powerflex/roles/powerflex_activemq/molecule/activemq_install/converge.yml31
-rw-r--r--ansible_collections/dellemc/powerflex/roles/powerflex_activemq/molecule/activemq_install/molecule.yml1
-rw-r--r--ansible_collections/dellemc/powerflex/roles/powerflex_activemq/molecule/activemq_uninstallation/converge.yml67
-rw-r--r--ansible_collections/dellemc/powerflex/roles/powerflex_activemq/molecule/activemq_uninstallation/molecule.yml1
-rw-r--r--ansible_collections/dellemc/powerflex/roles/powerflex_activemq/tasks/install_activemq.yml30
-rw-r--r--ansible_collections/dellemc/powerflex/roles/powerflex_activemq/tasks/main.yml8
-rw-r--r--ansible_collections/dellemc/powerflex/roles/powerflex_activemq/tasks/uninstall_activemq.yml20
-rw-r--r--ansible_collections/dellemc/powerflex/roles/powerflex_activemq/vars/main.yml1
-rw-r--r--ansible_collections/dellemc/powerflex/roles/powerflex_common/README.md3
-rw-r--r--ansible_collections/dellemc/powerflex/roles/powerflex_common/defaults/main.yml4
-rw-r--r--ansible_collections/dellemc/powerflex/roles/powerflex_common/meta/main.yml41
-rw-r--r--ansible_collections/dellemc/powerflex/roles/powerflex_common/tasks/install_java_CentOS.yml5
-rw-r--r--ansible_collections/dellemc/powerflex/roles/powerflex_common/tasks/install_java_RedHat.yml5
-rw-r--r--ansible_collections/dellemc/powerflex/roles/powerflex_common/tasks/install_java_Rocky.yml5
-rw-r--r--ansible_collections/dellemc/powerflex/roles/powerflex_common/tasks/install_java_SLES.yml5
-rw-r--r--ansible_collections/dellemc/powerflex/roles/powerflex_common/tasks/install_java_Ubuntu.yml10
-rw-r--r--ansible_collections/dellemc/powerflex/roles/powerflex_common/tasks/install_packages_CentOS.yml36
-rw-r--r--ansible_collections/dellemc/powerflex/roles/powerflex_common/tasks/install_packages_RedHat.yml36
-rw-r--r--ansible_collections/dellemc/powerflex/roles/powerflex_common/tasks/install_packages_Rocky.yml36
-rw-r--r--ansible_collections/dellemc/powerflex/roles/powerflex_common/tasks/install_packages_SLES.yml36
-rw-r--r--ansible_collections/dellemc/powerflex/roles/powerflex_common/tasks/install_packages_Ubuntu.yml51
-rw-r--r--ansible_collections/dellemc/powerflex/roles/powerflex_common/tasks/install_packages_VMkernel.yml50
-rw-r--r--ansible_collections/dellemc/powerflex/roles/powerflex_common/tasks/install_packages_WindowsOS.yml20
-rw-r--r--ansible_collections/dellemc/powerflex/roles/powerflex_common/tasks/install_powerflex.yml8
-rw-r--r--ansible_collections/dellemc/powerflex/roles/powerflex_common/tasks/main.yml14
-rw-r--r--ansible_collections/dellemc/powerflex/roles/powerflex_common/vars/CentOS.yml4
-rw-r--r--ansible_collections/dellemc/powerflex/roles/powerflex_common/vars/RedHat.yml6
-rw-r--r--ansible_collections/dellemc/powerflex/roles/powerflex_common/vars/Rocky.yml6
-rw-r--r--ansible_collections/dellemc/powerflex/roles/powerflex_common/vars/SLES.yml4
-rw-r--r--ansible_collections/dellemc/powerflex/roles/powerflex_common/vars/Ubuntu.yml5
-rw-r--r--ansible_collections/dellemc/powerflex/roles/powerflex_common/vars/VMkernel.yml2
-rw-r--r--ansible_collections/dellemc/powerflex/roles/powerflex_common/vars/WindowsOS.yml2
-rw-r--r--ansible_collections/dellemc/powerflex/roles/powerflex_config/README.md165
-rw-r--r--ansible_collections/dellemc/powerflex/roles/powerflex_config/defaults/main.yml6
-rw-r--r--ansible_collections/dellemc/powerflex/roles/powerflex_config/meta/argument_specs.yml50
-rw-r--r--ansible_collections/dellemc/powerflex/roles/powerflex_config/meta/main.yml25
-rw-r--r--ansible_collections/dellemc/powerflex/roles/powerflex_config/molecule/configure_protection_domain/converge.yml47
-rw-r--r--ansible_collections/dellemc/powerflex/roles/powerflex_config/molecule/configure_protection_domain/molecule.yml1
-rw-r--r--ansible_collections/dellemc/powerflex/roles/powerflex_config/tasks/main.yml79
-rw-r--r--ansible_collections/dellemc/powerflex/roles/powerflex_config/vars/main.yml2
-rw-r--r--ansible_collections/dellemc/powerflex/roles/powerflex_gateway/README.md160
-rw-r--r--ansible_collections/dellemc/powerflex/roles/powerflex_gateway/defaults/main.yml17
-rw-r--r--ansible_collections/dellemc/powerflex/roles/powerflex_gateway/handlers/main.yml6
-rw-r--r--ansible_collections/dellemc/powerflex/roles/powerflex_gateway/meta/argument_specs.yml47
-rw-r--r--ansible_collections/dellemc/powerflex/roles/powerflex_gateway/meta/main.yml28
-rw-r--r--ansible_collections/dellemc/powerflex/roles/powerflex_gateway/molecule/gateway_installation/converge.yml29
-rw-r--r--ansible_collections/dellemc/powerflex/roles/powerflex_gateway/molecule/gateway_installation/molecule.yml1
-rw-r--r--ansible_collections/dellemc/powerflex/roles/powerflex_gateway/molecule/gateway_installation_invalid_path_rpm/converge.yml32
-rw-r--r--ansible_collections/dellemc/powerflex/roles/powerflex_gateway/molecule/gateway_installation_invalid_path_rpm/molecule.yml4
-rw-r--r--ansible_collections/dellemc/powerflex/roles/powerflex_gateway/molecule/gateway_uninstallation/converge.yml48
-rw-r--r--ansible_collections/dellemc/powerflex/roles/powerflex_gateway/molecule/gateway_uninstallation/molecule.yml1
-rw-r--r--ansible_collections/dellemc/powerflex/roles/powerflex_gateway/tasks/install_gateway.yml52
-rw-r--r--ansible_collections/dellemc/powerflex/roles/powerflex_gateway/tasks/install_keepalived.yml28
-rw-r--r--ansible_collections/dellemc/powerflex/roles/powerflex_gateway/tasks/main.yml8
-rw-r--r--ansible_collections/dellemc/powerflex/roles/powerflex_gateway/tasks/uninstall_gateway.yml20
-rw-r--r--ansible_collections/dellemc/powerflex/roles/powerflex_gateway/templates/keepalived.conf.j218
-rw-r--r--ansible_collections/dellemc/powerflex/roles/powerflex_gateway/vars/CentOS.yml4
-rw-r--r--ansible_collections/dellemc/powerflex/roles/powerflex_gateway/vars/RedHat.yml4
-rw-r--r--ansible_collections/dellemc/powerflex/roles/powerflex_gateway/vars/SLES.yml4
-rw-r--r--ansible_collections/dellemc/powerflex/roles/powerflex_gateway/vars/Ubuntu.yml4
-rw-r--r--ansible_collections/dellemc/powerflex/roles/powerflex_gateway/vars/main.yml6
-rw-r--r--ansible_collections/dellemc/powerflex/roles/powerflex_lia/README.md170
-rw-r--r--ansible_collections/dellemc/powerflex/roles/powerflex_lia/defaults/main.yml5
-rw-r--r--ansible_collections/dellemc/powerflex/roles/powerflex_lia/meta/main.yml27
-rw-r--r--ansible_collections/dellemc/powerflex/roles/powerflex_lia/molecule/lia_install/converge.yml30
-rw-r--r--ansible_collections/dellemc/powerflex/roles/powerflex_lia/molecule/lia_install/molecule.yml1
-rw-r--r--ansible_collections/dellemc/powerflex/roles/powerflex_lia/molecule/lia_installation_invalid_path_rpm/converge.yml34
-rw-r--r--ansible_collections/dellemc/powerflex/roles/powerflex_lia/molecule/lia_installation_invalid_path_rpm/molecule.yml4
-rw-r--r--ansible_collections/dellemc/powerflex/roles/powerflex_lia/molecule/lia_uninstallation/converge.yml30
-rw-r--r--ansible_collections/dellemc/powerflex/roles/powerflex_lia/molecule/lia_uninstallation/molecule.yml1
-rw-r--r--ansible_collections/dellemc/powerflex/roles/powerflex_lia/tasks/install_lia.yml22
-rw-r--r--ansible_collections/dellemc/powerflex/roles/powerflex_lia/tasks/main.yml8
-rw-r--r--ansible_collections/dellemc/powerflex/roles/powerflex_lia/tasks/uninstall_lia.yml19
-rw-r--r--ansible_collections/dellemc/powerflex/roles/powerflex_lia/vars/main.yml6
-rw-r--r--ansible_collections/dellemc/powerflex/roles/powerflex_mdm/README.md142
-rw-r--r--ansible_collections/dellemc/powerflex/roles/powerflex_mdm/defaults/main.yml9
-rw-r--r--ansible_collections/dellemc/powerflex/roles/powerflex_mdm/meta/argument_spec.yml28
-rw-r--r--ansible_collections/dellemc/powerflex/roles/powerflex_mdm/meta/main.yml29
-rw-r--r--ansible_collections/dellemc/powerflex/roles/powerflex_mdm/molecule/mdm_installation/converge.yml63
-rw-r--r--ansible_collections/dellemc/powerflex/roles/powerflex_mdm/molecule/mdm_installation/molecule.yml1
-rw-r--r--ansible_collections/dellemc/powerflex/roles/powerflex_mdm/molecule/mdm_uninstallation/converge.yml53
-rw-r--r--ansible_collections/dellemc/powerflex/roles/powerflex_mdm/molecule/mdm_uninstallation/molecule.yml1
-rw-r--r--ansible_collections/dellemc/powerflex/roles/powerflex_mdm/tasks/add_certs.yml168
-rw-r--r--ansible_collections/dellemc/powerflex/roles/powerflex_mdm/tasks/install_mdm.yml32
-rw-r--r--ansible_collections/dellemc/powerflex/roles/powerflex_mdm/tasks/install_powerflex3x_mdm.yml128
-rw-r--r--ansible_collections/dellemc/powerflex/roles/powerflex_mdm/tasks/install_powerflex4x_mdm.yml101
-rw-r--r--ansible_collections/dellemc/powerflex/roles/powerflex_mdm/tasks/main.yml8
-rw-r--r--ansible_collections/dellemc/powerflex/roles/powerflex_mdm/tasks/mdm_set_facts.yml40
-rw-r--r--ansible_collections/dellemc/powerflex/roles/powerflex_mdm/tasks/remove_mdm.yml58
-rw-r--r--ansible_collections/dellemc/powerflex/roles/powerflex_mdm/tasks/uninstall_mdm.yml19
-rw-r--r--ansible_collections/dellemc/powerflex/roles/powerflex_mdm/vars/CentOS.yml5
-rw-r--r--ansible_collections/dellemc/powerflex/roles/powerflex_mdm/vars/RedHat.yml5
-rw-r--r--ansible_collections/dellemc/powerflex/roles/powerflex_mdm/vars/SLES.yml3
-rw-r--r--ansible_collections/dellemc/powerflex/roles/powerflex_mdm/vars/Ubuntu.yml4
-rw-r--r--ansible_collections/dellemc/powerflex/roles/powerflex_mdm/vars/main.yml6
-rw-r--r--ansible_collections/dellemc/powerflex/roles/powerflex_sdc/README.md311
-rw-r--r--ansible_collections/dellemc/powerflex/roles/powerflex_sdc/defaults/main.yml21
-rw-r--r--ansible_collections/dellemc/powerflex/roles/powerflex_sdc/handlers/main.yml5
-rw-r--r--ansible_collections/dellemc/powerflex/roles/powerflex_sdc/meta/main.yml25
-rw-r--r--ansible_collections/dellemc/powerflex/roles/powerflex_sdc/molecule/sdc_install_map_volume_uninstall_negative/converge.yml118
-rw-r--r--ansible_collections/dellemc/powerflex/roles/powerflex_sdc/molecule/sdc_install_map_volume_uninstall_negative/molecule.yml4
-rw-r--r--ansible_collections/dellemc/powerflex/roles/powerflex_sdc/molecule/sdc_installation/converge.yml62
-rw-r--r--ansible_collections/dellemc/powerflex/roles/powerflex_sdc/molecule/sdc_installation/molecule.yml1
-rw-r--r--ansible_collections/dellemc/powerflex/roles/powerflex_sdc/molecule/sdc_installation_invalid_path_rpm/converge.yml54
-rw-r--r--ansible_collections/dellemc/powerflex/roles/powerflex_sdc/molecule/sdc_installation_invalid_path_rpm/molecule.yml4
-rw-r--r--ansible_collections/dellemc/powerflex/roles/powerflex_sdc/molecule/sdc_uninstallation/converge.yml57
-rw-r--r--ansible_collections/dellemc/powerflex/roles/powerflex_sdc/molecule/sdc_uninstallation/molecule.yml1
-rw-r--r--ansible_collections/dellemc/powerflex/roles/powerflex_sdc/molecule/var_values.yml7
-rw-r--r--ansible_collections/dellemc/powerflex/roles/powerflex_sdc/molecule/wrong_sdc_credentials/converge.yml20
-rw-r--r--ansible_collections/dellemc/powerflex/roles/powerflex_sdc/molecule/wrong_sdc_credentials/inventory4
-rw-r--r--ansible_collections/dellemc/powerflex/roles/powerflex_sdc/molecule/wrong_sdc_credentials/molecule.yml11
-rw-r--r--ansible_collections/dellemc/powerflex/roles/powerflex_sdc/molecule/wrong_sdc_ip/converge.yml20
-rw-r--r--ansible_collections/dellemc/powerflex/roles/powerflex_sdc/molecule/wrong_sdc_ip/inventory4
-rw-r--r--ansible_collections/dellemc/powerflex/roles/powerflex_sdc/molecule/wrong_sdc_ip/molecule.yml11
-rw-r--r--ansible_collections/dellemc/powerflex/roles/powerflex_sdc/tasks/configure_sdc.yml28
-rw-r--r--ansible_collections/dellemc/powerflex/roles/powerflex_sdc/tasks/install_sdc.yml75
-rw-r--r--ansible_collections/dellemc/powerflex/roles/powerflex_sdc/tasks/main.yml8
-rw-r--r--ansible_collections/dellemc/powerflex/roles/powerflex_sdc/tasks/register_esxi_sdc.yml47
-rw-r--r--ansible_collections/dellemc/powerflex/roles/powerflex_sdc/tasks/remove_sdc.yml20
-rw-r--r--ansible_collections/dellemc/powerflex/roles/powerflex_sdc/tasks/uninstall_esxi_sdc.yml30
-rw-r--r--ansible_collections/dellemc/powerflex/roles/powerflex_sdc/tasks/uninstall_sdc.yml40
-rw-r--r--ansible_collections/dellemc/powerflex/roles/powerflex_sdc/templates/driver_sync.conf.j231
-rw-r--r--ansible_collections/dellemc/powerflex/roles/powerflex_sdc/vars/main.yml5
-rw-r--r--ansible_collections/dellemc/powerflex/roles/powerflex_sdr/README.md145
-rw-r--r--ansible_collections/dellemc/powerflex/roles/powerflex_sdr/defaults/main.yml9
-rw-r--r--ansible_collections/dellemc/powerflex/roles/powerflex_sdr/meta/argument_specs.yml34
-rw-r--r--ansible_collections/dellemc/powerflex/roles/powerflex_sdr/meta/main.yml21
-rw-r--r--ansible_collections/dellemc/powerflex/roles/powerflex_sdr/molecule/sdr_installation/converge.yml30
-rw-r--r--ansible_collections/dellemc/powerflex/roles/powerflex_sdr/molecule/sdr_installation/molecule.yml1
-rw-r--r--ansible_collections/dellemc/powerflex/roles/powerflex_sdr/molecule/sdr_installation_invalid_path_rpm/converge.yml34
-rw-r--r--ansible_collections/dellemc/powerflex/roles/powerflex_sdr/molecule/sdr_installation_invalid_path_rpm/molecule.yml4
-rw-r--r--ansible_collections/dellemc/powerflex/roles/powerflex_sdr/molecule/sdr_installation_invalid_pd/converge.yml20
-rw-r--r--ansible_collections/dellemc/powerflex/roles/powerflex_sdr/molecule/sdr_installation_invalid_pd/molecule.yml4
-rw-r--r--ansible_collections/dellemc/powerflex/roles/powerflex_sdr/molecule/sdr_uninstallation/converge.yml66
-rw-r--r--ansible_collections/dellemc/powerflex/roles/powerflex_sdr/molecule/sdr_uninstallation/molecule.yml1
-rw-r--r--ansible_collections/dellemc/powerflex/roles/powerflex_sdr/molecule/var_values.yml3
-rw-r--r--ansible_collections/dellemc/powerflex/roles/powerflex_sdr/tasks/add_sdr.yml142
-rw-r--r--ansible_collections/dellemc/powerflex/roles/powerflex_sdr/tasks/main.yml8
-rw-r--r--ansible_collections/dellemc/powerflex/roles/powerflex_sdr/tasks/remove_sdr.yml120
-rw-r--r--ansible_collections/dellemc/powerflex/roles/powerflex_sdr/tasks/sdr_set_facts.yml7
-rw-r--r--ansible_collections/dellemc/powerflex/roles/powerflex_sdr/vars/main.yml2
-rw-r--r--ansible_collections/dellemc/powerflex/roles/powerflex_sds/README.md243
-rw-r--r--ansible_collections/dellemc/powerflex/roles/powerflex_sds/defaults/main.yml4
-rw-r--r--ansible_collections/dellemc/powerflex/roles/powerflex_sds/meta/argument_spec.yml88
-rw-r--r--ansible_collections/dellemc/powerflex/roles/powerflex_sds/meta/main.yml24
-rw-r--r--ansible_collections/dellemc/powerflex/roles/powerflex_sds/molecule/sds_installation/converge.yml55
-rw-r--r--ansible_collections/dellemc/powerflex/roles/powerflex_sds/molecule/sds_installation/molecule.yml1
-rw-r--r--ansible_collections/dellemc/powerflex/roles/powerflex_sds/molecule/sds_uninstallation/converge.yml50
-rw-r--r--ansible_collections/dellemc/powerflex/roles/powerflex_sds/molecule/sds_uninstallation/molecule.yml1
-rw-r--r--ansible_collections/dellemc/powerflex/roles/powerflex_sds/molecule/var_values.yml11
-rw-r--r--ansible_collections/dellemc/powerflex/roles/powerflex_sds/tasks/install_sds.yml112
-rw-r--r--ansible_collections/dellemc/powerflex/roles/powerflex_sds/tasks/main.yml8
-rw-r--r--ansible_collections/dellemc/powerflex/roles/powerflex_sds/tasks/uninstall_sds.yml31
-rw-r--r--ansible_collections/dellemc/powerflex/roles/powerflex_sds/vars/main.yml5
-rw-r--r--ansible_collections/dellemc/powerflex/roles/powerflex_tb/README.md210
-rw-r--r--ansible_collections/dellemc/powerflex/roles/powerflex_tb/defaults/main.yml8
-rw-r--r--ansible_collections/dellemc/powerflex/roles/powerflex_tb/meta/argument_spec.yml85
-rw-r--r--ansible_collections/dellemc/powerflex/roles/powerflex_tb/meta/argument_specs.yml65
-rw-r--r--ansible_collections/dellemc/powerflex/roles/powerflex_tb/meta/main.yml25
-rw-r--r--ansible_collections/dellemc/powerflex/roles/powerflex_tb/molecule/tb_installation/converge.yml35
-rw-r--r--ansible_collections/dellemc/powerflex/roles/powerflex_tb/molecule/tb_installation/molecule.yml1
-rw-r--r--ansible_collections/dellemc/powerflex/roles/powerflex_tb/molecule/tb_uninstallation/converge.yml19
-rw-r--r--ansible_collections/dellemc/powerflex/roles/powerflex_tb/molecule/tb_uninstallation/molecule.yml1
-rw-r--r--ansible_collections/dellemc/powerflex/roles/powerflex_tb/molecule/var_values.yml7
-rw-r--r--ansible_collections/dellemc/powerflex/roles/powerflex_tb/molecule/wrong_tb_credentials/converge.yml20
-rw-r--r--ansible_collections/dellemc/powerflex/roles/powerflex_tb/molecule/wrong_tb_credentials/inventory4
-rw-r--r--ansible_collections/dellemc/powerflex/roles/powerflex_tb/molecule/wrong_tb_credentials/molecule.yml11
-rw-r--r--ansible_collections/dellemc/powerflex/roles/powerflex_tb/tasks/install_tb.yml8
-rw-r--r--ansible_collections/dellemc/powerflex/roles/powerflex_tb/tasks/install_tb3x.yml81
-rw-r--r--ansible_collections/dellemc/powerflex/roles/powerflex_tb/tasks/install_tb4x.yml69
-rw-r--r--ansible_collections/dellemc/powerflex/roles/powerflex_tb/tasks/main.yml31
-rw-r--r--ansible_collections/dellemc/powerflex/roles/powerflex_tb/tasks/set_tb_ips.yml29
-rw-r--r--ansible_collections/dellemc/powerflex/roles/powerflex_tb/tasks/uninstall_tb.yml97
-rw-r--r--ansible_collections/dellemc/powerflex/roles/powerflex_tb/vars/main.yml6
-rw-r--r--ansible_collections/dellemc/powerflex/roles/powerflex_webui/README.md165
-rw-r--r--ansible_collections/dellemc/powerflex/roles/powerflex_webui/defaults/main.yml6
-rw-r--r--ansible_collections/dellemc/powerflex/roles/powerflex_webui/meta/argument_specs.yml52
-rw-r--r--ansible_collections/dellemc/powerflex/roles/powerflex_webui/meta/main.yml29
-rw-r--r--ansible_collections/dellemc/powerflex/roles/powerflex_webui/molecule/webui_installation/converge.yml30
-rw-r--r--ansible_collections/dellemc/powerflex/roles/powerflex_webui/molecule/webui_installation/molecule.yml1
-rw-r--r--ansible_collections/dellemc/powerflex/roles/powerflex_webui/molecule/webui_installation_invalid_path_rpm/converge.yml34
-rw-r--r--ansible_collections/dellemc/powerflex/roles/powerflex_webui/molecule/webui_installation_invalid_path_rpm/molecule.yml4
-rw-r--r--ansible_collections/dellemc/powerflex/roles/powerflex_webui/molecule/webui_uninstallation/converge.yml48
-rw-r--r--ansible_collections/dellemc/powerflex/roles/powerflex_webui/molecule/webui_uninstallation/molecule.yml1
-rw-r--r--ansible_collections/dellemc/powerflex/roles/powerflex_webui/tasks/install_webui.yml23
-rw-r--r--ansible_collections/dellemc/powerflex/roles/powerflex_webui/tasks/main.yml8
-rw-r--r--ansible_collections/dellemc/powerflex/roles/powerflex_webui/tasks/uninstall_webui.yml20
-rw-r--r--ansible_collections/dellemc/powerflex/roles/powerflex_webui/vars/main.yml5
-rw-r--r--ansible_collections/dellemc/powerflex/tests/sanity/ignore-2.13.txt11
-rw-r--r--ansible_collections/dellemc/powerflex/tests/sanity/ignore-2.14.txt31
-rw-r--r--ansible_collections/dellemc/powerflex/tests/sanity/ignore-2.15.txt42
-rw-r--r--ansible_collections/dellemc/powerflex/tests/sanity/ignore-2.16.txt28
-rw-r--r--ansible_collections/dellemc/powerflex/tests/sanity/ignore-2.17.txt (renamed from ansible_collections/dellemc/powerflex/tests/sanity/ignore-2.12.txt)2
-rw-r--r--ansible_collections/dellemc/powerflex/tests/unit/plugins/module_utils/libraries/__init__.py0
-rw-r--r--ansible_collections/dellemc/powerflex/tests/unit/plugins/module_utils/libraries/fail_json.py21
-rw-r--r--ansible_collections/dellemc/powerflex/tests/unit/plugins/module_utils/libraries/initial_mock.py17
-rw-r--r--ansible_collections/dellemc/powerflex/tests/unit/plugins/module_utils/libraries/powerflex_unit_base.py40
-rw-r--r--ansible_collections/dellemc/powerflex/tests/unit/plugins/module_utils/mock_device_api.py146
-rw-r--r--ansible_collections/dellemc/powerflex/tests/unit/plugins/module_utils/mock_fail_json.py21
-rw-r--r--ansible_collections/dellemc/powerflex/tests/unit/plugins/module_utils/mock_fault_set_api.py69
-rw-r--r--ansible_collections/dellemc/powerflex/tests/unit/plugins/module_utils/mock_info_api.py105
-rw-r--r--ansible_collections/dellemc/powerflex/tests/unit/plugins/module_utils/mock_protection_domain_api.py124
-rw-r--r--ansible_collections/dellemc/powerflex/tests/unit/plugins/module_utils/mock_replication_consistency_group_api.py1
-rw-r--r--ansible_collections/dellemc/powerflex/tests/unit/plugins/module_utils/mock_replication_pair_api.py11
-rw-r--r--ansible_collections/dellemc/powerflex/tests/unit/plugins/module_utils/mock_sdc_api.py64
-rw-r--r--ansible_collections/dellemc/powerflex/tests/unit/plugins/module_utils/mock_sds_api.py147
-rw-r--r--ansible_collections/dellemc/powerflex/tests/unit/plugins/module_utils/mock_snapshot_policy_api.py186
-rw-r--r--ansible_collections/dellemc/powerflex/tests/unit/plugins/module_utils/mock_storagepool_api.py166
-rw-r--r--ansible_collections/dellemc/powerflex/tests/unit/plugins/module_utils/mock_volume_api.py64
-rw-r--r--ansible_collections/dellemc/powerflex/tests/unit/plugins/modules/test_device.py471
-rw-r--r--ansible_collections/dellemc/powerflex/tests/unit/plugins/modules/test_fault_set.py215
-rw-r--r--ansible_collections/dellemc/powerflex/tests/unit/plugins/modules/test_info.py475
-rw-r--r--ansible_collections/dellemc/powerflex/tests/unit/plugins/modules/test_protection_domain.py542
-rw-r--r--ansible_collections/dellemc/powerflex/tests/unit/plugins/modules/test_replication_consistency_group.py125
-rw-r--r--ansible_collections/dellemc/powerflex/tests/unit/plugins/modules/test_replication_pair.py132
-rw-r--r--ansible_collections/dellemc/powerflex/tests/unit/plugins/modules/test_sdc.py192
-rw-r--r--ansible_collections/dellemc/powerflex/tests/unit/plugins/modules/test_sds.py630
-rw-r--r--ansible_collections/dellemc/powerflex/tests/unit/plugins/modules/test_snapshot_policy.py502
-rw-r--r--ansible_collections/dellemc/powerflex/tests/unit/plugins/modules/test_storagepool.py355
-rw-r--r--ansible_collections/dellemc/powerflex/tests/unit/plugins/modules/test_volume.py664
-rw-r--r--ansible_collections/dellemc/powerflex/tests/unit/requirements.txt (renamed from ansible_collections/dellemc/powerflex/tests/requirements.txt)0
299 files changed, 20732 insertions, 1681 deletions
diff --git a/ansible_collections/dellemc/powerflex/.ansible-lint b/ansible_collections/dellemc/powerflex/.ansible-lint
new file mode 100644
index 000000000..f615bf255
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/.ansible-lint
@@ -0,0 +1,2 @@
+exclude_paths:
+ - .github/
diff --git a/ansible_collections/dellemc/powerflex/.ansible-lint-ignore b/ansible_collections/dellemc/powerflex/.ansible-lint-ignore
new file mode 100644
index 000000000..1b2e7405d
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/.ansible-lint-ignore
@@ -0,0 +1,27 @@
+roles/powerflex_activemq/defaults/main.yml var-naming[no-role-prefix]
+roles/powerflex_config/defaults/main.yml var-naming[no-role-prefix]
+roles/powerflex_gateway/defaults/main.yml var-naming[no-role-prefix]
+roles/powerflex_gateway/vars/CentOS.yml var-naming[no-role-prefix]
+roles/powerflex_gateway/vars/Ubuntu.yml var-naming[no-role-prefix]
+roles/powerflex_gateway/vars/RedHat.yml var-naming[no-role-prefix]
+roles/powerflex_gateway/vars/SLES.yml var-naming[no-role-prefix]
+roles/powerflex_gateway/vars/main.yml var-naming[no-role-prefix]
+roles/powerflex_lia/defaults/main.yml var-naming[no-role-prefix]
+roles/powerflex_lia/vars/main.yml var-naming[no-role-prefix]
+roles/powerflex_mdm/defaults/main.yml var-naming[no-role-prefix]
+roles/powerflex_mdm/vars/main.yml var-naming[no-role-prefix]
+roles/powerflex_sdc/defaults/main.yml var-naming[no-role-prefix]
+roles/powerflex_sdc/vars/main.yml var-naming[no-role-prefix]
+roles/powerflex_sdr/defaults/main.yml var-naming[no-role-prefix]
+roles/powerflex_sds/defaults/main.yml var-naming[no-role-prefix]
+roles/powerflex_sds/vars/main.yml var-naming[no-role-prefix]
+roles/powerflex_tb/defaults/main.yml var-naming[no-role-prefix]
+roles/powerflex_tb/vars/main.yml var-naming[no-role-prefix]
+roles/powerflex_webui/defaults/main.yml var-naming[no-role-prefix]
+roles/powerflex_webui/vars/main.yml var-naming[no-role-prefix]
+roles/powerflex_gateway/molecule/gateway_installation_invalid_path_rpm/converge.yml var-naming[no-role-prefix]
+roles/powerflex_lia/molecule/lia_installation_invalid_path_rpm/converge.yml var-naming[no-role-prefix]
+roles/powerflex_sdc/molecule/sdc_installation_invalid_path_rpm/converge.yml var-naming[no-role-prefix]
+roles/powerflex_sdr/molecule/sdr_installation_invalid_path_rpm/converge.yml var-naming[no-role-prefix]
+roles/powerflex_sds/molecule/sds_installation/converge.yml var-naming[no-role-prefix]
+roles/powerflex_webui/molecule/webui_installation_invalid_path_rpm/converge.yml var-naming[no-role-prefix]
diff --git a/ansible_collections/dellemc/powerflex/.github/CODEOWNERS b/ansible_collections/dellemc/powerflex/.github/CODEOWNERS
index f59815dc4..f783d12bf 100644
--- a/ansible_collections/dellemc/powerflex/.github/CODEOWNERS
+++ b/ansible_collections/dellemc/powerflex/.github/CODEOWNERS
@@ -12,8 +12,7 @@
# Jennifer John (Jennifer-John)
# Meenakshi Dembi (meenakshidembi691)
# Pavan Mudunuri (Pavan-Mudunuri)
-# Previnkumar G (Previnkumar-G)
# Trisha Datta (trisha-dell)
# for all files:
-* @kuttattz @Bhavneet-Sharma @Jennifer-John @meenakshidembi691 @Pavan-Mudunuri @Previnkumar-G @trisha-dell
+* @kuttattz @Bhavneet-Sharma @Jennifer-John @meenakshidembi691 @Pavan-Mudunuri @trisha-dell
diff --git a/ansible_collections/dellemc/powerflex/.github/workflows/ansible-test.yml b/ansible_collections/dellemc/powerflex/.github/workflows/ansible-test.yml
index 6a2f0fe7f..988cba19e 100644
--- a/ansible_collections/dellemc/powerflex/.github/workflows/ansible-test.yml
+++ b/ansible_collections/dellemc/powerflex/.github/workflows/ansible-test.yml
@@ -2,9 +2,8 @@ name: CI
on:
push:
- branches: [ main ]
+ branches: [main]
pull_request:
- branches: [ main ]
schedule:
- cron: '0 3 * * *'
@@ -15,15 +14,15 @@ jobs:
strategy:
fail-fast: false
matrix:
- ansible-version: [stable-2.12]
+ ansible-version: [stable-2.14, stable-2.15, stable-2.16, devel]
steps:
- name: Check out code
- uses: actions/checkout@v2
+ uses: actions/checkout@v3
- - name: Set up Python 3.9
- uses: actions/setup-python@v1
+ - name: Set up Python 3.11
+ uses: actions/setup-python@v4
with:
- python-version: 3.9
+ python-version: 3.11
- name: Install ansible (${{ matrix.ansible-version }})
run: pip install https://github.com/ansible/ansible/archive/${{ matrix.ansible-version }}.tar.gz --disable-pip-version-check
@@ -39,49 +38,36 @@ jobs:
###
# Unit tests (OPTIONAL)
- #
+ #
# https://docs.ansible.com/ansible/latest/dev_guide/testing_units.html
unit:
- name: Unit Tests
+ name: Unit Tests (â’¶${{ matrix.ansible }} with ${{ matrix.python }} python)
needs: [build]
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
- python-version: ["3.9", "3.10", "3.11"]
- ansible-version: [stable-2.12, stable-2.13, stable-2.14]
+ python: ['3.9', '3.10', '3.11']
+ ansible:
+ - stable-2.14
+ - stable-2.15
+ - stable-2.16
+ - devel
exclude:
- # Python 3.11 is supported only from ansible-core 2.14 onwards
- - python-version: "3.11"
- ansible-version: stable-2.12
- - python-version: "3.11"
- ansible-version: stable-2.13
-
+ - ansible: stable-2.16
+ python: '3.9'
+ - ansible: devel
+ python: '3.9'
steps:
- - name: Set up Python ${{ matrix.python-version }}
- uses: actions/setup-python@v1
+ - name: Perform unit testing with ansible-test
+ uses: ansible-community/ansible-test-gh-action@release/v1
with:
- python-version: ${{ matrix.python-version }}
-
- - name: Install ansible (${{ matrix.ansible-version }}) version
- run: pip install https://github.com/ansible/ansible/archive/${{ matrix.ansible-version }}.tar.gz --disable-pip-version-check
-
- - name: Download migrated collection artifacts
- uses: actions/download-artifact@v1
- with:
- name: collection
- path: .cache/collection-tarballs
-
- - name: Setup Unit test Pre-requisites
- run: |
- ansible-galaxy collection install .cache/collection-tarballs/*.tar.gz
- if [ -f /home/runner/.ansible/collections/ansible_collections/dellemc/powerflex/tests/requirements.txt ]; then pip install -r /home/runner/.ansible/collections/ansible_collections/dellemc/powerflex/tests/requirements.txt; fi
-
- - name: Run Unit tests using ansible-test
- run: ansible-test units -v --color --python ${{ matrix.python-version }} --coverage
- working-directory: /home/runner/.ansible/collections/ansible_collections/dellemc/powerflex
+ testing-type: units
+ coverage: always
+ ansible-core-version: ${{ matrix.ansible }}
+ target-python-version: ${{ matrix.python }}
###
# Sanity tests (REQUIRED)
@@ -89,37 +75,75 @@ jobs:
# https://docs.ansible.com/ansible/latest/dev_guide/testing_sanity.html
sanity:
- name: Sanity Tests
+ name: Sanity (â’¶${{ matrix.ansible }} with ${{ matrix.python }} python)
+ needs: [build]
+ strategy:
+ matrix:
+ python: ['3.9', '3.10', '3.11']
+ ansible:
+ - stable-2.14
+ - stable-2.15
+ - stable-2.16
+ - devel
+ exclude:
+ - ansible: stable-2.16
+ python: '3.9'
+ - ansible: devel
+ python: '3.9'
+ runs-on: ubuntu-latest
+ steps:
+ - name: Perform sanity testing
+ uses: ansible-community/ansible-test-gh-action@release/v1
+ with:
+ ansible-core-version: ${{ matrix.ansible }}
+ target-python-version: ${{ matrix.python }}
+ testing-type: sanity
+ pull-request-change-detection: true
+ coverage: never
+
+ lint:
+ name: Ansible lint
runs-on: ubuntu-latest
needs: [build]
strategy:
fail-fast: false
matrix:
- ansible-version: [stable-2.12, stable-2.13, stable-2.14]
+ python-version: ["3.9", "3.10", "3.11"]
+ ansible-version: [stable-2.14, stable-2.15, stable-2.16, devel]
+ exclude:
+ # Ansible-core 2.16 is supported only from Python 3.10 onwards
+ - python-version: "3.9"
+ ansible-version: stable-2.16
+ - python-version: '3.9'
+ ansible-version: devel
steps:
- - name: Set up Python 3.9
- uses: actions/setup-python@v1
+ # Important: This sets up your GITHUB_WORKSPACE environment variable
+ - name: Checkout the source code
+ uses: actions/checkout@v3
with:
- # it is just required to run that once as "ansible-test sanity" in the docker image
- # will run on all python versions it supports.
- python-version: 3.9
+ fetch-depth: 0 # needed for progressive mode to work
+
+ - name: Set up Python ${{ matrix.python-version }}
+ uses: actions/setup-python@v4
+ with:
+ python-version: ${{ matrix.python-version }}
- name: Install ansible (${{ matrix.ansible-version }}) version
run: pip install https://github.com/ansible/ansible/archive/${{ matrix.ansible-version }}.tar.gz --disable-pip-version-check
+ - name: Install ansible lint
+ run: pip install ansible-lint --disable-pip-version-check
+
- name: Download migrated collection artifacts
uses: actions/download-artifact@v1
with:
name: collection
path: .cache/collection-tarballs
- - name: Setup Sanity test Pre-requisites
+ - name: Install collection build
run: ansible-galaxy collection install .cache/collection-tarballs/*.tar.gz
- # run ansible-test sanity inside of Docker.
- # The docker container has all the pinned dependencies that are required
- # and all python versions ansible supports.
- - name: Run sanity tests
- run: ansible-test sanity --docker -v --color
- working-directory: /home/runner/.ansible/collections/ansible_collections/dellemc/powerflex
+ - name: Run Ansible lint
+ run: ansible-lint --show-relpath
+ working-directory: /home/runner/work/ansible-powerflex/ansible-powerflex
diff --git a/ansible_collections/dellemc/powerflex/.gitignore b/ansible_collections/dellemc/powerflex/.gitignore
new file mode 100644
index 000000000..29185f6d8
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/.gitignore
@@ -0,0 +1,3 @@
+output
+__pycache__/
+*.log
diff --git a/ansible_collections/dellemc/powerflex/CHANGELOG.rst b/ansible_collections/dellemc/powerflex/CHANGELOG.rst
index 85d9eedbe..8e67089a3 100644
--- a/ansible_collections/dellemc/powerflex/CHANGELOG.rst
+++ b/ansible_collections/dellemc/powerflex/CHANGELOG.rst
@@ -5,6 +5,79 @@ Dellemc.PowerFlex Change Logs
.. contents:: Topics
+v2.2.0
+======
+
+Minor Changes
+-------------
+
+- The Info module is enhanced to retrieve lists related to fault sets, service templates, deployments, and managed devices.
+- The SDS module has been enhanced to facilitate SDS creation within a fault set.
+
+New Modules
+-----------
+
+- dellemc.powerflex.fault_set - Manage Fault Sets on Dell PowerFlex
+
+v2.1.0
+======
+
+Minor Changes
+-------------
+
+- Added support for PowerFlex Denver version(4.5.x) to TB and Config role.
+
+v2.0.1
+======
+
+Minor Changes
+-------------
+
+- Added Ansible role to support creation and deletion of protection domain, storage pool and fault set.
+- Added Ansible role to support installation and uninstallation of Active MQ.
+- Added support for PowerFlex Denver version(4.5.x)
+- Added support for SDC installation on ESXi, Rocky Linux and Windows OS.
+
+v1.9.0
+======
+
+Minor Changes
+-------------
+
+- Added Ansible role to support installation and uninstallation of Gateway.
+- Added Ansible role to support installation and uninstallation of SDR.
+- Added Ansible role to support installation and uninstallation of Web UI.
+
+v1.8.0
+======
+
+Minor Changes
+-------------
+
+- Added Ansible role to support installation and uninstallation of LIA.
+- Added Ansible role to support installation and uninstallation of MDM.
+- Added Ansible role to support installation and uninstallation of SDS.
+- Added Ansible role to support installation and uninstallation of TB.
+
+v1.7.0
+======
+
+Minor Changes
+-------------
+
+- Added Ansible role to support installation and uninstallation of SDC.
+- Added sample playbooks for the modules.
+- Device module is enhanced to support force addition of device to the SDS.
+- Info module is enhanced to list statistics in snapshot policies.
+- Replication consistency group module is enhanced to support failover, restore, reverse, switchover, and sync operations.
+- SDC module is enhanced to configure performance profile and to remove SDC.
+- Updated modules to adhere with ansible community guidelines.
+
+New Modules
+-----------
+
+- dellemc.powerflex.snapshot_policy - Manage snapshot policies on Dell PowerFlex
+
v1.6.0
======
diff --git a/ansible_collections/dellemc/powerflex/FILES.json b/ansible_collections/dellemc/powerflex/FILES.json
index 4ede25f55..63f1250cd 100644
--- a/ansible_collections/dellemc/powerflex/FILES.json
+++ b/ansible_collections/dellemc/powerflex/FILES.json
@@ -8,6 +8,20 @@
"format": 1
},
{
+ "name": ".ansible-lint",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "b1faa2fe6cb8f8029a0aae03332dc309e5144309c0a7ae2df24c25727e6f70cf",
+ "format": 1
+ },
+ {
+ "name": ".ansible-lint-ignore",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "4b4fe6df81a08da8afb8ed93daaa9b64b5611e25105de856cc868c84730dda31",
+ "format": 1
+ },
+ {
"name": ".github",
"ftype": "dir",
"chksum_type": null,
@@ -18,7 +32,7 @@
"name": ".github/CODEOWNERS",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "4412819a133de95ababdc5c0c84eec9f0a5aef9aeeb4480949a234b36eef5ab0",
+ "chksum_sha256": "02e53b61090f135ec71115de45f5be3ec18cf5ebe90c17eeb41e4a15a0fa5df5",
"format": 1
},
{
@@ -81,14 +95,21 @@
"name": ".github/workflows/ansible-test.yml",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "2251f32ec892a841be8731bb3aa6eda6576295a0f36070aa695c747163a2685f",
+ "chksum_sha256": "8d53fd6c5db3875651823edfc1873621987d946e7dcc591ff4c17eb92963df52",
+ "format": 1
+ },
+ {
+ "name": ".gitignore",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "694bb98c227881d903739c8f06c133d492a4b328a0c748372f3a25e03bde4e3d",
"format": 1
},
{
"name": "CHANGELOG.rst",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "a1337d01a02ea059a85bcbd9432d6c705f3f00fb6a1446592bab6f2b25cf5923",
+ "chksum_sha256": "22481877ddc56823870ff6e2bdd9eec6c683d39bd79f991076e3dcb75488eedd",
"format": 1
},
{
@@ -109,7 +130,7 @@
"name": "README.md",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "9baeed19af496eed600058a06ae630e36264a702714a542832631d1fab314b21",
+ "chksum_sha256": "b9979afcbcc34a8b63191ce20efa873eefdcbe51a2fa53860477eb79f3563aa2",
"format": 1
},
{
@@ -123,14 +144,14 @@
"name": "changelogs/.plugin-cache.yaml",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "2aac2827117fcb7c4cea8a7cb983444cf76bc2e9e9e06efaa0fe53636e67cd70",
+ "chksum_sha256": "8d9577349e7feb8bdd19d062101868da3cca572de5cb9dfda913c25256dbd15f",
"format": 1
},
{
"name": "changelogs/changelog.yaml",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "277bd367c5b619493b34186e20ef45bcfc4d637ee720989fa9e86888336dba3a",
+ "chksum_sha256": "51a6d9b03a9ac47e7bd547d5d375b6e1021ceff6dc26ac69f06c0fbbc9f618d5",
"format": 1
},
{
@@ -179,21 +200,21 @@
"name": "docs/CONTRIBUTING.md",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "9562b4e8244177783c2c8f2957504debc712365301b74cf89bb9b30ae828bdc4",
+ "chksum_sha256": "6826d5757fe5a1bece4b674a3c304b6834602d4cd6aeab7bc86bfe85a36913fe",
"format": 1
},
{
"name": "docs/INSTALLATION.md",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "882f1c42c4e696b6261a1835474e0a12e1b8bf2d1b267668d6758bd9bac2c979",
+ "chksum_sha256": "baa58ede82a6b28b7a2d83beead5ac1a100975dcdfca4cb3e0e44253dae316e5",
"format": 1
},
{
"name": "docs/ISSUE_TRIAGE.md",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "40924c971f83b32f4f76fa47f03efef00cce6c71342f3c1f806e07e80123fdd3",
+ "chksum_sha256": "bf51c339af375a21f6448ff4c504fca9c94362332f73d28bfc8ce2d3b1bb6983",
"format": 1
},
{
@@ -207,21 +228,21 @@
"name": "docs/MAINTAINER_GUIDE.md",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "9ac1eabe5d52c3d5e8551cdf66fe082681210ebc42c2a76354d3d8424c007445",
+ "chksum_sha256": "50e95d3b07b93b9633f1c77b6eaa5a19e4f4f7e9825498d958cc071ef014ac42",
"format": 1
},
{
"name": "docs/Release Notes.md",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "07bb492fdf0c2f31e9dc06951b1687954470c2986f8d67615abd3e7b3c20c058",
+ "chksum_sha256": "75ef358ffba38ac7cf03ff24d68626d9e5112a046a2b1eba2a10a6d345a97c0d",
"format": 1
},
{
"name": "docs/SECURITY.md",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "f42eb3ec9c98a1525a25e029842ec8d1b8c74f47fda04ba71ef33618005b80f9",
+ "chksum_sha256": "27159d1a795f9d6328a1f85bd95b7b1f55b698de821c979b2feba6e6a70599e9",
"format": 1
},
{
@@ -242,77 +263,91 @@
"name": "docs/modules/device.rst",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "46ca8976a32f4063c01fcc9eaf438a33f9e2daa0cb37f7a3f9e6282ca33cb42c",
+ "chksum_sha256": "3b2a96dab77d81e653c02750a23f0417e1c2ff24b31026dcd0f22134e8c2666e",
+ "format": 1
+ },
+ {
+ "name": "docs/modules/fault_set.rst",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "3ca14b067fd1764db25799feb20a0b15c2999ae9aa9f015ef6b06c8759c34f7f",
"format": 1
},
{
"name": "docs/modules/info.rst",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "c5ab92b8dbd1a21ef2aa6d80bae38f2ab9e2138ca83a4906d81cfa5f1f28f72c",
+ "chksum_sha256": "ff01e0057c388959a6460ac1b1b6f026ecf7d57e190067beed9e28876cad4a67",
"format": 1
},
{
"name": "docs/modules/mdm_cluster.rst",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "29668a2e0b10c61fc7c5446359b5ddfe8cb3b00245028d440c30b1d7dca2051b",
+ "chksum_sha256": "653df53c3af3b726a58b24fc0a78d882655a35ea40283530044ec78b5e3b7023",
"format": 1
},
{
"name": "docs/modules/protection_domain.rst",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "f8ef67d0e6f1a8bfb56e670db89dc59c7e8597a257f20a0acb0cc062fcafa14b",
+ "chksum_sha256": "789a3c50b0037017815b0f0b911ee462f50389da17ab1d55c649d572f137c822",
"format": 1
},
{
"name": "docs/modules/replication_consistency_group.rst",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "e2261304db5cbee90c7ad6350f97b952c12cf9b2fab2420c545357c6901aa9e4",
+ "chksum_sha256": "d371f5f275878b1e4995902946845a7b1e947705bd432593714d50069d20611e",
"format": 1
},
{
"name": "docs/modules/replication_pair.rst",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "e953f529b134d196e956d4a41dd23d0c6e399e8e38ecf1a626ee3616551a031f",
+ "chksum_sha256": "0a1c6ac731f5fd9343b52a740474045f2600ab0867a73692facc619b68cba6ce",
"format": 1
},
{
"name": "docs/modules/sdc.rst",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "625ee7f9eadbc534e907cff27572c7bd0b20f1344907e7a69ae219bcef07f425",
+ "chksum_sha256": "0fbbcfcfc18c7c8ce473e614d4858887150aa689b4ba251b6f61997011c7c049",
"format": 1
},
{
"name": "docs/modules/sds.rst",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "32d801b928f96c4ff6cf0453a6ef7e682886ff9c138a674e7251c70bb6404363",
+ "chksum_sha256": "0646d49b08b50b182c8730a5dd2ee2033e11afbeeac93f3bd45cd3e62cd702ff",
"format": 1
},
{
"name": "docs/modules/snapshot.rst",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "9b22fcc5dbb030ccf891fdfea5af1a701de60dfab3c45c1e48a748e93f375979",
+ "chksum_sha256": "cf0f97870e12989f585e770efd5571b5de22a6154b73df241c3c1a700e7cb6fe",
+ "format": 1
+ },
+ {
+ "name": "docs/modules/snapshot_policy.rst",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "e4e54cfcca15c4e906727771c4055b8dce46dd9089daf39d694d82a688599156",
"format": 1
},
{
"name": "docs/modules/storagepool.rst",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "8d69cf12ab9aacbf631022bb261a51f55cf4431a93be6a181b1baf174765fadc",
+ "chksum_sha256": "6eda9bc51092e6737beb53c7d16b22989888802171c28786ec1459733df62b5f",
"format": 1
},
{
"name": "docs/modules/volume.rst",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "f9396dd953a3b076107e9df14ea6cb4234bf6e4a5d8f258162bc6319caf2dcfe",
+ "chksum_sha256": "67dead1b0e93a922e10e44969da93e2aa17604fc13078d76477f5f39d4391114",
"format": 1
},
{
@@ -333,7 +368,196 @@
"name": "meta/runtime.yml",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "3593e8970a00d95a557377bd7af2f50e5212620def3ed4134c989a33dfd8ec4f",
+ "chksum_sha256": "67490e6204f2b0ef55e2cad348fa79da6d137455dd1e9a25c51cac5cc22dd464",
+ "format": 1
+ },
+ {
+ "name": "playbooks",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "playbooks/modules",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "playbooks/modules/device.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "a5e4383ecec8db6a9688a08165d4b69e0785f16e2899e8fe494a43f723a67781",
+ "format": 1
+ },
+ {
+ "name": "playbooks/modules/fault_set.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "68bca9de5442d9babc69af7f0e6b0d5211371e87887fcfaaf4ee16683ff5f2ac",
+ "format": 1
+ },
+ {
+ "name": "playbooks/modules/info.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "36b79dc7559347030ccf349b815be9e6431a807b3ab9064e009163c10970b397",
+ "format": 1
+ },
+ {
+ "name": "playbooks/modules/mdm_cluster.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "6e4985543886231427949e4e953d67879618c90f9fa4a8de7f0f68b6aec3b3f3",
+ "format": 1
+ },
+ {
+ "name": "playbooks/modules/protection_domain.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "724461ac790970ef8eeafe747cd307bef239bb777ebeca34462088785bbff289",
+ "format": 1
+ },
+ {
+ "name": "playbooks/modules/replication_consistency_group.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "748579a1e3a3c23dc4bd8eabfa4010dc05c14df990fa0b9f6c362e03424f8975",
+ "format": 1
+ },
+ {
+ "name": "playbooks/modules/replication_pair.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "84bb70c79296a52f29afb0ba9e2dd50a78984bb27813557907d23413a256dc38",
+ "format": 1
+ },
+ {
+ "name": "playbooks/modules/sdc.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "6d45e67ade07a162a48566777e8580693e213a2712f7766e3a64b0d1dd0f4be1",
+ "format": 1
+ },
+ {
+ "name": "playbooks/modules/sds.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "807c5576f3cbf5fa23a608d2323289459675ac4f05b285c1ef8dcfe9986febc1",
+ "format": 1
+ },
+ {
+ "name": "playbooks/modules/snapshot.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "ad72095356b2a0c26be85a2a1bbdf88b0a7d6765975f158191506c374ea64ec5",
+ "format": 1
+ },
+ {
+ "name": "playbooks/modules/snapshot_policy.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "5a03fd882ceab91443e588e0f67f7a485750d3009dadaac077a284e4f19626a4",
+ "format": 1
+ },
+ {
+ "name": "playbooks/modules/storagepool.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "7059aed35251235a82b26324e6fab795fc260c72851d72affc54295b8733e94a",
+ "format": 1
+ },
+ {
+ "name": "playbooks/modules/volume.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "30700d36779e822b4f3d41282238e60a6fa12589d3601038402d17d3463a9b30",
+ "format": 1
+ },
+ {
+ "name": "playbooks/roles",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "playbooks/roles/group_vars",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "playbooks/roles/group_vars/all",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "05ae8d3b9bba106581f346b384915000397c6a481054917b4a398fa6646fa93b",
+ "format": 1
+ },
+ {
+ "name": "playbooks/roles/host_vars",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "playbooks/roles/host_vars/node2",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "1dc167e7f271cff2a5604331d50db9ec8347d653d5e1af2aacaeb3650b5534a1",
+ "format": 1
+ },
+ {
+ "name": "playbooks/roles/inventory",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "9ffb23d7831198734c5309e8318b299a7a9acd2eb0496faab883be891ed0ab3e",
+ "format": 1
+ },
+ {
+ "name": "playbooks/roles/site.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "c9299ecdfebf82aecf23576dafdd5c8fcf22ee3cc09b530eb589b59584844d23",
+ "format": 1
+ },
+ {
+ "name": "playbooks/roles/site_powerflex45.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "ca3a21c0f998486d5c01a0e60da7d40556f1c1f7e761d1e67d5e43a88c53a743",
+ "format": 1
+ },
+ {
+ "name": "playbooks/roles/uninstall_powerflex.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "740113596a3141bb24ff16434c4118ff7ab37400efebd1e5fea4cd6ed0ac7d91",
+ "format": 1
+ },
+ {
+ "name": "playbooks/roles/uninstall_powerflex45.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "f0c976ffd9d4100867876c06c7adb0c76a62e7688b23a3d617b5785d2f0d3127",
+ "format": 1
+ },
+ {
+ "name": "playbooks/roles/vars_files",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "playbooks/roles/vars_files/connection.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "d2f056bba9e21e4c3e3c1448d98f5f156e59265d2e777a160999d29781eadb68",
"format": 1
},
{
@@ -354,7 +578,7 @@
"name": "plugins/doc_fragments/powerflex.py",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "042fd3430b1ecc1ce01ce3efafa2f2d0fca1d814b891a756f686b5b543eb3bef",
+ "chksum_sha256": "801968c2f21f016d0f0861831e74ed4fc804e68a39b021c05f39b38437978bc0",
"format": 1
},
{
@@ -386,6 +610,34 @@
"format": 1
},
{
+ "name": "plugins/module_utils/storage/dell/libraries",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/storage/dell/libraries/__init__.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/storage/dell/libraries/configuration.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "52238590dd2a4c4ad071b030b815e59a6e44b47cd27d513a9ee29c46574498a1",
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/storage/dell/libraries/powerflex_base.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "4dc786237539c84bae0371a2df3b4b4daaaaf9fc3672b5eb38228ed838e820fe",
+ "format": 1
+ },
+ {
"name": "plugins/module_utils/storage/dell/logging_handler.py",
"ftype": "file",
"chksum_type": "sha256",
@@ -396,7 +648,7 @@
"name": "plugins/module_utils/storage/dell/utils.py",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "87e9c4d3570ace6a236080e285e3b3c12b4e5c763064334b861ddb38ea37b264",
+ "chksum_sha256": "402f820473dc2725d9d3fa1787854861651003a86873c1613951f5faefe9e68c",
"format": 1
},
{
@@ -410,84 +662,98 @@
"name": "plugins/modules/device.py",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "9531ecfeaa468b126d90e0932723d12d434dd604a089de4770b0b2dfcd5b9253",
+ "chksum_sha256": "b105384eabdfe9a37857beea28f6407ede241572378c345f24c5576b18c92c47",
+ "format": 1
+ },
+ {
+ "name": "plugins/modules/fault_set.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "abcd956cd704a085cc14ea5ffb860b5386285828e3b4885c89868c6f4d8bf376",
"format": 1
},
{
"name": "plugins/modules/info.py",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "119a221a17294de856e4840235897100116fe32e51f11aae69fc735ba0979a78",
+ "chksum_sha256": "af50f59fca9f9bc84a8ecd622173279f436a7ac96654b86a5afa547aa317123f",
"format": 1
},
{
"name": "plugins/modules/mdm_cluster.py",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "f431240cc0c646a9da72c2537c3cb0d549525082e28380e5f50076ca565c87a4",
+ "chksum_sha256": "f86ed2d81a47fd46d4a5caf8558fa7c471a5f0923eb5e9b5b04b5b22ddc8ac3c",
"format": 1
},
{
"name": "plugins/modules/protection_domain.py",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "53142b5ab071e1b841af1857e0408df1d2d29b9e5a261a04b0acf1e84a3eb851",
+ "chksum_sha256": "e361fb3eb15155e96e6c6a96e6499864631728497ebe515032d4821a6ad6cf0d",
"format": 1
},
{
"name": "plugins/modules/replication_consistency_group.py",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "4c803e9eae58c8eb1da6af18374ef3d30249b7da818cf11e3d8daabb1a3cc421",
+ "chksum_sha256": "c89740feeebf6b438580a445e7cb1910ce7ee65985f4d35f0539523b75748bc8",
"format": 1
},
{
"name": "plugins/modules/replication_pair.py",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "5bab68d67ea04ba0edbb316edf95bad9a71a3400b250edcb5c19e114629f8543",
+ "chksum_sha256": "d4894497203afa31721e55dc1010b7e760d77dcbc1d1b870932f0423dcc7c018",
"format": 1
},
{
"name": "plugins/modules/sdc.py",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "b8104d04864a2270a5ceb5cf7e66f681125dec8510faf60d0054f4ae0e8739c2",
+ "chksum_sha256": "c00725c74b13a4f7c14915c005fa39379c1163e88ac30af8f792191b4e385259",
"format": 1
},
{
"name": "plugins/modules/sds.py",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "dc02c40e9b6d49c9df942e275de073a4854cfb6e0b27f2a876583e3d094b7803",
+ "chksum_sha256": "e82ea900c116a86b5ecd3b014ea18aa2a5562bb4d4fd02f30cd994573ab2840f",
"format": 1
},
{
"name": "plugins/modules/snapshot.py",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "3f653d2f3c1f0dc8a2d496cab89e240fe7a77e7d3d5e7f88a47f718ae0dbc07c",
+ "chksum_sha256": "6db23d9479a439ba60ccae6e840e2c6f1780fd86dbf662c97d226c2cf44589a4",
+ "format": 1
+ },
+ {
+ "name": "plugins/modules/snapshot_policy.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "19b773c17db1c93a834068233ed02d38d243665e392b9dc0ced445050a1ee208",
"format": 1
},
{
"name": "plugins/modules/storagepool.py",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "7f9c9179a1cfe50510946135ee8ff0eb7b9b027e4f7d7afa53cc20e35f6a1b5d",
+ "chksum_sha256": "efd6c30ca7e5d8d61c13e3e1c154d28d80c8962e256c7dc4a0114f34e41c678d",
"format": 1
},
{
"name": "plugins/modules/volume.py",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "4c8e0677e4071d2288a6b13969279e10873e41410eaabf80c344f56206dcedb9",
+ "chksum_sha256": "05e5c8cb7d221301b4ecf801a216b1d914b165ffce7f13a6195704807868414a",
"format": 1
},
{
"name": "requirements.txt",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "b02f408522b5f335ac6c1ef0d7ee5dd72a0a24492de516545ac4071d315882db",
+ "chksum_sha256": "f4719535bc9626a724bf0cf63b26d3a7400b6e3651707c96b97b57feec0d7966",
"format": 1
},
{
@@ -498,45 +764,2096 @@
"format": 1
},
{
- "name": "tests",
+ "name": "roles",
"ftype": "dir",
"chksum_type": null,
"chksum_sha256": null,
"format": 1
},
{
- "name": "tests/requirements.txt",
+ "name": "roles/README.md",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "65e6091d1c8d88a703555bd13590bb95248fb0b7376d3ed1d660e2b9d65581c8",
+ "chksum_sha256": "af67416ffb08aba153e5a175c6bf11b3143377b5157c6a38bf1ec60c134a7ab7",
"format": 1
},
{
- "name": "tests/sanity",
+ "name": "roles/molecule.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "f79dd5ffbd02ca2fab32d6a0f598f6ae0b3b74510531434059dbf2f7817f8099",
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_activemq",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_activemq/README.md",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "e6755bbede6d1e524cdd77d56db388826625b72d3c34505c9ea035c7729836cc",
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_activemq/defaults",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_activemq/defaults/main.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "b82c7ccfa9146c89e0c96cde77331352f7dce52b8b941f435e0840ad8353b42c",
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_activemq/meta",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_activemq/meta/argument_specs.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "fe170de55352213d09fa2fec1345ee50866e5189fc542d80e534423d28f9d1f8",
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_activemq/meta/main.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "47b2715cc84c7e0d2b09d87d89fbe40e296b75ee3b486ebff26df70948e66e83",
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_activemq/molecule",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_activemq/molecule/activemq_install",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_activemq/molecule/activemq_install/converge.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "a107a478ed271356da7529757ab82d4a86a75ece8fbaa4676a6bb52e6a752943",
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_activemq/molecule/activemq_install/molecule.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "f52d711103d50a437830c6fbcd04fb4bab49a0f82f6d26d1c791c6e8488dd090",
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_activemq/molecule/activemq_uninstallation",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_activemq/molecule/activemq_uninstallation/converge.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "e1afaa3cef1fdd48d1337dd0a240df5882652af2e0540bbc2ec630943b9b2120",
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_activemq/molecule/activemq_uninstallation/molecule.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "f52d711103d50a437830c6fbcd04fb4bab49a0f82f6d26d1c791c6e8488dd090",
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_activemq/tasks",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_activemq/tasks/install_activemq.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "77bbb231e144677d15f51387b6fd097141881f7ea3fe195ee288b5cc1e265386",
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_activemq/tasks/main.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "f030cf68a9440e55f64453550db811a81520f22639e0c5137611c88c90bd4631",
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_activemq/tasks/uninstall_activemq.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "305f78c22d168356444da38b6458315727b09dc64883405a1b2f6df81886ece9",
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_activemq/vars",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_activemq/vars/main.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "f52d711103d50a437830c6fbcd04fb4bab49a0f82f6d26d1c791c6e8488dd090",
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_common",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_common/README.md",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "cae3210b759727b69f931f52984f70ef5daacccf7d9c9ace228b2fdfefa98ae5",
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_common/defaults",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_common/defaults/main.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "e225b63e0df93aa50aadd844eb2387ad0a85d12fca71e71b6f553508d1c4f46c",
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_common/meta",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_common/meta/main.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "58ae5fc45575b66485d65079e57a6900228d9e899606083f9cf07e32b6c6915c",
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_common/tasks",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_common/tasks/install_java_CentOS.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "3f3c2c23c0040011d8e51fdf258b301fb3b6fd7c83dbedea9de1866ede4aaeed",
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_common/tasks/install_java_RedHat.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "3f3c2c23c0040011d8e51fdf258b301fb3b6fd7c83dbedea9de1866ede4aaeed",
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_common/tasks/install_java_Rocky.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "3f3c2c23c0040011d8e51fdf258b301fb3b6fd7c83dbedea9de1866ede4aaeed",
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_common/tasks/install_java_SLES.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "5b51bd9a722a0fc78c239b55d613ce99ee84acf3ed78a32527260880919432b8",
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_common/tasks/install_java_Ubuntu.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "6aaeed0116c9bee61cefd1567a7da5b47e1e3cd16ce4a8aa946c0df779683ded",
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_common/tasks/install_packages_CentOS.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "2b8e04b6364ab1c48faad08fb920bc773c93fc683a6810bd1f818565b964f5d6",
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_common/tasks/install_packages_RedHat.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "ad23ff6e1d29ef0a873310263d7263a260cf00b4dce9829de2648431d432cbd8",
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_common/tasks/install_packages_Rocky.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "2b8e04b6364ab1c48faad08fb920bc773c93fc683a6810bd1f818565b964f5d6",
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_common/tasks/install_packages_SLES.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "ad23ff6e1d29ef0a873310263d7263a260cf00b4dce9829de2648431d432cbd8",
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_common/tasks/install_packages_Ubuntu.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "5bd502a4e483634be08c3714d9de47d015d240014de4f80a2642db50f17e341a",
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_common/tasks/install_packages_VMkernel.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "9f79f89df904d70f64bde83e6ab21d158016c988b894ff8f89d5f4f20ad54900",
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_common/tasks/install_packages_WindowsOS.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "c465a08183097556c47104e59e1af4e72728946f3b313e6db817b1d2dab5e36f",
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_common/tasks/install_powerflex.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "78b2d64d46817cf14b236a3abbdbfe89e7944939fa6c64048c914cc6591470d8",
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_common/tasks/main.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "ef454a91cb89c05213f2533d1ffd0587159395d84719c68d126165b6e93e0ffe",
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_common/vars",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_common/vars/CentOS.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "5c3b690f7ffdaabd86cb539b4372e9695c211b9dce2614ce0db26d919c18943a",
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_common/vars/RedHat.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "c800cb014ab63853a900a4b9f8d74a189d2ec287d49cfb5014be7b6bbb27ebb2",
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_common/vars/Rocky.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "c800cb014ab63853a900a4b9f8d74a189d2ec287d49cfb5014be7b6bbb27ebb2",
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_common/vars/SLES.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "b1f0ded0f52745cbf6f34e5219c18429328fbdff918b545e601d8925f2c3ba84",
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_common/vars/Ubuntu.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "f15bca60317d404b90485e5a37088bdba2de34a685cdcf916713e175c5350e61",
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_common/vars/VMkernel.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "26252cf7865a30e28c65dafd3b489a29ddf34bb7ced073e6ccd769f5c28d51fd",
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_common/vars/WindowsOS.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "26252cf7865a30e28c65dafd3b489a29ddf34bb7ced073e6ccd769f5c28d51fd",
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_config",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_config/README.md",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "50f0bc8167947aa34a1b0cf9cea69990d9a40a293f79783ec8da7fd812371bfa",
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_config/defaults",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_config/defaults/main.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "1d4b0772eafd5452c699dec19d5da4ac1fa054a3194790e62365a0a65f860498",
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_config/meta",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_config/meta/argument_specs.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "0a1a6a397916a8e44d902f9d7928880b18d9162de9451d0c4c26498fce1f74dc",
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_config/meta/main.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "67ce4af79de10ac4e36f88569c04f3c6ab48f7c547cb7a255b210367b72127e0",
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_config/molecule",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_config/molecule/configure_protection_domain",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_config/molecule/configure_protection_domain/converge.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "e6fdfc4a605e3c48451c2d209c9db4c13e5c80049a1ca99abf00eac1c2eae95f",
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_config/molecule/configure_protection_domain/molecule.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "f52d711103d50a437830c6fbcd04fb4bab49a0f82f6d26d1c791c6e8488dd090",
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_config/tasks",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_config/tasks/main.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "3118853f9e23c714f8c950127932f93c233be8b0c23cfffe7adc089e84db1128",
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_config/vars",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_config/vars/main.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "6c5b40a10152e19face2a4c0d04c0535edda12e1cd75a44e468cb0c3afc50e79",
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_gateway",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_gateway/README.md",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "78da0171fbf8710887bbd39b5acf826680369452fd2da5891d3782b2011e9670",
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_gateway/defaults",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_gateway/defaults/main.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "b5f08d57ce4cdfb0ea87265fcccccf9cd17db90e0e4dedd310c7509db00be7bd",
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_gateway/handlers",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_gateway/handlers/main.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "b9efe72f76dd2dd4bd46b702eac3a8ae2a6672217ed883912592faa615ef5009",
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_gateway/meta",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_gateway/meta/argument_specs.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "058a0ad19f4acacb127afed55109379e9ece33b4daa3af988c96073dc4a83d0a",
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_gateway/meta/main.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "0bcd2a1c60ed6c530cb1658d4f190ca9ed27fe874af7bde2cd5ed61e1ee54a48",
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_gateway/molecule",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_gateway/molecule/gateway_installation",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_gateway/molecule/gateway_installation/converge.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "0d268a507bae0c798781e45a056c42fa922728fddce8fbc4b7947a4577710223",
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_gateway/molecule/gateway_installation/molecule.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "f52d711103d50a437830c6fbcd04fb4bab49a0f82f6d26d1c791c6e8488dd090",
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_gateway/molecule/gateway_installation_invalid_path_rpm",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_gateway/molecule/gateway_installation_invalid_path_rpm/converge.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "103ad6ab73d206689f78f486207a61bce8b81ed00c4af630db08fdd499d22a48",
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_gateway/molecule/gateway_installation_invalid_path_rpm/molecule.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "fb07e75205f81f1ef5ce32019fd265ff69cd41ca7f9f8ea4350d850007a11f1e",
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_gateway/molecule/gateway_uninstallation",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_gateway/molecule/gateway_uninstallation/converge.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "fbab1f810b6ab487e71d471233bf64841998669a1864ec0ed804ed105752380d",
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_gateway/molecule/gateway_uninstallation/molecule.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "f52d711103d50a437830c6fbcd04fb4bab49a0f82f6d26d1c791c6e8488dd090",
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_gateway/tasks",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_gateway/tasks/install_gateway.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "19e292b4d470e21255049aa4fbea7b29d3de1815187954f9e979e5af5821b1ba",
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_gateway/tasks/install_keepalived.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "c5012b69295e36ac759ab5fa4aa16cf0b2ac7a336964aa75b9e3f8473c4f0e64",
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_gateway/tasks/main.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "c49d3ebb5baa84f39f8c7d7e051e352aad04f8146dbb3ac67f9e30369870c2ed",
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_gateway/tasks/uninstall_gateway.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "7f2f246e702551abd867899d2425d11d03a9fd6a18209b7c8061fe97a06e775b",
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_gateway/templates",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_gateway/templates/keepalived.conf.j2",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "863d4319f4275a7f426e88636e6da3bdf48e47fa941e0ffff5be5c91ee82c5d7",
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_gateway/vars",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_gateway/vars/CentOS.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "51ad2fc40b6f7a22e610d192fd8db399e6f80727d46036899160c8a87695cbcf",
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_gateway/vars/RedHat.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "51ad2fc40b6f7a22e610d192fd8db399e6f80727d46036899160c8a87695cbcf",
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_gateway/vars/SLES.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "51ad2fc40b6f7a22e610d192fd8db399e6f80727d46036899160c8a87695cbcf",
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_gateway/vars/Ubuntu.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "51ad2fc40b6f7a22e610d192fd8db399e6f80727d46036899160c8a87695cbcf",
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_gateway/vars/main.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "5e15fb4eaca6121330eaf6c8bb0806486a6181e6dc01c33280bcb8d9f2725937",
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_lia",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_lia/README.md",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "21c25b94f7bb413f8fccc84d2d07930b2e229fabbd102ea07e18a9816d0a111f",
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_lia/defaults",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_lia/defaults/main.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "cda68414c37f37fa79b0b614cede6cf518f2590ce191d9b3ef3320706cbd35d8",
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_lia/meta",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_lia/meta/main.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "d3a506ea087962f1516d76f9e061ecd071c9ca5837a6769bb3a49ba67e0e56b5",
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_lia/molecule",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_lia/molecule/lia_install",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_lia/molecule/lia_install/converge.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "7ca1b5a0d00a83e4c6fa99cdbb9434b372d542bc618aab4afee362656df64b2d",
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_lia/molecule/lia_install/molecule.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "f52d711103d50a437830c6fbcd04fb4bab49a0f82f6d26d1c791c6e8488dd090",
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_lia/molecule/lia_installation_invalid_path_rpm",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_lia/molecule/lia_installation_invalid_path_rpm/converge.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "9f5c5ec7322ef081a8f5cb2a01a0fb089cd54f8d5d61c2b207c4fd013b074272",
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_lia/molecule/lia_installation_invalid_path_rpm/molecule.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "fb07e75205f81f1ef5ce32019fd265ff69cd41ca7f9f8ea4350d850007a11f1e",
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_lia/molecule/lia_uninstallation",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_lia/molecule/lia_uninstallation/converge.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "93ab98e341639d90f13e11aad6896f9984f33c3e2a70fd966f8800e8726c2fa7",
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_lia/molecule/lia_uninstallation/molecule.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "f52d711103d50a437830c6fbcd04fb4bab49a0f82f6d26d1c791c6e8488dd090",
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_lia/tasks",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_lia/tasks/install_lia.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "2ace8d8c2789df5acea0bc156f0db8730471c2996ee7ca0194211a63708c72eb",
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_lia/tasks/main.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "4a9c13a1d411868f588fb056390e73b987564b46252090ceaf8bd39a5d079e90",
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_lia/tasks/uninstall_lia.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "d01b0079ccdd0f0c8d9f0b750cb06d293d2a947c1d9495270c42f5d0522396e8",
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_lia/vars",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_lia/vars/main.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "b8a48687c0206129cf2c25a2f27c0e348a2a77ba706ceaa06bef4f413e2e8618",
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_mdm",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_mdm/README.md",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "24207df570457b47d51e23a304ea6dbfd69226d630efd6b3059a1b7d1017bd63",
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_mdm/defaults",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_mdm/defaults/main.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "1f0157c2ccd54a764f80279a2d5b28daa31b459dc6afafe7004b6ae32c154fd0",
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_mdm/meta",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_mdm/meta/argument_spec.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "6f7a63718623aadf88e7ad90fcb807bca342e54123f3afc06db58d526aea5afb",
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_mdm/meta/main.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "488a2623195e46d379ca093f85696993276b836a4383fabcf322230f844f0b98",
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_mdm/molecule",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_mdm/molecule/mdm_installation",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_mdm/molecule/mdm_installation/converge.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "0e53f77f0eeba3073115f13986a4929920e8b4854e445e27e4b8d22b89e8877c",
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_mdm/molecule/mdm_installation/molecule.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "f52d711103d50a437830c6fbcd04fb4bab49a0f82f6d26d1c791c6e8488dd090",
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_mdm/molecule/mdm_uninstallation",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_mdm/molecule/mdm_uninstallation/converge.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "7e6fd302767d35dc7efe0cfce2ec5a8ec30eb4a8973d169876381ebe04d6e93a",
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_mdm/molecule/mdm_uninstallation/molecule.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "f52d711103d50a437830c6fbcd04fb4bab49a0f82f6d26d1c791c6e8488dd090",
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_mdm/tasks",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_mdm/tasks/add_certs.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "44f64893ed75464ab59e0396ce0ed30730c1cecc4023c8dbbb6a52e9e2789f2a",
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_mdm/tasks/install_mdm.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "b0ed3f5966ab8e9febfd505ea92a5c6e39eecd709ed5ab8ff3c7f32f22551e2c",
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_mdm/tasks/install_powerflex3x_mdm.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "cf85c85f40c93bace8684a17a0326921e190bcecdc028d38fea65bb10d82d824",
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_mdm/tasks/install_powerflex4x_mdm.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "9c56bea05dbe554fec279bb36958e567bd784461f451e0d1b1e2342f833c8b15",
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_mdm/tasks/main.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "002b7d40098887264323d57dd8f7d3a0c5e3648c52618056a3d1b014065c4cc3",
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_mdm/tasks/mdm_set_facts.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "964e2e7af5c8e65f8c394349480de1a138a0d5f75cae6bd068d90a36b206dfd3",
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_mdm/tasks/remove_mdm.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "715f7a6393ead6b40bedeca50e77f0b5acb2b2b084ab96974cd63eef274521ab",
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_mdm/tasks/uninstall_mdm.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "fbc39000ccaf46c6b11752b14170bfdf8f358ecec4abfc4156be148fcb89f2dd",
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_mdm/vars",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_mdm/vars/CentOS.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "0835a62a7ce35708916899ff96820f3ef59738f9d4eea873c24159aa22c25912",
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_mdm/vars/RedHat.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "0835a62a7ce35708916899ff96820f3ef59738f9d4eea873c24159aa22c25912",
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_mdm/vars/SLES.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "4537b87aacbcef7e0b9688ec63cadf406a05482a2a6d67f363898124b0246bf9",
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_mdm/vars/Ubuntu.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "1605f49f1d1907ca15ff20bc675bd5f1698cab3eb75f316f3395ada48ad009a2",
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_mdm/vars/main.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "1001e39b69436d27e5bf7d6189e67c35f3b085bd1b27d92ed08496b39ada5991",
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_sdc",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_sdc/README.md",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "fae5f2f2eb80349652cf02f87caf76ad149c41998c6ef2e291289f25ff5d849c",
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_sdc/defaults",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_sdc/defaults/main.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "9cee69ae196e0f0abecc93111ffeda653e64c7ee46e3cd7d413ae96bb96879e0",
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_sdc/handlers",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_sdc/handlers/main.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "a57d62c97bd821f681daff01237cae53aa374797d72b5648f552b4fefb4659ce",
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_sdc/meta",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_sdc/meta/main.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "eea1ded78ed8ffcf2e2d95d74f9a93c51a6fe0bd9583457ebd4eb440a2fd4b0b",
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_sdc/molecule",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_sdc/molecule/sdc_install_map_volume_uninstall_negative",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_sdc/molecule/sdc_install_map_volume_uninstall_negative/converge.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "1bcff33e6d9440c3222812394d2f0e040f5243f7480c5d1712f1e45014d994df",
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_sdc/molecule/sdc_install_map_volume_uninstall_negative/molecule.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "fb07e75205f81f1ef5ce32019fd265ff69cd41ca7f9f8ea4350d850007a11f1e",
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_sdc/molecule/sdc_installation",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_sdc/molecule/sdc_installation/converge.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "2ac1e705337d1ca2e9726935da337dd4bd7f38dcf92a9564f164fad1388d479c",
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_sdc/molecule/sdc_installation/molecule.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "f52d711103d50a437830c6fbcd04fb4bab49a0f82f6d26d1c791c6e8488dd090",
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_sdc/molecule/sdc_installation_invalid_path_rpm",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_sdc/molecule/sdc_installation_invalid_path_rpm/converge.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "3a5ae0cc210b48e046f8d047e483c5512022792d51b020aefd85ce455f3ff096",
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_sdc/molecule/sdc_installation_invalid_path_rpm/molecule.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "fb07e75205f81f1ef5ce32019fd265ff69cd41ca7f9f8ea4350d850007a11f1e",
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_sdc/molecule/sdc_uninstallation",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_sdc/molecule/sdc_uninstallation/converge.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "8fd5671bbffbbd61bb65f32e7922cf0a81eb9cd1f740e2ebffd279719fdef04c",
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_sdc/molecule/sdc_uninstallation/molecule.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "f52d711103d50a437830c6fbcd04fb4bab49a0f82f6d26d1c791c6e8488dd090",
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_sdc/molecule/var_values.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "19a9cf102ab96db1e9767003d7e3633d4941faafde44a6881884d4c34c6020f5",
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_sdc/molecule/wrong_sdc_credentials",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_sdc/molecule/wrong_sdc_credentials/converge.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "ded6dbcd70dff56f2631e6a244255f95be1cf02cc0aa69aff21259ae7fd83a1c",
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_sdc/molecule/wrong_sdc_credentials/inventory",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "1ba46d85f8ab818324fd855aab079cda7d64620fa10790e8244164c5bdb652d5",
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_sdc/molecule/wrong_sdc_credentials/molecule.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "848afca44a73743eaa85dbb917a74063e8c32fa006effa466e7bc118c8047462",
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_sdc/molecule/wrong_sdc_ip",
"ftype": "dir",
"chksum_type": null,
"chksum_sha256": null,
"format": 1
},
{
- "name": "tests/sanity/ignore-2.12.txt",
+ "name": "roles/powerflex_sdc/molecule/wrong_sdc_ip/converge.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "d7f97b722802c66b7fa85d54a109df152f9f14eea245c70e274fcd1431148642",
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_sdc/molecule/wrong_sdc_ip/inventory",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "f7aeba3eeadbdef5786e878836b153893618d27d584833642125387a860f65db",
+ "chksum_sha256": "083126dbceb96fa9eda3133b66dae550267420475dd047dea661d9a7a41d076e",
"format": 1
},
{
- "name": "tests/sanity/ignore-2.13.txt",
+ "name": "roles/powerflex_sdc/molecule/wrong_sdc_ip/molecule.yml",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "f7aeba3eeadbdef5786e878836b153893618d27d584833642125387a860f65db",
+ "chksum_sha256": "848afca44a73743eaa85dbb917a74063e8c32fa006effa466e7bc118c8047462",
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_sdc/tasks",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_sdc/tasks/configure_sdc.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "835f953daacd8245f0f42663642da91d3217814ad3b6eb86464ccfd1ac5b094e",
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_sdc/tasks/install_sdc.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "edcacc16abd2e2ddca0262e215130db8851d691f1c52ec54b641a1390180b386",
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_sdc/tasks/main.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "7e8fc7838167ed1117469801021bfc4280c1dad474fc2966b5d08a1536354f48",
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_sdc/tasks/register_esxi_sdc.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "ed36076c66cd63ccefb2b3deee053948a973e06525e02122da1000536533ea63",
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_sdc/tasks/remove_sdc.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "04d013b6a62d8de30992fa523069b5b8b62a265aa8d4be5fc26074049680fed5",
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_sdc/tasks/uninstall_esxi_sdc.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "65156b0e16111eaaedb8541d63e044d2ff75e007bbfd57b75fad4fb2c633ca64",
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_sdc/tasks/uninstall_sdc.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "d90f77f4e0282050ebaa950b6a5408699afc88d437624a2d4f22e729c006c3b2",
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_sdc/templates",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_sdc/templates/driver_sync.conf.j2",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "5141f8e9855c43c072c5ea9d183b04abd77bce087e0f28e5fc1c4ec03406d6ad",
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_sdc/vars",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_sdc/vars/main.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "2cac1229947ca43a32a09b18d5d8a612cbe34a27c91651a9fde2e17ede3ac14d",
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_sdr",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_sdr/README.md",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "a691f0f80b76f468b3778063faf2e9212499ab7299178b009b8b860c37824d80",
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_sdr/defaults",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_sdr/defaults/main.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "754ebb4bd2dbdc114ed56c6f4ae3a6178d0255ef886b841b164b22d215ef4e5d",
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_sdr/meta",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_sdr/meta/argument_specs.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "8de2736e7e478c6305a0b65938ed54196ffcd0488150653220ed1f0f8d36ab74",
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_sdr/meta/main.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "50b31345b42827e025fd5afec900b3b23647c3dfc2d5cef152dcf407544f7f0b",
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_sdr/molecule",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_sdr/molecule/sdr_installation",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_sdr/molecule/sdr_installation/converge.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "9696a14f08f84ec5053b13c2763227ac8f639a868a305a6c16f6ba76ffeecb01",
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_sdr/molecule/sdr_installation/molecule.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "f52d711103d50a437830c6fbcd04fb4bab49a0f82f6d26d1c791c6e8488dd090",
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_sdr/molecule/sdr_installation_invalid_path_rpm",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_sdr/molecule/sdr_installation_invalid_path_rpm/converge.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "7e5dd006b187975e92aa28bdc72d3096fb5943d8f5f218784f0f96cbd38127ed",
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_sdr/molecule/sdr_installation_invalid_path_rpm/molecule.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "fb07e75205f81f1ef5ce32019fd265ff69cd41ca7f9f8ea4350d850007a11f1e",
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_sdr/molecule/sdr_installation_invalid_pd",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_sdr/molecule/sdr_installation_invalid_pd/converge.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "7960142d685e35f7c1f7f9be5e31c9dd0c8d94571265598e271f75059da1e982",
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_sdr/molecule/sdr_installation_invalid_pd/molecule.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "fb07e75205f81f1ef5ce32019fd265ff69cd41ca7f9f8ea4350d850007a11f1e",
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_sdr/molecule/sdr_uninstallation",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_sdr/molecule/sdr_uninstallation/converge.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "6eb27f1902fa084c8ef53597d01549d0449b0d341678f2471d16d9072bc010ef",
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_sdr/molecule/sdr_uninstallation/molecule.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "f52d711103d50a437830c6fbcd04fb4bab49a0f82f6d26d1c791c6e8488dd090",
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_sdr/molecule/var_values.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "382637add6d6a2db94e224c132f86b0d9380b501f36c9ab4268325df9c430504",
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_sdr/tasks",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_sdr/tasks/add_sdr.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "fc927ff472f1c3ad858e340c56d7e82c9bdfcbb44d48e5a9d03338285689f129",
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_sdr/tasks/main.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "c76dfc83066a2d92a77b25fa60fdfb15aef7e3dec5502dca515e088bed69afee",
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_sdr/tasks/remove_sdr.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "2852a831ab356a6d98ffc7c4168ab07555371253333dcf020fe651a06d1aa56c",
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_sdr/tasks/sdr_set_facts.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "0002943fb0a2f5b53a94bb3eed51c925aae0082afaa410e7311f2186e7e5a503",
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_sdr/vars",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_sdr/vars/main.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "1c315c7375a4cd99eb3ae1ddc4787aad21fb5a12612f8e443a4db508939afe66",
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_sds",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_sds/README.md",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "89d4828a0e5898780fbe6fe9280824d3de1f26d12b7e06e2e9da8253f0fb8af2",
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_sds/defaults",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_sds/defaults/main.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "289a6838353344a9d0d2b4eb5fe8fe492cb288921c81a056134dd75643b31f0e",
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_sds/meta",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_sds/meta/argument_spec.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "97b60036a372edaf905d2cdef4308350b54d33c97abc28a3b20ece909cba19a0",
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_sds/meta/main.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "764bcf3640368151dd82536115c56321c99f7800f00ed836e0f8525112f86711",
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_sds/molecule",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_sds/molecule/sds_installation",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_sds/molecule/sds_installation/converge.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "52385b21a6278abf2eb63119413f9ee69f28d486e024941845e08df89a57dcb2",
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_sds/molecule/sds_installation/molecule.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "f52d711103d50a437830c6fbcd04fb4bab49a0f82f6d26d1c791c6e8488dd090",
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_sds/molecule/sds_uninstallation",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_sds/molecule/sds_uninstallation/converge.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "d71ebf5f0128d362492c7cc0295e445741ac74b9b08393fcaae1c59954206860",
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_sds/molecule/sds_uninstallation/molecule.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "f52d711103d50a437830c6fbcd04fb4bab49a0f82f6d26d1c791c6e8488dd090",
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_sds/molecule/var_values.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "8b7c4419e76fff49ac90e65639d49cd0cbfdb761ef9f0d57c990a11a89d73618",
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_sds/tasks",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_sds/tasks/install_sds.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "23d5238154edc4827205019c6b649941414f6f80155d6b0273a74c6f435f3c46",
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_sds/tasks/main.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "9d095071bd11063c88ad32c000f9195035b18974a09689d73a25af4675627dd0",
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_sds/tasks/uninstall_sds.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "9e532c9c65cc6ba5ac908094bd36e86981665ca2957af2b1049a820ed6c46544",
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_sds/vars",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_sds/vars/main.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "6d8445cfb0caca38e7d795dd6377f7f82c28e5a19362a987ac2a4b7f8c67dd30",
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_tb",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_tb/README.md",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "a4c2a6465154e52d18d00744cf92fb9323f791a521a95855036ae95a57ef4f33",
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_tb/defaults",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_tb/defaults/main.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "4245290a68a27bf29cb14c1ec2f7819b966d793bf206f9f96546f75943d856ea",
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_tb/meta",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_tb/meta/argument_spec.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "e8a0837c5ad44db6b6992cf7872cee9e14b61fc329f42622d0d51c2683454a39",
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_tb/meta/argument_specs.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "b47f1125de3a8508e82f8f24565340ff4be18fe9ac627bf353714feb79c41243",
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_tb/meta/main.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "968447c9752cfdc4ba23f931124685b222438b2d777e95b123f94271c51540b5",
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_tb/molecule",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_tb/molecule/tb_installation",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_tb/molecule/tb_installation/converge.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "6565516f258d1145478dc9f9875692a4e360efb4747e8716e7c2a4dc4fca1de8",
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_tb/molecule/tb_installation/molecule.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "f52d711103d50a437830c6fbcd04fb4bab49a0f82f6d26d1c791c6e8488dd090",
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_tb/molecule/tb_uninstallation",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_tb/molecule/tb_uninstallation/converge.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "c3201f8a7b21a280ae338081acae67ccaaaacf2933f0cfc6d8431123c340a754",
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_tb/molecule/tb_uninstallation/molecule.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "f52d711103d50a437830c6fbcd04fb4bab49a0f82f6d26d1c791c6e8488dd090",
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_tb/molecule/var_values.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "e11323f688c1f2cd2a14b673f3b693e276a8fa7f74c40451c191c0d4e1fc7dc6",
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_tb/molecule/wrong_tb_credentials",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_tb/molecule/wrong_tb_credentials/converge.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "801f340e649aa9c47d6a276ba04b0fd935343827681b4a44f9ef4e147bc4da73",
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_tb/molecule/wrong_tb_credentials/inventory",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "d4d765dfe4bad1c992c32818407ee9bf4371a8ea27effc8f68cd8895bcb03586",
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_tb/molecule/wrong_tb_credentials/molecule.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "848afca44a73743eaa85dbb917a74063e8c32fa006effa466e7bc118c8047462",
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_tb/tasks",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_tb/tasks/install_tb.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "caa6a56b3fa75ddbb6faf6e1f1d66f41365b876126c16739de31d331a1c97d73",
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_tb/tasks/install_tb3x.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "97b692e2c115a1e7b4b11f4e8de9dd5260720f683dae1259c2840eff2701fa2d",
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_tb/tasks/install_tb4x.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "40b6cf736e196e90e599dc4808970ebd64b9a1f848ad7ea0c29184408ecb6ea8",
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_tb/tasks/main.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "08a3777cb59f0f1717c06fa2684a2e302604864aa583375248bb44bb6d1c970a",
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_tb/tasks/set_tb_ips.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "3f6c75067f5575f48ada843abd9456ace5582fdc9f8e0d5483ea46724a0f35f0",
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_tb/tasks/uninstall_tb.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "8cecdc3db2cde3ad690e85e5f61e60532228c0448f8d9211a7caa502c783fa03",
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_tb/vars",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_tb/vars/main.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "55503bf2cc541fd1a1ab486a6a373b4a7137db3dfb447fe9204b6a632a616658",
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_webui",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_webui/README.md",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "672eec983b5b452217a7e3171ab25d01f49b129eef9385879bcbfcde2ce13643",
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_webui/defaults",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_webui/defaults/main.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "d069029f1fc52b0e96c74117aedc12b00eefd65a80dd0328cb6bc73971d5aed1",
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_webui/meta",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_webui/meta/argument_specs.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "faacb710d3c980aa1cd61fe93e796995d6a81fd909f9c3602016228009c280ca",
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_webui/meta/main.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "3b61f169bc3623baedec1ca9fc731fdde72b54b5dd9a0cc91a6768dc93bbe5d1",
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_webui/molecule",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_webui/molecule/webui_installation",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_webui/molecule/webui_installation/converge.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "eb770492b3ab6c09306cc3c5b73929f9d00a26aaf66e8f026d1a1e057dfa8c3c",
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_webui/molecule/webui_installation/molecule.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "f52d711103d50a437830c6fbcd04fb4bab49a0f82f6d26d1c791c6e8488dd090",
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_webui/molecule/webui_installation_invalid_path_rpm",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_webui/molecule/webui_installation_invalid_path_rpm/converge.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "bb0b220f53dfd25374445f277fc8d624b645baa8b17724f963423e54cfd610ad",
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_webui/molecule/webui_installation_invalid_path_rpm/molecule.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "fb07e75205f81f1ef5ce32019fd265ff69cd41ca7f9f8ea4350d850007a11f1e",
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_webui/molecule/webui_uninstallation",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_webui/molecule/webui_uninstallation/converge.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "c14da947fb44bed580dd1158cc0d6ce7632f27aa35ff7bbdab860d42cea58d69",
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_webui/molecule/webui_uninstallation/molecule.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "f52d711103d50a437830c6fbcd04fb4bab49a0f82f6d26d1c791c6e8488dd090",
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_webui/tasks",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_webui/tasks/install_webui.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "744e4d2c5c8c207cb66f0182a8693398337b7b01187bff23bfc93187db151af8",
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_webui/tasks/main.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "eb47d17fbae2d1e555c21a33c91df81945b093be1e700fcca57041e72178d337",
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_webui/tasks/uninstall_webui.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "9f5b341c049bea1530507bc9d254405e46c52935c95f8e58133dbc263d0be8e0",
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_webui/vars",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "roles/powerflex_webui/vars/main.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "245d6c7575348c20112262cadaab84a9b121061ca65e5a35b9d5bf6e77e1bb33",
+ "format": 1
+ },
+ {
+ "name": "tests",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/sanity",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
"format": 1
},
{
"name": "tests/sanity/ignore-2.14.txt",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "f7aeba3eeadbdef5786e878836b153893618d27d584833642125387a860f65db",
+ "chksum_sha256": "2bbf73bd4314d2ddf3be259531d6053876cf3eedfb2064d3f499230d35a5e29d",
+ "format": 1
+ },
+ {
+ "name": "tests/sanity/ignore-2.15.txt",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "2bbf73bd4314d2ddf3be259531d6053876cf3eedfb2064d3f499230d35a5e29d",
+ "format": 1
+ },
+ {
+ "name": "tests/sanity/ignore-2.16.txt",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "284e11dfcae8cd7417880d605cff0c5fca335d506122707fc0dff6485480c1eb",
+ "format": 1
+ },
+ {
+ "name": "tests/sanity/ignore-2.17.txt",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "e67ee7fbc5af526a8d088ddfdb7ce9a576c4fadac1d8146a5615ddc6654d3269",
"format": 1
},
{
@@ -575,6 +2892,41 @@
"format": 1
},
{
+ "name": "tests/unit/plugins/module_utils/libraries",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/unit/plugins/module_utils/libraries/__init__.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
+ "format": 1
+ },
+ {
+ "name": "tests/unit/plugins/module_utils/libraries/fail_json.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "b4651e84e9043d17705aa947c73b9e7ec49b71b11f7c5cbb5612f7e91dc0ff92",
+ "format": 1
+ },
+ {
+ "name": "tests/unit/plugins/module_utils/libraries/initial_mock.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "751778383a2d8f1f5b31d6c6d91753ae34573734123732fdc86693005a90926a",
+ "format": 1
+ },
+ {
+ "name": "tests/unit/plugins/module_utils/libraries/powerflex_unit_base.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "de609408a22a2862a0acde77a4d5824e4447a543f94b78f392c909f63a6b01b3",
+ "format": 1
+ },
+ {
"name": "tests/unit/plugins/module_utils/mock_api_exception.py",
"ftype": "file",
"chksum_type": "sha256",
@@ -582,10 +2934,31 @@
"format": 1
},
{
+ "name": "tests/unit/plugins/module_utils/mock_device_api.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "d7ee89ad10ea3b96aaebeaa9f28613bc42eba0bf044e4c894361623d28f1fad1",
+ "format": 1
+ },
+ {
+ "name": "tests/unit/plugins/module_utils/mock_fail_json.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "0eb805b800a7d22fd4df5e1c1af400fb97039287432df90aa7cb2e14a4f8f465",
+ "format": 1
+ },
+ {
+ "name": "tests/unit/plugins/module_utils/mock_fault_set_api.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "85d0af59ac2484455b0437ee20f4281042bffded623ad997e0578aa5dffbc8b7",
+ "format": 1
+ },
+ {
"name": "tests/unit/plugins/module_utils/mock_info_api.py",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "5af286883c568b21c90cf5444cde5742123db9819355ffaac2904bb68f31977c",
+ "chksum_sha256": "cab119466db38050d440bc7201e8b8a5124c5ec0696f4a695089a970c02d7602",
"format": 1
},
{
@@ -599,21 +2972,28 @@
"name": "tests/unit/plugins/module_utils/mock_protection_domain_api.py",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "1005c8842b81ff4c5613992e5f80fb25cfa6ac36d1a9274e574caf04d7510584",
+ "chksum_sha256": "28de95a3b777048b61d1ef5ef4813fb5051bcbd200342750336cc988baa53f39",
"format": 1
},
{
"name": "tests/unit/plugins/module_utils/mock_replication_consistency_group_api.py",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "5bf628a051d160856652bda22e45d35303db612874edc75ed4e2e8b4a270fba3",
+ "chksum_sha256": "7bc14ef8f7a52a7a2b3a9046129d4fae30472f023b25b4676310244abff77e24",
"format": 1
},
{
"name": "tests/unit/plugins/module_utils/mock_replication_pair_api.py",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "46cd89d10f82a9d5e6992ff1b7470d79c37e6da91daed75d6381e8d52d45dca4",
+ "chksum_sha256": "520bd7a73c6eba60ce38a116236b35c61a168562c11f7ca20681110f8699540a",
+ "format": 1
+ },
+ {
+ "name": "tests/unit/plugins/module_utils/mock_sdc_api.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "b60f8ff13f723a48ef0fffe747d73c3ffac1296f72e08a3444eeb70959fd0f22",
"format": 1
},
{
@@ -624,17 +3004,31 @@
"format": 1
},
{
+ "name": "tests/unit/plugins/module_utils/mock_sds_api.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "d582368c1264a56168e5a09adeeeed2fb90a51482abf8fef89c8dc369ccc7508",
+ "format": 1
+ },
+ {
+ "name": "tests/unit/plugins/module_utils/mock_snapshot_policy_api.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "1f4007b601d0de25a813c234ba5f5248b4d0dc406d7b6e36cf6331db5bc17bd6",
+ "format": 1
+ },
+ {
"name": "tests/unit/plugins/module_utils/mock_storagepool_api.py",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "f2552190a68b46919f44632fe65972825624ab201e330771e1992e57de253d27",
+ "chksum_sha256": "5e7e3dfc7f6ac68a53092f5ba3292ec4c7c861f6972ca9c290f223ef10c8afad",
"format": 1
},
{
"name": "tests/unit/plugins/module_utils/mock_volume_api.py",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "5e61698f03d1a7ec229c5ffb6a4def656e806e5dd234a0e15b2136fba839a2d7",
+ "chksum_sha256": "e13dde31e52c7f6b1644b3e139ffa6cbb3a36fa7a3f51f273f85c778a3ea0cc5",
"format": 1
},
{
@@ -652,10 +3046,24 @@
"format": 1
},
{
+ "name": "tests/unit/plugins/modules/test_device.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "594ee3ea44a69c55a9ab207b180f4ab0f704a30ccbf5d9cee33744e1d7867148",
+ "format": 1
+ },
+ {
+ "name": "tests/unit/plugins/modules/test_fault_set.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "883cd39ea6dc8150116ad11ca9545edf893b8b511b7d2abe20879926d0f7c029",
+ "format": 1
+ },
+ {
"name": "tests/unit/plugins/modules/test_info.py",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "ed576811997bf0be077369720b8cd5e0cfeacaabc1a51146236ba6fbfdff0492",
+ "chksum_sha256": "e50a6d20e1e279a687e1b6b699530a3abe8d83768e0d215189e54b0f9d8a12f0",
"format": 1
},
{
@@ -669,42 +3077,63 @@
"name": "tests/unit/plugins/modules/test_protection_domain.py",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "8d2d1e320a857f994db4e48ce5406ca5bbfe21cd7c37d9f8d3bb1e07db8d333e",
+ "chksum_sha256": "fd400c16d0e64a7197daaed295af4d76953136911171258051688aec388ae0f9",
"format": 1
},
{
"name": "tests/unit/plugins/modules/test_replication_consistency_group.py",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "3e96fa4bddbbf108235fe087e8c3d2d0a0f737d38048150c4936296ca6a5288f",
+ "chksum_sha256": "f0093d18a711eecb2287755008d89d3a16dcc81f342e0104614f08f3c47cbd48",
"format": 1
},
{
"name": "tests/unit/plugins/modules/test_replication_pair.py",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "ede467557d94a229f775030f699ebd93e5f33be6061fcf76f78797fdf2039b6f",
+ "chksum_sha256": "971564355415601cfe4cd54aa55ce6a6b1ca56e9ba064e56cfd1761de6683c87",
+ "format": 1
+ },
+ {
+ "name": "tests/unit/plugins/modules/test_sdc.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "3f990cc3ab9a042f124f93a6b2c772f8433ab606f9325e1999dd93a3593cea5d",
+ "format": 1
+ },
+ {
+ "name": "tests/unit/plugins/modules/test_sds.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "798578aceabaf9a3b5323117cbbd02b1b9dde2091ce5c210c63273d372cb7f90",
+ "format": 1
+ },
+ {
+ "name": "tests/unit/plugins/modules/test_snapshot_policy.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "27f26fc2d95cb5d4d22f74a42b36795c33cc7a4739f5ac0027d608b735f15fe7",
"format": 1
},
{
"name": "tests/unit/plugins/modules/test_storagepool.py",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "4f78779e6b346b612915d92323ca0d1850e68da65b95f32205cc585b622b48be",
+ "chksum_sha256": "8c6bad9def6e6b32b7358bca2c4494be3c077fe49b47b08fc2e0c7305fcdb685",
"format": 1
},
{
"name": "tests/unit/plugins/modules/test_volume.py",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "4742f5675d613a39bd5929b24e6f515d034bebf8defc1c98bb8fe69444785015",
+ "chksum_sha256": "baca53f593d97d7c3ab2f76767c1d00870cb7cee265b704f216d85c73cb268ac",
"format": 1
},
{
- "name": "ansible.cfg",
+ "name": "tests/unit/requirements.txt",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "5360ab997ea2c7ed8a6efc7e8324e7b6ec7479af057fe15ff23fe885f05b58b2",
+ "chksum_sha256": "65e6091d1c8d88a703555bd13590bb95248fb0b7376d3ed1d660e2b9d65581c8",
"format": 1
}
],
diff --git a/ansible_collections/dellemc/powerflex/MANIFEST.json b/ansible_collections/dellemc/powerflex/MANIFEST.json
index c4490da54..50f25fa4c 100644
--- a/ansible_collections/dellemc/powerflex/MANIFEST.json
+++ b/ansible_collections/dellemc/powerflex/MANIFEST.json
@@ -2,7 +2,7 @@
"collection_info": {
"namespace": "dellemc",
"name": "powerflex",
- "version": "1.6.0",
+ "version": "2.2.0",
"authors": [
"Akash Shendge <ansible.team@dell.com>",
"Arindam Datta <ansible.team@dell.com>",
@@ -10,7 +10,8 @@
"Rajshree Khare <ansible.team@dell.com>",
"Bhavneet Sharma <ansible.team@dell.com>",
"Ananthu S Kuttattu <ansible.team@dell.com>",
- "Trisha Datta <ansible.team@dell.com>"
+ "Trisha Datta <ansible.team@dell.com>",
+ "Pavan Mudunuri <ansible.team@dell.com>"
],
"readme": "README.md",
"tags": [
@@ -23,16 +24,16 @@
],
"license_file": null,
"dependencies": {},
- "repository": "https://github.com/dell/ansible-powerflex/tree/1.6.0",
- "documentation": "https://github.com/dell/ansible-powerflex/tree/1.6.0/docs",
- "homepage": "https://github.com/dell/ansible-powerflex/tree/1.6.0",
+ "repository": "https://github.com/dell/ansible-powerflex/tree/2.2.0",
+ "documentation": "https://github.com/dell/ansible-powerflex/tree/2.2.0/docs",
+ "homepage": "https://github.com/dell/ansible-powerflex/tree/2.2.0",
"issues": "https://www.dell.com/community/Automation/bd-p/Automation"
},
"file_manifest_file": {
"name": "FILES.json",
"ftype": "file",
"chksum_type": "sha256",
- "chksum_sha256": "9401ba4e254d7dab2e4f6dda4e4fdeeba8908b05c66552d423da74ef44e53ec3",
+ "chksum_sha256": "7b6cac9b3795806daa37e451654c294ca112eab9b180e45ccc02b4ae0c2f12ff",
"format": 1
},
"format": 1
diff --git a/ansible_collections/dellemc/powerflex/README.md b/ansible_collections/dellemc/powerflex/README.md
index b40e14f64..a12d86b97 100644
--- a/ansible_collections/dellemc/powerflex/README.md
+++ b/ansible_collections/dellemc/powerflex/README.md
@@ -2,33 +2,33 @@
The Ansible Modules for Dell Technologies (Dell) PowerFlex allow Data Center and IT administrators to use RedHat Ansible to automate and orchestrate the provisioning and management of Dell PowerFlex storage systems.
-The capabilities of the Ansible modules are managing SDCs, volumes, snapshots, storage pools, replication consistency groups, replication pairs, SDSs, devices, protection domains, MDM cluster, and to gather high level facts from the storage system. The options available are list, show, create, modify and delete. These tasks can be executed by running simple playbooks written in yaml syntax. The modules are written so that all the operations are idempotent, so making multiple identical requests has the same effect as making a single request.
+The capabilities of the Ansible modules are managing SDCs, volumes, snapshots, snapshot policy, storage pools, replication consistency groups, replication pairs, SDSs, devices, protection domains, MDM cluster, Fault Set and to gather high level facts from the storage system. The options available are list, show, create, modify and delete. These tasks can be executed by running simple playbooks written in yaml syntax. The modules are written so that all the operations are idempotent, so making multiple identical requests has the same effect as making a single request.
## Table of contents
-* [Code of conduct](https://github.com/dell/ansible-powerflex/blob/1.6.0/docs/CODE_OF_CONDUCT.md)
-* [Maintainer guide](https://github.com/dell/ansible-powerflex/blob/1.6.0/docs/MAINTAINER_GUIDE.md)
-* [Committer guide](https://github.com/dell/ansible-powerflex/blob/1.6.0/docs/COMMITTER_GUIDE.md)
-* [Contributing guide](https://github.com/dell/ansible-powerflex/blob/1.6.0/docs/CONTRIBUTING.md)
-* [Branching strategy](https://github.com/dell/ansible-powerflex/blob/1.6.0/docs/BRANCHING.md)
-* [List of adopters](https://github.com/dell/ansible-powerflex/blob/1.6.0/docs/ADOPTERS.md)
-* [Maintainers](https://github.com/dell/ansible-powerflex/blob/1.6.0/docs/MAINTAINERS.md)
-* [Support](https://github.com/dell/ansible-powerflex/blob/1.6.0/docs/SUPPORT.md)
+* [Code of conduct](https://github.com/dell/ansible-powerflex/blob/2.2.0/docs/CODE_OF_CONDUCT.md)
+* [Maintainer guide](https://github.com/dell/ansible-powerflex/blob/2.2.0/docs/MAINTAINER_GUIDE.md)
+* [Committer guide](https://github.com/dell/ansible-powerflex/blob/2.2.0/docs/COMMITTER_GUIDE.md)
+* [Contributing guide](https://github.com/dell/ansible-powerflex/blob/2.2.0/docs/CONTRIBUTING.md)
+* [Branching strategy](https://github.com/dell/ansible-powerflex/blob/2.2.0/docs/BRANCHING.md)
+* [List of adopters](https://github.com/dell/ansible-powerflex/blob/2.2.0/docs/ADOPTERS.md)
+* [Maintainers](https://github.com/dell/ansible-powerflex/blob/2.2.0/docs/MAINTAINERS.md)
+* [Support](https://github.com/dell/ansible-powerflex/blob/2.2.0/docs/SUPPORT.md)
* [License](#license)
-* [Security](https://github.com/dell/ansible-powerflex/blob/1.6.0/docs/SECURITY.md)
+* [Security](https://github.com/dell/ansible-powerflex/blob/2.2.0/docs/SECURITY.md)
* [Prerequisites](#prerequisites)
* [List of Ansible modules for Dell PowerFlex](#list-of-ansible-modules-for-dell-powerflex)
* [Installation and execution of Ansible modules for Dell PowerFlex](#installation-and-execution-of-ansible-modules-for-dell-powerflex)
* [Releasing, Maintenance and Deprecation](#releasing-maintenance-and-deprecation)
## License
-The Ansible collection for PowerFlex is released and licensed under the GPL-3.0 license. See [LICENSE](https://github.com/dell/ansible-powerflex/blob/1.6.0/LICENSE) for the full terms. Ansible modules and modules utilities that are part of the Ansible collection for PowerFlex are released and licensed under the Apache 2.0 license. See [MODULE-LICENSE](https://github.com/dell/ansible-powerflex/blob/1.6.0/MODULE-LICENSE) for the full terms.
+The Ansible collection for PowerFlex is released and licensed under the GPL-3.0 license. See [LICENSE](https://github.com/dell/ansible-powerflex/blob/2.2.0/LICENSE) for the full terms. Ansible modules and modules utilities that are part of the Ansible collection for PowerFlex are released and licensed under the Apache 2.0 license. See [MODULE-LICENSE](https://github.com/dell/ansible-powerflex/blob/2.2.0/MODULE-LICENSE) for the full terms.
## Prerequisites
| **Ansible Modules** | **PowerFlex/VxFlex OS Version** | **SDK version** | **Python version** | **Ansible** |
|---------------------|-----------------------|-------|--------------------|--------------------------|
-| v1.6.0 |3.5 <br> 3.6 <br> 4.0 | 1.7.0 | 3.9.x <br> 3.10.x <br> 3.11.x | 2.12 <br> 2.13 <br> 2.14 |
+| v2.2.0 |3.6 <br> 4.0 <br> 4.5 | 1.9.0 | 3.9.x <br> 3.10.x <br> 3.11.x | 2.14 <br> 2.15 <br> 2.16 |
* Please follow PyPowerFlex installation instructions on [PyPowerFlex Documentation](https://github.com/dell/python-powerflex)
@@ -36,27 +36,29 @@ The Ansible collection for PowerFlex is released and licensed under the GPL-3.0
The modules are written in such a way that all requests are idempotent and hence fault-tolerant. It essentially means that the result of a successfully performed request is independent of the number of times it is executed.
## List of Ansible modules for Dell PowerFlex
- * [Info module](https://github.com/dell/ansible-powerflex/blob/1.6.0/docs/modules/info.rst)
- * [Snapshot module](https://github.com/dell/ansible-powerflex/blob/1.6.0/docs/modules/snapshot.rst)
- * [SDC module](https://github.com/dell/ansible-powerflex/blob/1.6.0/docs/modules/sdc.rst)
- * [Storage pool module](https://github.com/dell/ansible-powerflex/blob/1.6.0/docs/modules/storagepool.rst)
- * [Volume module](https://github.com/dell/ansible-powerflex/blob/1.6.0/docs/modules/volume.rst)
- * [SDS module](https://github.com/dell/ansible-powerflex/blob/1.6.0/docs/modules/sds.rst)
- * [Device Module](https://github.com/dell/ansible-powerflex/blob/1.6.0/docs/modules/device.rst)
- * [Protection Domain Module](https://github.com/dell/ansible-powerflex/blob/1.6.0/docs/modules/protection_domain.rst)
- * [MDM Cluster Module](https://github.com/dell/ansible-powerflex/blob/1.6.0/docs/modules/mdm_cluster.rst)
- * [Replication Consistency Grop Module](https://github.com/dell/ansible-powerflex/blob/1.6.0/docs/modules/replication_consistency_group.rst)
- * [Replication Pair Module](https://github.com/dell/ansible-powerflex/blob/1.6.0/docs/modules/replication_pair.rst)
+ * [Info module](https://github.com/dell/ansible-powerflex/blob/2.2.0/docs/modules/info.rst)
+ * [Snapshot module](https://github.com/dell/ansible-powerflex/blob/2.2.0/docs/modules/snapshot.rst)
+ * [SDC module](https://github.com/dell/ansible-powerflex/blob/2.2.0/docs/modules/sdc.rst)
+ * [Storage pool module](https://github.com/dell/ansible-powerflex/blob/2.2.0/docs/modules/storagepool.rst)
+ * [Volume module](https://github.com/dell/ansible-powerflex/blob/2.2.0/docs/modules/volume.rst)
+ * [SDS module](https://github.com/dell/ansible-powerflex/blob/2.2.0/docs/modules/sds.rst)
+ * [Device Module](https://github.com/dell/ansible-powerflex/blob/2.2.0/docs/modules/device.rst)
+ * [Protection Domain Module](https://github.com/dell/ansible-powerflex/blob/2.2.0/docs/modules/protection_domain.rst)
+ * [MDM Cluster Module](https://github.com/dell/ansible-powerflex/blob/2.2.0/docs/modules/mdm_cluster.rst)
+ * [Replication Consistency Group Module](https://github.com/dell/ansible-powerflex/blob/2.2.0/docs/modules/replication_consistency_group.rst)
+ * [Replication Pair Module](https://github.com/dell/ansible-powerflex/blob/2.2.0/docs/modules/replication_pair.rst)
+ * [Snapshot Policy Module](https://github.com/dell/ansible-powerflex/blob/2.2.0/docs/modules/snapshot_policy.rst)
+ * [Fault Sets Module](https://github.com/dell/ansible-powerflex/blob/2.2.0/docs/modules/fault_set.rst)
## Installation and execution of Ansible modules for Dell PowerFlex
-The installation and execution steps of Ansible modules for Dell PowerFlex can be found [here](https://github.com/dell/ansible-powerflex/blob/1.6.0/docs/INSTALLATION.md).
+The installation and execution steps of Ansible modules for Dell PowerFlex can be found [here](https://github.com/dell/ansible-powerflex/blob/2.2.0/docs/INSTALLATION.md).
## Releasing, Maintenance and Deprecation
-Ansible Modules for Dell Technnologies PowerFlex follows [Semantic Versioning](https://semver.org/).
+Ansible Modules for Dell Technologies PowerFlex follows [Semantic Versioning](https://semver.org/).
New version will be release regularly if significant changes (bug fix or new feature) are made in the collection.
-Released code versions are located on "release" branches with names of the form "release-x.y.z" where x.y.z corresponds to the version number. More information on branching strategy followed can be found [here](https://github.com/dell/ansible-powerflex/blob/1.6.0/docs/BRANCHING.md).
+Released code versions are located on "release" branches with names of the form "release-x.y.z" where x.y.z corresponds to the version number. More information on branching strategy followed can be found [here](https://github.com/dell/ansible-powerflex/blob/2.2.0/docs/BRANCHING.md).
Ansible Modules for Dell Technologies PowerFlex deprecation cycle is aligned with that of [Ansible](https://docs.ansible.com/ansible/latest/dev_guide/module_lifecycle.html). \ No newline at end of file
diff --git a/ansible_collections/dellemc/powerflex/ansible.cfg b/ansible_collections/dellemc/powerflex/ansible.cfg
deleted file mode 100644
index c10d1da22..000000000
--- a/ansible_collections/dellemc/powerflex/ansible.cfg
+++ /dev/null
@@ -1,484 +0,0 @@
-# config file for ansible -- https://ansible.com/
-# ===============================================
-
-# nearly all parameters can be overridden in ansible-playbook
-# or with command line flags. ansible will read ANSIBLE_CONFIG,
-# ansible.cfg in the current working directory, .ansible.cfg in
-# the home directory or /etc/ansible/ansible.cfg, whichever it
-# finds first
-
-[defaults]
-
-# some basic default values...
-
-#inventory = /etc/ansible/hosts
-#library = /usr/share/my_modules/
-#module_utils = /usr/share/my_module_utils/
-#remote_tmp = ~/.ansible/tmp
-#local_tmp = ~/.ansible/tmp
-#plugin_filters_cfg = /etc/ansible/plugin_filters.yml
-#forks = 5
-#poll_interval = 15
-#sudo_user = root
-#ask_sudo_pass = True
-#ask_pass = True
-#transport = smart
-#remote_port = 22
-#module_lang = C
-#module_set_locale = False
-
-# plays will gather facts by default, which contain information about
-# the remote system.
-#
-# smart - gather by default, but don't regather if already gathered
-# implicit - gather by default, turn off with gather_facts: False
-# explicit - do not gather by default, must say gather_facts: True
-#gathering = implicit
-
-# This only affects the gathering done by a play's gather_facts directive,
-# by default gathering retrieves all facts subsets
-# all - gather all subsets
-# network - gather min and network facts
-# hardware - gather hardware facts (longest facts to retrieve)
-# virtual - gather min and virtual facts
-# facter - import facts from facter
-# ohai - import facts from ohai
-# You can combine them using comma (ex: network,virtual)
-# You can negate them using ! (ex: !hardware,!facter,!ohai)
-# A minimal set of facts is always gathered.
-#gather_subset = all
-
-# some hardware related facts are collected
-# with a maximum timeout of 10 seconds. This
-# option lets you increase or decrease that
-# timeout to something more suitable for the
-# environment.
-# gather_timeout = 10
-
-# additional paths to search for roles in, colon separated
-#roles_path = /etc/ansible/roles
-
-# uncomment this to disable SSH key host checking
-#host_key_checking = False
-
-# change the default callback, you can only have one 'stdout' type enabled at a time.
-#stdout_callback = skippy
-
-
-## Ansible ships with some plugins that require whitelisting,
-## this is done to avoid running all of a type by default.
-## These setting lists those that you want enabled for your system.
-## Custom plugins should not need this unless plugin author specifies it.
-
-# enable callback plugins, they can output to stdout but cannot be 'stdout' type.
-#callback_whitelist = timer, mail
-
-# Determine whether includes in tasks and handlers are "static" by
-# default. As of 2.0, includes are dynamic by default. Setting these
-# values to True will make includes behave more like they did in the
-# 1.x versions.
-#task_includes_static = False
-#handler_includes_static = False
-
-# Controls if a missing handler for a notification event is an error or a warning
-#error_on_missing_handler = True
-
-# change this for alternative sudo implementations
-#sudo_exe = sudo
-
-# What flags to pass to sudo
-# WARNING: leaving out the defaults might create unexpected behaviours
-#sudo_flags = -H -S -n
-
-# SSH timeout
-#timeout = 10
-
-# default user to use for playbooks if user is not specified
-# (/usr/bin/ansible will use current user as default)
-#remote_user = root
-
-# logging is off by default unless this path is defined
-# if so defined, consider logrotate
-#log_path = /var/log/ansible.log
-
-# default module name for /usr/bin/ansible
-#module_name = command
-
-# use this shell for commands executed under sudo
-# you may need to change this to bin/bash in rare instances
-# if sudo is constrained
-#executable = /bin/sh
-
-# if inventory variables overlap, does the higher precedence one win
-# or are hash values merged together? The default is 'replace' but
-# this can also be set to 'merge'.
-#hash_behaviour = replace
-
-# by default, variables from roles will be visible in the global variable
-# scope. To prevent this, the following option can be enabled, and only
-# tasks and handlers within the role will see the variables there
-#private_role_vars = yes
-
-# list any Jinja2 extensions to enable here:
-#jinja2_extensions = jinja2.ext.do,jinja2.ext.i18n
-
-# if set, always use this private key file for authentication, same as
-# if passing --private-key to ansible or ansible-playbook
-#private_key_file = /path/to/file
-
-# If set, configures the path to the Vault password file as an alternative to
-# specifying --vault-password-file on the command line.
-#vault_password_file = /path/to/vault_password_file
-
-# format of string {{ ansible_managed }} available within Jinja2
-# templates indicates to users editing templates files will be replaced.
-# replacing {file}, {host} and {uid} and strftime codes with proper values.
-#ansible_managed = Ansible managed: {file} modified on %Y-%m-%d %H:%M:%S by {uid} on {host}
-# {file}, {host}, {uid}, and the timestamp can all interfere with idempotence
-# in some situations so the default is a static string:
-#ansible_managed = Ansible managed
-
-# by default, ansible-playbook will display "Skipping [host]" if it determines a task
-# should not be run on a host. Set this to "False" if you don't want to see these "Skipping"
-# messages. NOTE: the task header will still be shown regardless of whether or not the
-# task is skipped.
-#display_skipped_hosts = True
-
-# by default, if a task in a playbook does not include a name: field then
-# ansible-playbook will construct a header that includes the task's action but
-# not the task's args. This is a security feature because ansible cannot know
-# if the *module* considers an argument to be no_log at the time that the
-# header is printed. If your environment doesn't have a problem securing
-# stdout from ansible-playbook (or you have manually specified no_log in your
-# playbook on all of the tasks where you have secret information) then you can
-# safely set this to True to get more informative messages.
-#display_args_to_stdout = False
-
-# by default (as of 1.3), Ansible will raise errors when attempting to dereference
-# Jinja2 variables that are not set in templates or action lines. Uncomment this line
-# to revert the behavior to pre-1.3.
-#error_on_undefined_vars = False
-
-# by default (as of 1.6), Ansible may display warnings based on the configuration of the
-# system running ansible itself. This may include warnings about 3rd party packages or
-# other conditions that should be resolved if possible.
-# to disable these warnings, set the following value to False:
-#system_warnings = True
-
-# by default (as of 1.4), Ansible may display deprecation warnings for language
-# features that should no longer be used and will be removed in future versions.
-# to disable these warnings, set the following value to False:
-#deprecation_warnings = True
-
-# (as of 1.8), Ansible can optionally warn when usage of the shell and
-# command module appear to be simplified by using a default Ansible module
-# instead. These warnings can be silenced by adjusting the following
-# setting or adding warn=yes or warn=no to the end of the command line
-# parameter string. This will for example suggest using the git module
-# instead of shelling out to the git command.
-# command_warnings = False
-
-
-# set plugin path directories here, separate with colons
-#action_plugins = /usr/share/ansible/plugins/action
-#cache_plugins = /usr/share/ansible/plugins/cache
-#callback_plugins = /usr/share/ansible/plugins/callback
-#connection_plugins = /usr/share/ansible/plugins/connection
-#lookup_plugins = /usr/share/ansible/plugins/lookup
-#inventory_plugins = /usr/share/ansible/plugins/inventory
-#vars_plugins = /usr/share/ansible/plugins/vars
-#filter_plugins = /usr/share/ansible/plugins/filter
-#test_plugins = /usr/share/ansible/plugins/test
-#terminal_plugins = /usr/share/ansible/plugins/terminal
-#strategy_plugins = /usr/share/ansible/plugins/strategy
-
-
-# by default, ansible will use the 'linear' strategy but you may want to try
-# another one
-#strategy = free
-
-# by default callbacks are not loaded for /bin/ansible, enable this if you
-# want, for example, a notification or logging callback to also apply to
-# /bin/ansible runs
-#bin_ansible_callbacks = False
-
-
-# don't like cows? that's unfortunate.
-# set to 1 if you don't want cowsay support or export ANSIBLE_NOCOWS=1
-#nocows = 1
-
-# set which cowsay stencil you'd like to use by default. When set to 'random',
-# a random stencil will be selected for each task. The selection will be filtered
-# against the `cow_whitelist` option below.
-#cow_selection = default
-#cow_selection = random
-
-# when using the 'random' option for cowsay, stencils will be restricted to this list.
-# it should be formatted as a comma-separated list with no spaces between names.
-# NOTE: line continuations here are for formatting purposes only, as the INI parser
-# in python does not support them.
-#cow_whitelist=bud-frogs,bunny,cheese,daemon,default,dragon,elephant-in-snake,elephant,eyes,\
-# hellokitty,kitty,luke-koala,meow,milk,moofasa,moose,ren,sheep,small,stegosaurus,\
-# stimpy,supermilker,three-eyes,turkey,turtle,tux,udder,vader-koala,vader,www
-
-# don't like colors either?
-# set to 1 if you don't want colors, or export ANSIBLE_NOCOLOR=1
-#nocolor = 1
-
-# if set to a persistent type (not 'memory', for example 'redis') fact values
-# from previous runs in Ansible will be stored. This may be useful when
-# wanting to use, for example, IP information from one group of servers
-# without having to talk to them in the same playbook run to get their
-# current IP information.
-#fact_caching = memory
-
-
-# retry files
-# When a playbook fails by default a .retry file will be created in ~/
-# You can disable this feature by setting retry_files_enabled to False
-# and you can change the location of the files by setting retry_files_save_path
-
-#retry_files_enabled = False
-#retry_files_save_path = ~/.ansible-retry
-
-# squash actions
-# Ansible can optimise actions that call modules with list parameters
-# when looping. Instead of calling the module once per with_ item, the
-# module is called once with all items at once. Currently this only works
-# under limited circumstances, and only with parameters named 'name'.
-#squash_actions = apk,apt,dnf,homebrew,pacman,pkgng,yum,zypper
-
-# prevents logging of task data, off by default
-#no_log = False
-
-# prevents logging of tasks, but only on the targets, data is still logged on the master/controller
-#no_target_syslog = False
-
-# controls whether Ansible will raise an error or warning if a task has no
-# choice but to create world readable temporary files to execute a module on
-# the remote machine. This option is False by default for security. Users may
-# turn this on to have behaviour more like Ansible prior to 2.1.x. See
-# https://docs.ansible.com/ansible/become.html#becoming-an-unprivileged-user
-# for more secure ways to fix this than enabling this option.
-#allow_world_readable_tmpfiles = False
-
-# controls the compression level of variables sent to
-# worker processes. At the default of 0, no compression
-# is used. This value must be an integer from 0 to 9.
-#var_compression_level = 9
-
-# controls what compression method is used for new-style ansible modules when
-# they are sent to the remote system. The compression types depend on having
-# support compiled into both the controller's python and the client's python.
-# The names should match with the python Zipfile compression types:
-# * ZIP_STORED (no compression. available everywhere)
-# * ZIP_DEFLATED (uses zlib, the default)
-# These values may be set per host via the ansible_module_compression inventory
-# variable
-#module_compression = 'ZIP_DEFLATED'
-
-# This controls the cutoff point (in bytes) on --diff for files
-# set to 0 for unlimited (RAM may suffer!).
-#max_diff_size = 1048576
-
-# This controls how ansible handles multiple --tags and --skip-tags arguments
-# on the CLI. If this is True then multiple arguments are merged together. If
-# it is False, then the last specified argument is used and the others are ignored.
-# This option will be removed in 2.8.
-#merge_multiple_cli_flags = True
-
-# Controls showing custom stats at the end, off by default
-#show_custom_stats = True
-
-# Controls which files to ignore when using a directory as inventory with
-# possibly multiple sources (both static and dynamic)
-#inventory_ignore_extensions = ~, .orig, .bak, .ini, .cfg, .retry, .pyc, .pyo
-
-# This family of modules use an alternative execution path optimized for network appliances
-# only update this setting if you know how this works, otherwise it can break module execution
-#network_group_modules=eos, nxos, ios, iosxr, junos, vyos
-
-# When enabled, this option allows lookups (via variables like {{lookup('foo')}} or when used as
-# a loop with `with_foo`) to return data that is not marked "unsafe". This means the data may contain
-# jinja2 templating language which will be run through the templating engine.
-# ENABLING THIS COULD BE A SECURITY RISK
-#allow_unsafe_lookups = False
-
-# set default errors for all plays
-#any_errors_fatal = False
-
-[inventory]
-# enable inventory plugins, default: 'host_list', 'script', 'yaml', 'ini'
-#enable_plugins = host_list, virtualbox, yaml, constructed
-
-# ignore these extensions when parsing a directory as inventory source
-#ignore_extensions = .pyc, .pyo, .swp, .bak, ~, .rpm, .md, .txt, ~, .orig, .ini, .cfg, .retry
-
-# ignore files matching these patterns when parsing a directory as inventory source
-#ignore_patterns=
-
-# If 'true' unparsed inventory sources become fatal errors, they are warnings otherwise.
-#unparsed_is_failed=False
-
-[privilege_escalation]
-#become=True
-#become_method=sudo
-#become_user=root
-#become_ask_pass=False
-
-[paramiko_connection]
-
-# uncomment this line to cause the paramiko connection plugin to not record new host
-# keys encountered. Increases performance on new host additions. Setting works independently of the
-# host key checking setting above.
-#record_host_keys=False
-
-# by default, Ansible requests a pseudo-terminal for commands executed under sudo. Uncomment this
-# line to disable this behaviour.
-#pty=False
-
-# paramiko will default to looking for SSH keys initially when trying to
-# authenticate to remote devices. This is a problem for some network devices
-# that close the connection after a key failure. Uncomment this line to
-# disable the Paramiko look for keys function
-#look_for_keys = False
-
-# When using persistent connections with Paramiko, the connection runs in a
-# background process. If the host doesn't already have a valid SSH key, by
-# default Ansible will prompt to add the host key. This will cause connections
-# running in background processes to fail. Uncomment this line to have
-# Paramiko automatically add host keys.
-#host_key_auto_add = True
-
-[ssh_connection]
-
-# ssh arguments to use
-# Leaving off ControlPersist will result in poor performance, so use
-# paramiko on older platforms rather than removing it, -C controls compression use
-#ssh_args = -C -o ControlMaster=auto -o ControlPersist=60s
-
-# The base directory for the ControlPath sockets.
-# This is the "%(directory)s" in the control_path option
-#
-# Example:
-# control_path_dir = /tmp/.ansible/cp
-#control_path_dir = ~/.ansible/cp
-
-# The path to use for the ControlPath sockets. This defaults to a hashed string of the hostname,
-# port and username (empty string in the config). The hash mitigates a common problem users
-# found with long hostames and the conventional %(directory)s/ansible-ssh-%%h-%%p-%%r format.
-# In those cases, a "too long for Unix domain socket" ssh error would occur.
-#
-# Example:
-# control_path = %(directory)s/%%h-%%r
-#control_path =
-
-# Enabling pipelining reduces the number of SSH operations required to
-# execute a module on the remote server. This can result in a significant
-# performance improvement when enabled, however when using "sudo:" you must
-# first disable 'requiretty' in /etc/sudoers
-#
-# By default, this option is disabled to preserve compatibility with
-# sudoers configurations that have requiretty (the default on many distros).
-#
-#pipelining = False
-
-# Control the mechanism for transferring files (old)
-# * smart = try sftp and then try scp [default]
-# * True = use scp only
-# * False = use sftp only
-#scp_if_ssh = smart
-
-# Control the mechanism for transferring files (new)
-# If set, this will override the scp_if_ssh option
-# * sftp = use sftp to transfer files
-# * scp = use scp to transfer files
-# * piped = use 'dd' over SSH to transfer files
-# * smart = try sftp, scp, and piped, in that order [default]
-#transfer_method = smart
-
-# if False, sftp will not use batch mode to transfer files. This may cause some
-# types of file transfer failures impossible to catch however, and should
-# only be disabled if your sftp version has problems with batch mode
-#sftp_batch_mode = False
-
-# The -tt argument is passed to ssh when pipelining is not enabled because sudo
-# requires a tty by default.
-#use_tty = True
-
-[persistent_connection]
-
-# Configures the persistent connection timeout value in seconds. This value is
-# how long the persistent connection will remain idle before it is destroyed.
-# If the connection doesn't receive a request before the timeout value
-# expires, the connection is shutdown. The default value is 30 seconds.
-#connect_timeout = 30
-
-# Configures the persistent connection retry timeout. This value configures the
-# the retry timeout that ansible-connection will wait to connect
-# to the local domain socket. This value must be larger than the
-# ssh timeout (timeout) and less than persistent connection idle timeout (connect_timeout).
-# The default value is 15 seconds.
-#connect_retry_timeout = 15
-
-# The command timeout value defines the amount of time to wait for a command
-# or RPC call before timing out. The value for the command timeout must
-# be less than the value of the persistent connection idle timeout (connect_timeout)
-# The default value is 10 second.
-#command_timeout = 10
-
-[accelerate]
-#accelerate_port = 5099
-#accelerate_timeout = 30
-#accelerate_connect_timeout = 5.0
-
-# The daemon timeout is measured in minutes. This time is measured
-# from the last activity to the accelerate daemon.
-#accelerate_daemon_timeout = 30
-
-# If set to yes, accelerate_multi_key will allow multiple
-# private keys to be uploaded to it, though each user must
-# have access to the system via SSH to add a new key. The default
-# is "no".
-#accelerate_multi_key = yes
-
-[selinux]
-# file systems that require special treatment when dealing with security context
-# the default behaviour that copies the existing context or uses the user default
-# needs to be changed to use the file system dependent context.
-#special_context_filesystems=nfs,vboxsf,fuse,ramfs,9p
-
-# Set this to yes to allow libvirt_lxc connections to work without SELinux.
-#libvirt_lxc_noseclabel = yes
-
-[colors]
-#highlight = white
-#verbose = blue
-#warn = bright purple
-#error = red
-#debug = dark gray
-#deprecate = purple
-#skip = cyan
-#unreachable = red
-#ok = green
-#changed = yellow
-#diff_add = green
-#diff_remove = red
-#diff_lines = cyan
-
-
-[diff]
-# Always print diff when running ( same as always running with -D/--diff )
-# always = no
-
-# Set how many context lines to show in diff
-# context = 3
-
-[galaxy]
-server_list = automation_hub
-
-[galaxy_server.automation_hub]
-url=https://cloud.redhat.com/api/automation-hub/
-auth_url=https://sso.redhat.com/auth/realms/redhat-external/protocol/openid-connect/token/
-
-token=eyJhbGciOiJIUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJhZDUyMjdhMy1iY2ZkLTRjZjAtYTdiNi0zOTk4MzVhMDg1NjYifQ.eyJpYXQiOjE2NzkzMDkyMTcsImp0aSI6IjJmZTdjZjA1LTAxZDQtNDMwMi1iMWNlLTgzNjlhNWJmNjViMyIsImlzcyI6Imh0dHBzOi8vc3NvLnJlZGhhdC5jb20vYXV0aC9yZWFsbXMvcmVkaGF0LWV4dGVybmFsIiwiYXVkIjoiaHR0cHM6Ly9zc28ucmVkaGF0LmNvbS9hdXRoL3JlYWxtcy9yZWRoYXQtZXh0ZXJuYWwiLCJzdWIiOiJmOjUyOGQ3NmZmLWY3MDgtNDNlZC04Y2Q1LWZlMTZmNGZlMGNlNjpqZW5uaWZlcl9qb2huIiwidHlwIjoiT2ZmbGluZSIsImF6cCI6ImNsb3VkLXNlcnZpY2VzIiwibm9uY2UiOiJmZTY2MGYxMS1kODFjLTQ2YWItYTkzNS1hZTAxZmY2MjA2OTciLCJzZXNzaW9uX3N0YXRlIjoiMzI3ZDlhNjgtZTkxMi00N2NiLWI3NDctNWE5YmQzZTJlZjlmIiwic2NvcGUiOiJvcGVuaWQgYXBpLmlhbS5zZXJ2aWNlX2FjY291bnRzIGFwaS5pYW0ub3JnYW5pemF0aW9uIG9mZmxpbmVfYWNjZXNzIiwic2lkIjoiMzI3ZDlhNjgtZTkxMi00N2NiLWI3NDctNWE5YmQzZTJlZjlmIn0.iGbseoF6AXetWNa0sFsfzbmzvizwaBcY0rd14YFJqcU \ No newline at end of file
diff --git a/ansible_collections/dellemc/powerflex/changelogs/.plugin-cache.yaml b/ansible_collections/dellemc/powerflex/changelogs/.plugin-cache.yaml
index 51f28a3fa..b2098aee6 100644
--- a/ansible_collections/dellemc/powerflex/changelogs/.plugin-cache.yaml
+++ b/ansible_collections/dellemc/powerflex/changelogs/.plugin-cache.yaml
@@ -6,6 +6,7 @@ plugins:
callback: {}
cliconf: {}
connection: {}
+ filter: {}
httpapi: {}
inventory: {}
lookup: {}
@@ -55,6 +56,11 @@ plugins:
name: snapshot
namespace: ''
version_added: 1.0.0
+ snapshot_policy:
+ description: Manage snapshot policies on Dell PowerFlex
+ name: snapshot_policy
+ namespace: ''
+ version_added: 1.7.0
storagepool:
description: Managing Dell PowerFlex storage pool
name: storagepool
@@ -68,5 +74,6 @@ plugins:
netconf: {}
shell: {}
strategy: {}
+ test: {}
vars: {}
-version: 1.6.0
+version: 2.1.0
diff --git a/ansible_collections/dellemc/powerflex/changelogs/changelog.yaml b/ansible_collections/dellemc/powerflex/changelogs/changelog.yaml
index 37e6bbb43..8211b2b91 100644
--- a/ansible_collections/dellemc/powerflex/changelogs/changelog.yaml
+++ b/ansible_collections/dellemc/powerflex/changelogs/changelog.yaml
@@ -2,90 +2,147 @@ ancestor: null
releases:
1.0.0:
modules:
- - description: Gathering information about Dell PowerFlex
- name: info
- namespace: ''
- - description: Manage SDCs on Dell PowerFlex
- name: sdc
- namespace: ''
- - description: Manage Snapshots on Dell PowerFlex
- name: snapshot
- namespace: ''
- - description: Managing Dell PowerFlex storage pool
- name: storagepool
- namespace: ''
- - description: Manage volumes on Dell PowerFlex
- name: volume
- namespace: ''
+ - description: Gathering information about Dell PowerFlex
+ name: info
+ namespace: ''
+ - description: Manage SDCs on Dell PowerFlex
+ name: sdc
+ namespace: ''
+ - description: Manage Snapshots on Dell PowerFlex
+ name: snapshot
+ namespace: ''
+ - description: Managing Dell PowerFlex storage pool
+ name: storagepool
+ namespace: ''
+ - description: Manage volumes on Dell PowerFlex
+ name: volume
+ namespace: ''
release_date: '2021-03-24'
1.1.0:
changes:
minor_changes:
- - Added dual licensing.
- - Gatherfacts module is enhanced to list devices.
+ - Added dual licensing.
+ - Gatherfacts module is enhanced to list devices.
modules:
- - description: Manage device on Dell PowerFlex
- name: device
- namespace: ''
- - description: Manage SDS on Dell PowerFlex
- name: sds
- namespace: ''
+ - description: Manage device on Dell PowerFlex
+ name: device
+ namespace: ''
+ - description: Manage SDS on Dell PowerFlex
+ name: sds
+ namespace: ''
release_date: '2021-09-28'
1.1.1:
changes:
deprecated_features:
- - The dellemc_powerflex_gatherfacts module is deprecated and replaced with dellemc_powerflex_info
+ - The dellemc_powerflex_gatherfacts module is deprecated and replaced with dellemc_powerflex_info
trivial:
- - Product Guide, Release Notes and ReadMe updated as per community guidelines.
+ - Product Guide, Release Notes and ReadMe updated as per community guidelines.
release_date: '2021-12-16'
1.2.0:
changes:
minor_changes:
- - Names of previously released modules have been changed from dellemc_powerflex_\<module name>
- to \<module name>.
+ - Names of previously released modules have been changed from dellemc_powerflex_\<module
+ name> to \<module name>.
modules:
- - description: Manage Protection Domain on Dell PowerFlex
- name: protection_domain
- namespace: ''
+ - description: Manage Protection Domain on Dell PowerFlex
+ name: protection_domain
+ namespace: ''
release_date: '2022-03-25'
1.3.0:
changes:
minor_changes:
- - Added execution environment manifest file to support building an execution
- environment with ansible-builder.
- - Enabled the check_mode support for info module
+ - Added execution environment manifest file to support building an execution
+ environment with ansible-builder.
+ - Enabled the check_mode support for info module
modules:
- - description: Manage MDM cluster on Dell PowerFlex
- name: mdm_cluster
- namespace: ''
+ - description: Manage MDM cluster on Dell PowerFlex
+ name: mdm_cluster
+ namespace: ''
release_date: '2022-06-28'
1.4.0:
changes:
minor_changes:
- - Added support for 4.0.x release of PowerFlex OS.
- - Info module is enhanced to support the listing volumes and storage pools with
- statistics data.
- - Storage pool module is enhanced to get the details with statistics data.
- - Volume module is enhanced to get the details with statistics data.
+ - Added support for 4.0.x release of PowerFlex OS.
+ - Info module is enhanced to support the listing volumes and storage pools with
+ statistics data.
+ - Storage pool module is enhanced to get the details with statistics data.
+ - Volume module is enhanced to get the details with statistics data.
release_date: '2022-09-27'
1.5.0:
changes:
minor_changes:
- - Info module is enhanced to support the listing replication consistency groups.
- - Renamed gateway_host to hostname
- - Renamed verifycert to validate_certs.
- - Updated modules to adhere with ansible community guidelines.
+ - Info module is enhanced to support the listing replication consistency groups.
+ - Renamed gateway_host to hostname
+ - Renamed verifycert to validate_certs.
+ - Updated modules to adhere with ansible community guidelines.
modules:
- - description: Manage replication consistency groups on Dell PowerFlex
- name: replication_consistency_group
- namespace: ''
+ - description: Manage replication consistency groups on Dell PowerFlex
+ name: replication_consistency_group
+ namespace: ''
release_date: '2022-12-22'
1.6.0:
changes:
minor_changes:
- - Info module is enhanced to support the listing of replication pairs.
+ - Info module is enhanced to support the listing of replication pairs.
modules:
- - description: Manage replication pairs on Dell PowerFlex
- name: replication_pair
- namespace: ''
+ - description: Manage replication pairs on Dell PowerFlex
+ name: replication_pair
+ namespace: ''
release_date: '2023-03-31'
+ 1.7.0:
+ changes:
+ minor_changes:
+ - Added Ansible role to support installation and uninstallation of SDC.
+ - Added sample playbooks for the modules.
+ - Device module is enhanced to support force addition of device to the SDS.
+ - Info module is enhanced to list statistics in snapshot policies.
+ - Replication consistency group module is enhanced to support failover, restore,
+ reverse, switchover, and sync operations.
+ - SDC module is enhanced to configure performance profile and to remove SDC.
+ - Updated modules to adhere with ansible community guidelines.
+ modules:
+ - description: Manage snapshot policies on Dell PowerFlex
+ name: snapshot_policy
+ namespace: ''
+ release_date: '2023-06-30'
+ 1.8.0:
+ changes:
+ minor_changes:
+ - Added Ansible role to support installation and uninstallation of LIA.
+ - Added Ansible role to support installation and uninstallation of MDM.
+ - Added Ansible role to support installation and uninstallation of SDS.
+ - Added Ansible role to support installation and uninstallation of TB.
+ release_date: '2023-08-31'
+ 1.9.0:
+ changes:
+ minor_changes:
+ - Added Ansible role to support installation and uninstallation of Gateway.
+ - Added Ansible role to support installation and uninstallation of SDR.
+ - Added Ansible role to support installation and uninstallation of Web UI.
+ release_date: '2023-09-29'
+ 2.0.1:
+ changes:
+ minor_changes:
+ - Added Ansible role to support creation and deletion of protection domain,
+ storage pool and fault set.
+ - Added Ansible role to support installation and uninstallation of Active MQ.
+ - Added support for PowerFlex Denver version(4.5.x)
+ - Added support for SDC installation on ESXi, Rocky Linux and Windows OS.
+ release_date: '2023-10-31'
+ 2.1.0:
+ changes:
+ minor_changes:
+ - Added support for PowerFlex Denver version(4.5.x) to TB and Config role.
+ release_date: '2023-11-30'
+ 2.2.0:
+ changes:
+ minor_changes:
+ - The SDS module has been enhanced to facilitate SDS creation within
+ a fault set.
+ - The Info module is enhanced to retrieve lists related to fault sets,
+ service templates, deployments, and managed devices.
+ modules:
+ - description: Manage Fault Sets on Dell PowerFlex
+ name: fault_set
+ namespace: ''
+ release_date: '2024-02-29'
diff --git a/ansible_collections/dellemc/powerflex/docs/CONTRIBUTING.md b/ansible_collections/dellemc/powerflex/docs/CONTRIBUTING.md
index 642f94d23..726c931d0 100644
--- a/ansible_collections/dellemc/powerflex/docs/CONTRIBUTING.md
+++ b/ansible_collections/dellemc/powerflex/docs/CONTRIBUTING.md
@@ -1,5 +1,5 @@
<!--
-Copyright (c) 2022 Dell Inc., or its subsidiaries. All Rights Reserved.
+Copyright (c) 2024 Dell Inc., or its subsidiaries. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -10,7 +10,7 @@ You may obtain a copy of the License at
# How to contribute
-Become one of the contributors to this project! We thrive to build a welcoming and open community for anyone who wants to use the project or contribute to it. There are just a few small guidelines you need to follow. To help us create a safe and positive community experience for all, we require all participants to adhere to the [Code of Conduct](https://github.com/dell/ansible-powerflex/blob/1.6.0/CODE_OF_CONDUCT.md).
+Become one of the contributors to this project! We thrive to build a welcoming and open community for anyone who wants to use the project or contribute to it. There are just a few small guidelines you need to follow. To help us create a safe and positive community experience for all, we require all participants to adhere to the [Code of Conduct](https://github.com/dell/ansible-powerflex/blob/2.2.0/CODE_OF_CONDUCT.md).
## Table of contents
@@ -76,7 +76,7 @@ Triage helps ensure that issues resolve quickly by:
If you don't have the knowledge or time to code, consider helping with _issue triage_. The Ansible modules for Dell PowerFlex community will thank you for saving them time by spending some of yours.
-Read more about the ways you can [Triage issues](https://github.com/dell/ansible-powerflex/blob/1.6.0/ISSUE_TRIAGE.md).
+Read more about the ways you can [Triage issues](https://github.com/dell/ansible-powerflex/blob/2.2.0/ISSUE_TRIAGE.md).
## Your first contribution
@@ -89,7 +89,7 @@ When you're ready to contribute, it's time to create a pull request.
## Branching
-* [Branching Strategy for Ansible modules for Dell PowerFlex](https://github.com/dell/ansible-powerflex/blob/1.6.0/BRANCHING.md)
+* [Branching Strategy for Ansible modules for Dell PowerFlex](https://github.com/dell/ansible-powerflex/blob/2.2.0/BRANCHING.md)
## Signing your commits
@@ -144,7 +144,7 @@ Make sure that the title for your pull request uses the same format as the subje
### Quality gates for pull requests
-GitHub Actions are used to enforce quality gates when a pull request is created or when any commit is made to the pull request. These GitHub Actions enforce our minimum code quality requirement for any code that get checked into the repository. If any of the quality gates fail, it is expected that the contributor will look into the check log, understand the problem and resolve the issue. If help is needed, please feel free to reach out the maintainers of the project for [support](https://github.com/dell/ansible-powerflex/blob/1.6.0/SUPPORT.md).
+GitHub Actions are used to enforce quality gates when a pull request is created or when any commit is made to the pull request. These GitHub Actions enforce our minimum code quality requirement for any code that get checked into the repository. If any of the quality gates fail, it is expected that the contributor will look into the check log, understand the problem and resolve the issue. If help is needed, please feel free to reach out the maintainers of the project for [support](https://github.com/dell/ansible-powerflex/blob/2.2.0/SUPPORT.md).
#### Code sanitization
diff --git a/ansible_collections/dellemc/powerflex/docs/INSTALLATION.md b/ansible_collections/dellemc/powerflex/docs/INSTALLATION.md
index 1dff703ec..86861574b 100644
--- a/ansible_collections/dellemc/powerflex/docs/INSTALLATION.md
+++ b/ansible_collections/dellemc/powerflex/docs/INSTALLATION.md
@@ -1,5 +1,5 @@
<!--
-Copyright (c) 2022 Dell Inc., or its subsidiaries. All Rights Reserved.
+Copyright (c) 2024 Dell Inc., or its subsidiaries. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -41,7 +41,7 @@ You may obtain a copy of the License at
* Download the latest tar build from any of the available distribution channel [Ansible Galaxy](https://galaxy.ansible.com/dellemc/powerflex) /[Automation Hub](https://console.redhat.com/ansible/automation-hub/repo/published/dellemc/powerflex) and use this command to install the collection anywhere in your system:
- ansible-galaxy collection install dellemc-powerflex-1.6.0.tar.gz -p <install_path>
+ ansible-galaxy collection install dellemc-powerflex-2.2.0.tar.gz -p <install_path>
* Set the environment variable:
@@ -68,7 +68,7 @@ You may obtain a copy of the License at
## Ansible modules execution
-The Ansible server must be configured with Python library for PowerFlex to run the Ansible playbooks. The [Documents](https://github.com/dell/ansible-powerflex/blob/1.6.0/docs/) provide information on different Ansible modules along with their functions and syntax. The parameters table in the Product Guide provides information on various parameters which needs to be configured before running the modules.
+The Ansible server must be configured with Python library for PowerFlex to run the Ansible playbooks. The [Documents](https://github.com/dell/ansible-powerflex/blob/2.2.0/docs/) provide information on different Ansible modules along with their functions and syntax. The parameters table in the Product Guide provides information on various parameters which needs to be configured before running the modules.
## SSL certificate validation
diff --git a/ansible_collections/dellemc/powerflex/docs/ISSUE_TRIAGE.md b/ansible_collections/dellemc/powerflex/docs/ISSUE_TRIAGE.md
index 18423aa1e..8871da8e3 100644
--- a/ansible_collections/dellemc/powerflex/docs/ISSUE_TRIAGE.md
+++ b/ansible_collections/dellemc/powerflex/docs/ISSUE_TRIAGE.md
@@ -1,5 +1,5 @@
<!--
-Copyright (c) 2022 Dell Inc., or its subsidiaries. All Rights Reserved.
+Copyright (c) 2024 Dell Inc., or its subsidiaries. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -43,8 +43,8 @@ Should explain what happened, what was expected and how to reproduce it together
- Ansible Version: [e.g. 2.14]
- Python Version [e.g. 3.11]
- - Ansible modules for Dell PowerFlex Version: [e.g. 1.6.0]
- - PowerFlex SDK version: [e.g. PyPowerFlex 1.7.0]
+ - Ansible modules for Dell PowerFlex Version: [e.g. 2.2.0]
+ - PowerFlex SDK version: [e.g. PyPowerFlex 1.9.0]
- Any other additional information...
#### Feature requests
diff --git a/ansible_collections/dellemc/powerflex/docs/MAINTAINER_GUIDE.md b/ansible_collections/dellemc/powerflex/docs/MAINTAINER_GUIDE.md
index b92cebabe..e2d1d90e0 100644
--- a/ansible_collections/dellemc/powerflex/docs/MAINTAINER_GUIDE.md
+++ b/ansible_collections/dellemc/powerflex/docs/MAINTAINER_GUIDE.md
@@ -1,5 +1,5 @@
<!--
-Copyright (c) 2022 Dell Inc., or its subsidiaries. All Rights Reserved.
+Copyright (c) 2024 Dell Inc., or its subsidiaries. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -27,7 +27,7 @@ If a candidate is approved, a Maintainer contacts the candidate to invite them t
## Maintainer policies
* Lead by example
-* Follow the [Code of Conduct](https://github.com/dell/ansible-powerflex/blob/1.6.0/CODE_OF_CONDUCT.md) and the guidelines in the [Contributing](https://github.com/dell/ansible-powerflex/blob/1.6.0/CONTRIBUTING.md) and [Committer](https://github.com/dell/ansible-powerflex/blob/1.6.0/COMMITTER_GUIDE.md) guides
+* Follow the [Code of Conduct](https://github.com/dell/ansible-powerflex/blob/2.2.0/CODE_OF_CONDUCT.md) and the guidelines in the [Contributing](https://github.com/dell/ansible-powerflex/blob/2.2.0/CONTRIBUTING.md) and [Committer](https://github.com/dell/ansible-powerflex/blob/2.2.0/COMMITTER_GUIDE.md) guides
* Promote a friendly and collaborative environment within our community
* Be actively engaged in discussions, answering questions, updating defects, and reviewing pull requests
* Criticize code, not people. Ideally, tell the contributor a better way to do what they need.
diff --git a/ansible_collections/dellemc/powerflex/docs/Release Notes.md b/ansible_collections/dellemc/powerflex/docs/Release Notes.md
index a45d82eb7..9ce24b6d0 100644
--- a/ansible_collections/dellemc/powerflex/docs/Release Notes.md
+++ b/ansible_collections/dellemc/powerflex/docs/Release Notes.md
@@ -1,8 +1,8 @@
**Ansible Modules for Dell Technologies PowerFlex**
=========================================
-### Release notes 1.6.0
+### Release notes 2.2.0
-> © 2023 Dell Inc. or its subsidiaries. All rights reserved. Dell
+> © 2024 Dell Inc. or its subsidiaries. All rights reserved. Dell
> and other trademarks are trademarks of Dell Inc. or its
> subsidiaries. Other trademarks may be trademarks of their respective
> owners.
@@ -26,9 +26,9 @@ The table in this section lists the revision history of this document.
Table 1. Revision history
-| Revision | Date | Description |
-|----------|----------------|-------------------------------------------------------------|
-| 01 | March 2023 | Current release of Ansible Modules for Dell PowerFlex 1.6.0 |
+| Revision | Date | Description |
+|----------|-----------------|-------------------------------------------------------------|
+| 01 | February 2024 | Current release of Ansible Modules for Dell PowerFlex 2.2.0 |
Product description
-------------------
@@ -36,7 +36,7 @@ Product description
The Ansible modules for Dell PowerFlex are used to automate and orchestrate
the deployment, configuration, and management of Dell PowerFlex storage
systems. The capabilities of Ansible modules are managing volumes,
-storage pools, SDCs, snapshots, SDSs, replication consistency groups, replication pairs, devices, protection domain and MDM
+storage pools, SDCs, snapshots, snapshot policy, SDSs, replication consistency groups, replication pairs, devices, protection domain, MDM and fault sets.
cluster, and obtaining high-level information about a PowerFlex system information.
The modules use playbooks to list, show, create, delete, and modify
each of the entities.
@@ -44,28 +44,30 @@ each of the entities.
New features and enhancements
-----------------------------
Along with the previous release deliverables, this release supports following features -
-- Info module is enhanced to support the listing of replication pairs.
-- Added new module for replication pairs.
+- Fault set module is introduced to create, get details, rename and delete fault sets.
+- The SDS module has been enhanced to facilitate SDS creation within a fault set.
+- The Info module is enhanced to retrieve lists related to fault sets, service templates, deployments, and managed devices.
Known issues
------------
-- Setting the RF cache and performance profile of the SDS during its creation fails intermittently on PowerFlex version 3.5
-- The creation of replication pair fails when copy_type is specified as OfflineCopy on PowerFlex version 4.0
+- Setting the RF cache and performance profile of the SDS during its creation fails intermittently on PowerFlex version 3.5.
+- The creation of replication pair fails when copy_type is specified as OfflineCopy on PowerFlex version 4.0.
+- Pagination in info module with offset and limit fetches more than expected records when listing service templates or deployments.
+- Templates are fetched using the info module in spite of setting include_templates to false when listing deployments.
Limitations
-----------
-- The API is accepting a negative integer value for overall_limit in the network_limits for a specific protection domain.
+
Distribution
------------
The software package is available for download from the [Ansible Modules
-for PowerFlex GitHub](https://github.com/dell/ansible-powerflex/tree/1.6.0) page.
+for PowerFlex GitHub](https://github.com/dell/ansible-powerflex/tree/2.2.0) page.
Documentation
-------------
-The documentation is available on [Ansible Modules for PowerFlex GitHub](https://github.com/dell/ansible-powerflex/tree/1.6.0/docs)
+The documentation is available on [Ansible Modules for PowerFlex GitHub](https://github.com/dell/ansible-powerflex/tree/2.2.0/docs)
page. It includes the following:
- README
- Release Notes (this document)
- - Product Guide
diff --git a/ansible_collections/dellemc/powerflex/docs/SECURITY.md b/ansible_collections/dellemc/powerflex/docs/SECURITY.md
index e34152322..77323e456 100644
--- a/ansible_collections/dellemc/powerflex/docs/SECURITY.md
+++ b/ansible_collections/dellemc/powerflex/docs/SECURITY.md
@@ -1,5 +1,5 @@
<!--
-Copyright (c) 2022 Dell Inc., or its subsidiaries. All Rights Reserved.
+Copyright (c) 2024 Dell Inc., or its subsidiaries. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -12,7 +12,7 @@ You may obtain a copy of the License at
The Ansible modules for Dell PowerFlex repository are inspected for security vulnerabilities via blackduck scans and static code analysis.
-In addition to this, there are various security checks that get executed against a branch when a pull request is created/updated. Please refer to [pull request](https://github.com/dell/ansible-powerflex/blob/1.6.0/docs/CONTRIBUTING.md#Pull-requests) for more information.
+In addition to this, there are various security checks that get executed against a branch when a pull request is created/updated. Please refer to [pull request](https://github.com/dell/ansible-powerflex/blob/2.2.0/docs/CONTRIBUTING.md#Pull-requests) for more information.
## Reporting a vulnerability
diff --git a/ansible_collections/dellemc/powerflex/docs/modules/device.rst b/ansible_collections/dellemc/powerflex/docs/modules/device.rst
index 35ae246aa..4fcd82854 100644
--- a/ansible_collections/dellemc/powerflex/docs/modules/device.rst
+++ b/ansible_collections/dellemc/powerflex/docs/modules/device.rst
@@ -20,9 +20,9 @@ Requirements
------------
The below requirements are needed on the host that executes this module.
-- A Dell PowerFlex storage system version 3.5 or later.
-- Ansible-core 2.12 or later.
-- PyPowerFlex 1.6.0.
+- A Dell PowerFlex storage system version 3.6 or later.
+- Ansible-core 2.14 or later.
+- PyPowerFlex 1.9.0.
- Python 3.9, 3.10 or 3.11.
@@ -134,6 +134,14 @@ Parameters
State of the device.
+ force (optional, bool, False)
+ Using the Force flag to add a device.
+
+ Use this flag, to overwrite existing data on the device.
+
+ Use this flag with caution, because all data on the device will be destroyed.
+
+
hostname (True, str, None)
IP or FQDN of the PowerFlex host.
@@ -201,6 +209,22 @@ Examples
protection_domain_name: "domain1"
external_acceleration_type: "ReadAndWrite"
state: "present"
+ - name: Add a device with force flag
+ dellemc.powerflex.device:
+ hostname: "{{hostname}}"
+ username: "{{username}}"
+ password: "{{password}}"
+ validate_certs: "{{validate_certs}}"
+ port: "{{port}}"
+ current_pathname: "/dev/sdb"
+ sds_name: "node1"
+ media_type: "HDD"
+ device_name: "device2"
+ storage_pool_name: "pool1"
+ protection_domain_name: "domain1"
+ external_acceleration_type: "ReadAndWrite"
+ force: true
+ state: "present"
- name: Get device details using device_id
dellemc.powerflex.device:
hostname: "{{hostname}}"
@@ -232,23 +256,23 @@ Examples
state: "present"
- name: Remove a device using device_id
dellemc.powerflex.device:
- hostname: "{{hostname}}"
- username: "{{username}}"
- password: "{{password}}"
- validate_certs: "{{validate_certs}}"
- port: "{{port}}"
- device_id: "76eb7e2f00010000"
- state: "absent"
+ hostname: "{{hostname}}"
+ username: "{{username}}"
+ password: "{{password}}"
+ validate_certs: "{{validate_certs}}"
+ port: "{{port}}"
+ device_id: "76eb7e2f00010000"
+ state: "absent"
- name: Remove a device using (current_pathname, sds_id)
dellemc.powerflex.device:
- hostname: "{{hostname}}"
- username: "{{username}}"
- password: "{{password}}"
- validate_certs: "{{validate_certs}}"
- port: "{{port}}"
- current_pathname: "/dev/sdb"
- sds_name: "node1"
- state: "absent"
+ hostname: "{{hostname}}"
+ username: "{{username}}"
+ password: "{{password}}"
+ validate_certs: "{{validate_certs}}"
+ port: "{{port}}"
+ current_pathname: "/dev/sdb"
+ sds_name: "node1"
+ state: "absent"
diff --git a/ansible_collections/dellemc/powerflex/docs/modules/fault_set.rst b/ansible_collections/dellemc/powerflex/docs/modules/fault_set.rst
new file mode 100644
index 000000000..55b9972bc
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/docs/modules/fault_set.rst
@@ -0,0 +1,215 @@
+.. _fault_set_module:
+
+
+fault_set -- Manage Fault Sets on Dell PowerFlex
+================================================
+
+.. contents::
+ :local:
+ :depth: 1
+
+
+Synopsis
+--------
+
+Managing fault sets on PowerFlex storage system includes creating, getting details, renaming and deleting a fault set.
+
+
+
+Requirements
+------------
+The below requirements are needed on the host that executes this module.
+
+- A Dell PowerFlex storage system version 3.6 or later.
+- Ansible-core 2.14 or later.
+- PyPowerFlex 1.9.0.
+- Python 3.9, 3.10 or 3.11.
+
+
+
+Parameters
+----------
+
+ fault_set_name (optional, str, None)
+ Name of the Fault Set.
+
+ Mutually exclusive with *fault_set_id*.
+
+
+ fault_set_id (optional, str, None)
+ ID of the Fault Set.
+
+ Mutually exclusive with *fault_set_name*.
+
+
+ protection_domain_name (optional, str, None)
+ Name of protection domain.
+
+ Mutually exclusive with *protection_domain_id*.
+
+
+ protection_domain_id (optional, str, None)
+ ID of the protection domain.
+
+ Mutually exclusive with *protection_domain_name*.
+
+
+ fault_set_new_name (optional, str, None)
+ New name of the fault set.
+
+
+ state (optional, str, present)
+ State of the Fault Set.
+
+
+ hostname (True, str, None)
+ IP or FQDN of the PowerFlex host.
+
+
+ username (True, str, None)
+ The username of the PowerFlex host.
+
+
+ password (True, str, None)
+ The password of the PowerFlex host.
+
+
+ validate_certs (optional, bool, True)
+ Boolean variable to specify whether or not to validate SSL certificate.
+
+ ``true`` - Indicates that the SSL certificate should be verified.
+
+ ``false`` - Indicates that the SSL certificate should not be verified.
+
+
+ port (optional, int, 443)
+ Port number through which communication happens with PowerFlex host.
+
+
+ timeout (False, int, 120)
+ Time after which connection will get terminated.
+
+ It is to be mentioned in seconds.
+
+
+
+
+
+Notes
+-----
+
+.. note::
+ - The *check_mode* is supported.
+ - When *fault_set_name* is provided, *protection_domain_name* or *protection_domain_id* must be provided.
+ - The modules present in the collection named as 'dellemc.powerflex' are built to support the Dell PowerFlex storage platform.
+
+
+
+
+Examples
+--------
+
+.. code-block:: yaml+jinja
+
+
+
+ - name: Create Fault Set on Protection Domain
+ dellemc.powerflex.fault_set:
+ hostname: "{{ hostname }}"
+ username: "{{ username }}"
+ password: "{{ password }}"
+ validate_certs: "{{ validate_certs }}"
+ fault_set_name: "{{ fault_set_name }}"
+ protection_domain_name: "{{ pd_name }}"
+ state: present
+
+ - name: Rename Fault Set
+ dellemc.powerflex.fault_set:
+ hostname: "{{ hostname }}"
+ username: "{{ username }}"
+ password: "{{ password }}"
+ validate_certs: "{{ validate_certs }}"
+ fault_set_name: "{{ fault_set_name }}"
+ fault_set_new_name: "{{ fault_set_new_name }}"
+ state: present
+
+ - name: Get details of a Fault Set
+ dellemc.powerflex.fault_set:
+ hostname: "{{ hostname }}"
+ username: "{{ username }}"
+ password: "{{ password }}"
+ validate_certs: "{{ validate_certs }}"
+ fault_set_id: "{{ fault_set_id }}"
+ state: present
+
+ - name: Delete Fault Set
+ dellemc.powerflex.fault_set:
+ hostname: "{{ hostname }}"
+ username: "{{ username }}"
+ password: "{{ password }}"
+ validate_certs: "{{ validate_certs }}"
+ fault_set_id: "{{ fault_set_id }}"
+ state: absent
+
+
+
+Return Values
+-------------
+
+changed (always, bool, false)
+ Whether or not the resource has changed.
+
+
+fault_set_details (always, dict, {'protectionDomainId': 'da721a8300000000', 'protectionDomainName': 'sample-pd', 'name': 'fs_001', 'id': 'eb44b70500000000', 'links': []})
+ Details of fault set.
+
+
+ protectionDomainId (, str, )
+ Unique identifier of the protection domain.
+
+
+ protectionDomainName (, str, )
+ Name of the protection domain.
+
+
+ name (, str, )
+ Name of the fault set.
+
+
+ id (, str, )
+ Unique identifier of the fault set.
+
+
+ SDS (, list, )
+ List of SDS associated to the fault set.
+
+
+ links (, list, )
+ Fault set links.
+
+
+ href (, str, )
+ Fault Set instance URL.
+
+
+ rel (, str, )
+ Relationship of fault set with different entities.
+
+
+
+
+
+
+
+Status
+------
+
+
+
+
+
+Authors
+~~~~~~~
+
+- Carlos Tronco (@ctronco) <ansible.team@dell.com>
+- Trisha Datta (@trisha-dell) <ansible.team@dell.com>
diff --git a/ansible_collections/dellemc/powerflex/docs/modules/info.rst b/ansible_collections/dellemc/powerflex/docs/modules/info.rst
index 735fb04f4..ced8e0aaa 100644
--- a/ansible_collections/dellemc/powerflex/docs/modules/info.rst
+++ b/ansible_collections/dellemc/powerflex/docs/modules/info.rst
@@ -14,6 +14,8 @@ Synopsis
Gathering information about Dell PowerFlex storage system includes getting the api details, list of volumes, SDSs, SDCs, storage pools, protection domains, snapshot policies, and devices.
+Gathering information about Dell PowerFlex Manager includes getting the list of managed devices, deployments and service templates.
+
Requirements
@@ -21,8 +23,8 @@ Requirements
The below requirements are needed on the host that executes this module.
- A Dell PowerFlex storage system version 3.5 or later.
-- Ansible-core 2.12 or later.
-- PyPowerFlex 1.6.0.
+- Ansible-core 2.14 or later.
+- PyPowerFlex 1.8.0.
- Python 3.9, 3.10 or 3.11.
@@ -51,6 +53,14 @@ Parameters
Replication pairs - ``replication_pair``.
+ Fault Sets - ``fault_set``.
+
+ Service templates - ``service_template``.
+
+ Managed devices - ``managed_device``.
+
+ Deployments - ``deployment``.
+
filters (optional, list, None)
List of filters to support filtered output for storage entities.
@@ -67,12 +77,60 @@ Parameters
filter_operator (True, str, None)
Operation to be performed on filter key.
+ Choice *'contains'* is supported for gather_subset keys *service_template*, *managed_device*, *deployment*.
+
filter_value (True, str, None)
Value of the filter key.
+ limit (optional, int, 50)
+ Page limit.
+
+ Supported for gather_subset keys *service_template*, *managed_device*, *deployment*.
+
+
+ offset (optional, int, 0)
+ Pagination offset.
+
+ Supported for gather_subset keys *service_template*, *managed_device*, *deployment*.
+
+
+ sort (optional, str, None)
+ Sort the returned components based on specified field.
+
+ Supported for gather_subset keys *service_template*, *managed_device*, *deployment*.
+
+ The supported sort keys for the gather_subset can be referred from PowerFlex Manager API documentation in developer.dell.com.
+
+
+ include_devices (optional, bool, True)
+ Include devices in response.
+
+ Applicable when gather_subset is *deployment*.
+
+
+ include_template (optional, bool, True)
+ Include service templates in response.
+
+ Applicable when gather_subset is *deployment*.
+
+
+ full (optional, bool, False)
+ Specify if response is full or brief.
+
+ Applicable when gather_subset is *deployment*, *service_template*.
+
+ For *deployment* specify to use full templates including resources in response.
+
+
+ include_attachments (optional, bool, True)
+ Include attachments.
+
+ Applicable when gather_subset is *service_template*.
+
+
hostname (True, str, None)
IP or FQDN of the PowerFlex host.
@@ -111,6 +169,8 @@ Notes
.. note::
- The *check_mode* is supported.
+ - The supported filter keys for the gather_subset can be referred from PowerFlex Manager API documentation in developer.dell.com.
+ - The *filter*, *sort*, *limit* and *offset* options will be ignored when more than one *gather_subset* is specified along with *service_template*, *managed_device* or *deployment*.
- The modules present in the collection named as 'dellemc.powerflex' are built to support the Dell PowerFlex storage platform.
@@ -138,6 +198,7 @@ Examples
- device
- rcg
- replication_pair
+ - fault_set
- name: Get a subset list of PowerFlex volumes
dellemc.powerflex.info:
@@ -152,6 +213,35 @@ Examples
filter_operator: "equal"
filter_value: "ansible_test"
+ - name: Get deployment and resource provisioning info
+ dellemc.powerflex.info:
+ hostname: "{{hostname}}"
+ username: "{{username}}"
+ password: "{{password}}"
+ validate_certs: "{{validate_certs}}"
+ gather_subset:
+ - managed_device
+ - deployment
+ - service_template
+
+ - name: Get deployment with filter, sort, pagination
+ dellemc.powerflex.info:
+ hostname: "{{hostname}}"
+ username: "{{username}}"
+ password: "{{password}}"
+ validate_certs: "{{validate_certs}}"
+ gather_subset:
+ - deployment
+ filters:
+ - filter_key: "name"
+ filter_operator: "contains"
+ filter_value: "partial"
+ sort: name
+ limit: 10
+ offset: 10
+ include_devices: true
+ include_template: true
+
Return Values
@@ -638,6 +728,119 @@ Replication_pairs (Always, list, {'copyType': 'OnlineCopy', 'id': '23aa0bc900000
+Fault_Sets (always, list, [{'protectionDomainId': 'da721a8300000000', 'protectionDomainName': 'fault_set_1', 'name': 'at1zbs1t6cp2sds1d1fs1', 'SDS': [], 'id': 'eb44b70500000000', 'links': [{'rel': 'self', 'href': '/api/instances/FaultSet::eb44b70500000000'}, {'rel': '/api/FaultSet/relationship/Statistics', 'href': '/api/instances/FaultSet::eb44b70500000000/relationships/Statistics'}, {'rel': '/api/FaultSet/relationship/Sds', 'href': '/api/instances/FaultSet::eb44b70500000000/relationships/Sds'}, {'rel': '/api/parent/relationship/protectionDomainId', 'href': '/api/instances/ProtectionDomain::da721a8300000000'}]}, {'protectionDomainId': 'da721a8300000000', 'protectionDomainName': 'fault_set_2', 'name': 'at1zbs1t6cp2sds1d1fs3', 'SDS': [], 'id': 'eb44b70700000002', 'links': [{'rel': 'self', 'href': '/api/instances/FaultSet::eb44b70700000002'}, {'rel': '/api/FaultSet/relationship/Statistics', 'href': '/api/instances/FaultSet::eb44b70700000002/relationships/Statistics'}, {'rel': '/api/FaultSet/relationship/Sds', 'href': '/api/instances/FaultSet::eb44b70700000002/relationships/Sds'}, {'rel': '/api/parent/relationship/protectionDomainId', 'href': '/api/instances/ProtectionDomain::da721a8300000000'}]}])
+ Details of fault sets.
+
+
+ protectionDomainId (, str, )
+ The ID of the protection domain.
+
+
+ name (, str, )
+ device name.
+
+
+ id (, str, )
+ device id.
+
+
+
+ManagedDevices (when I(gather_subset) is I(managed_device), list, [{'refId': 'softwareOnlyServer-10.1.1.1', 'refType': None, 'ipAddress': '10.1.1.1', 'currentIpAddress': '10.1.1.1', 'serviceTag': 'VMware-42 15 a5 f9 65 e6 63 0e-36 79 59 73 7b 3a 68 cd-SW', 'model': 'VMware Virtual Platform', 'deviceType': 'SoftwareOnlyServer', 'discoverDeviceType': 'SOFTWAREONLYSERVER_CENTOS', 'displayName': 'vpi1011-c1n1', 'managedState': 'UNMANAGED', 'state': 'READY', 'inUse': False, 'serviceReferences': [], 'statusMessage': None, 'firmwareName': 'Default Catalog - PowerFlex 4.5.0.0', 'customFirmware': False, 'needsAttention': False, 'manufacturer': 'VMware, Inc.', 'systemId': None, 'health': 'RED', 'healthMessage': 'Inventory run failed.', 'operatingSystem': 'N/A', 'numberOfCPUs': 0, 'cpuType': None, 'nics': 0, 'memoryInGB': 0, 'infraTemplateDate': None, 'infraTemplateId': None, 'serverTemplateDate': None, 'serverTemplateId': None, 'inventoryDate': None, 'complianceCheckDate': '2024-02-05T18:31:31.213+00:00', 'discoveredDate': '2024-02-05T18:31:30.992+00:00', 'deviceGroupList': {'paging': None, 'deviceGroup': [{'link': None, 'groupSeqId': -1, 'groupName': 'Global', 'groupDescription': None, 'createdDate': None, 'createdBy': 'admin', 'updatedDate': None, 'updatedBy': None, 'managedDeviceList': None, 'groupUserList': None}]}, 'detailLink': {'title': 'softwareOnlyServer-10.1.1.1', 'href': '/AsmManager/ManagedDevice/softwareOnlyServer-10.1.1.1', 'rel': 'describedby', 'type': None}, 'credId': 'bc97cefb-5eb4-4c20-8e39-d1a2b809c9f5', 'compliance': 'NONCOMPLIANT', 'failuresCount': 0, 'chassisId': None, 'parsedFacts': None, 'config': None, 'hostname': 'vpi1011-c1n1', 'osIpAddress': None, 'osAdminCredential': None, 'osImageType': None, 'lastJobs': None, 'puppetCertName': 'red_hat-10.1.1.1', 'svmAdminCredential': None, 'svmName': None, 'svmIpAddress': None, 'svmImageType': None, 'flexosMaintMode': 0, 'esxiMaintMode': 0, 'vmList': []}])
+ Details of all devices from inventory.
+
+
+ deviceType (, str, )
+ Device Type.
+
+
+ serviceTag (, str, )
+ Service Tag.
+
+
+ serverTemplateId (, str, )
+ The ID of the server template.
+
+
+ state (, str, )
+ The state of the device.
+
+
+ managedState (, str, )
+ The managed state of the device.
+
+
+ compliance (, str, )
+ The compliance state of the device.
+
+
+ systemId (, str, )
+ The system ID.
+
+
+
+Deployments (when I(gather_subset) is I(deployment), list, [{'id': '8aaa80658cd602e0018cda8b257f78ce', 'deploymentName': 'Test-Update - K', 'deploymentDescription': 'Test-Update - K', 'deploymentValid': None, 'retry': False, 'teardown': False, 'teardownAfterCancel': False, 'removeService': False, 'createdDate': '2024-01-05T16:53:21.407+00:00', 'createdBy': 'admin', 'updatedDate': '2024-02-11T17:00:05.657+00:00', 'updatedBy': 'system', 'deploymentScheduledDate': None, 'deploymentStartedDate': '2024-01-05T16:53:22.886+00:00', 'deploymentFinishedDate': None, 'serviceTemplate': {'id': '8aaa80658cd602e0018cda8b257f78ce', 'templateName': 'block-only (8aaa80658cd602e0018cda8b257f78ce)', 'templateDescription': 'Storage - Software Only deployment', 'templateType': 'VxRack FLEX', 'templateVersion': '4.5.0.0', 'templateValid': {'valid': True, 'messages': []}, 'originalTemplateId': 'c44cb500-020f-4562-9456-42ec1eb5f9b2', 'templateLocked': False, 'draft': False, 'inConfiguration': False, 'createdDate': '2024-01-05T16:53:22.083+00:00', 'createdBy': None, 'updatedDate': '2024-02-09T06:00:09.602+00:00', 'lastDeployedDate': None, 'updatedBy': None, 'components': [{'id': '6def7edd-bae2-4420-93bf-9ceb051bbb65', 'componentID': 'component-scaleio-gateway-1', 'identifier': None, 'componentValid': {'valid': True, 'messages': []}, 'puppetCertName': 'scaleio-block-legacy-gateway', 'osPuppetCertName': None, 'name': 'block-legacy-gateway', 'type': 'SCALEIO', 'subType': 'STORAGEONLY', 'teardown': False, 'helpText': None, 'managementIpAddress': None, 'configFile': None, 'serialNumber': None, 'asmGUID': 'scaleio-block-legacy-gateway', 'relatedComponents': {'625b0e17-9b91-4bc0-864c-d0111d42d8d0': 'Node (Software Only)', '961a59eb-80c3-4a3a-84b7-2101e9831527': 'Node (Software Only)-2', 'bca710a5-7cdf-481e-b729-0b53e02873ee': 'Node (Software Only)-3'}, 'resources': [], 'refId': None, 'cloned': False, 'clonedFromId': None, 'manageFirmware': False, 'brownfield': False, 'instances': 1, 'clonedFromAsmGuid': None, 'ip': None}], 'category': 'block-only', 'allUsersAllowed': True, 'assignedUsers': [], 'manageFirmware': True, 'useDefaultCatalog': False, 'firmwareRepository': None, 'licenseRepository': None, 'configuration': None, 'serverCount': 3, 'storageCount': 1, 'clusterCount': 1, 'serviceCount': 0, 'switchCount': 0, 'vmCount': 0, 'sdnasCount': 0, 'brownfieldTemplateType': 'NONE', 'networks': [{'id': '8aaa80648cd5fb9b018cda46e4e50000', 'name': 'mgmt', 'description': '', 'type': 'SCALEIO_MANAGEMENT', 'vlanId': 850, 'static': True, 'staticNetworkConfiguration': {'gateway': '10.1.1.1', 'subnet': '1.1.1.0', 'primaryDns': '10.1.1.1', 'secondaryDns': '10.1.1.1', 'dnsSuffix': None, 'ipRange': [{'id': '8aaa80648cd5fb9b018cda46e5080001', 'startingIp': '10.1.1.1', 'endingIp': '10.1.1.1', 'role': None}], 'ipAddress': None, 'staticRoute': None}, 'destinationIpAddress': '10.1.1.1'}], 'blockServiceOperationsMap': {'scaleio-block-legacy-gateway': {'blockServiceOperationsMap': {}}}}, 'scheduleDate': None, 'status': 'complete', 'compliant': True, 'deploymentDevice': [{'refId': 'scaleio-block-legacy-gateway', 'refType': None, 'logDump': None, 'status': None, 'statusEndTime': None, 'statusStartTime': None, 'deviceHealth': 'GREEN', 'healthMessage': 'OK', 'compliantState': 'COMPLIANT', 'brownfieldStatus': 'NOT_APPLICABLE', 'deviceType': 'scaleio', 'deviceGroupName': None, 'ipAddress': 'block-legacy-gateway', 'currentIpAddress': '10.1.1.1', 'serviceTag': 'block-legacy-gateway', 'componentId': None, 'statusMessage': None, 'model': 'PowerFlex Gateway', 'cloudLink': False, 'dasCache': False, 'deviceState': 'READY', 'puppetCertName': 'scaleio-block-legacy-gateway', 'brownfield': False}], 'vms': None, 'updateServerFirmware': True, 'useDefaultCatalog': False, 'firmwareRepository': {'id': '8aaa80658cd602e0018cd996a1c91bdc', 'name': 'Intelligent Catalog 45.373.00', 'sourceLocation': None, 'sourceType': None, 'diskLocation': None, 'filename': None, 'md5Hash': None, 'username': None, 'password': None, 'downloadStatus': None, 'createdDate': None, 'createdBy': None, 'updatedDate': None, 'updatedBy': None, 'defaultCatalog': False, 'embedded': False, 'state': None, 'softwareComponents': [], 'softwareBundles': [], 'deployments': [], 'bundleCount': 0, 'componentCount': 0, 'userBundleCount': 0, 'minimal': False, 'downloadProgress': 0, 'extractProgress': 0, 'fileSizeInGigabytes': None, 'signedKeySourceLocation': None, 'signature': None, 'custom': False, 'needsAttention': False, 'jobId': None, 'rcmapproved': False}, 'firmwareRepositoryId': '8aaa80658cd602e0018cd996a1c91bdc', 'licenseRepository': None, 'licenseRepositoryId': None, 'individualTeardown': False, 'deploymentHealthStatusType': 'green', 'assignedUsers': [], 'allUsersAllowed': True, 'owner': 'admin', 'noOp': False, 'firmwareInit': False, 'disruptiveFirmware': False, 'preconfigureSVM': False, 'preconfigureSVMAndUpdate': False, 'servicesDeployed': 'NONE', 'precalculatedDeviceHealth': None, 'lifecycleModeReasons': [], 'jobDetails': None, 'numberOfDeployments': 0, 'operationType': 'NONE', 'operationStatus': None, 'operationData': None, 'deploymentValidationResponse': None, 'currentStepCount': None, 'totalNumOfSteps': None, 'currentStepMessage': None, 'customImage': 'os_sles', 'originalDeploymentId': None, 'currentBatchCount': None, 'totalBatchCount': None, 'templateValid': True, 'lifecycleMode': False, 'vds': False, 'scaleUp': False, 'brownfield': False, 'configurationChange': False}])
+ Details of all deployments.
+
+
+ id (, str, )
+ Deployment ID.
+
+
+ deploymentName (, str, )
+ Deployment name.
+
+
+ status (, str, )
+ The status of deployment.
+
+
+ firmwareRepository (, dict, )
+ The firmware repository.
+
+
+ signature (, str, )
+ The signature details.
+
+
+ downloadStatus (, str, )
+ The download status.
+
+
+ rcmapproved (, bool, )
+ If RCM approved.
+
+
+
+
+ServiceTemplates (when I(gather_subset) is I(service_template), list, [{'id': '2434144f-7795-4245-a04b-6fcb771697d7', 'templateName': 'Storage- 100Gb', 'templateDescription': 'Storage Only 4 Node deployment with 100Gb networking', 'templateType': 'VxRack FLEX', 'templateVersion': '4.5-213', 'templateValid': {'valid': True, 'messages': []}, 'originalTemplateId': 'ff80808177f880fc0177f883bf1e0027', 'templateLocked': True, 'draft': False, 'inConfiguration': False, 'createdDate': '2024-01-04T19:47:23.534+00:00', 'createdBy': 'system', 'updatedDate': None, 'lastDeployedDate': None, 'updatedBy': None, 'components': [{'id': '43dec024-85a9-4901-9e8e-fa0d3c417f7b', 'componentID': 'component-scaleio-gateway-1', 'identifier': None, 'componentValid': {'valid': True, 'messages': []}, 'puppetCertName': None, 'osPuppetCertName': None, 'name': 'PowerFlex Cluster', 'type': 'SCALEIO', 'subType': 'STORAGEONLY', 'teardown': False, 'helpText': None, 'managementIpAddress': None, 'configFile': None, 'serialNumber': None, 'asmGUID': None, 'relatedComponents': {'c5c46733-012c-4dca-af9b-af46d73d045a': 'Storage Only Node'}, 'resources': [], 'refId': None, 'cloned': False, 'clonedFromId': None, 'manageFirmware': False, 'brownfield': False, 'instances': 1, 'clonedFromAsmGuid': None, 'ip': None}], 'category': 'Sample Templates', 'allUsersAllowed': False, 'assignedUsers': [], 'manageFirmware': True, 'useDefaultCatalog': True, 'firmwareRepository': None, 'licenseRepository': None, 'configuration': None, 'serverCount': 4, 'storageCount': 0, 'clusterCount': 1, 'serviceCount': 0, 'switchCount': 0, 'vmCount': 0, 'sdnasCount': 0, 'brownfieldTemplateType': 'NONE', 'networks': [{'id': 'ff80808177f8823b0177f8bb82d80005', 'name': 'flex-data2', 'description': '', 'type': 'SCALEIO_DATA', 'vlanId': 105, 'static': True, 'staticNetworkConfiguration': {'gateway': None, 'subnet': '1.1.1.0', 'primaryDns': None, 'secondaryDns': None, 'dnsSuffix': None, 'ipRange': None, 'ipAddress': None, 'staticRoute': None}, 'destinationIpAddress': '1.1.1.0'}], 'blockServiceOperationsMap': {}}])
+ Details of all service templates.
+
+
+ templateName (, str, )
+ Template name.
+
+
+ templateDescription (, str, )
+ Template description.
+
+
+ templateType (, str, )
+ Template type.
+
+
+ templateVersion (, str, )
+ Template version.
+
+
+ category (, str, )
+ The template category.
+
+
+ serverCount (, int, )
+ Server count.
+
+
+
@@ -652,4 +855,6 @@ Authors
~~~~~~~
- Arindam Datta (@dattaarindam) <ansible.team@dell.com>
+- Trisha Datta (@trisha-dell) <ansible.team@dell.com>
+- Jennifer John (@Jennifer-John) <ansible.team@dell.com>
diff --git a/ansible_collections/dellemc/powerflex/docs/modules/mdm_cluster.rst b/ansible_collections/dellemc/powerflex/docs/modules/mdm_cluster.rst
index 5131860e1..babb39b6c 100644
--- a/ansible_collections/dellemc/powerflex/docs/modules/mdm_cluster.rst
+++ b/ansible_collections/dellemc/powerflex/docs/modules/mdm_cluster.rst
@@ -22,9 +22,9 @@ Requirements
------------
The below requirements are needed on the host that executes this module.
-- A Dell PowerFlex storage system version 3.5 or later.
-- Ansible-core 2.12 or later.
-- PyPowerFlex 1.6.0.
+- A Dell PowerFlex storage system version 3.6 or later.
+- Ansible-core 2.14 or later.
+- PyPowerFlex 1.9.0.
- Python 3.9, 3.10 or 3.11.
@@ -177,7 +177,7 @@ Notes
.. note::
- Parameters *mdm_name* or *mdm_id* are mandatory for rename and modify virtual IP interfaces.
- Parameters *mdm_name* or *mdm_id* are not required while modifying performance profile.
- - For change MDM cluster ownership operation, only changed as True will be returned and for idempotency case MDM cluster details will be returned.
+ - For change MDM cluster ownership operation, only changed as true will be returned and for idempotency case MDM cluster details will be returned.
- Reinstall all SDC after changing ownership to some newly added MDM.
- To add manager standby MDM, MDM package must be installed with manager role.
- The *check_mode* is supported.
@@ -267,7 +267,7 @@ Examples
validate_certs: "{{validate_certs}}"
port: "{{port}}"
mdm_name: "mdm_2"
- is_primary: True
+ is_primary: true
state: "present"
- name: Modify performance profile
@@ -300,7 +300,7 @@ Examples
port: "{{port}}"
mdm_name: "mdm_1"
virtual_ip_interface:
- - "ens224"
+ - "ens224"
state: "present"
- name: Clear virtual IP interface of the MDM
@@ -311,7 +311,7 @@ Examples
validate_certs: "{{validate_certs}}"
port: "{{port}}"
mdm_name: "mdm_1"
- clear_interfaces: True
+ clear_interfaces: true
state: "present"
diff --git a/ansible_collections/dellemc/powerflex/docs/modules/protection_domain.rst b/ansible_collections/dellemc/powerflex/docs/modules/protection_domain.rst
index ca47cb518..84c640ef4 100644
--- a/ansible_collections/dellemc/powerflex/docs/modules/protection_domain.rst
+++ b/ansible_collections/dellemc/powerflex/docs/modules/protection_domain.rst
@@ -20,9 +20,9 @@ Requirements
------------
The below requirements are needed on the host that executes this module.
-- A Dell PowerFlex storage system version 3.5 or later.
-- Ansible-core 2.12 or later.
-- PyPowerFlex 1.6.0.
+- A Dell PowerFlex storage system version 3.6 or later.
+- Ansible-core 2.14 or later.
+- PyPowerFlex 1.9.0.
- Python 3.9, 3.10 or 3.11.
diff --git a/ansible_collections/dellemc/powerflex/docs/modules/replication_consistency_group.rst b/ansible_collections/dellemc/powerflex/docs/modules/replication_consistency_group.rst
index ceaca0d39..76d959026 100644
--- a/ansible_collections/dellemc/powerflex/docs/modules/replication_consistency_group.rst
+++ b/ansible_collections/dellemc/powerflex/docs/modules/replication_consistency_group.rst
@@ -12,7 +12,7 @@ replication_consistency_group -- Manage replication consistency groups on Dell P
Synopsis
--------
-Managing replication consistency groups on PowerFlex storage system includes getting details, creating, modifying, creating snapshots, pause, resume, freeze, unfreeze, activate, inactivate and deleting a replication consistency group.
+Managing replication consistency groups on PowerFlex storage system includes getting details, creating, modifying, creating snapshots, pause, resume, freeze, unfreeze, activate, failover, reverse, restore, sync, switchover, inactivate and deleting a replication consistency group.
@@ -20,9 +20,9 @@ Requirements
------------
The below requirements are needed on the host that executes this module.
-- A Dell PowerFlex storage system version 3.5 or later.
-- Ansible-core 2.12 or later.
-- PyPowerFlex 1.6.0.
+- A Dell PowerFlex storage system version 3.6 or later.
+- Ansible-core 2.14 or later.
+- PyPowerFlex 1.9.0.
- Python 3.9, 3.10 or 3.11.
@@ -73,15 +73,41 @@ Parameters
pause (optional, bool, None)
Pause or resume the RCG.
+ This parameter is deprecated. Use rcg_state instead.
+
+
+ rcg_state (optional, str, None)
+ Specify an action for RCG.
+
+ Failover the RCG.
+
+ Reverse the RCG.
+
+ Restore the RCG.
+
+ Switchover the RCG.
+
+ Pause or resume the RCG.
+
+ Freeze or unfreeze the RCG.
+
+ Synchronize the RCG.
+
+
+ force (optional, bool, None)
+ Force switchover the RCG.
+
freeze (optional, bool, None)
Freeze or unfreeze the RCG.
+ This parameter is deprecated. Use rcg_state instead.
+
pause_mode (optional, str, None)
Pause mode.
- It is required if pause is set as True.
+ It is required if pause is set as true.
target_volume_access_mode (optional, str, None)
@@ -187,7 +213,7 @@ Notes
- The *check_mode* is supported.
- Idempotency is not supported for create snapshot operation.
- There is a delay in reflection of final state of RCG after few update operations on RCG.
- - In 3.6 and above, the replication consistency group will return back to consistent mode on changing to inconsistent mode if consistence barrier arrives. Hence idempotency on setting to inconsistent mode will return changed as True.
+ - In 3.6 and above, the replication consistency group will return back to consistent mode on changing to inconsistent mode if consistence barrier arrives. Hence idempotency on setting to inconsistent mode will return changed as true.
- The modules present in the collection named as 'dellemc.powerflex' are built to support the Dell PowerFlex storage platform.
@@ -217,7 +243,7 @@ Examples
validate_certs: "{{validate_certs}}"
port: "{{port}}"
rcg_id: "{{rcg_id}}"
- create_snapshot: True
+ create_snapshot: true
state: "present"
- name: Create a replication consistency group
@@ -250,7 +276,7 @@ Examples
rpo: 60
target_volume_access_mode: "ReadOnly"
activity_mode: "Inactive"
- is_consistent: True
+ is_consistent: true
- name: Rename replication consistency group
dellemc.powerflex.replication_consistency_group:
@@ -270,7 +296,7 @@ Examples
validate_certs: "{{validate_certs}}"
port: "{{port}}"
rcg_name: "rcg_test"
- pause: True
+ rcg_state: "pause"
pause_mode: "StopDataTransfer"
- name: Resume replication consistency group
@@ -281,7 +307,7 @@ Examples
validate_certs: "{{validate_certs}}"
port: "{{port}}"
rcg_name: "rcg_test"
- pause: False
+ rcg_state: "resume"
- name: Freeze replication consistency group
dellemc.powerflex.replication_consistency_group:
@@ -291,7 +317,7 @@ Examples
validate_certs: "{{validate_certs}}"
port: "{{port}}"
rcg_name: "rcg_test"
- freeze: True
+ rcg_state: "freeze"
- name: UnFreeze replication consistency group
dellemc.powerflex.replication_consistency_group:
@@ -301,7 +327,57 @@ Examples
validate_certs: "{{validate_certs}}"
port: "{{port}}"
rcg_name: "rcg_test"
- freeze: False
+ rcg_state: "unfreeze"
+
+ - name: Failover replication consistency group
+ dellemc.powerflex.replication_consistency_group:
+ hostname: "{{hostname}}"
+ username: "{{username}}"
+ password: "{{password}}"
+ validate_certs: "{{validate_certs}}"
+ port: "{{port}}"
+ rcg_name: "rcg_test"
+ rcg_state: "failover"
+
+ - name: Reverse replication consistency group
+ dellemc.powerflex.replication_consistency_group:
+ hostname: "{{hostname}}"
+ username: "{{username}}"
+ password: "{{password}}"
+ validate_certs: "{{validate_certs}}"
+ port: "{{port}}"
+ rcg_name: "rcg_test"
+ rcg_state: "reverse"
+
+ - name: Restore replication consistency group
+ dellemc.powerflex.replication_consistency_group:
+ hostname: "{{hostname}}"
+ username: "{{username}}"
+ password: "{{password}}"
+ validate_certs: "{{validate_certs}}"
+ port: "{{port}}"
+ rcg_name: "rcg_test"
+ rcg_state: "restore"
+
+ - name: Switchover replication consistency group
+ dellemc.powerflex.replication_consistency_group:
+ hostname: "{{hostname}}"
+ username: "{{username}}"
+ password: "{{password}}"
+ validate_certs: "{{validate_certs}}"
+ port: "{{port}}"
+ rcg_name: "rcg_test"
+ rcg_state: "switchover"
+
+ - name: Synchronize replication consistency group
+ dellemc.powerflex.replication_consistency_group:
+ hostname: "{{hostname}}"
+ username: "{{username}}"
+ password: "{{password}}"
+ validate_certs: "{{validate_certs}}"
+ port: "{{port}}"
+ rcg_name: "rcg_test"
+ rcg_state: "sync"
- name: Delete replication consistency group
dellemc.powerflex.replication_consistency_group:
diff --git a/ansible_collections/dellemc/powerflex/docs/modules/replication_pair.rst b/ansible_collections/dellemc/powerflex/docs/modules/replication_pair.rst
index c83241cd3..254a2eb1d 100644
--- a/ansible_collections/dellemc/powerflex/docs/modules/replication_pair.rst
+++ b/ansible_collections/dellemc/powerflex/docs/modules/replication_pair.rst
@@ -20,9 +20,9 @@ Requirements
------------
The below requirements are needed on the host that executes this module.
-- A Dell PowerFlex storage system version 3.5 or later.
-- Ansible-core 2.12 or later.
-- PyPowerFlex 1.6.0.
+- A Dell PowerFlex storage system version 3.6 or later.
+- Ansible-core 2.14 or later.
+- PyPowerFlex 1.9.0.
- Python 3.9, 3.10 or 3.11.
@@ -174,8 +174,8 @@ Notes
.. note::
- The *check_mode* is supported.
+ - In 4.0 the creation of replication pair fails when *copy_type* is specified as ``OfflineCopy``.
- The modules present in the collection named as 'dellemc.powerflex' are built to support the Dell PowerFlex storage platform.
- - In 4.0 the creation of replication pair fails when I(copy_type) is specified as C(OfflineCopy).
@@ -186,7 +186,6 @@ Examples
.. code-block:: yaml+jinja
-
- name: Get replication pair details
dellemc.powerflex.replication_pair:
hostname: "{{hostname}}"
@@ -224,11 +223,11 @@ Examples
copy_type: "OnlineCopy"
name: "pair1"
remote_peer:
- hostname: "{{hostname}}"
- username: "{{username}}"
- password: "{{password}}"
- validate_certs: "{{validate_certs}}"
- port: "{{port}}"
+ hostname: "{{hostname}}"
+ username: "{{username}}"
+ password: "{{password}}"
+ validate_certs: "{{validate_certs}}"
+ port: "{{port}}"
- name: Pause replication pair
dellemc.powerflex.replication_pair:
@@ -238,7 +237,7 @@ Examples
validate_certs: "{{validate_certs}}"
port: "{{port}}"
pair_name: "pair1"
- pause: True
+ pause: true
- name: Resume replication pair
dellemc.powerflex.replication_pair:
@@ -248,7 +247,7 @@ Examples
validate_certs: "{{validate_certs}}"
port: "{{port}}"
pair_name: "pair1"
- pause: False
+ pause: false
- name: Delete replication pair
dellemc.powerflex.replication_pair:
diff --git a/ansible_collections/dellemc/powerflex/docs/modules/sdc.rst b/ansible_collections/dellemc/powerflex/docs/modules/sdc.rst
index 585267891..ad375ebc7 100644
--- a/ansible_collections/dellemc/powerflex/docs/modules/sdc.rst
+++ b/ansible_collections/dellemc/powerflex/docs/modules/sdc.rst
@@ -20,9 +20,9 @@ Requirements
------------
The below requirements are needed on the host that executes this module.
-- A Dell PowerFlex storage system version 3.5 or later.
-- Ansible-core 2.12 or later.
-- PyPowerFlex 1.6.0.
+- A Dell PowerFlex storage system version 3.6 or later.
+- Ansible-core 2.14 or later.
+- PyPowerFlex 1.9.0.
- Python 3.9, 3.10 or 3.11.
@@ -58,6 +58,12 @@ Parameters
New name of the SDC. Used to rename the SDC.
+ performance_profile (optional, str, None)
+ Define the performance profile as *Compact* or *HighPerformance*.
+
+ The high performance profile configures a predefined set of parameters for very high performance use cases.
+
+
state (True, str, None)
State of the SDC.
@@ -130,6 +136,25 @@ Examples
sdc_new_name: "centos_sdc_renamed"
state: "present"
+ - name: Modify performance profile of SDC using SDC name
+ dellemc.powerflex.sdc:
+ hostname: "{{hostname}}"
+ username: "{{username}}"
+ password: "{{password}}"
+ validate_certs: "{{validate_certs}}"
+ sdc_name: "centos_sdc"
+ performance_profile: "Compact"
+ state: "present"
+
+ - name: Remove SDC using SDC name
+ dellemc.powerflex.sdc:
+ hostname: "{{hostname}}"
+ username: "{{username}}"
+ password: "{{password}}"
+ validate_certs: "{{validate_certs}}"
+ sdc_name: "centos_sdc"
+ state: "absent"
+
Return Values
diff --git a/ansible_collections/dellemc/powerflex/docs/modules/sds.rst b/ansible_collections/dellemc/powerflex/docs/modules/sds.rst
index 81a1b3908..f5c29516f 100644
--- a/ansible_collections/dellemc/powerflex/docs/modules/sds.rst
+++ b/ansible_collections/dellemc/powerflex/docs/modules/sds.rst
@@ -20,9 +20,9 @@ Requirements
------------
The below requirements are needed on the host that executes this module.
-- A Dell PowerFlex storage system version 3.5 or later.
-- Ansible-core 2.12 or later.
-- PyPowerFlex 1.6.0.
+- A Dell PowerFlex storage system version 3.6 or later.
+- Ansible-core 2.14 or later.
+- PyPowerFlex 1.9.0.
- Python 3.9, 3.10 or 3.11.
@@ -109,6 +109,18 @@ Parameters
Default value by API is ``HighPerformance``.
+ fault_set_name (optional, str, None)
+ Name of the fault set.
+
+ Mutually exclusive with *fault_set_id*.
+
+
+ fault_set_id (optional, str, None)
+ Unique identifier of the fault set.
+
+ Mutually exclusive with *fault_set_name*.
+
+
state (True, str, None)
State of the SDS.
@@ -156,7 +168,7 @@ Notes
- There can be 1 or more IPs with role 'sdcOnly'.
- There must be only 1 IP with SDS role (either with role 'all' or 'sdsOnly').
- SDS can be created with RF cache disabled, but, be aware that the RF cache is not always updated. In this case, the user should re-try the operation.
- - The *check_mode* is not supported.
+ - The *check_mode* is supported.
- The modules present in the collection named as 'dellemc.powerflex' are built to support the Dell PowerFlex storage platform.
@@ -192,12 +204,14 @@ Examples
port: "{{port}}"
sds_name: "node1"
protection_domain_name: "domain1"
+ fault_set_name: "faultset1"
sds_ip_list:
- ip: "198.10.xxx.xxx"
role: "sdcOnly"
sds_ip_state: "present-in-sds"
rmcache_enabled: true
rmcache_size: 128
+ fault_set_name: "{{fault_set_name}}"
performance_profile: "HighPerformance"
state: "present"
@@ -332,6 +346,9 @@ sds_details (When SDS exists, dict, {'authenticationError': 'None', 'certificate
Fault set ID.
+ faultSetName (, str, )
+ Name of the Fault set.
+
fglMetadataCacheSize (, int, )
FGL metadata cache size.
@@ -513,4 +530,5 @@ Authors
~~~~~~~
- Rajshree Khare (@khareRajshree) <ansible.team@dell.com>
+- Trisha Datta (@trisha-dell) <ansible.team@dell.com>
diff --git a/ansible_collections/dellemc/powerflex/docs/modules/snapshot.rst b/ansible_collections/dellemc/powerflex/docs/modules/snapshot.rst
index dafed946d..052453ad2 100644
--- a/ansible_collections/dellemc/powerflex/docs/modules/snapshot.rst
+++ b/ansible_collections/dellemc/powerflex/docs/modules/snapshot.rst
@@ -20,9 +20,9 @@ Requirements
------------
The below requirements are needed on the host that executes this module.
-- A Dell PowerFlex storage system version 3.5 or later.
-- Ansible-core 2.12 or later.
-- PyPowerFlex 1.6.0.
+- A Dell PowerFlex storage system version 3.6 or later.
+- Ansible-core 2.14 or later.
+- PyPowerFlex 1.9.0.
- Python 3.9, 3.10 or 3.11.
@@ -206,7 +206,7 @@ Examples
validate_certs: "{{validate_certs}}"
snapshot_name: "ansible_snapshot"
vol_name: "ansible_volume"
- read_only: False
+ read_only: false
desired_retention: 2
state: "present"
@@ -227,9 +227,9 @@ Examples
validate_certs: "{{validate_certs}}"
snapshot_id: "fe6cb28200000007"
sdc:
- - sdc_ip: "198.10.xxx.xxx"
- - sdc_id: "663ac0d200000001"
- allow_multiple_mappings: True
+ - sdc_ip: "198.10.xxx.xxx"
+ - sdc_id: "663ac0d200000001"
+ allow_multiple_mappings: true
sdc_state: "mapped"
state: "present"
@@ -241,13 +241,13 @@ Examples
validate_certs: "{{validate_certs}}"
snapshot_id: "fe6cb28200000007"
sdc:
- - sdc_ip: "198.10.xxx.xxx"
- iops_limit: 11
- bandwidth_limit: 4096
- - sdc_id: "663ac0d200000001"
- iops_limit: 20
- bandwidth_limit: 2048
- allow_multiple_mappings: True
+ - sdc_ip: "198.10.xxx.xxx"
+ iops_limit: 11
+ bandwidth_limit: 4096
+ - sdc_id: "663ac0d200000001"
+ iops_limit: 20
+ bandwidth_limit: 2048
+ allow_multiple_mappings: true
sdc_state: "mapped"
state: "present"
diff --git a/ansible_collections/dellemc/powerflex/docs/modules/snapshot_policy.rst b/ansible_collections/dellemc/powerflex/docs/modules/snapshot_policy.rst
new file mode 100644
index 000000000..deab7f050
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/docs/modules/snapshot_policy.rst
@@ -0,0 +1,414 @@
+.. _snapshot_policy_module:
+
+
+snapshot_policy -- Manage snapshot policies on Dell PowerFlex
+=============================================================
+
+.. contents::
+ :local:
+ :depth: 1
+
+
+Synopsis
+--------
+
+Managing snapshot policies on PowerFlex storage system includes creating, getting details, modifying attributes, adding a source volume, removing a source volume and deleting a snapshot policy.
+
+
+
+Requirements
+------------
+The below requirements are needed on the host that executes this module.
+
+- A Dell PowerFlex storage system version 3.6 or later.
+- Ansible-core 2.14 or later.
+- PyPowerFlex 1.8.0.
+- Python 3.9, 3.10 or 3.11.
+
+
+
+Parameters
+----------
+
+ snapshot_policy_name (optional, str, None)
+ The name of the snapshot policy.
+
+ It is unique across the PowerFlex array.
+
+ Mutually exclusive with *snapshot_policy_id*.
+
+
+ snapshot_policy_id (optional, str, None)
+ The unique identifier of the snapshot policy.
+
+ Except create operation, all other operations can be performed using *snapshot_policy_id*.
+
+ Mutually exclusive with *snapshot_policy_name*.
+
+
+ auto_snapshot_creation_cadence (optional, dict, None)
+ The auto snapshot creation cadence of the snapshot policy.
+
+
+ time (optional, int, None)
+ The time between creation of two snapshots.
+
+
+ unit (optional, str, Minute)
+ The unit of the auto snapshot creation cadence.
+
+
+
+ num_of_retained_snapshots_per_level (optional, list, None)
+ Number of retained snapshots per level.
+
+
+ new_name (optional, str, None)
+ New name of the snapshot policy.
+
+
+ access_mode (optional, str, None)
+ Access mode of the snapshot policy.
+
+
+ secure_snapshots (optional, bool, None)
+ Whether to secure snapshots or not.
+
+ Used only in the create operation.
+
+
+ source_volume (optional, list, None)
+ The source volume details to be added or removed.
+
+
+ id (optional, str, None)
+ The unique identifier of the source volume to be added or removed.
+
+ Mutually exclusive with *name*.
+
+
+ name (optional, str, None)
+ The name of the source volume to be added or removed.
+
+ Mutually exclusive with *id*.
+
+
+ auto_snap_removal_action (optional, str, None)
+ Ways to handle the snapshots created by the policy (auto snapshots).
+
+ Must be provided when *state* is set to ``'absent'``.
+
+
+ detach_locked_auto_snapshots (optional, bool, None)
+ Whether to detach the locked auto snapshots during removal of source volume.
+
+
+ state (optional, str, present)
+ The state of the source volume.
+
+ When ``present``, source volume will be added to the snapshot policy.
+
+ When ``absent``, source volume will be removed from the snapshot policy.
+
+
+
+ pause (optional, bool, None)
+ Whether to pause or resume the snapshot policy.
+
+
+ state (optional, str, present)
+ State of the snapshot policy.
+
+
+ hostname (True, str, None)
+ IP or FQDN of the PowerFlex host.
+
+
+ username (True, str, None)
+ The username of the PowerFlex host.
+
+
+ password (True, str, None)
+ The password of the PowerFlex host.
+
+
+ validate_certs (optional, bool, True)
+ Boolean variable to specify whether or not to validate SSL certificate.
+
+ ``true`` - Indicates that the SSL certificate should be verified.
+
+ ``false`` - Indicates that the SSL certificate should not be verified.
+
+
+ port (optional, int, 443)
+ Port number through which communication happens with PowerFlex host.
+
+
+ timeout (False, int, 120)
+ Time after which connection will get terminated.
+
+ It is to be mentioned in seconds.
+
+
+
+
+
+Notes
+-----
+
+.. note::
+ - The *check_mode* is supported.
+ - The modules present in the collection named as 'dellemc.powerflex' are built to support the Dell PowerFlex storage platform.
+
+
+
+
+Examples
+--------
+
+.. code-block:: yaml+jinja
+
+
+ - name: Create a snapshot policy
+ dellemc.powerflex.snapshot_policy:
+ hostname: "{{hostname}}"
+ username: "{{username}}"
+ password: "{{password}}"
+ validate_certs: "{{validate_certs}}"
+ snapshot_policy_name: "snapshot_policy_name_1"
+ access_mode: "READ_WRITE"
+ secure_snapshots: false
+ auto_snapshot_creation_cadence:
+ time: 1
+ unit: "Hour"
+ num_of_retained_snapshots_per_level:
+ - 20
+ state: "present"
+
+ - name: Get snapshot policy details using name
+ dellemc.powerflex.snapshot_policy:
+ hostname: "{{hostname}}"
+ username: "{{username}}"
+ password: "{{password}}"
+ validate_certs: "{{validate_certs}}"
+ snapshot_policy_name: "snapshot_policy_name_1"
+
+ - name: Get snapshot policy details using id
+ dellemc.powerflex.snapshot_policy:
+ hostname: "{{hostname}}"
+ username: "{{username}}"
+ password: "{{password}}"
+ validate_certs: "{{validate_certs}}"
+ snapshot_policy_id: "snapshot_policy_id_1"
+
+ - name: Modify a snapshot policy
+ dellemc.powerflex.snapshot_policy:
+ hostname: "{{hostname}}"
+ username: "{{username}}"
+ password: "{{password}}"
+ validate_certs: "{{validate_certs}}"
+ snapshot_policy_name: "snapshot_policy_name_1"
+ auto_snapshot_creation_cadence:
+ time: 2
+ unit: "Hour"
+ num_of_retained_snapshots_per_level:
+ - 40
+
+ - name: Rename a snapshot policy
+ dellemc.powerflex.snapshot_policy:
+ hostname: "{{hostname}}"
+ username: "{{username}}"
+ password: "{{password}}"
+ validate_certs: "{{validate_certs}}"
+ snapshot_policy_name: "snapshot_policy_name_1"
+ new_name: "snapshot_policy_name_1_new"
+
+ - name: Add source volume
+ dellemc.powerflex.snapshot_policy:
+ hostname: "{{hostname}}"
+ username: "{{username}}"
+ password: "{{password}}"
+ validate_certs: "{{validate_certs}}"
+ snapshot_policy_name: "snapshot_policy_name_1"
+ source_volume:
+ - name: "source_volume_name_1"
+ - id: "source_volume_id_2"
+ state: "present"
+
+ - name: Remove source volume
+ dellemc.powerflex.snapshot_policy:
+ hostname: "{{hostname}}"
+ username: "{{username}}"
+ password: "{{password}}"
+ validate_certs: "{{validate_certs}}"
+ snapshot_policy_name: "{{snapshot_policy_name}}"
+ source_volume:
+ - name: "source_volume_name_1"
+ auto_snap_removal_action: 'Remove'
+ state: "absent"
+ - id: "source_volume_id_2"
+ auto_snap_removal_action: 'Remove'
+ detach_locked_auto_snapshots: true
+ state: "absent"
+
+ - name: Pause a snapshot policy
+ dellemc.powerflex.snapshot_policy:
+ hostname: "{{hostname}}"
+ username: "{{username}}"
+ password: "{{password}}"
+ validate_certs: "{{validate_certs}}"
+ snapshot_policy_name: "{{snapshot_policy_name}}"
+ pause: true
+
+ - name: Resume a snapshot policy
+ dellemc.powerflex.snapshot_policy:
+ hostname: "{{hostname}}"
+ username: "{{username}}"
+ password: "{{password}}"
+ validate_certs: "{{validate_certs}}"
+ snapshot_policy_name: "{{snapshot_policy_name}}"
+ pause: false
+
+ - name: Delete a snapshot policy
+ dellemc.powerflex.snapshot_policy:
+ hostname: "{{hostname}}"
+ username: "{{username}}"
+ password: "{{password}}"
+ validate_certs: "{{validate_certs}}"
+ snapshot_policy_name: "snapshot_policy_name"
+ state: "absent"
+
+
+
+Return Values
+-------------
+
+changed (always, bool, false)
+ Whether or not the resource has changed.
+
+
+snapshot_policy_details (When snapshot policy exists, dict, {'autoSnapshotCreationCadenceInMin': 120, 'id': '15ae842800000004', 'lastAutoSnapshotCreationFailureReason': 'NR', 'lastAutoSnapshotFailureInFirstLevel': False, 'links': [{'href': '/api/instances/SnapshotPolicy::15ae842800000004', 'rel': 'self'}, {'href': '/api/instances/SnapshotPolicy::15ae842800000004/relationships/Statistics', 'rel': '/api/SnapshotPolicy/relationship/Statistics'}, {'href': '/api/instances/SnapshotPolicy::15ae842800000004/relationships/SourceVolume', 'rel': '/api/SnapshotPolicy/relationship/SourceVolume'}, {'href': '/api/instances/SnapshotPolicy::15ae842800000004/relationships/AutoSnapshotVolume', 'rel': '/api/SnapshotPolicy/relationship/AutoSnapshotVolume'}, {'href': '/api/instances/System::0e7a082862fedf0f', 'rel': '/api/parent/relationship/systemId'}], 'maxVTreeAutoSnapshots': 40, 'name': 'Sample_snapshot_policy_1', 'nextAutoSnapshotCreationTime': 1683709201, 'numOfAutoSnapshots': 0, 'numOfCreationFailures': 0, 'numOfExpiredButLockedSnapshots': 0, 'numOfLockedSnapshots': 0, 'numOfRetainedSnapshotsPerLevel': [40], 'numOfSourceVolumes': 0, 'secureSnapshots': False, 'snapshotAccessMode': 'ReadWrite', 'snapshotPolicyState': 'Active', 'statistics': {'autoSnapshotVolIds': [], 'expiredButLockedSnapshotsIds': [], 'numOfAutoSnapshots': 0, 'numOfExpiredButLockedSnapshots': 0, 'numOfSrcVols': 0, 'srcVolIds': []}, 'systemId': '0e7a082862fedf0f', 'timeOfLastAutoSnapshot': 0, 'timeOfLastAutoSnapshotCreationFailure': 0})
+ Details of the snapshot policy.
+
+
+ autoSnapshotCreationCadenceInMin (, int, )
+ The snapshot rule of the snapshot policy.
+
+
+ id (, str, )
+ The ID of the snapshot policy.
+
+
+ lastAutoSnapshotCreationFailureReason (, str, )
+ The reason for the failure of last auto snapshot creation .
+
+
+ name (, str, )
+ Name of the snapshot policy.
+
+
+ lastAutoSnapshotFailureInFirstLevel (, bool, )
+ Whether the last auto snapshot in first level failed.
+
+
+ maxVTreeAutoSnapshots (, int, )
+ Maximum number of VTree auto snapshots.
+
+
+ nextAutoSnapshotCreationTime (, int, )
+ The time of creation of the next auto snapshot.
+
+
+ numOfAutoSnapshots (, int, )
+ Number of auto snapshots.
+
+
+ numOfCreationFailures (, int, )
+ Number of creation failures.
+
+
+ numOfExpiredButLockedSnapshots (, int, )
+ Number of expired but locked snapshots.
+
+
+ numOfLockedSnapshots (, int, )
+ Number of locked snapshots.
+
+
+ numOfRetainedSnapshotsPerLevel (, list, )
+ Number of snapshots retained per level
+
+
+ numOfSourceVolumes (, int, )
+ Number of source volumes.
+
+
+ secureSnapshots (, bool, )
+ Whether the snapshots are secured.
+
+
+ snapshotAccessMode (, str, )
+ Access mode of the snapshots.
+
+
+ snapshotPolicyState (, str, )
+ State of the snapshot policy.
+
+
+ systemId (, str, )
+ Unique identifier of the PowerFlex system.
+
+
+ timeOfLastAutoSnapshot (, str, )
+ Time of the last auto snapshot creation.
+
+
+ timeOfLastAutoSnapshotCreationFailure (, str, )
+ Time of the failure of the last auto snapshot creation.
+
+
+ statistics (, dict, )
+ Statistics details of the snapshot policy.
+
+
+ autoSnapshotVolIds (, list, )
+ Volume Ids of all the auto snapshots.
+
+
+ expiredButLockedSnapshotsIds (, list, )
+ Ids of expired but locked snapshots.
+
+
+ numOfAutoSnapshots (, int, )
+ Number of auto snapshots.
+
+
+ numOfExpiredButLockedSnapshots (, int, )
+ Number of expired but locked snapshots.
+
+
+ numOfSrcVols (, int, )
+ Number of source volumes.
+
+
+ srcVolIds (, list, )
+ Ids of the source volumes.
+
+
+
+
+
+
+
+Status
+------
+
+
+
+
+
+Authors
+~~~~~~~
+
+- Trisha Datta (@trisha-dell) <ansible.team@dell.com>
+
diff --git a/ansible_collections/dellemc/powerflex/docs/modules/storagepool.rst b/ansible_collections/dellemc/powerflex/docs/modules/storagepool.rst
index 9037ef73c..76d94966d 100644
--- a/ansible_collections/dellemc/powerflex/docs/modules/storagepool.rst
+++ b/ansible_collections/dellemc/powerflex/docs/modules/storagepool.rst
@@ -20,9 +20,9 @@ Requirements
------------
The below requirements are needed on the host that executes this module.
-- A Dell PowerFlex storage system version 3.5 or later.
-- Ansible-core 2.12 or later.
-- PyPowerFlex 1.6.0.
+- A Dell PowerFlex storage system version 3.6 or later.
+- Ansible-core 2.14 or later.
+- PyPowerFlex 1.9.0.
- Python 3.9, 3.10 or 3.11.
@@ -134,7 +134,6 @@ Examples
.. code-block:: yaml+jinja
-
- name: Get the details of storage pool by name
dellemc.powerflex.storagepool:
hostname: "{{hostname}}"
@@ -173,8 +172,8 @@ Examples
validate_certs: "{{validate_certs}}"
storage_pool_name: "ansible_test_pool"
protection_domain_id: "1c957da800000000"
- use_rmcache: True
- use_rfcache: True
+ use_rmcache: true
+ use_rfcache: true
state: "present"
- name: Rename storage pool by id
diff --git a/ansible_collections/dellemc/powerflex/docs/modules/volume.rst b/ansible_collections/dellemc/powerflex/docs/modules/volume.rst
index 10cf8de84..f3345a6d1 100644
--- a/ansible_collections/dellemc/powerflex/docs/modules/volume.rst
+++ b/ansible_collections/dellemc/powerflex/docs/modules/volume.rst
@@ -22,9 +22,9 @@ Requirements
------------
The below requirements are needed on the host that executes this module.
-- A Dell PowerFlex storage system version 3.5 or later.
-- Ansible-core 2.12 or later.
-- PyPowerFlex 1.6.0.
+- A Dell PowerFlex storage system version 3.6 or later.
+- Ansible-core 2.14 or later.
+- PyPowerFlex 1.9.0.
- Python 3.9, 3.10 or 3.11.
@@ -133,7 +133,7 @@ Parameters
allow_multiple_mappings (optional, bool, None)
Specifies whether to allow or not allow multiple mappings.
- If the volume is mapped to one SDC then for every new mapping *allow_multiple_mappings* has to be passed as True.
+ If the volume is mapped to one SDC then for every new mapping *allow_multiple_mappings* has to be passed as true.
sdc (optional, list, None)
@@ -188,13 +188,13 @@ Parameters
delete_snapshots (optional, bool, None)
- If ``True``, the volume and all its dependent snapshots will be deleted.
+ If ``true``, the volume and all its dependent snapshots will be deleted.
- If ``False``, only the volume will be deleted.
+ If ``false``, only the volume will be deleted.
It can be specified only when the *state* is ``absent``.
- It defaults to ``False``, if not specified.
+ It defaults to ``false``, if not specified.
state (True, str, None)
@@ -262,7 +262,7 @@ Examples
protection_domain_name: "pd_1"
vol_type: "THICK_PROVISIONED"
compression_type: "NORMAL"
- use_rmcache: True
+ use_rmcache: true
size: 16
state: "present"
@@ -274,7 +274,7 @@ Examples
validate_certs: "{{validate_certs}}"
port: "{{port}}"
vol_name: "sample_volume"
- allow_multiple_mappings: True
+ allow_multiple_mappings: true
sdc:
- sdc_id: "92A304DB-EFD7-44DF-A07E-D78134CC9764"
access_mode: "READ_WRITE"
@@ -310,7 +310,7 @@ Examples
iops_limit: 20
- sdc_ip: "198.10.xxx.xxx"
access_mode: "READ_ONLY"
- allow_multiple_mappings: True
+ allow_multiple_mappings: true
sdc_state: "mapped"
state: "present"
@@ -345,7 +345,7 @@ Examples
validate_certs: "{{validate_certs}}"
port: "{{port}}"
vol_name: "sample_volume"
- delete_snapshots: False
+ delete_snapshots: false
state: "absent"
- name: Delete the Volume and all its dependent snapshots
@@ -356,7 +356,7 @@ Examples
validate_certs: "{{validate_certs}}"
port: "{{port}}"
vol_name: "sample_volume"
- delete_snapshots: True
+ delete_snapshots: true
state: "absent"
diff --git a/ansible_collections/dellemc/powerflex/meta/runtime.yml b/ansible_collections/dellemc/powerflex/meta/runtime.yml
index 0e8263eda..6fa701d3a 100644
--- a/ansible_collections/dellemc/powerflex/meta/runtime.yml
+++ b/ansible_collections/dellemc/powerflex/meta/runtime.yml
@@ -1,5 +1,5 @@
---
-requires_ansible: ">=2.12"
+requires_ansible: ">=2.14.0"
plugin_routing:
modules:
dellemc_powerflex_gatherfacts:
diff --git a/ansible_collections/dellemc/powerflex/playbooks/modules/device.yml b/ansible_collections/dellemc/powerflex/playbooks/modules/device.yml
new file mode 100644
index 000000000..34bbb12b5
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/playbooks/modules/device.yml
@@ -0,0 +1,90 @@
+---
+- name: Device Operations
+ hosts: localhost
+ connection: local
+ gather_facts: false
+ vars:
+ hostname: 'x.x.x.x'
+ username: 'admin'
+ password: 'Password'
+ validate_certs: false
+
+ tasks:
+ - name: Add a device
+ dellemc.powerflex.device:
+ hostname: "{{ hostname }}"
+ username: "{{ username }}"
+ password: "{{ password }}"
+ validate_certs: "{{ validate_certs }}"
+ current_pathname: "/dev/sdb"
+ sds_name: "node1"
+ media_type: "HDD"
+ device_name: "device2"
+ storage_pool_name: "pool1"
+ protection_domain_name: "domain1"
+ external_acceleration_type: "ReadAndWrite"
+ state: "present"
+
+ - name: Add a device with force flag
+ dellemc.powerflex.device:
+ hostname: "{{ hostname }}"
+ username: "{{ username }}"
+ password: "{{ password }}"
+ validate_certs: "{{ validate_certs }}"
+ current_pathname: "/dev/sdb"
+ sds_name: "node1"
+ media_type: "HDD"
+ device_name: "device2"
+ storage_pool_name: "pool1"
+ protection_domain_name: "domain1"
+ external_acceleration_type: "ReadAndWrite"
+ force: true
+ state: "present"
+
+ - name: Get device details using device_id
+ dellemc.powerflex.device:
+ hostname: "{{ hostname }}"
+ username: "{{ username }}"
+ password: "{{ password }}"
+ validate_certs: "{{ validate_certs }}"
+ device_id: "d7fe088900000000"
+ state: "present"
+
+ - name: Get device details using (current_pathname, sds_name)
+ dellemc.powerflex.device:
+ hostname: "{{ hostname }}"
+ username: "{{ username }}"
+ password: "{{ password }}"
+ validate_certs: "{{ validate_certs }}"
+ current_pathname: "/dev/sdb"
+ sds_name: "node0"
+ state: "present"
+
+ - name: Get device details using (current_pathname, sds_id)
+ dellemc.powerflex.device:
+ hostname: "{{ hostname }}"
+ username: "{{ username }}"
+ password: "{{ password }}"
+ validate_certs: "{{ validate_certs }}"
+ current_pathname: "/dev/sdb"
+ sds_id: "5717d71800000000"
+ state: "present"
+
+ - name: Remove a device using device_id
+ dellemc.powerflex.device:
+ hostname: "{{ hostname }}"
+ username: "{{ username }}"
+ password: "{{ password }}"
+ validate_certs: "{{ validate_certs }}"
+ device_id: "76eb7e2f00010000"
+ state: "absent"
+
+ - name: Remove a device using (current_pathname, sds_name)
+ dellemc.powerflex.device:
+ hostname: "{{ hostname }}"
+ username: "{{ username }}"
+ password: "{{ password }}"
+ validate_certs: "{{ validate_certs }}"
+ current_pathname: "/dev/sdb"
+ sds_name: "node1"
+ state: "absent"
diff --git a/ansible_collections/dellemc/powerflex/playbooks/modules/fault_set.yml b/ansible_collections/dellemc/powerflex/playbooks/modules/fault_set.yml
new file mode 100644
index 000000000..ee61b3733
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/playbooks/modules/fault_set.yml
@@ -0,0 +1,67 @@
+---
+- name: Fault set Operations
+ hosts: localhost
+ connection: local
+ gather_facts: false
+ vars:
+ hostname: 'x.x.x.x'
+ username: 'admin'
+ password: 'Password'
+ validate_certs: false
+ host_port: 443
+ fault_set_name: "sample_fault_set_name_1"
+ protection_domain_name: "Ansible-PD1"
+
+ tasks:
+ - name: Create fault set
+ dellemc.powerflex.fault_set:
+ hostname: "{{ hostname }}"
+ username: "{{ username }}"
+ password: "{{ password }}"
+ validate_certs: "{{ validate_certs }}"
+ port: "{{ host_port }}"
+ fault_set_name: "{{ fault_set_name }}"
+ protection_domain_name: "{{ protection_domain_name }}"
+
+ - name: Get fault set details using name and protection domain
+ register: result
+ dellemc.powerflex.fault_set:
+ hostname: "{{ hostname }}"
+ username: "{{ username }}"
+ password: "{{ password }}"
+ validate_certs: "{{ validate_certs }}"
+ port: "{{ host_port }}"
+ fault_set_name: "{{ fault_set_name }}"
+ protection_domain_name: "{{ protection_domain_name }}"
+
+ - name: Get fault set details using ID
+ dellemc.powerflex.fault_set:
+ hostname: "{{ hostname }}"
+ username: "{{ username }}"
+ password: "{{ password }}"
+ validate_certs: "{{ validate_certs }}"
+ port: "{{ host_port }}"
+ fault_set_id: "{{ result.fault_set_details.id }}"
+ state: "present"
+
+ - name: Rename fault set details using ID
+ dellemc.powerflex.fault_set:
+ hostname: "{{ hostname }}"
+ username: "{{ username }}"
+ password: "{{ password }}"
+ validate_certs: "{{ validate_certs }}"
+ port: "{{ host_port }}"
+ fault_set_id: "{{ result.fault_set_details.id }}"
+ fault_set_new_name: "fault_set_new_name"
+ state: "present"
+
+ - name: Delete fault set
+ dellemc.powerflex.fault_set:
+ hostname: "{{ hostname }}"
+ username: "{{ username }}"
+ password: "{{ password }}"
+ validate_certs: "{{ validate_certs }}"
+ port: "{{ host_port }}"
+ fault_set_name: "fault_set_new_name"
+ protection_domain_name: "{{ protection_domain_name }}"
+ state: "absent"
diff --git a/ansible_collections/dellemc/powerflex/playbooks/modules/info.yml b/ansible_collections/dellemc/powerflex/playbooks/modules/info.yml
new file mode 100644
index 000000000..b1a16785a
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/playbooks/modules/info.yml
@@ -0,0 +1,80 @@
+---
+- name: Gatherfacts Module Operations on PowerFlex
+ hosts: localhost
+ connection: local
+ vars:
+ hostname: 'x.x.x.x'
+ username: 'admin'
+ password: 'Password'
+ validate_certs: false
+ host_port: 443
+ tasks:
+ - name: Get detailed list of PowerFlex Entities
+ dellemc.powerflex.info:
+ hostname: "{{ hostname }}"
+ username: "{{ username }}"
+ password: "{{ password }}"
+ validate_certs: "{{ validate_certs }}"
+ gather_subset:
+ - vol
+ - storage_pool
+ - protection_domain
+ - sdc
+ - sds
+ - snapshot_policy
+ - device
+ - rcg
+ - replication_pair
+ - fault_set
+
+ - name: Get specific volume details
+ dellemc.powerflex.info:
+ hostname: "{{ hostname }}"
+ username: "{{ username }}"
+ password: "{{ password }}"
+ validate_certs: "{{ validate_certs }}"
+ gather_subset:
+ - vol
+ filters:
+ - filter_key: "name"
+ filter_operator: "equal"
+ filter_value: "ansible_test"
+
+ - name: Get specific fault set list
+ dellemc.powerflex.info:
+ hostname: "{{ hostname }}"
+ username: "{{ username }}"
+ password: "{{ password }}"
+ validate_certs: "{{ validate_certs }}"
+ gather_subset:
+ - fault_set
+ filters:
+ - filter_key: "name"
+ filter_operator: "equal"
+ filter_value: "node4"
+
+ - name: Get managed device, deployments, service templates
+ dellemc.powerflex.info:
+ hostname: "{{ hostname }}"
+ username: "{{ username }}"
+ password: "{{ password }}"
+ validate_certs: "{{ validate_certs }}"
+ gather_subset:
+ - managed_device
+ - deployment
+ - service_template
+
+ - name: Get deployment with filter, sort, pagination
+ dellemc.powerflex.info:
+ hostname: "{{ hostname }}"
+ username: "{{ username }}"
+ password: "{{ password }}"
+ validate_certs: "{{ validate_certs }}"
+ gather_subset:
+ - deployment
+ filters:
+ - filter_key: "name"
+ filter_operator: "contains"
+ filter_value: "partial"
+ sort: name
+ limit: 10
diff --git a/ansible_collections/dellemc/powerflex/playbooks/modules/mdm_cluster.yml b/ansible_collections/dellemc/powerflex/playbooks/modules/mdm_cluster.yml
new file mode 100644
index 000000000..bdc44bdfa
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/playbooks/modules/mdm_cluster.yml
@@ -0,0 +1,148 @@
+---
+- name: MDM cluster Operations
+ hosts: localhost
+ connection: local
+ gather_facts: false
+ vars:
+ hostname: 'x.x.x.x'
+ username: 'admin'
+ password: 'Password'
+ validate_certs: false
+ host_port: 443
+
+ tasks:
+ - name: Get MDM cluster
+ dellemc.powerflex.mdm_cluster:
+ hostname: "{{ hostname }}"
+ username: "{{ username }}"
+ password: "{{ password }}"
+ validate_certs: "{{ validate_certs }}"
+ port: "{{ host_port }}"
+ state: "present"
+
+ - name: Rename MDM cluster with check_mode
+ dellemc.powerflex.mdm_cluster:
+ hostname: "{{ hostname }}"
+ username: "{{ username }}"
+ password: "{{ password }}"
+ validate_certs: "{{ validate_certs }}"
+ port: "{{ host_port }}"
+ mdm_id: "1c13c3847c971201"
+ mdm_new_name: "node_renamed"
+ state: "present"
+ check_mode: true
+
+ - name: Rename MDM cluster
+ dellemc.powerflex.mdm_cluster:
+ hostname: "{{ hostname }}"
+ username: "{{ username }}"
+ password: "{{ password }}"
+ validate_certs: "{{ validate_certs }}"
+ port: "{{ host_port }}"
+ mdm_id: "1c13c3847c971201"
+ mdm_new_name: "node_renamed"
+ state: "present"
+
+ - name: Remove standby MDM
+ dellemc.powerflex.mdm_cluster:
+ hostname: "{{ hostname }}"
+ username: "{{ username }}"
+ password: "{{ password }}"
+ validate_certs: "{{ validate_certs }}"
+ port: "{{ host_port }}"
+ mdm_name: "mdm_node1"
+ state: "absent"
+
+ - name: Add standby MDM
+ dellemc.powerflex.mdm_cluster:
+ hostname: "{{ hostname }}"
+ username: "{{ username }}"
+ password: "{{ password }}"
+ validate_certs: "{{ validate_certs }}"
+ port: "{{ host_port }}"
+ mdm_name: "mdm_standby_node"
+ standby_mdm:
+ mdm_ips:
+ - "10.x.x.x"
+ role: "TieBreaker"
+ management_ips:
+ - "10.x.x.x"
+ port: 9011
+ state: "present"
+
+ - name: Change MDM cluster owner
+ dellemc.powerflex.mdm_cluster:
+ hostname: "{{ hostname }}"
+ username: "{{ username }}"
+ password: "{{ password }}"
+ validate_certs: "{{ validate_certs }}"
+ port: "{{ host_port }}"
+ mdm_name: "node_renamed"
+ is_primary: true
+ state: "present"
+
+ - name: Change virtual IP interface
+ dellemc.powerflex.mdm_cluster:
+ hostname: "{{ hostname }}"
+ username: "{{ username }}"
+ password: "{{ password }}"
+ validate_certs: "{{ validate_certs }}"
+ port: "{{ host_port }}"
+ mdm_name: "mdm_manager_node"
+ virtual_ip_interfaces:
+ - "ens224"
+ - "ens256"
+ state: "present"
+
+ - name: Clear virtual IP interface
+ dellemc.powerflex.mdm_cluster:
+ hostname: "{{ hostname }}"
+ username: "{{ username }}"
+ password: "{{ password }}"
+ validate_certs: "{{ validate_certs }}"
+ port: "{{ host_port }}"
+ mdm_name: "mdm_manager_node"
+ clear_interfaces: true
+ state: "present"
+
+ - name: Change Performance profile
+ dellemc.powerflex.mdm_cluster:
+ hostname: "{{ hostname }}"
+ username: "{{ username }}"
+ password: "{{ password }}"
+ validate_certs: "{{ validate_certs }}"
+ port: "{{ host_port }}"
+ performance_profile: "Compact"
+ state: "present"
+
+ - name: Switch cluster mode to FiveNodes
+ dellemc.powerflex.mdm_cluster:
+ hostname: "{{ hostname }}"
+ username: "{{ username }}"
+ password: "{{ password }}"
+ validate_certs: "{{ validate_certs }}"
+ port: "{{ host_port }}"
+ cluster_mode: "FiveNodes"
+ mdm:
+ - mdm_name: "mdm_manger_node"
+ mdm_type: "Secondary"
+ - mdm_name: "mdm_tiebreaker_node"
+ mdm_type: "TieBreaker"
+ mdm_state: "present-in-cluster"
+ state: "present"
+
+ - name: Switch cluster mode to ThreeNodes
+ dellemc.powerflex.mdm_cluster:
+ hostname: "{{ hostname }}"
+ username: "{{ username }}"
+ password: "{{ password }}"
+ validate_certs: "{{ validate_certs }}"
+ port: "{{ host_port }}"
+ cluster_mode: "ThreeNodes"
+ mdm:
+ - mdm_name: "mdm_manger_node"
+ mdm_type: "Secondary"
+ - mdm_name: "mdm_tiebreaker_node"
+ mdm_type: "TieBreaker"
+ mdm_state: "absent-in-cluster"
+ state: "present"
diff --git a/ansible_collections/dellemc/powerflex/playbooks/modules/protection_domain.yml b/ansible_collections/dellemc/powerflex/playbooks/modules/protection_domain.yml
new file mode 100644
index 000000000..5911a90aa
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/playbooks/modules/protection_domain.yml
@@ -0,0 +1,124 @@
+---
+- name: Protection domain Operations
+ hosts: localhost
+ connection: local
+ gather_facts: false
+ vars:
+ hostname: 'x.x.x.x'
+ username: 'admin'
+ password: 'Password'
+ validate_certs: false
+ host_port: 443
+
+ tasks:
+ - name: Create Protection Domain
+ register: result
+ dellemc.powerflex.protection_domain:
+ hostname: "{{ hostname }}"
+ username: "{{ username }}"
+ password: "{{ password }}"
+ validate_certs: "{{ validate_certs }}"
+ port: "{{ host_port }}"
+ protection_domain_name: "domain_1"
+ network_limits:
+ rebuild_limit: 10240
+ rebalance_limit: 10240
+ vtree_migration_limit: 10240
+ overall_limit: 20480
+ bandwidth_unit: "KBps"
+ rf_cache_limits:
+ page_size: 32
+ pass_through_mode: "Write"
+ state: "present"
+
+ - name: Get Protection Domain
+ dellemc.powerflex.protection_domain:
+ hostname: "{{ hostname }}"
+ username: "{{ username }}"
+ password: "{{ password }}"
+ validate_certs: "{{ validate_certs }}"
+ port: "{{ host_port }}"
+ protection_domain_name: "domain_1"
+ state: "present"
+
+ - name: Set Protection Domain id
+ ansible.builtin.set_fact:
+ pd_id: "{{ result.protection_domain_details.id }}"
+
+ - name: Get Protection Domain using ID
+ dellemc.powerflex.protection_domain:
+ hostname: "{{ hostname }}"
+ username: "{{ username }}"
+ password: "{{ password }}"
+ validate_certs: "{{ validate_certs }}"
+ port: "{{ host_port }}"
+ protection_domain_id: "{{ pd_id }}"
+ state: "present"
+
+ - name: Modify Protection Domain using ID
+ register: result
+ dellemc.powerflex.protection_domain:
+ hostname: "{{ hostname }}"
+ username: "{{ username }}"
+ password: "{{ password }}"
+ validate_certs: "{{ validate_certs }}"
+ port: "{{ host_port }}"
+ is_active: false
+ protection_domain_id: "{{ pd_id }}"
+ protection_domain_new_name: "domain_1_renamed"
+ network_limits:
+ rebuild_limit: 12
+ rebalance_limit: 12
+ overall_limit: 22
+ bandwidth_unit: "GBps"
+ rf_cache_limits:
+ is_enabled: false
+ page_size: 16
+ max_io_limit: 128
+ pass_through_mode: "Write"
+ state: "present"
+
+ - name: Modify Protection Domain using ID - Idempotecny
+ register: result
+ dellemc.powerflex.protection_domain:
+ hostname: "{{ hostname }}"
+ username: "{{ username }}"
+ password: "{{ password }}"
+ validate_certs: "{{ validate_certs }}"
+ port: "{{ host_port }}"
+ is_active: false
+ protection_domain_id: "{{ pd_id }}"
+ protection_domain_new_name: "domain_1_renamed"
+ network_limits:
+ rebuild_limit: 12
+ rebalance_limit: 12
+ overall_limit: 22
+ bandwidth_unit: "GBps"
+ rf_cache_limits:
+ is_enabled: false
+ page_size: 16
+ max_io_limit: 128
+ pass_through_mode: "Write"
+ state: "present"
+
+ - name: Delete Protection Domain using ID
+ register: result
+ dellemc.powerflex.protection_domain:
+ hostname: "{{ hostname }}"
+ username: "{{ username }}"
+ password: "{{ password }}"
+ validate_certs: "{{ validate_certs }}"
+ port: "{{ host_port }}"
+ protection_domain_id: "{{ pd_id }}"
+ state: "absent"
+
+ - name: Delete Protection Domain - Idempotecny
+ register: result
+ dellemc.powerflex.protection_domain:
+ hostname: "{{ hostname }}"
+ username: "{{ username }}"
+ password: "{{ password }}"
+ validate_certs: "{{ validate_certs }}"
+ port: "{{ host_port }}"
+ protection_domain_name: "domain_1_renamed"
+ state: "absent"
diff --git a/ansible_collections/dellemc/powerflex/playbooks/modules/replication_consistency_group.yml b/ansible_collections/dellemc/powerflex/playbooks/modules/replication_consistency_group.yml
new file mode 100644
index 000000000..043808f53
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/playbooks/modules/replication_consistency_group.yml
@@ -0,0 +1,234 @@
+---
+- name: Replication consistency group operations on PowerFlex array.
+ hosts: localhost
+ connection: local
+ gather_facts: false
+ vars:
+ hostname: 'x.x.x.x'
+ remote_hostname: '**.**.**.**'
+ username: 'admin'
+ password: 'Password'
+ validate_certs: false
+ rcg_name: "rcg_test"
+ rcg_id: "aadc17d900000002"
+
+ tasks:
+ - name: Get RCG details by name
+ register: result
+ dellemc.powerflex.replication_consistency_group:
+ hostname: "{{ hostname }}"
+ username: "{{ username }}"
+ password: "{{ password }}"
+ validate_certs: "{{ validate_certs }}"
+ rcg_name: "{{ rcg_name }}"
+
+ - name: Get RCG details by id
+ register: result
+ dellemc.powerflex.replication_consistency_group:
+ hostname: "{{ hostname }}"
+ username: "{{ username }}"
+ password: "{{ password }}"
+ validate_certs: "{{ validate_certs }}"
+ rcg_id: "{{ rcg_id }}"
+ state: "present"
+
+ - name: Create an RCG snapshot - check mode
+ register: result
+ dellemc.powerflex.replication_consistency_group:
+ hostname: "{{ hostname }}"
+ username: "{{ username }}"
+ password: "{{ password }}"
+ validate_certs: "{{ validate_certs }}"
+ rcg_name: "{{ rcg_name }}"
+ create_snapshot: true
+ check_mode: true
+
+ - name: Create an RCG snapshot
+ register: result
+ dellemc.powerflex.replication_consistency_group:
+ hostname: "{{ hostname }}"
+ username: "{{ username }}"
+ password: "{{ password }}"
+ validate_certs: "{{ validate_certs }}"
+ rcg_name: "{{ rcg_name }}"
+ create_snapshot: true
+
+ - name: Create an RCG
+ register: result
+ dellemc.powerflex.replication_consistency_group:
+ hostname: "{{ hostname }}"
+ username: "{{ username }}"
+ password: "{{ password }}"
+ validate_certs: "{{ validate_certs }}"
+ rcg_name: "{{ rcg_name }}"
+ rpo: 120
+ protection_domain_name: "domain1"
+ activity_mode: "Active"
+ remote_peer:
+ hostname: "{{ remote_hostname }}"
+ username: "{{ username }}"
+ password: "{{ password }}"
+ validate_certs: "{{ validate_certs }}"
+ protection_domain_name: "domain1"
+
+ - name: Modify RCG rpo
+ register: result
+ dellemc.powerflex.replication_consistency_group:
+ hostname: "{{ hostname }}"
+ username: "{{ username }}"
+ password: "{{ password }}"
+ validate_certs: "{{ validate_certs }}"
+ rcg_name: "{{ rcg_name }}"
+ rpo: 50
+
+ - name: Modify RCG target volume access mode
+ register: result
+ dellemc.powerflex.replication_consistency_group:
+ hostname: "{{ hostname }}"
+ username: "{{ username }}"
+ password: "{{ password }}"
+ validate_certs: "{{ validate_certs }}"
+ rcg_name: "{{ rcg_name }}"
+ target_volume_access_mode: "ReadOnly"
+
+ - name: Pause RCG
+ register: result
+ dellemc.powerflex.replication_consistency_group:
+ hostname: "{{ hostname }}"
+ username: "{{ username }}"
+ password: "{{ password }}"
+ validate_certs: "{{ validate_certs }}"
+ rcg_name: "{{ rcg_name }}"
+ rcg_state: 'pause'
+ pause_mode: "StopDataTransfer"
+
+ - name: Resume RCG
+ register: result
+ dellemc.powerflex.replication_consistency_group:
+ hostname: "{{ hostname }}"
+ username: "{{ username }}"
+ password: "{{ password }}"
+ validate_certs: "{{ validate_certs }}"
+ rcg_name: "{{ rcg_name }}"
+ rcg_state: 'resume'
+
+ - name: Freeze RCG
+ register: result
+ dellemc.powerflex.replication_consistency_group:
+ hostname: "{{ hostname }}"
+ username: "{{ username }}"
+ password: "{{ password }}"
+ validate_certs: "{{ validate_certs }}"
+ rcg_name: "{{ rcg_name }}"
+ rcg_state: 'freeze'
+
+ - name: UnFreeze RCG
+ register: result
+ dellemc.powerflex.replication_consistency_group:
+ hostname: "{{ hostname }}"
+ username: "{{ username }}"
+ password: "{{ password }}"
+ validate_certs: "{{ validate_certs }}"
+ rcg_name: "{{ rcg_name }}"
+ rcg_state: 'unfreeze'
+
+ - name: Set RCG as consistent
+ register: result
+ dellemc.powerflex.replication_consistency_group:
+ hostname: "{{ hostname }}"
+ username: "{{ username }}"
+ password: "{{ password }}"
+ validate_certs: "{{ validate_certs }}"
+ rcg_name: "{{ rcg_name }}"
+ is_consistent: true
+
+ - name: Inactivate RCG
+ register: result
+ dellemc.powerflex.replication_consistency_group:
+ hostname: "{{ hostname }}"
+ username: "{{ username }}"
+ password: "{{ password }}"
+ validate_certs: "{{ validate_certs }}"
+ rcg_name: "{{ rcg_name }}"
+ activity_mode: "InActive"
+
+ - name: Rename RCG
+ register: result
+ dellemc.powerflex.replication_consistency_group:
+ hostname: "{{ hostname }}"
+ username: "{{ username }}"
+ password: "{{ password }}"
+ validate_certs: "{{ validate_certs }}"
+ rcg_name: "{{ rcg_name }}"
+ new_rcg_rename: "rename_rcg"
+
+ - name: Delete RCG
+ register: result
+ dellemc.powerflex.replication_consistency_group:
+ hostname: "{{ hostname }}"
+ username: "{{ username }}"
+ password: "{{ password }}"
+ validate_certs: "{{ validate_certs }}"
+ rcg_name: "{{ rcg_name }}"
+ state: "absent"
+
+ - name: Failover RCG
+ register: result
+ dellemc.powerflex.replication_consistency_group:
+ hostname: "{{ hostname }}"
+ username: "{{ username }}"
+ password: "{{ password }}"
+ validate_certs: "{{ validate_certs }}"
+ rcg_name: "{{ rcg_name }}"
+ rcg_state: "failover"
+
+ - name: Restore RCG
+ register: result
+ dellemc.powerflex.replication_consistency_group:
+ hostname: "{{ hostname }}"
+ username: "{{ username }}"
+ password: "{{ password }}"
+ validate_certs: "{{ validate_certs }}"
+ rcg_name: "{{ rcg_name }}"
+ rcg_state: "restore"
+
+ - name: Switchover RCG
+ register: result
+ dellemc.powerflex.replication_consistency_group:
+ hostname: "{{ hostname }}"
+ username: "{{ username }}"
+ password: "{{ password }}"
+ validate_certs: "{{ validate_certs }}"
+ rcg_name: "{{ rcg_name }}"
+ rcg_state: "switchover"
+
+ - name: Reverse RCG
+ register: result
+ dellemc.powerflex.replication_consistency_group:
+ hostname: "{{ hostname }}"
+ username: "{{ username }}"
+ password: "{{ password }}"
+ validate_certs: "{{ validate_certs }}"
+ rcg_name: "{{ rcg_name }}"
+ rcg_state: "reverse"
+
+ - name: Force switchover RCG
+ register: result
+ dellemc.powerflex.replication_consistency_group:
+ hostname: "{{ hostname }}"
+ username: "{{ username }}"
+ password: "{{ password }}"
+ validate_certs: "{{ validate_certs }}"
+ rcg_name: "{{ rcg_name }}"
+ rcg_state: "switchover"
+ force: true
+
+ - name: Synchronize RCG
+ register: result
+ dellemc.powerflex.replication_consistency_group:
+ hostname: "{{ hostname }}"
+ username: "{{ username }}"
+ password: "{{ password }}"
+ validate_certs: "{{ validate_certs }}"
+ rcg_name: "{{ rcg_name }}"
+ rcg_state: "sync"
diff --git a/ansible_collections/dellemc/powerflex/playbooks/modules/replication_pair.yml b/ansible_collections/dellemc/powerflex/playbooks/modules/replication_pair.yml
new file mode 100644
index 000000000..b24313843
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/playbooks/modules/replication_pair.yml
@@ -0,0 +1,82 @@
+---
+- name: Replication pair operations on PowerFlex array.
+ hosts: localhost
+ connection: local
+ gather_facts: false
+ vars:
+ hostname: 'x.x.x.x'
+ remote_hostname: '**.**.**.**'
+ username: 'admin'
+ password: 'Password'
+ validate_certs: false
+ rcg_name: "test_rcg"
+ rcg_id: "aadc17d900000002"
+
+ tasks:
+ - name: Get replication pair details by name
+ register: result
+ dellemc.powerflex.replication_pair:
+ hostname: "{{ hostname }}"
+ username: "{{ username }}"
+ password: "{{ password }}"
+ validate_certs: "{{ validate_certs }}"
+ pair_name: "test_pair"
+
+ - name: Add replication pair to RCG
+ register: result
+ dellemc.powerflex.replication_pair:
+ hostname: "{{ hostname }}"
+ username: "{{ username }}"
+ password: "{{ password }}"
+ validate_certs: "{{ validate_certs }}"
+ pairs:
+ - source_volume_name: "ans_test_vol"
+ target_volume_name: "ans_env8_vol"
+ copy_type: "OnlineCopy"
+ name: "test_pair"
+ rcg_name: "test_rcg"
+ remote_peer:
+ hostname: "{{ remote_hostname }}"
+ username: "{{ username }}"
+ password: "{{ password }}"
+ validate_certs: "{{ validate_certs }}"
+
+ - name: Pause Replication pair
+ register: result
+ dellemc.powerflex.replication_pair:
+ hostname: "{{ hostname }}"
+ username: "{{ username }}"
+ password: "{{ password }}"
+ validate_certs: "{{ validate_certs }}"
+ pair_name: "test_pair"
+ pause: true
+
+ - name: Resume Replication pair
+ register: result
+ dellemc.powerflex.replication_pair:
+ hostname: "{{ hostname }}"
+ username: "{{ username }}"
+ password: "{{ password }}"
+ validate_certs: "{{ validate_certs }}"
+ pair_name: "test_pair"
+ pause: false
+
+ - name: Delete replication pair
+ register: result
+ dellemc.powerflex.replication_pair:
+ hostname: "{{ hostname }}"
+ username: "{{ username }}"
+ password: "{{ password }}"
+ validate_certs: "{{ validate_certs }}"
+ pair_name: "test_pair"
+ state: "absent"
+
+ - name: Delete replication pair - Idemotency
+ register: result
+ dellemc.powerflex.replication_pair:
+ hostname: "{{ hostname }}"
+ username: "{{ username }}"
+ password: "{{ password }}"
+ validate_certs: "{{ validate_certs }}"
+ pair_name: "test_pair"
+ state: "absent"
diff --git a/ansible_collections/dellemc/powerflex/playbooks/modules/sdc.yml b/ansible_collections/dellemc/powerflex/playbooks/modules/sdc.yml
new file mode 100644
index 000000000..b796f02da
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/playbooks/modules/sdc.yml
@@ -0,0 +1,64 @@
+---
+- name: SDC Operations
+ hosts: localhost
+ connection: local
+ gather_facts: false
+ vars:
+ hostname: 'x.x.x.x'
+ username: 'admin'
+ password: 'Password'
+ validate_certs: false
+ sdc_name: test_sdc
+
+ tasks:
+ - name: Get sdc details using sdc ip
+ register: result
+ dellemc.powerflex.sdc:
+ hostname: "{{ hostname }}"
+ username: "{{ username }}"
+ password: "{{ password }}"
+ validate_certs: "{{ validate_certs }}"
+ sdc_ip: "1.1.1.1"
+ state: "present"
+
+ - name: Set sdc id
+ ansible.builtin.set_fact:
+ sdc_id: "{{ result.sdc_details.id }}"
+
+ - name: Rename sdc using sdc id
+ dellemc.powerflex.sdc:
+ hostname: "{{ hostname }}"
+ username: "{{ username }}"
+ password: "{{ password }}"
+ validate_certs: "{{ validate_certs }}"
+ sdc_id: "{{ sdc_id }}"
+ sdc_new_name: "{{ sdc_name }}"
+ state: "present"
+
+ - name: Get sdc details using sdc name
+ dellemc.powerflex.sdc:
+ hostname: "{{ hostname }}"
+ username: "{{ username }}"
+ password: "{{ password }}"
+ validate_certs: "{{ validate_certs }}"
+ sdc_name: "{{ sdc_name }}"
+ state: "present"
+
+ - name: Set performance profile of sdc using sdc name
+ dellemc.powerflex.sdc:
+ hostname: "{{ hostname }}"
+ username: "{{ username }}"
+ password: "{{ password }}"
+ validate_certs: "{{ validate_certs }}"
+ sdc_name: "{{ sdc_name }}"
+ performance_profile: "HighPerformance"
+ state: "present"
+
+ - name: Remove sdc
+ dellemc.powerflex.sdc:
+ hostname: "{{ hostname }}"
+ username: "{{ username }}"
+ password: "{{ password }}"
+ validate_certs: "{{ validate_certs }}"
+ sdc_name: "{{ sdc_name }}"
+ state: "absent"
diff --git a/ansible_collections/dellemc/powerflex/playbooks/modules/sds.yml b/ansible_collections/dellemc/powerflex/playbooks/modules/sds.yml
new file mode 100644
index 000000000..30a9b0906
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/playbooks/modules/sds.yml
@@ -0,0 +1,149 @@
+---
+- name: SDS Operations
+ hosts: localhost
+ connection: local
+ gather_facts: false
+ vars:
+ hostname: 'x.x.x.x'
+ username: 'admin'
+ password: 'Password'
+ validate_certs: false
+ host_port: 443
+ sds_name_1: "sds_node_1"
+ sds_name_2: "sds_node_2"
+ sds_name_1_new: "sds_node1_new"
+ protection_domain_name: "Ansible-PD1"
+
+ tasks:
+ - name: Create SDS
+ register: result
+ dellemc.powerflex.sds:
+ hostname: "{{ hostname }}"
+ username: "{{ username }}"
+ password: "{{ password }}"
+ validate_certs: "{{ validate_certs }}"
+ port: "{{ host_port }}"
+ sds_name: "{{ sds_name_1 }}"
+ protection_domain_name: "{{ protection_domain_name }}"
+ sds_ip_list:
+ - ip: '**.**.**.**'
+ role: "all"
+ sds_ip_state: "present-in-sds"
+ state: "present"
+
+ - name: Create SDS with all parameters
+ dellemc.powerflex.sds:
+ hostname: "{{ hostname }}"
+ username: "{{ username }}"
+ password: "{{ password }}"
+ validate_certs: "{{ validate_certs }}"
+ port: "{{ host_port }}"
+ sds_name: "{{ sds_name_2 }}"
+ fault_set_name: "sample_fault_set"
+ protection_domain_name: "{{ protection_domain_name }}"
+ sds_ip_list:
+ - ip: '**.**.**.**'
+ role: "all"
+ sds_ip_state: "present-in-sds"
+ rmcache_enabled: true
+ rmcache_size: 210
+ state: "present"
+
+ - name: Get SDS details using name
+ dellemc.powerflex.sds:
+ hostname: "{{ hostname }}"
+ username: "{{ username }}"
+ password: "{{ password }}"
+ validate_certs: "{{ validate_certs }}"
+ port: "{{ host_port }}"
+ sds_name: "{{ sds_name_2 }}"
+ state: "present"
+
+ - name: Get SDS details using ID
+ dellemc.powerflex.sds:
+ hostname: "{{ hostname }}"
+ username: "{{ username }}"
+ password: "{{ password }}"
+ validate_certs: "{{ validate_certs }}"
+ port: "{{ host_port }}"
+ sds_id: "{{ result.sds_details }}"
+ state: "present"
+
+ - name: Modify SDS attributes using name
+ register: result
+ dellemc.powerflex.sds:
+ hostname: "{{ hostname }}"
+ username: "{{ username }}"
+ password: "{{ password }}"
+ validate_certs: "{{ validate_certs }}"
+ port: "{{ host_port }}"
+ sds_name: "{{ sds_name_1 }}"
+ sds_new_name: "{{ sds_name_1_new }}"
+ rfcache_enabled: false
+ rmcache_enabled: true
+ rmcache_size: 256
+ performance_profile: "HighPerformance"
+ state: "present"
+
+ - name: Modify SDS attributes using ID
+ dellemc.powerflex.sds:
+ hostname: "{{ hostname }}"
+ username: "{{ username }}"
+ password: "{{ password }}"
+ validate_certs: "{{ validate_certs }}"
+ port: "{{ host_port }}"
+ sds_id: "{{ result.sds_details }}"
+ sds_new_name: "{{ sds_name_1 }}"
+ rfcache_enabled: true
+ rmcache_enabled: true
+ rmcache_size: 210
+ performance_profile: "Compact"
+ state: "present"
+
+ - name: Add IP and role to an SDS
+ dellemc.powerflex.sds:
+ hostname: "{{ hostname }}"
+ username: "{{ username }}"
+ password: "{{ password }}"
+ validate_certs: "{{ validate_certs }}"
+ port: "{{ host_port }}"
+ sds_name: "{{ sds_name_1 }}"
+ sds_ip_list:
+ - ip: '**.**.**.**'
+ role: "sdcOnly"
+ sds_ip_state: "present-in-sds"
+ state: "present"
+
+ - name: Remove IP and role from an SDS
+ dellemc.powerflex.sds:
+ hostname: "{{ hostname }}"
+ username: "{{ username }}"
+ password: "{{ password }}"
+ validate_certs: "{{ validate_certs }}"
+ port: "{{ host_port }}"
+ sds_name: "{{ sds_name_1 }}"
+ sds_ip_list:
+ - ip: '**.**.**.**'
+ role: "sdcOnly"
+ sds_ip_state: "absent-in-sds"
+ state: "present"
+
+ - name: Delete SDS using name
+ dellemc.powerflex.sds:
+ hostname: "{{ hostname }}"
+ username: "{{ username }}"
+ password: "{{ password }}"
+ validate_certs: "{{ validate_certs }}"
+ port: "{{ host_port }}"
+ sds_name: "{{ sds_name_2 }}"
+ state: "absent"
+
+ - name: Delete SDS using ID
+ dellemc.powerflex.sds:
+ hostname: "{{ hostname }}"
+ username: "{{ username }}"
+ password: "{{ password }}"
+ validate_certs: "{{ validate_certs }}"
+ port: "{{ host_port }}"
+ sds_id: "{{ result.sds_details }}"
+ state: "absent"
diff --git a/ansible_collections/dellemc/powerflex/playbooks/modules/snapshot.yml b/ansible_collections/dellemc/powerflex/playbooks/modules/snapshot.yml
new file mode 100644
index 000000000..33975539c
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/playbooks/modules/snapshot.yml
@@ -0,0 +1,130 @@
+---
+- name: Snapshot Operations
+ hosts: localhost
+ connection: local
+ gather_facts: false
+ vars:
+ hostname: 'x.x.x.x'
+ username: 'admin'
+ password: 'Password'
+ validate_certs: false
+
+ tasks:
+ - name: Create snapshot
+ register: result
+ dellemc.powerflex.snapshot:
+ hostname: "{{ hostname }}"
+ username: "{{ username }}"
+ password: "{{ password }}"
+ validate_certs: "{{ validate_certs }}"
+ snapshot_name: "ansible_snapshot_1"
+ vol_name: "ansible_volume"
+ state: "present"
+
+ - name: Set snapshot id
+ ansible.builtin.set_fact:
+ snapshot_id: "{{ result.snapshot_details.id }}"
+
+ - name: Create snapshot with retention
+ dellemc.powerflex.snapshot:
+ hostname: "{{ hostname }}"
+ username: "{{ username }}"
+ password: "{{ password }}"
+ validate_certs: "{{ validate_certs }}"
+ snapshot_name: "ansible_snapshot_2"
+ vol_name: "ansible_volume"
+ desired_retention: 2
+ state: "present"
+
+ - name: Get snapshot details using snapshot id
+ dellemc.powerflex.snapshot:
+ hostname: "{{ hostname }}"
+ username: "{{ username }}"
+ password: "{{ password }}"
+ validate_certs: "{{ validate_certs }}"
+ snapshot_id: "{{ snapshot_id }}"
+ state: "present"
+
+ - name: Modify the retention
+ dellemc.powerflex.snapshot:
+ hostname: "{{ hostname }}"
+ username: "{{ username }}"
+ password: "{{ password }}"
+ validate_certs: "{{ validate_certs }}"
+ snapshot_name: "ansible_snapshot_2"
+ desired_retention: 4
+ state: "present"
+
+ - name: Map snapshot to SDC
+ dellemc.powerflex.snapshot:
+ hostname: "{{ hostname }}"
+ username: "{{ username }}"
+ password: "{{ password }}"
+ validate_certs: "{{ validate_certs }}"
+ snapshot_id: "{{ snapshot_id }}"
+ sdc:
+ - sdc_ip: '**.**.**.**'
+ - sdc_id: "663ac0d200000001"
+ allow_multiple_mappings: true
+ sdc_state: "mapped"
+ state: "present"
+
+ - name: Modify the attributes of SDC mapped to snapshot
+ dellemc.powerflex.snapshot:
+ hostname: "{{ hostname }}"
+ username: "{{ username }}"
+ password: "{{ password }}"
+ validate_certs: "{{ validate_certs }}"
+ snapshot_id: "{{ snapshot_id }}"
+ sdc:
+ - sdc_ip: '**.**.**.**'
+ iops_limit: 11
+ bandwidth_limit: 4096
+ - sdc_id: "663ac0d200000001"
+ iops_limit: 20
+ bandwidth_limit: 2048
+ sdc_state: "mapped"
+ state: "present"
+
+ - name: Extend the size of snapshot
+ dellemc.powerflex.snapshot:
+ hostname: "{{ hostname }}"
+ username: "{{ username }}"
+ password: "{{ password }}"
+ validate_certs: "{{ validate_certs }}"
+ snapshot_id: "{{ snapshot_id }}"
+ size: 16
+ state: "present"
+
+ - name: Unmap SDCs from snapshot
+ dellemc.powerflex.snapshot:
+ hostname: "{{ hostname }}"
+ username: "{{ username }}"
+ password: "{{ password }}"
+ validate_certs: "{{ validate_certs }}"
+ snapshot_id: "{{ snapshot_id }}"
+ sdc:
+ - sdc_ip: '**.**.**.**'
+ - sdc_id: "663ac0d200000001"
+ sdc_state: "unmapped"
+ state: "present"
+
+ - name: Rename snapshot
+ dellemc.powerflex.snapshot:
+ hostname: "{{ hostname }}"
+ username: "{{ username }}"
+ password: "{{ password }}"
+ validate_certs: "{{ validate_certs }}"
+ snapshot_id: "{{ snapshot_id }}"
+ snapshot_new_name: "ansible_renamed_snapshot"
+ state: "present"
+
+ - name: Delete snapshot
+ dellemc.powerflex.snapshot:
+ hostname: "{{ hostname }}"
+ username: "{{ username }}"
+ password: "{{ password }}"
+ validate_certs: "{{ validate_certs }}"
+ snapshot_id: "{{ snapshot_id }}"
+ remove_mode: "ONLY_ME"
+ state: "absent"
diff --git a/ansible_collections/dellemc/powerflex/playbooks/modules/snapshot_policy.yml b/ansible_collections/dellemc/powerflex/playbooks/modules/snapshot_policy.yml
new file mode 100644
index 000000000..fd83a2a60
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/playbooks/modules/snapshot_policy.yml
@@ -0,0 +1,215 @@
+---
+- name: Snapshot Policy operations on powerflex array.
+ hosts: localhost
+ connection: local
+ gather_facts: false
+ vars:
+ hostname: 'x.x.x.x'
+ username: 'admin'
+ password: 'Password'
+ validate_certs: false
+ snapshot_policy_name: "Ansible_snap_policy_1"
+ source_volume_name1: "Ansible_volume_4"
+ source_volume_name2: "Ansible_volume_5"
+ snapshot_policy_name_new: "Ansible_snap_policy_1_new"
+
+ tasks:
+ - name: Create a snapshot policy in check mode
+ dellemc.powerflex.snapshot_policy:
+ hostname: "{{ hostname }}"
+ username: "{{ username }}"
+ password: "{{ password }}"
+ validate_certs: "{{ validate_certs }}"
+ snapshot_policy_name: "{{ snapshot_policy_name }}"
+ access_mode: "READ_WRITE"
+ secure_snapshots: false
+ auto_snapshot_creation_cadence:
+ time: 1
+ unit: "Hour"
+ num_of_retained_snapshots_per_level:
+ - 20
+ state: "present"
+ check_mode: true
+
+ - name: Create a snapshot policy
+ register: result
+ dellemc.powerflex.snapshot_policy:
+ hostname: "{{ hostname }}"
+ username: "{{ username }}"
+ password: "{{ password }}"
+ validate_certs: "{{ validate_certs }}"
+ snapshot_policy_name: "{{ snapshot_policy_name }}"
+ access_mode: "READ_WRITE"
+ secure_snapshots: false
+ auto_snapshot_creation_cadence:
+ time: 1
+ unit: "Hour"
+ num_of_retained_snapshots_per_level:
+ - 20
+ state: "present"
+
+ - name: Get snapshot policy details using name
+ dellemc.powerflex.snapshot_policy:
+ hostname: "{{ hostname }}"
+ username: "{{ username }}"
+ password: "{{ password }}"
+ validate_certs: "{{ validate_certs }}"
+ snapshot_policy_name: "{{ snapshot_policy_name }}"
+
+ - name: Get snapshot policy details using id
+ dellemc.powerflex.snapshot_policy:
+ hostname: "{{ hostname }}"
+ username: "{{ username }}"
+ password: "{{ password }}"
+ validate_certs: "{{ validate_certs }}"
+ snapshot_policy_id: "{{ result.snapshot_policy_details.id }}"
+
+ - name: Modify a snapshot policy - check mode
+ register: result
+ dellemc.powerflex.snapshot_policy:
+ hostname: "{{ hostname }}"
+ username: "{{ username }}"
+ password: "{{ password }}"
+ validate_certs: "{{ validate_certs }}"
+ snapshot_policy_name: "{{ snapshot_policy_name }}"
+ auto_snapshot_creation_cadence:
+ time: 2
+ unit: "Hour"
+ num_of_retained_snapshots_per_level:
+ - 40
+ check_mode: true
+
+ - name: Modify a snapshot policy
+ dellemc.powerflex.snapshot_policy:
+ hostname: "{{ hostname }}"
+ username: "{{ username }}"
+ password: "{{ password }}"
+ validate_certs: "{{ validate_certs }}"
+ snapshot_policy_name: "{{ snapshot_policy_name }}"
+ auto_snapshot_creation_cadence:
+ time: 2
+ unit: "Hour"
+ num_of_retained_snapshots_per_level:
+ - 40
+
+ - name: Rename a snapshot policy
+ dellemc.powerflex.snapshot_policy:
+ hostname: "{{ hostname }}"
+ username: "{{ username }}"
+ password: "{{ password }}"
+ validate_certs: "{{ validate_certs }}"
+ snapshot_policy_name: "{{ snapshot_policy_name }}"
+ new_name: "{{ snapshot_policy_name_new }}"
+
+ - name: Add source volume - check mode
+ dellemc.powerflex.snapshot_policy:
+ hostname: "{{ hostname }}"
+ username: "{{ username }}"
+ password: "{{ password }}"
+ validate_certs: "{{ validate_certs }}"
+ snapshot_policy_name: "{{ snapshot_policy_name_new }}"
+ source_volume:
+ - name: "{{ source_volume_name1 }}"
+ - name: "{{ source_volume_name2 }}"
+ state: "present"
+ check_mode: true
+
+ - name: Add source volume
+ dellemc.powerflex.snapshot_policy:
+ hostname: "{{ hostname }}"
+ username: "{{ username }}"
+ password: "{{ password }}"
+ validate_certs: "{{ validate_certs }}"
+ snapshot_policy_name: "{{ snapshot_policy_name_new }}"
+ source_volume:
+ - name: "{{ source_volume_name1 }}"
+ - name: "{{ source_volume_name2 }}"
+ state: "present"
+
+ - name: Remove source volume - check mode
+ dellemc.powerflex.snapshot_policy:
+ hostname: "{{ hostname }}"
+ username: "{{ username }}"
+ password: "{{ password }}"
+ validate_certs: "{{ validate_certs }}"
+ snapshot_policy_name: "{{ snapshot_policy_name_new }}"
+ source_volume:
+ - name: "{{ source_volume_name1 }}"
+ auto_snap_removal_action: 'Remove'
+ state: "absent"
+ - name: "{{ source_volume_name2 }}"
+ auto_snap_removal_action: 'Remove'
+ state: "absent"
+ check_mode: true
+
+ - name: Remove source volume
+ dellemc.powerflex.snapshot_policy:
+ hostname: "{{ hostname }}"
+ username: "{{ username }}"
+ password: "{{ password }}"
+ validate_certs: "{{ validate_certs }}"
+ snapshot_policy_name: "{{ snapshot_policy_name_new }}"
+ source_volume:
+ - name: "{{ source_volume_name1 }}"
+ auto_snap_removal_action: 'Remove'
+ state: "absent"
+ - name: "{{ source_volume_name2 }}"
+ auto_snap_removal_action: 'Remove'
+ state: "absent"
+
+ - name: Pause snapshot policy - check mode
+ dellemc.powerflex.snapshot_policy:
+ hostname: "{{ hostname }}"
+ username: "{{ username }}"
+ password: "{{ password }}"
+ validate_certs: "{{ validate_certs }}"
+ snapshot_policy_name: "{{ snapshot_policy_name_new }}"
+ pause: true
+ check_mode: true
+
+ - name: Pause snapshot policy
+ dellemc.powerflex.snapshot_policy:
+ hostname: "{{ hostname }}"
+ username: "{{ username }}"
+ password: "{{ password }}"
+ validate_certs: "{{ validate_certs }}"
+ snapshot_policy_name: "{{ snapshot_policy_name_new }}"
+ pause: true
+
+ - name: Resume snapshot policy - check mode
+ dellemc.powerflex.snapshot_policy:
+ hostname: "{{ hostname }}"
+ username: "{{ username }}"
+ password: "{{ password }}"
+ validate_certs: "{{ validate_certs }}"
+ snapshot_policy_name: "{{ snapshot_policy_name_new }}"
+ pause: false
+ check_mode: true
+
+ - name: Resume snapshot policy
+ dellemc.powerflex.snapshot_policy:
+ hostname: "{{ hostname }}"
+ username: "{{ username }}"
+ password: "{{ password }}"
+ validate_certs: "{{ validate_certs }}"
+ snapshot_policy_name: "{{ snapshot_policy_name_new }}"
+ pause: false
+
+ - name: Delete snapshot policy - check mode
+ dellemc.powerflex.snapshot_policy:
+ hostname: "{{ hostname }}"
+ username: "{{ username }}"
+ password: "{{ password }}"
+ validate_certs: "{{ validate_certs }}"
+ snapshot_policy_name: "{{ snapshot_policy_name_new }}"
+ state: "absent"
+ check_mode: true
+
+ - name: Delete snapshot policy
+ dellemc.powerflex.snapshot_policy:
+ hostname: "{{ hostname }}"
+ username: "{{ username }}"
+ password: "{{ password }}"
+ validate_certs: "{{ validate_certs }}"
+ snapshot_policy_name: "{{ snapshot_policy_name_new }}"
+ state: "absent"
diff --git a/ansible_collections/dellemc/powerflex/playbooks/modules/storagepool.yml b/ansible_collections/dellemc/powerflex/playbooks/modules/storagepool.yml
new file mode 100644
index 000000000..7e7860a0a
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/playbooks/modules/storagepool.yml
@@ -0,0 +1,61 @@
+---
+- name: Storage Pool Module Operations on PowerFlex
+ hosts: localhost
+ connection: local
+ vars:
+ hostname: 'x.x.x.x'
+ username: 'admin'
+ password: 'Password'
+ validate_certs: false
+ pool_name: "ansible_test_pool6"
+ protection_domain_name: "domain1"
+
+ tasks:
+ - name: Create a new Storage pool
+ register: result
+ dellemc.powerflex.storagepool:
+ hostname: "{{ hostname }}"
+ username: "{{ username }}"
+ password: "{{ password }}"
+ validate_certs: "{{ validate_certs }}"
+ storage_pool_name: "{{ pool_name }}"
+ protection_domain_name: "{{ protection_domain_name }}"
+ media_type: "HDD"
+ state: "present"
+
+ - name: Set pool id
+ ansible.builtin.set_fact:
+ pool_id: "{{ result.storage_pool_details.id }}"
+
+ - name: Rename Storage pool by id
+ dellemc.powerflex.storagepool:
+ hostname: "{{ hostname }}"
+ username: "{{ username }}"
+ password: "{{ password }}"
+ validate_certs: "{{ validate_certs }}"
+ storage_pool_id: "{{ pool_id }}"
+ storage_pool_new_name: "new_ansible_pool"
+ state: "present"
+
+ - name: Restore the name of Storage pool by id
+ dellemc.powerflex.storagepool:
+ hostname: "{{ hostname }}"
+ username: "{{ username }}"
+ password: "{{ password }}"
+ validate_certs: "{{ validate_certs }}"
+ storage_pool_id: "{{ pool_id }}"
+ storage_pool_new_name: "{{ pool_name }}"
+ state: "present"
+
+
+ - name: Modify a Storage pool by name
+ dellemc.powerflex.storagepool:
+ hostname: "{{ hostname }}"
+ username: "{{ username }}"
+ password: "{{ password }}"
+ validate_certs: "{{ validate_certs }}"
+ storage_pool_name: "ansible_test_pool"
+ protection_domain_name: "{{ protection_domain_name }}"
+ use_rmcache: true
+ use_rfcache: true
+ state: "present"
diff --git a/ansible_collections/dellemc/powerflex/playbooks/modules/volume.yml b/ansible_collections/dellemc/powerflex/playbooks/modules/volume.yml
new file mode 100644
index 000000000..da5f255bb
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/playbooks/modules/volume.yml
@@ -0,0 +1,133 @@
+---
+- name: Volume operations on powerflex array.
+ hosts: localhost
+ connection: local
+ gather_facts: false
+ vars:
+ hostname: 'x.x.x.x'
+ username: 'admin'
+ password: 'Password'
+ validate_certs: false
+ protection_domain_name: "domain1"
+ storage_pool_name: "pool1"
+ snapshot_policy_name: "sample_snap_policy_1"
+ vol_name: "sample_ansible_volume_20"
+
+ tasks:
+ - name: Create a volume
+ register: result
+ dellemc.powerflex.volume:
+ hostname: "{{ hostname }}"
+ username: "{{ username }}"
+ password: "{{ password }}"
+ validate_certs: "{{ validate_certs }}"
+ vol_name: "{{ vol_name }}"
+ storage_pool_name: "{{ storage_pool_name }}"
+ protection_domain_name: "{{ protection_domain_name }}"
+ snapshot_policy_name: "{{ snapshot_policy_name }}"
+ sdc:
+ - sdc_ip: '**.**.**.**'
+ - sdc_id: "663ac0d200000001"
+ allow_multiple_mappings: true
+ sdc_state: "mapped"
+ size: 8
+ state: "present"
+
+ - name: Set volume id
+ ansible.builtin.set_fact:
+ vol_id: "{{ result.volume_details.id }}"
+
+ - name: Get volume details using volume id
+ dellemc.powerflex.volume:
+ hostname: "{{ hostname }}"
+ username: "{{ username }}"
+ password: "{{ password }}"
+ validate_certs: "{{ validate_certs }}"
+ vol_id: "{{ vol_id }}"
+ state: "present"
+
+ - name: Get volume details using volume name
+ dellemc.powerflex.volume:
+ hostname: "{{ hostname }}"
+ username: "{{ username }}"
+ password: "{{ password }}"
+ validate_certs: "{{ validate_certs }}"
+ vol_name: "{{ vol_name }}"
+ state: "present"
+
+ - name: Modify the size
+ dellemc.powerflex.volume:
+ hostname: "{{ hostname }}"
+ username: "{{ username }}"
+ password: "{{ password }}"
+ validate_certs: "{{ validate_certs }}"
+ vol_name: "{{ vol_name }}"
+ size: 16
+ state: "present"
+
+ - name: Map volume to SDC and remove snapshot policy
+ dellemc.powerflex.volume:
+ hostname: "{{ hostname }}"
+ username: "{{ username }}"
+ password: "{{ password }}"
+ validate_certs: "{{ validate_certs }}"
+ vol_id: "{{ vol_id }}"
+ sdc:
+ - sdc_ip: '**.**.**.**'
+ - sdc_id: "663ac0d200000001"
+ allow_multiple_mappings: true
+ snapshot_policy_name: ""
+ auto_snap_remove_type: "remove"
+ sdc_state: "mapped"
+ state: "present"
+
+ - name: Modify the attributes of SDC mapped to volume
+ dellemc.powerflex.volume:
+ hostname: "{{ hostname }}"
+ username: "{{ username }}"
+ password: "{{ password }}"
+ validate_certs: "{{ validate_certs }}"
+ vol_id: "{{ vol_id }}"
+ sdc:
+ - sdc_ip: '**.**.**.**'
+ iops_limit: 11
+ bandwidth_limit: 4096
+ - sdc_id: "663ac0d200000001"
+ iops_limit: 20
+ bandwidth_limit: 2048
+ allow_multiple_mappings: true
+ sdc_state: "mapped"
+ state: "present"
+
+ - name: Unmap SDCs from volume
+ dellemc.powerflex.volume:
+ hostname: "{{ hostname }}"
+ username: "{{ username }}"
+ password: "{{ password }}"
+ validate_certs: "{{ validate_certs }}"
+ vol_id: "{{ vol_id }}"
+ sdc:
+ - sdc_ip: '**.**.**.**'
+ - sdc_id: "663ac0d200000001"
+ sdc_state: "unmapped"
+ state: "present"
+
+ - name: Rename volume
+ dellemc.powerflex.volume:
+ hostname: "{{ hostname }}"
+ username: "{{ username }}"
+ password: "{{ password }}"
+ validate_certs: "{{ validate_certs }}"
+ vol_id: "{{ vol_id }}"
+ vol_new_name: "ansible_renamed_volume"
+ state: "present"
+
+ - name: Delete volume
+ dellemc.powerflex.volume:
+ hostname: "{{ hostname }}"
+ username: "{{ username }}"
+ password: "{{ password }}"
+ validate_certs: "{{ validate_certs }}"
+ vol_id: "{{ vol_id }}"
+ delete_snapshots: true
+ state: "absent"
diff --git a/ansible_collections/dellemc/powerflex/playbooks/roles/group_vars/all b/ansible_collections/dellemc/powerflex/playbooks/roles/group_vars/all
new file mode 100644
index 000000000..1031958fc
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/playbooks/roles/group_vars/all
@@ -0,0 +1,51 @@
+powerflex_common_file_install_location: "/var/tmp"
+powerflex_common_esxi_files_location: "/tmp/"
+powerflex_common_win_package_location: "C:\\Windows\\Temp"
+# powerflex sdc params
+powerflex_sdc_driver_sync_repo_address: 'ftp://ftp.emc.com/'
+powerflex_sdc_driver_sync_repo_user: 'QNzgdxXix'
+powerflex_sdc_driver_sync_repo_password: 'Aw3wFAwAq3'
+powerflex_sdc_driver_sync_repo_local_dir: '/bin/emc/scaleio/scini_sync/driver_cache/'
+powerflex_sdc_driver_sync_user_private_rsa_key_src: ''
+powerflex_sdc_driver_sync_user_private_rsa_key_dest: '/bin/emc/scaleio/scini_sync/scini_key'
+powerflex_sdc_driver_sync_repo_public_rsa_key_src: ''
+powerflex_sdc_driver_sync_repo_public_rsa_key_dest: '/bin/emc/scaleio/scini_sync/scini_repo_key.pub'
+powerflex_sdc_driver_sync_module_sigcheck: 1
+powerflex_sdc_driver_sync_emc_public_gpg_key_src: ../../../files/RPM-GPG-KEY-powerflex_2.0.*.0
+powerflex_sdc_driver_sync_emc_public_gpg_key_dest: '/bin/emc/scaleio/scini_sync/emc_key.pub'
+powerflex_sdc_driver_sync_sync_pattern: .*
+powerflex_sdc_state: present
+# powerflex mdm role params
+powerflex_mdm_state: present
+powerflex_mdm_password: 'Password123'
+powerflex_mdm_cert_password: 'Password123!'
+i_am_sure: 1
+powerflex_mdm_virtual_ip: ''
+# powerflex lia params
+powerflex_lia_state: present
+powerflex_lia_token: Cluster1!
+# powerflex tb params
+powerflex_tb_state: present
+powerflex_tb_cert_password: "{{ powerflex_mdm_cert_password }}"
+# powerflex sds params
+powerflex_sds_number: 1
+powerflex_sds_disks: { ansible_available_disks: ['/dev/sdb'] }
+powerflex_sds_disks_type: HDD
+powerflex_sds_protection_domain: domain1
+powerflex_sds_storage_pool: pool1
+powerflex_sds_role: all
+powerflex_sds_device_media_type: HDD
+powerflex_sds_device_name: '/dev/sdb'
+powerflex_sds_external_acceleration_type: ReadAndWrite
+powerflex_sds_state: present
+# powerflex webui params
+powerflex_webui_state: present
+# powerflex gateway role params
+powerflex_gateway_state: present
+powerflex_gateway_admin_password: Password123
+powerflex_gateway_http_port: 80
+powerflex_gateway_https_port: 443
+powerflex_gateway_is_redundant: false
+#powerflex sdr params
+powerflex_protection_domain_name: domain1
+powerflex_storage_pool_name: pool1
diff --git a/ansible_collections/dellemc/powerflex/playbooks/roles/host_vars/node2 b/ansible_collections/dellemc/powerflex/playbooks/roles/host_vars/node2
new file mode 100644
index 000000000..20a36d660
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/playbooks/roles/host_vars/node2
@@ -0,0 +1,11 @@
+# SDC params
+powerflex_sdc_name: sdc_test
+powerflex_sdc_performance_profile: Compact
+#TB params
+powerflex_tb_primary_name: primary_tb
+powerflex_tb_secondary_name: secondary_tb
+powerflex_tb_cluster_mode: "ThreeNodes"
+powerflex_protection_domain_name: "domain1"
+powerflex_fault_sets: ['fs1','fs2','fs3']
+powerflex_media_type: 'SSD' # When version is R3
+powerflex_storage_pool_name: "pool1"
diff --git a/ansible_collections/dellemc/powerflex/playbooks/roles/inventory b/ansible_collections/dellemc/powerflex/playbooks/roles/inventory
new file mode 100644
index 000000000..24fc6f241
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/playbooks/roles/inventory
@@ -0,0 +1,41 @@
+node0 ansible_host=10.1.1.1 ansible_port=22 ansible_ssh_pass=password ansible_user=root
+node1 ansible_host=10.x.x.x ansible_port=22 ansible_ssh_pass=password ansible_user=root
+node2 ansible_host=10.x.x.y ansible_port=22 ansible_ssh_pass=password ansible_user=root
+
+[activemq]
+node0
+node1
+
+[lia]
+node0
+node1
+node2
+
+[mdm]
+node0
+node1
+
+[gateway]
+node2
+
+[tb]
+node2
+
+[config]
+node1
+
+[sdc]
+node2
+
+[sds]
+node0
+node1
+node2
+
+[webui]
+node1
+
+[sdr]
+node0
+node1
+node2
diff --git a/ansible_collections/dellemc/powerflex/playbooks/roles/site.yml b/ansible_collections/dellemc/powerflex/playbooks/roles/site.yml
new file mode 100644
index 000000000..7f325d4d5
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/playbooks/roles/site.yml
@@ -0,0 +1,64 @@
+---
+- name: "Install PowerFlex Common"
+ hosts: all
+ roles:
+ - powerflex_common
+
+- name: Install and configure PowerFlex MDM
+ hosts: mdm
+ roles:
+ - powerflex_mdm
+
+- name: Install and configure PowerFlex gateway
+ hosts: gateway
+ roles:
+ - powerflex_gateway
+
+- name: Install and configure PowerFlex TB
+ hosts: tb
+ vars_files:
+ - vars_files/connection.yml
+ roles:
+ - powerflex_tb
+
+- name: Configure protection domain, fault set and storage pool.
+ hosts: config
+ vars_files:
+ - vars_files/connection.yml
+ roles:
+ - powerflex_config
+
+- name: Install and configure PowerFlex Web UI
+ hosts: webui
+ vars_files:
+ - vars_files/connection.yml
+ roles:
+ - powerflex_webui
+
+- name: Install and configure PowerFlex SDC
+ hosts: sdc
+ vars_files:
+ - vars_files/connection.yml
+ roles:
+ - powerflex_sdc
+
+- name: Install and configure PowerFlex LIA
+ hosts: lia
+ vars_files:
+ - vars_files/connection.yml
+ roles:
+ - powerflex_lia
+
+- name: Install and configure PowerFlex SDS
+ hosts: sds
+ vars_files:
+ - vars_files/connection.yml
+ roles:
+ - powerflex_sds
+
+- name: Install PowerFlex SDR
+ hosts: sdr
+ vars_files:
+ - vars_files/connection.yml
+ roles:
+ - powerflex_sdr
diff --git a/ansible_collections/dellemc/powerflex/playbooks/roles/site_powerflex45.yml b/ansible_collections/dellemc/powerflex/playbooks/roles/site_powerflex45.yml
new file mode 100644
index 000000000..bd75f6bbe
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/playbooks/roles/site_powerflex45.yml
@@ -0,0 +1,59 @@
+---
+- name: "Install PowerFlex Common"
+ hosts: all
+ roles:
+ - powerflex_common
+
+- name: Install and configure PowerFlex ActiveMQ
+ hosts: activemq
+ vars_files:
+ - vars_files/connection.yml
+ roles:
+ - powerflex_activemq
+
+- name: Install and configure PowerFlex LIA
+ hosts: lia
+ vars_files:
+ - vars_files/connection.yml
+ roles:
+ - powerflex_lia
+
+- name: Install and configure PowerFlex MDM
+ hosts: mdm
+ roles:
+ - powerflex_mdm
+
+- name: Install and configure PowerFlex TB
+ hosts: tb
+ vars_files:
+ - vars_files/connection.yml
+ roles:
+ - powerflex_tb
+
+- name: Configure protection domain, fault set and storage pool.
+ hosts: config
+ vars_files:
+ - vars_files/connection.yml
+ roles:
+ - powerflex_config
+
+- name: Install and configure PowerFlex SDC
+ hosts: sdc
+ vars_files:
+ - vars_files/connection.yml
+ roles:
+ - powerflex_sdc
+
+- name: Install and configure PowerFlex SDS
+ hosts: sds
+ vars_files:
+ - vars_files/connection.yml
+ roles:
+ - powerflex_sds
+
+- name: Install PowerFlex SDR
+ hosts: sdr
+ vars_files:
+ - vars_files/connection.yml
+ roles:
+ - powerflex_sdr
diff --git a/ansible_collections/dellemc/powerflex/playbooks/roles/uninstall_powerflex.yml b/ansible_collections/dellemc/powerflex/playbooks/roles/uninstall_powerflex.yml
new file mode 100644
index 000000000..464cca789
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/playbooks/roles/uninstall_powerflex.yml
@@ -0,0 +1,64 @@
+---
+- name: Uninstall PowerFlex SDC
+ hosts: sdc
+ vars_files:
+ - vars_files/connection.yml
+ roles:
+ - powerflex_sdc
+ vars:
+ powerflex_sdc_state: absent
+
+- name: Uninstall PowerFlex SDS
+ hosts: sds
+ vars_files:
+ - vars_files/connection.yml
+ roles:
+ - powerflex_sds
+ vars:
+ powerflex_sds_state: absent
+
+- name: Uninstall PowerFlex SDR
+ hosts: sdr
+ vars_files:
+ - vars_files/connection.yml
+ roles:
+ - powerflex_sdr
+ vars:
+ powerflex_sdr_state: absent
+
+- name: Uninstall PowerFlex LIA
+ hosts: lia
+ roles:
+ - powerflex_lia
+ vars:
+ powerflex_lia_state: absent
+
+- name: Uninstall PowerFlex web UI
+ hosts: webui
+ roles:
+ - powerflex_webui
+ vars:
+ powerflex_webui_state: absent
+
+- name: Uninstall PowerFlex TB
+ hosts: tb
+ vars_files:
+ - vars_files/connection.yml
+ roles:
+ - powerflex_tb
+ vars:
+ powerflex_tb_state: absent
+
+- name: Uninstall PowerFlex gateway
+ hosts: gateway
+ roles:
+ - powerflex_gateway
+ vars:
+ powerflex_gateway_state: absent
+
+- name: Uninstall PowerFlex MDM
+ hosts: mdm
+ roles:
+ - powerflex_mdm
+ vars:
+ powerflex_mdm_state: absent
diff --git a/ansible_collections/dellemc/powerflex/playbooks/roles/uninstall_powerflex45.yml b/ansible_collections/dellemc/powerflex/playbooks/roles/uninstall_powerflex45.yml
new file mode 100644
index 000000000..2d36b5bc0
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/playbooks/roles/uninstall_powerflex45.yml
@@ -0,0 +1,61 @@
+---
+- name: Uninstall PowerFlex SDC
+ hosts: sdc
+ vars_files:
+ - vars_files/connection.yml
+ roles:
+ - powerflex_sdc
+ vars:
+ powerflex_sdc_state: absent
+
+- name: Uninstall PowerFlex SDS
+ hosts: sds
+ vars_files:
+ - vars_files/connection.yml
+ roles:
+ - powerflex_sds
+ vars:
+ powerflex_sds_state: absent
+
+- name: Uninstall PowerFlex SDR
+ hosts: sdr
+ vars_files:
+ - vars_files/connection.yml
+ roles:
+ - powerflex_sdr
+ vars:
+ powerflex_sdr_state: absent
+
+- name: Uninstall PowerFlex TB
+ hosts: tb
+ vars_files:
+ - vars_files/connection.yml
+ roles:
+ - powerflex_tb
+ vars:
+ powerflex_tb_state: absent
+
+- name: Uninstall PowerFlex MDM
+ hosts: mdm
+ roles:
+ - powerflex_mdm
+ vars_files:
+ - vars_files/connection.yml
+ vars:
+ powerflex_mdm_state: absent
+
+- name: Uninstall PowerFlex LIA
+ hosts: lia
+ roles:
+ - powerflex_lia
+ vars:
+ powerflex_lia_state: absent
+
+- name: Uninstall PowerFlex ActiveMQ
+ hosts: activemq
+ vars:
+ powerflex_activemq_state: absent
+ vars_files:
+ - vars_files/connection.yml
+ roles:
+ - powerflex_activemq
diff --git a/ansible_collections/dellemc/powerflex/playbooks/roles/vars_files/connection.yml b/ansible_collections/dellemc/powerflex/playbooks/roles/vars_files/connection.yml
new file mode 100644
index 000000000..98bc8526b
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/playbooks/roles/vars_files/connection.yml
@@ -0,0 +1,8 @@
+---
+# common params
+hostname: 10.XX.XX.XX
+username: 'user'
+password: 'password'
+validate_certs: false
+port: 443
+timeout: 120
diff --git a/ansible_collections/dellemc/powerflex/plugins/doc_fragments/powerflex.py b/ansible_collections/dellemc/powerflex/plugins/doc_fragments/powerflex.py
index 349680345..0c0e0d9e1 100644
--- a/ansible_collections/dellemc/powerflex/plugins/doc_fragments/powerflex.py
+++ b/ansible_collections/dellemc/powerflex/plugins/doc_fragments/powerflex.py
@@ -1,4 +1,4 @@
-# Copyright: (c) 2020, 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)
from __future__ import absolute_import, division, print_function
@@ -48,12 +48,12 @@ class ModuleDocFragment(object):
- Time after which connection will get terminated.
- It is to be mentioned in seconds.
type: int
- required: False
+ required: false
default: 120
requirements:
- - A Dell PowerFlex storage system version 3.5 or later.
- - Ansible-core 2.12 or later.
- - PyPowerFlex 1.6.0.
+ - A Dell PowerFlex storage system version 3.6 or later.
+ - Ansible-core 2.14 or later.
+ - PyPowerFlex 1.9.0.
- Python 3.9, 3.10 or 3.11.
notes:
- The modules present in the collection named as 'dellemc.powerflex'
diff --git a/ansible_collections/dellemc/powerflex/plugins/module_utils/storage/dell/libraries/__init__.py b/ansible_collections/dellemc/powerflex/plugins/module_utils/storage/dell/libraries/__init__.py
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/plugins/module_utils/storage/dell/libraries/__init__.py
diff --git a/ansible_collections/dellemc/powerflex/plugins/module_utils/storage/dell/libraries/configuration.py b/ansible_collections/dellemc/powerflex/plugins/module_utils/storage/dell/libraries/configuration.py
new file mode 100644
index 000000000..b7ca3ec9a
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/plugins/module_utils/storage/dell/libraries/configuration.py
@@ -0,0 +1,121 @@
+# 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
+
+LOG = utils.get_logger('configuration')
+
+
+class Configuration:
+
+ """
+ The configuration SDK class with shared configuration operations.
+ """
+
+ def __init__(self, powerflex_conn, module):
+ """
+ Initialize the configuration class
+ :param configuration: The configuration SDK instance
+ :param module: Ansible module object
+ """
+ self.module = module
+ self.powerflex_conn = powerflex_conn
+
+ def get_protection_domain(
+ self, protection_domain_name=None, protection_domain_id=None
+ ):
+ """
+ Get protection domain details
+ :param protection_domain_name: Name of the protection domain
+ :param protection_domain_id: ID of the protection domain
+ :return: Protection domain details if exists
+ :rtype: dict
+ """
+
+ name_or_id = (
+ protection_domain_id if protection_domain_id else protection_domain_name
+ )
+
+ try:
+ if protection_domain_id:
+ pd_details = self.powerflex_conn.protection_domain.get(
+ filter_fields={"id": protection_domain_id}
+ )
+
+ else:
+ pd_details = self.powerflex_conn.protection_domain.get(
+ filter_fields={"name": protection_domain_name}
+ )
+
+ if len(pd_details) == 0:
+ error_msg = (
+ "Unable to find the protection domain with " "'%s'." % name_or_id
+ )
+ LOG.error(error_msg)
+ self.module.fail_json(msg=error_msg)
+
+ return pd_details[0]
+
+ except Exception as e:
+ error_msg = (
+ "Failed to get the protection domain '%s' with "
+ "error '%s'" % (name_or_id, str(e))
+ )
+ LOG.error(error_msg)
+ self.module.fail_json(msg=error_msg)
+
+ def get_fault_set(self, fault_set_name=None, fault_set_id=None, protection_domain_id=None):
+ """Get fault set details
+ :param fault_set_name: Name of the fault set
+ :param fault_set_id: Id of the fault set
+ :param protection_domain_id: ID of the protection domain
+ :return: Fault set details
+ :rtype: dict
+ """
+ name_or_id = fault_set_id if fault_set_id \
+ else fault_set_name
+ try:
+ fs_details = {}
+ if fault_set_id:
+ fs_details = self.powerflex_conn.fault_set.get(
+ filter_fields={'id': name_or_id})
+
+ if fault_set_name:
+ fs_details = self.powerflex_conn.fault_set.get(
+ filter_fields={'name': name_or_id, 'protectionDomainId': protection_domain_id})
+
+ if not fs_details:
+ msg = f"Unable to find the fault set with {name_or_id}"
+ LOG.info(msg)
+ return None
+
+ return fs_details[0]
+
+ except Exception as e:
+ error_msg = f"Failed to get the fault set '{name_or_id}' with error '{str(e)}'"
+ LOG.error(error_msg)
+ self.module.fail_json(msg=error_msg)
+
+ def get_associated_sds(self, fault_set_id=None):
+ """Get associated SDS to a fault set
+ :param fault_set_id: Id of the fault set
+ :return: Associated SDS details
+ :rtype: dict
+ """
+ try:
+ if fault_set_id:
+ sds_details = self.powerflex_conn.fault_set.get_sdss(
+ fault_set_id=fault_set_id)
+
+ return sds_details
+
+ except Exception as e:
+ error_msg = f"Failed to get the associated SDS with error '{str(e)}'"
+ LOG.error(error_msg)
+ self.module.fail_json(msg=error_msg)
diff --git a/ansible_collections/dellemc/powerflex/plugins/module_utils/storage/dell/libraries/powerflex_base.py b/ansible_collections/dellemc/powerflex/plugins/module_utils/storage/dell/libraries/powerflex_base.py
new file mode 100644
index 000000000..0cfb2659f
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/plugins/module_utils/storage/dell/libraries/powerflex_base.py
@@ -0,0 +1,45 @@
+# 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
+
+LOG = utils.get_logger('powerflex_base')
+
+
+class PowerFlexBase:
+
+ '''PowerFlex Base Class'''
+
+ def __init__(self, ansible_module, ansible_module_params):
+ """
+ Initialize the powerflex base class
+
+ :param ansible_module: Ansible module class
+ :type ansible_module: AnsibleModule
+ :param ansible_module_params: Parameters for ansible module class
+ :type ansible_module_params: dict
+ """
+ self.module_params = utils.get_powerflex_gateway_host_parameters()
+ ansible_module_params['argument_spec'].update(self.module_params)
+
+ # Initialize the ansible module
+ self.module = ansible_module(
+ **ansible_module_params
+ )
+
+ utils.ensure_required_libs(self.module)
+ self.result = {"changed": False}
+
+ try:
+ self.powerflex_conn = utils.get_powerflex_gateway_host_connection(
+ self.module.params)
+ LOG.info("Got the PowerFlex system connection object instance")
+ except Exception as e:
+ LOG.error(str(e))
+ self.module.fail_json(msg=str(e))
diff --git a/ansible_collections/dellemc/powerflex/plugins/module_utils/storage/dell/utils.py b/ansible_collections/dellemc/powerflex/plugins/module_utils/storage/dell/utils.py
index 8503aeb0c..94024d498 100644
--- a/ansible_collections/dellemc/powerflex/plugins/module_utils/storage/dell/utils.py
+++ b/ansible_collections/dellemc/powerflex/plugins/module_utils/storage/dell/utils.py
@@ -1,4 +1,4 @@
-# Copyright: (c) 2021, 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)
from __future__ import absolute_import, division, print_function
@@ -17,14 +17,7 @@ from ansible.module_utils.basic import missing_required_lib
"""import PyPowerFlex lib"""
try:
from PyPowerFlex import PowerFlexClient
- from PyPowerFlex.objects.sds import Sds
- from PyPowerFlex.objects import protection_domain
- from PyPowerFlex.objects import storage_pool
- from PyPowerFlex.objects import sdc
- from PyPowerFlex.objects import volume
- from PyPowerFlex.objects import system
- from PyPowerFlex.objects.system import SnapshotDef
-
+ from PyPowerFlex.objects.system import SnapshotDef # pylint: disable=unused-import
HAS_POWERFLEX_SDK, POWERFLEX_SDK_IMP_ERR = True, None
except ImportError:
HAS_POWERFLEX_SDK, POWERFLEX_SDK_IMP_ERR = False, traceback.format_exc()
@@ -40,7 +33,7 @@ except ImportError:
"""importing dateutil"""
try:
- import dateutil.relativedelta
+ import dateutil.relativedelta # noqa # pylint: disable=unused-import
HAS_DATEUTIL, DATEUTIL_IMP_ERR = True, None
except ImportError:
HAS_DATEUTIL, DATEUTIL_IMP_ERR = False, traceback.format_exc()
@@ -87,10 +80,10 @@ def ensure_required_libs(module):
exception=PKG_RSRC_IMP_ERR)
if not HAS_POWERFLEX_SDK:
- module.fail_json(msg=missing_required_lib("PyPowerFlex V 1.6.0 or above"),
+ module.fail_json(msg=missing_required_lib("PyPowerFlex V 1.9.0 or above"),
exception=POWERFLEX_SDK_IMP_ERR)
- min_ver = '1.6.0'
+ min_ver = '1.9.0'
try:
curr_version = pkg_resources.require("PyPowerFlex")[0].version
supported_version = (parse_version(curr_version) >= parse_version(min_ver))
@@ -184,3 +177,19 @@ def is_invalid_name(name):
regexp = re.compile(r'^[a-zA-Z0-9!@#$%^~*_-]*$')
if not regexp.search(name):
return True
+
+
+def get_time_minutes(time, time_unit):
+ """Convert the given time to minutes"""
+
+ if time is not None and time > 0:
+ if time_unit in ('Hour'):
+ return time * 60
+ elif time_unit in ('Day'):
+ return time * 60 * 24
+ elif time_unit in ('Week'):
+ return time * 60 * 24 * 7
+ else:
+ return time
+ else:
+ return 0
diff --git a/ansible_collections/dellemc/powerflex/plugins/modules/device.py b/ansible_collections/dellemc/powerflex/plugins/modules/device.py
index a321315e3..e83353185 100644
--- a/ansible_collections/dellemc/powerflex/plugins/modules/device.py
+++ b/ansible_collections/dellemc/powerflex/plugins/modules/device.py
@@ -109,6 +109,14 @@ options:
choices: ['present', 'absent']
required: true
type: str
+ force:
+ description:
+ - Using the Force flag to add a device.
+ - Use this flag, to overwrite existing data on the device.
+ - Use this flag with caution, because all data on the device will be
+ destroyed.
+ type: bool
+ default: false
notes:
- The value for device_id is generated only after successful addition of the
device.
@@ -135,6 +143,22 @@ EXAMPLES = r'''
protection_domain_name: "domain1"
external_acceleration_type: "ReadAndWrite"
state: "present"
+- name: Add a device with force flag
+ dellemc.powerflex.device:
+ hostname: "{{hostname}}"
+ username: "{{username}}"
+ password: "{{password}}"
+ validate_certs: "{{validate_certs}}"
+ port: "{{port}}"
+ current_pathname: "/dev/sdb"
+ sds_name: "node1"
+ media_type: "HDD"
+ device_name: "device2"
+ storage_pool_name: "pool1"
+ protection_domain_name: "domain1"
+ external_acceleration_type: "ReadAndWrite"
+ force: true
+ state: "present"
- name: Get device details using device_id
dellemc.powerflex.device:
hostname: "{{hostname}}"
@@ -166,23 +190,23 @@ EXAMPLES = r'''
state: "present"
- name: Remove a device using device_id
dellemc.powerflex.device:
- hostname: "{{hostname}}"
- username: "{{username}}"
- password: "{{password}}"
- validate_certs: "{{validate_certs}}"
- port: "{{port}}"
- device_id: "76eb7e2f00010000"
- state: "absent"
+ hostname: "{{hostname}}"
+ username: "{{username}}"
+ password: "{{password}}"
+ validate_certs: "{{validate_certs}}"
+ port: "{{port}}"
+ device_id: "76eb7e2f00010000"
+ state: "absent"
- name: Remove a device using (current_pathname, sds_id)
dellemc.powerflex.device:
- hostname: "{{hostname}}"
- username: "{{username}}"
- password: "{{password}}"
- validate_certs: "{{validate_certs}}"
- port: "{{port}}"
- current_pathname: "/dev/sdb"
- sds_name: "node1"
- state: "absent"
+ hostname: "{{hostname}}"
+ username: "{{username}}"
+ password: "{{password}}"
+ validate_certs: "{{validate_certs}}"
+ port: "{{port}}"
+ current_pathname: "/dev/sdb"
+ sds_name: "node1"
+ state: "absent"
'''
RETURN = r'''
@@ -715,12 +739,11 @@ class PowerFlexDevice(object):
self.powerflex_conn.device.create(
current_pathname=current_pathname,
- sds_id=sds_id,
- acceleration_pool_id=acceleration_pool_id,
+ sds_id=sds_id, acceleration_pool_id=acceleration_pool_id,
external_acceleration_type=external_acceleration_type,
- media_type=media_type,
- name=device_name,
- storage_pool_id=storage_pool_id)
+ media_type=media_type, name=device_name,
+ storage_pool_id=storage_pool_id,
+ force=self.module.params['force'])
return True
except Exception as e:
error_msg = "Adding device %s operation failed with " \
@@ -1076,21 +1099,15 @@ def get_powerflex_device_parameters():
"""This method provide parameter required for the device module on
PowerFlex"""
return dict(
- current_pathname=dict(),
- device_name=dict(),
- device_id=dict(),
- sds_name=dict(),
- sds_id=dict(),
- storage_pool_name=dict(),
- storage_pool_id=dict(),
- acceleration_pool_id=dict(),
- acceleration_pool_name=dict(),
- protection_domain_name=dict(),
- protection_domain_id=dict(),
- external_acceleration_type=dict(choices=['Invalid', 'None', 'Read',
- 'Write', 'ReadAndWrite']),
+ current_pathname=dict(), device_name=dict(), device_id=dict(),
+ sds_name=dict(), sds_id=dict(), storage_pool_name=dict(),
+ storage_pool_id=dict(), acceleration_pool_id=dict(),
+ acceleration_pool_name=dict(), protection_domain_name=dict(),
+ protection_domain_id=dict(), external_acceleration_type=dict(
+ choices=['Invalid', 'None', 'Read', 'Write', 'ReadAndWrite']),
media_type=dict(choices=['HDD', 'SSD', 'NVDIMM']),
- state=dict(required=True, type='str', choices=['present', 'absent'])
+ state=dict(required=True, type='str', choices=['present', 'absent']),
+ force=dict(type='bool', default=False)
)
diff --git a/ansible_collections/dellemc/powerflex/plugins/modules/fault_set.py b/ansible_collections/dellemc/powerflex/plugins/modules/fault_set.py
new file mode 100644
index 000000000..bfa926dd6
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/plugins/modules/fault_set.py
@@ -0,0 +1,380 @@
+#!/usr/bin/python
+
+# Copyright: (c) 2024, Dell Technologies
+# Apache License version 2.0 (see MODULE-LICENSE or http://www.apache.org/licenses/LICENSE-2.0.txt)
+
+""" Ansible module for managing Fault Sets on Dell Technologies (Dell) PowerFlex"""
+
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+DOCUMENTATION = r'''
+module: fault_set
+version_added: '2.2.0'
+short_description: Manage Fault Sets on Dell PowerFlex
+description:
+- Managing fault sets on PowerFlex storage system includes creating,
+ getting details, renaming and deleting a fault set.
+author:
+- Carlos Tronco (@ctronco) <ansible.team@dell.com>
+- Trisha Datta (@trisha-dell) <ansible.team@dell.com>
+extends_documentation_fragment:
+ - dellemc.powerflex.powerflex
+options:
+ fault_set_name:
+ description:
+ - Name of the Fault Set.
+ - Mutually exclusive with I(fault_set_id).
+ type: str
+ fault_set_id:
+ description:
+ - ID of the Fault Set.
+ - Mutually exclusive with I(fault_set_name).
+ type: str
+ protection_domain_name:
+ description:
+ - Name of protection domain.
+ - Mutually exclusive with I(protection_domain_id).
+ type: str
+ protection_domain_id:
+ description:
+ - ID of the protection domain.
+ - Mutually exclusive with I(protection_domain_name).
+ type: str
+ fault_set_new_name:
+ description:
+ - New name of the fault set.
+ type: str
+ state:
+ description:
+ - State of the Fault Set.
+ choices: ['present', 'absent']
+ default: 'present'
+ type: str
+notes:
+ - The I(check_mode) is supported.
+ - When I(fault_set_name) is provided, I(protection_domain_name)
+ or I(protection_domain_id) must be provided.
+'''
+
+
+EXAMPLES = r'''
+
+- name: Create Fault Set on Protection Domain
+ dellemc.powerflex.fault_set:
+ hostname: "{{ hostname }}"
+ username: "{{ username }}"
+ password: "{{ password }}"
+ validate_certs: "{{ validate_certs }}"
+ fault_set_name: "{{ fault_set_name }}"
+ protection_domain_name: "{{ pd_name }}"
+ state: present
+
+- name: Rename Fault Set
+ dellemc.powerflex.fault_set:
+ hostname: "{{ hostname }}"
+ username: "{{ username }}"
+ password: "{{ password }}"
+ validate_certs: "{{ validate_certs }}"
+ fault_set_name: "{{ fault_set_name }}"
+ fault_set_new_name: "{{ fault_set_new_name }}"
+ state: present
+
+- name: Get details of a Fault Set
+ dellemc.powerflex.fault_set:
+ hostname: "{{ hostname }}"
+ username: "{{ username }}"
+ password: "{{ password }}"
+ validate_certs: "{{ validate_certs }}"
+ fault_set_id: "{{ fault_set_id }}"
+ state: present
+
+- name: Delete Fault Set
+ dellemc.powerflex.fault_set:
+ hostname: "{{ hostname }}"
+ username: "{{ username }}"
+ password: "{{ password }}"
+ validate_certs: "{{ validate_certs }}"
+ fault_set_id: "{{ fault_set_id }}"
+ state: absent
+'''
+
+RETURN = r'''
+changed:
+ description: Whether or not the resource has changed.
+ returned: always
+ type: bool
+ sample: 'false'
+
+fault_set_details:
+ description: Details of fault set.
+ returned: always
+ type: dict
+ contains:
+ protectionDomainId:
+ description: Unique identifier of the protection domain.
+ type: str
+ protectionDomainName:
+ description: Name of the protection domain.
+ type: str
+ name:
+ description: Name of the fault set.
+ type: str
+ id:
+ description: Unique identifier of the fault set.
+ type: str
+ SDS:
+ description: List of SDS associated to the fault set.
+ type: list
+ elements: dict
+ links:
+ description: Fault set links.
+ type: list
+ contains:
+ href:
+ description: Fault Set instance URL.
+ type: str
+ rel:
+ description: Relationship of fault set with different
+ entities.
+ type: str
+ sample: {
+ "protectionDomainId": "da721a8300000000",
+ "protectionDomainName": "sample-pd",
+ "name": "fs_001",
+ "id": "eb44b70500000000",
+ "links": []
+ }
+
+'''
+
+
+from ansible_collections.dellemc.powerflex.plugins.module_utils.storage.dell import (
+ utils,
+)
+from ansible_collections.dellemc.powerflex.plugins.module_utils.storage.dell.libraries.powerflex_base \
+ import PowerFlexBase
+from ansible_collections.dellemc.powerflex.plugins.module_utils.storage.dell.libraries.configuration \
+ import Configuration
+from ansible.module_utils.basic import AnsibleModule
+
+
+LOG = utils.get_logger("fault_set")
+
+
+class PowerFlexFaultSet(PowerFlexBase):
+ """Class with FaultSet operations"""
+
+ def __init__(self):
+ """Define all parameters required by this module"""
+
+ mutually_exclusive = [
+ ["fault_set_name", "fault_set_id"],
+ ["protection_domain_name", "protection_domain_id"],
+ ]
+ required_one_of = [["fault_set_name", "fault_set_id"]]
+
+ ansible_module_params = {
+ 'argument_spec': get_powerflex_fault_set_parameters(),
+ 'supports_check_mode': True,
+ 'mutually_exclusive': mutually_exclusive,
+ 'required_one_of': required_one_of
+ }
+ super().__init__(AnsibleModule, ansible_module_params)
+
+ self.result = dict(
+ changed=False,
+ fault_set_details={}
+ )
+
+ def get_protection_domain(
+ self, protection_domain_name=None, protection_domain_id=None
+ ):
+ """Get the details of a protection domain in a given PowerFlex storage
+ system"""
+ return Configuration(self.powerflex_conn, self.module).get_protection_domain(
+ protection_domain_name=protection_domain_name, protection_domain_id=protection_domain_id)
+
+ def get_associated_sds(
+ self, fault_set_id=None
+ ):
+ """Get the details of SDS associated to given fault set in a given PowerFlex storage
+ system"""
+ return Configuration(self.powerflex_conn, self.module).get_associated_sds(
+ fault_set_id=fault_set_id)
+
+ def create_fault_set(self, fault_set_name, protection_domain_id):
+ """
+ Create Fault Set
+ :param fault_set_name: Name of the fault set
+ :type fault_set_name: str
+ :param protection_domain_id: ID of the protection domain
+ :type protection_domain_id: str
+ :return: Boolean indicating if create operation is successful
+ """
+ try:
+ if not self.module.check_mode:
+ msg = (f"Creating fault set with name: {fault_set_name} on "
+ f"protection domain with id: {protection_domain_id}")
+ LOG.info(msg)
+ self.powerflex_conn.fault_set.create(
+ name=fault_set_name, protection_domain_id=protection_domain_id
+ )
+ return self.get_fault_set(
+ fault_set_name=fault_set_name,
+ protection_domain_id=protection_domain_id)
+
+ except Exception as e:
+ error_msg = (f"Create fault set {fault_set_name} operation failed "
+ f"with error {str(e)}")
+ LOG.error(error_msg)
+ self.module.fail_json(msg=error_msg)
+
+ def get_fault_set(self, fault_set_name=None, fault_set_id=None, protection_domain_id=None):
+ """Get fault set details
+ :param fault_set_name: Name of the fault set
+ :param fault_set_id: Id of the fault set
+ :param protection_domain_id: ID of the protection domain
+ :return: Fault set details
+ :rtype: dict
+ """
+ return Configuration(self.powerflex_conn, self.module).get_fault_set(
+ fault_set_name=fault_set_name, fault_set_id=fault_set_id, protection_domain_id=protection_domain_id)
+
+ def is_rename_required(self, fault_set_details, fault_set_params):
+ """To get the details of the fields to be modified."""
+
+ if fault_set_params['fault_set_new_name'] is not None and \
+ fault_set_params['fault_set_new_name'] != fault_set_details['name']:
+ return True
+
+ return False
+
+ def rename_fault_set(self, fault_set_id,
+ new_name):
+ """Perform rename operation on a fault set"""
+
+ try:
+ if not self.module.check_mode:
+ self.powerflex_conn.fault_set.rename(
+ fault_set_id=fault_set_id,
+ name=new_name)
+ return self.get_fault_set(
+ fault_set_id=fault_set_id)
+ except Exception as e:
+ msg = (f'Failed to rename the fault set instance '
+ f'with error {str(e)}')
+ LOG.error(msg)
+ self.module.fail_json(msg=msg)
+
+ def delete_fault_set(self, fault_set_id):
+ """Delete the Fault Set"""
+ try:
+ if not self.module.check_mode:
+ LOG.info(msg=f"Removing Fault Set {fault_set_id}")
+ self.powerflex_conn.fault_set.delete(fault_set_id)
+ LOG.info("returning None")
+ return None
+ return self.get_fault_set(
+ fault_set_id=fault_set_id)
+ except Exception as e:
+ errormsg = f"Removing Fault Set {fault_set_id} failed with error {str(e)}"
+ LOG.error(errormsg)
+ self.module.fail_json(msg=errormsg)
+
+ def validate_parameters(self, fault_set_params):
+ params = [fault_set_params['fault_set_name'], fault_set_params['fault_set_new_name']]
+ for param in params:
+ if param is not None and len(param.strip()) == 0:
+ error_msg = "Provide valid value for name for the " \
+ "creation/modification of the fault set."
+ LOG.error(error_msg)
+ self.module.fail_json(msg=error_msg)
+ if fault_set_params['fault_set_name'] is not None and \
+ fault_set_params['protection_domain_id'] is None and fault_set_params['protection_domain_name'] is None:
+ error_msg = "Provide protection_domain_id/protection_domain_name with fault_set_name."
+ LOG.error(error_msg)
+ self.module.fail_json(msg=error_msg)
+
+
+def get_powerflex_fault_set_parameters():
+ """This method provide parameter required for the Ansible Fault Set module on
+ PowerFlex"""
+ return dict(
+ fault_set_name=dict(),
+ fault_set_id=dict(),
+ protection_domain_name=dict(),
+ protection_domain_id=dict(),
+ fault_set_new_name=dict(),
+ state=dict(default='present', choices=['present', 'absent'])
+ )
+
+
+class FaultSetExitHandler():
+ def handle(self, fault_set_obj, fault_set_details):
+ fault_set_obj.result["fault_set_details"] = fault_set_details
+ if fault_set_details:
+ fault_set_obj.result["fault_set_details"]["protectionDomainName"] = \
+ fault_set_obj.get_protection_domain(
+ protection_domain_id=fault_set_details["protectionDomainId"])["name"]
+ fault_set_obj.result["fault_set_details"]["SDS"] = \
+ fault_set_obj.get_associated_sds(
+ fault_set_id=fault_set_details['id'])
+ fault_set_obj.module.exit_json(**fault_set_obj.result)
+
+
+class FaultSetDeleteHandler():
+ def handle(self, fault_set_obj, fault_set_params, fault_set_details):
+ if fault_set_params['state'] == 'absent' and fault_set_details:
+ fault_set_details = fault_set_obj.delete_fault_set(fault_set_details['id'])
+ fault_set_obj.result['changed'] = True
+
+ FaultSetExitHandler().handle(fault_set_obj, fault_set_details)
+
+
+class FaultSetRenameHandler():
+ def handle(self, fault_set_obj, fault_set_params, fault_set_details):
+ if fault_set_params['state'] == 'present' and fault_set_details:
+ is_rename_required = fault_set_obj.is_rename_required(fault_set_details, fault_set_params)
+ if is_rename_required:
+ fault_set_details = fault_set_obj.rename_fault_set(fault_set_id=fault_set_details['id'],
+ new_name=fault_set_params['fault_set_new_name'])
+ fault_set_obj.result['changed'] = True
+
+ FaultSetDeleteHandler().handle(fault_set_obj, fault_set_params, fault_set_details)
+
+
+class FaultSetCreateHandler():
+ def handle(self, fault_set_obj, fault_set_params, fault_set_details, pd_id):
+ if fault_set_params['state'] == 'present' and not fault_set_details:
+ fault_set_details = fault_set_obj.create_fault_set(fault_set_name=fault_set_params['fault_set_name'],
+ protection_domain_id=pd_id)
+ fault_set_obj.result['changed'] = True
+
+ FaultSetRenameHandler().handle(fault_set_obj, fault_set_params, fault_set_details)
+
+
+class FaultSetHandler():
+ def handle(self, fault_set_obj, fault_set_params):
+ fault_set_obj.validate_parameters(fault_set_params=fault_set_params)
+ pd_id = None
+ if fault_set_params['protection_domain_id'] or fault_set_params['protection_domain_name']:
+ pd_id = fault_set_obj.get_protection_domain(
+ protection_domain_id=fault_set_params['protection_domain_id'],
+ protection_domain_name=fault_set_params['protection_domain_name'])['id']
+ fault_set_details = fault_set_obj.get_fault_set(fault_set_id=fault_set_params['fault_set_id'],
+ fault_set_name=fault_set_params['fault_set_name'],
+ protection_domain_id=pd_id)
+ FaultSetCreateHandler().handle(fault_set_obj, fault_set_params, fault_set_details, pd_id)
+
+
+def main():
+ """ Create PowerFlex fault set object and perform action on it
+ based on user input from playbook."""
+ obj = PowerFlexFaultSet()
+ FaultSetHandler().handle(obj, obj.module.params)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/ansible_collections/dellemc/powerflex/plugins/modules/info.py b/ansible_collections/dellemc/powerflex/plugins/modules/info.py
index ff1401d63..33f3a8ad8 100644
--- a/ansible_collections/dellemc/powerflex/plugins/modules/info.py
+++ b/ansible_collections/dellemc/powerflex/plugins/modules/info.py
@@ -1,6 +1,6 @@
-#!/usr/bin/python
+# !/usr/bin/python
-# Copyright: (c) 2021, 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)
"""Ansible module for Gathering information about Dell Technologies (Dell) PowerFlex"""
@@ -21,12 +21,16 @@ description:
- Gathering information about Dell PowerFlex storage system includes
getting the api details, list of volumes, SDSs, SDCs, storage pools,
protection domains, snapshot policies, and devices.
+- Gathering information about Dell PowerFlex Manager includes getting the
+ list of managed devices, deployments and service templates.
extends_documentation_fragment:
- dellemc.powerflex.powerflex
author:
- Arindam Datta (@dattaarindam) <ansible.team@dell.com>
+- Trisha Datta (@trisha-dell) <ansible.team@dell.com>
+- Jennifer John (@Jennifer-John) <ansible.team@dell.com>
options:
gather_subset:
@@ -42,8 +46,13 @@ options:
- Devices - C(device).
- Replication consistency groups - C(rcg).
- Replication pairs - C(replication_pair).
+ - Fault Sets - C(fault_set).
+ - Service templates - C(service_template).
+ - Managed devices - C(managed_device).
+ - Deployments - C(deployment).
choices: [vol, storage_pool, protection_domain, sdc, sds,
- snapshot_policy, device, rcg, replication_pair]
+ snapshot_policy, device, rcg, replication_pair,
+ fault_set, service_template, managed_device, deployment]
type: list
elements: str
filters:
@@ -62,16 +71,63 @@ options:
filter_operator:
description:
- Operation to be performed on filter key.
+ - Choice I('contains') is supported for gather_subset keys I(service_template), I(managed_device), I(deployment).
type: str
- choices: [equal]
+ choices: [equal, contains]
required: true
filter_value:
description:
- Value of the filter key.
type: str
required: true
+ limit:
+ description:
+ - Page limit.
+ - Supported for gather_subset keys I(service_template), I(managed_device), I(deployment).
+ type: int
+ default: 50
+ offset:
+ description:
+ - Pagination offset.
+ - Supported for gather_subset keys I(service_template), I(managed_device), I(deployment).
+ type: int
+ default: 0
+ sort:
+ description:
+ - Sort the returned components based on specified field.
+ - Supported for gather_subset keys I(service_template), I(managed_device), I(deployment).
+ - The supported sort keys for the gather_subset can be referred from PowerFlex Manager API documentation in developer.dell.com.
+ type: str
+ include_devices:
+ description:
+ - Include devices in response.
+ - Applicable when gather_subset is I(deployment).
+ type: bool
+ default: true
+ include_template:
+ description:
+ - Include service templates in response.
+ - Applicable when gather_subset is I(deployment).
+ type: bool
+ default: true
+ full:
+ description:
+ - Specify if response is full or brief.
+ - Applicable when gather_subset is I(deployment), I(service_template).
+ - For I(deployment) specify to use full templates including resources in response.
+ type: bool
+ default: false
+ include_attachments:
+ description:
+ - Include attachments.
+ - Applicable when gather_subset is I(service_template).
+ type: bool
+ default: true
notes:
- The I(check_mode) is supported.
+ - The supported filter keys for the gather_subset can be referred from PowerFlex Manager API documentation in developer.dell.com.
+ - The I(filter), I(sort), I(limit) and I(offset) options will be ignored when more than one I(gather_subset) is specified along with
+ I(service_template), I(managed_device) or I(deployment).
'''
EXAMPLES = r'''
@@ -91,6 +147,7 @@ EXAMPLES = r'''
- device
- rcg
- replication_pair
+ - fault_set
- name: Get a subset list of PowerFlex volumes
dellemc.powerflex.info:
@@ -104,6 +161,35 @@ EXAMPLES = r'''
- filter_key: "name"
filter_operator: "equal"
filter_value: "ansible_test"
+
+- name: Get deployment and resource provisioning info
+ dellemc.powerflex.info:
+ hostname: "{{hostname}}"
+ username: "{{username}}"
+ password: "{{password}}"
+ validate_certs: "{{validate_certs}}"
+ gather_subset:
+ - managed_device
+ - deployment
+ - service_template
+
+- name: Get deployment with filter, sort, pagination
+ dellemc.powerflex.info:
+ hostname: "{{hostname}}"
+ username: "{{username}}"
+ password: "{{password}}"
+ validate_certs: "{{validate_certs}}"
+ gather_subset:
+ - deployment
+ filters:
+ - filter_key: "name"
+ filter_operator: "contains"
+ filter_value: "partial"
+ sort: name
+ limit: 10
+ offset: 10
+ include_devices: true
+ include_template: true
'''
RETURN = r'''
@@ -1147,19 +1233,557 @@ Replication_pairs:
"replicationConsistencyGroupId": "e2ce036b00000002",
"userRequestedPauseTransmitInitCopy": false
}
+Fault_Sets:
+ description: Details of fault sets.
+ returned: always
+ type: list
+ contains:
+ protectionDomainId:
+ description: The ID of the protection domain.
+ type: str
+ name:
+ description: device name.
+ type: str
+ id:
+ description: device id.
+ type: str
+ sample: [
+ {
+ "protectionDomainId": "da721a8300000000",
+ "protectionDomainName": "fault_set_1",
+ "name": "at1zbs1t6cp2sds1d1fs1",
+ "SDS": [],
+ "id": "eb44b70500000000",
+ "links": [
+ { "rel": "self", "href": "/api/instances/FaultSet::eb44b70500000000" },
+ {
+ "rel": "/api/FaultSet/relationship/Statistics",
+ "href": "/api/instances/FaultSet::eb44b70500000000/relationships/Statistics"
+ },
+ {
+ "rel": "/api/FaultSet/relationship/Sds",
+ "href": "/api/instances/FaultSet::eb44b70500000000/relationships/Sds"
+ },
+ {
+ "rel": "/api/parent/relationship/protectionDomainId",
+ "href": "/api/instances/ProtectionDomain::da721a8300000000"
+ }
+ ]
+ },
+ {
+ "protectionDomainId": "da721a8300000000",
+ "protectionDomainName": "fault_set_2",
+ "name": "at1zbs1t6cp2sds1d1fs3",
+ "SDS": [],
+ "id": "eb44b70700000002",
+ "links": [
+ { "rel": "self", "href": "/api/instances/FaultSet::eb44b70700000002" },
+ {
+ "rel": "/api/FaultSet/relationship/Statistics",
+ "href": "/api/instances/FaultSet::eb44b70700000002/relationships/Statistics"
+ },
+ {
+ "rel": "/api/FaultSet/relationship/Sds",
+ "href": "/api/instances/FaultSet::eb44b70700000002/relationships/Sds"
+ },
+ {
+ "rel": "/api/parent/relationship/protectionDomainId",
+ "href": "/api/instances/ProtectionDomain::da721a8300000000"
+ }
+ ]
+ }
+ ]
+ManagedDevices:
+ description: Details of all devices from inventory.
+ returned: when I(gather_subset) is I(managed_device)
+ type: list
+ contains:
+ deviceType:
+ description: Device Type.
+ type: str
+ serviceTag:
+ description: Service Tag.
+ type: str
+ serverTemplateId:
+ description: The ID of the server template.
+ type: str
+ state:
+ description: The state of the device.
+ type: str
+ managedState:
+ description: The managed state of the device.
+ type: str
+ compliance:
+ description: The compliance state of the device.
+ type: str
+ systemId:
+ description: The system ID.
+ type: str
+ sample: [{
+ "refId": "softwareOnlyServer-10.1.1.1",
+ "refType": null,
+ "ipAddress": "10.1.1.1",
+ "currentIpAddress": "10.1.1.1",
+ "serviceTag": "VMware-42 15 a5 f9 65 e6 63 0e-36 79 59 73 7b 3a 68 cd-SW",
+ "model": "VMware Virtual Platform",
+ "deviceType": "SoftwareOnlyServer",
+ "discoverDeviceType": "SOFTWAREONLYSERVER_CENTOS",
+ "displayName": "vpi1011-c1n1",
+ "managedState": "UNMANAGED",
+ "state": "READY",
+ "inUse": false,
+ "serviceReferences": [],
+ "statusMessage": null,
+ "firmwareName": "Default Catalog - PowerFlex 4.5.0.0",
+ "customFirmware": false,
+ "needsAttention": false,
+ "manufacturer": "VMware, Inc.",
+ "systemId": null,
+ "health": "RED",
+ "healthMessage": "Inventory run failed.",
+ "operatingSystem": "N/A",
+ "numberOfCPUs": 0,
+ "cpuType": null,
+ "nics": 0,
+ "memoryInGB": 0,
+ "infraTemplateDate": null,
+ "infraTemplateId": null,
+ "serverTemplateDate": null,
+ "serverTemplateId": null,
+ "inventoryDate": null,
+ "complianceCheckDate": "2024-02-05T18:31:31.213+00:00",
+ "discoveredDate": "2024-02-05T18:31:30.992+00:00",
+ "deviceGroupList": {
+ "paging": null,
+ "deviceGroup": [
+ {
+ "link": null,
+ "groupSeqId": -1,
+ "groupName": "Global",
+ "groupDescription": null,
+ "createdDate": null,
+ "createdBy": "admin",
+ "updatedDate": null,
+ "updatedBy": null,
+ "managedDeviceList": null,
+ "groupUserList": null
+ }
+ ]
+ },
+ "detailLink": {
+ "title": "softwareOnlyServer-10.1.1.1",
+ "href": "/AsmManager/ManagedDevice/softwareOnlyServer-10.1.1.1",
+ "rel": "describedby",
+ "type": null
+ },
+ "credId": "bc97cefb-5eb4-4c20-8e39-d1a2b809c9f5",
+ "compliance": "NONCOMPLIANT",
+ "failuresCount": 0,
+ "chassisId": null,
+ "parsedFacts": null,
+ "config": null,
+ "hostname": "vpi1011-c1n1",
+ "osIpAddress": null,
+ "osAdminCredential": null,
+ "osImageType": null,
+ "lastJobs": null,
+ "puppetCertName": "red_hat-10.1.1.1",
+ "svmAdminCredential": null,
+ "svmName": null,
+ "svmIpAddress": null,
+ "svmImageType": null,
+ "flexosMaintMode": 0,
+ "esxiMaintMode": 0,
+ "vmList": []
+ }]
+Deployments:
+ description: Details of all deployments.
+ returned: when I(gather_subset) is I(deployment)
+ type: list
+ contains:
+ id:
+ description: Deployment ID.
+ type: str
+ deploymentName:
+ description: Deployment name.
+ type: str
+ status:
+ description: The status of deployment.
+ type: str
+ firmwareRepository:
+ description: The firmware repository.
+ type: dict
+ contains:
+ signature:
+ description: The signature details.
+ type: str
+ downloadStatus:
+ description: The download status.
+ type: str
+ rcmapproved:
+ description: If RCM approved.
+ type: bool
+ sample: [{
+ "id": "8aaa80658cd602e0018cda8b257f78ce",
+ "deploymentName": "Test-Update - K",
+ "deploymentDescription": "Test-Update - K",
+ "deploymentValid": null,
+ "retry": false,
+ "teardown": false,
+ "teardownAfterCancel": false,
+ "removeService": false,
+ "createdDate": "2024-01-05T16:53:21.407+00:00",
+ "createdBy": "admin",
+ "updatedDate": "2024-02-11T17:00:05.657+00:00",
+ "updatedBy": "system",
+ "deploymentScheduledDate": null,
+ "deploymentStartedDate": "2024-01-05T16:53:22.886+00:00",
+ "deploymentFinishedDate": null,
+ "serviceTemplate": {
+ "id": "8aaa80658cd602e0018cda8b257f78ce",
+ "templateName": "block-only (8aaa80658cd602e0018cda8b257f78ce)",
+ "templateDescription": "Storage - Software Only deployment",
+ "templateType": "VxRack FLEX",
+ "templateVersion": "4.5.0.0",
+ "templateValid": {
+ "valid": true,
+ "messages": []
+ },
+ "originalTemplateId": "c44cb500-020f-4562-9456-42ec1eb5f9b2",
+ "templateLocked": false,
+ "draft": false,
+ "inConfiguration": false,
+ "createdDate": "2024-01-05T16:53:22.083+00:00",
+ "createdBy": null,
+ "updatedDate": "2024-02-09T06:00:09.602+00:00",
+ "lastDeployedDate": null,
+ "updatedBy": null,
+ "components": [
+ {
+ "id": "6def7edd-bae2-4420-93bf-9ceb051bbb65",
+ "componentID": "component-scaleio-gateway-1",
+ "identifier": null,
+ "componentValid": {
+ "valid": true,
+ "messages": []
+ },
+ "puppetCertName": "scaleio-block-legacy-gateway",
+ "osPuppetCertName": null,
+ "name": "block-legacy-gateway",
+ "type": "SCALEIO",
+ "subType": "STORAGEONLY",
+ "teardown": false,
+ "helpText": null,
+ "managementIpAddress": null,
+ "configFile": null,
+ "serialNumber": null,
+ "asmGUID": "scaleio-block-legacy-gateway",
+ "relatedComponents": {
+ "625b0e17-9b91-4bc0-864c-d0111d42d8d0": "Node (Software Only)",
+ "961a59eb-80c3-4a3a-84b7-2101e9831527": "Node (Software Only)-2",
+ "bca710a5-7cdf-481e-b729-0b53e02873ee": "Node (Software Only)-3"
+ },
+ "resources": [],
+ "refId": null,
+ "cloned": false,
+ "clonedFromId": null,
+ "manageFirmware": false,
+ "brownfield": false,
+ "instances": 1,
+ "clonedFromAsmGuid": null,
+ "ip": null
+ }
+ ],
+ "category": "block-only",
+ "allUsersAllowed": true,
+ "assignedUsers": [],
+ "manageFirmware": true,
+ "useDefaultCatalog": false,
+ "firmwareRepository": null,
+ "licenseRepository": null,
+ "configuration": null,
+ "serverCount": 3,
+ "storageCount": 1,
+ "clusterCount": 1,
+ "serviceCount": 0,
+ "switchCount": 0,
+ "vmCount": 0,
+ "sdnasCount": 0,
+ "brownfieldTemplateType": "NONE",
+ "networks": [
+ {
+ "id": "8aaa80648cd5fb9b018cda46e4e50000",
+ "name": "mgmt",
+ "description": "",
+ "type": "SCALEIO_MANAGEMENT",
+ "vlanId": 850,
+ "static": true,
+ "staticNetworkConfiguration": {
+ "gateway": "10.1.1.1",
+ "subnet": "1.1.1.0",
+ "primaryDns": "10.1.1.1",
+ "secondaryDns": "10.1.1.1",
+ "dnsSuffix": null,
+ "ipRange": [
+ {
+ "id": "8aaa80648cd5fb9b018cda46e5080001",
+ "startingIp": "10.1.1.1",
+ "endingIp": "10.1.1.1",
+ "role": null
+ }
+ ],
+ "ipAddress": null,
+ "staticRoute": null
+ },
+ "destinationIpAddress": "10.1.1.1"
+ }
+ ],
+ "blockServiceOperationsMap": {
+ "scaleio-block-legacy-gateway": {
+ "blockServiceOperationsMap": {}
+ }
+ }
+ },
+ "scheduleDate": null,
+ "status": "complete",
+ "compliant": true,
+ "deploymentDevice": [
+ {
+ "refId": "scaleio-block-legacy-gateway",
+ "refType": null,
+ "logDump": null,
+ "status": null,
+ "statusEndTime": null,
+ "statusStartTime": null,
+ "deviceHealth": "GREEN",
+ "healthMessage": "OK",
+ "compliantState": "COMPLIANT",
+ "brownfieldStatus": "NOT_APPLICABLE",
+ "deviceType": "scaleio",
+ "deviceGroupName": null,
+ "ipAddress": "block-legacy-gateway",
+ "currentIpAddress": "10.1.1.1",
+ "serviceTag": "block-legacy-gateway",
+ "componentId": null,
+ "statusMessage": null,
+ "model": "PowerFlex Gateway",
+ "cloudLink": false,
+ "dasCache": false,
+ "deviceState": "READY",
+ "puppetCertName": "scaleio-block-legacy-gateway",
+ "brownfield": false
+ }
+ ],
+ "vms": null,
+ "updateServerFirmware": true,
+ "useDefaultCatalog": false,
+ "firmwareRepository": {
+ "id": "8aaa80658cd602e0018cd996a1c91bdc",
+ "name": "Intelligent Catalog 45.373.00",
+ "sourceLocation": null,
+ "sourceType": null,
+ "diskLocation": null,
+ "filename": null,
+ "md5Hash": null,
+ "username": null,
+ "password": null,
+ "downloadStatus": null,
+ "createdDate": null,
+ "createdBy": null,
+ "updatedDate": null,
+ "updatedBy": null,
+ "defaultCatalog": false,
+ "embedded": false,
+ "state": null,
+ "softwareComponents": [],
+ "softwareBundles": [],
+ "deployments": [],
+ "bundleCount": 0,
+ "componentCount": 0,
+ "userBundleCount": 0,
+ "minimal": false,
+ "downloadProgress": 0,
+ "extractProgress": 0,
+ "fileSizeInGigabytes": null,
+ "signedKeySourceLocation": null,
+ "signature": null,
+ "custom": false,
+ "needsAttention": false,
+ "jobId": null,
+ "rcmapproved": false
+ },
+ "firmwareRepositoryId": "8aaa80658cd602e0018cd996a1c91bdc",
+ "licenseRepository": null,
+ "licenseRepositoryId": null,
+ "individualTeardown": false,
+ "deploymentHealthStatusType": "green",
+ "assignedUsers": [],
+ "allUsersAllowed": true,
+ "owner": "admin",
+ "noOp": false,
+ "firmwareInit": false,
+ "disruptiveFirmware": false,
+ "preconfigureSVM": false,
+ "preconfigureSVMAndUpdate": false,
+ "servicesDeployed": "NONE",
+ "precalculatedDeviceHealth": null,
+ "lifecycleModeReasons": [],
+ "jobDetails": null,
+ "numberOfDeployments": 0,
+ "operationType": "NONE",
+ "operationStatus": null,
+ "operationData": null,
+ "deploymentValidationResponse": null,
+ "currentStepCount": null,
+ "totalNumOfSteps": null,
+ "currentStepMessage": null,
+ "customImage": "os_sles",
+ "originalDeploymentId": null,
+ "currentBatchCount": null,
+ "totalBatchCount": null,
+ "templateValid": true,
+ "lifecycleMode": false,
+ "vds": false,
+ "scaleUp": false,
+ "brownfield": false,
+ "configurationChange": false
+ }]
+ServiceTemplates:
+ description: Details of all service templates.
+ returned: when I(gather_subset) is I(service_template)
+ type: list
+ contains:
+ templateName:
+ description: Template name.
+ type: str
+ templateDescription:
+ description: Template description.
+ type: str
+ templateType:
+ description: Template type.
+ type: str
+ templateVersion:
+ description: Template version.
+ type: str
+ category:
+ description: The template category.
+ type: str
+ serverCount:
+ description: Server count.
+ type: int
+ sample: [{
+ "id": "2434144f-7795-4245-a04b-6fcb771697d7",
+ "templateName": "Storage- 100Gb",
+ "templateDescription": "Storage Only 4 Node deployment with 100Gb networking",
+ "templateType": "VxRack FLEX",
+ "templateVersion": "4.5-213",
+ "templateValid": {
+ "valid": true,
+ "messages": []
+ },
+ "originalTemplateId": "ff80808177f880fc0177f883bf1e0027",
+ "templateLocked": true,
+ "draft": false,
+ "inConfiguration": false,
+ "createdDate": "2024-01-04T19:47:23.534+00:00",
+ "createdBy": "system",
+ "updatedDate": null,
+ "lastDeployedDate": null,
+ "updatedBy": null,
+ "components": [
+ {
+ "id": "43dec024-85a9-4901-9e8e-fa0d3c417f7b",
+ "componentID": "component-scaleio-gateway-1",
+ "identifier": null,
+ "componentValid": {
+ "valid": true,
+ "messages": []
+ },
+ "puppetCertName": null,
+ "osPuppetCertName": null,
+ "name": "PowerFlex Cluster",
+ "type": "SCALEIO",
+ "subType": "STORAGEONLY",
+ "teardown": false,
+ "helpText": null,
+ "managementIpAddress": null,
+ "configFile": null,
+ "serialNumber": null,
+ "asmGUID": null,
+ "relatedComponents": {
+ "c5c46733-012c-4dca-af9b-af46d73d045a": "Storage Only Node"
+ },
+ "resources": [],
+ "refId": null,
+ "cloned": false,
+ "clonedFromId": null,
+ "manageFirmware": false,
+ "brownfield": false,
+ "instances": 1,
+ "clonedFromAsmGuid": null,
+ "ip": null
+ }
+ ],
+ "category": "Sample Templates",
+ "allUsersAllowed": false,
+ "assignedUsers": [],
+ "manageFirmware": true,
+ "useDefaultCatalog": true,
+ "firmwareRepository": null,
+ "licenseRepository": null,
+ "configuration": null,
+ "serverCount": 4,
+ "storageCount": 0,
+ "clusterCount": 1,
+ "serviceCount": 0,
+ "switchCount": 0,
+ "vmCount": 0,
+ "sdnasCount": 0,
+ "brownfieldTemplateType": "NONE",
+ "networks": [
+ {
+ "id": "ff80808177f8823b0177f8bb82d80005",
+ "name": "flex-data2",
+ "description": "",
+ "type": "SCALEIO_DATA",
+ "vlanId": 105,
+ "static": true,
+ "staticNetworkConfiguration": {
+ "gateway": null,
+ "subnet": "1.1.1.0",
+ "primaryDns": null,
+ "secondaryDns": null,
+ "dnsSuffix": null,
+ "ipRange": null,
+ "ipAddress": null,
+ "staticRoute": null
+ },
+ "destinationIpAddress": "1.1.1.0"
+ }
+ ],
+ "blockServiceOperationsMap": {}
+ }]
'''
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.dellemc.powerflex.plugins.module_utils.storage.dell \
import utils
+from ansible_collections.dellemc.powerflex.plugins.module_utils.storage.dell.libraries.configuration \
+ import Configuration
+import re
LOG = utils.get_logger('info')
+UNSUPPORTED_SUBSET_FOR_VERSION = 'One or more specified subset is not supported for the PowerFlex version.'
+POWERFLEX_MANAGER_GATHER_SUBSET = {'managed_device', 'deployment', 'service_template'}
+MIN_SUPPORTED_POWERFLEX_MANAGER_VERSION = 4.0
+ERROR_CODES = r'PARSE002|FILTER002|FILTER003'
+
class PowerFlexInfo(object):
"""Class with Info operations"""
- filter_mapping = {'equal': 'eq.'}
+ filter_mapping = {'equal': 'eq', 'contains': 'co'}
def __init__(self):
""" Define all parameters required by this module"""
@@ -1265,7 +1889,7 @@ class PowerFlexInfo(object):
return result_list(sds)
except Exception as e:
- msg = 'Get sds list from powerflex array failed with' \
+ msg = 'Get SDS list from powerflex array failed with' \
' error %s' % (str(e))
LOG.error(msg)
self.module.fail_json(msg=msg)
@@ -1395,19 +2019,24 @@ class PowerFlexInfo(object):
system """
try:
- LOG.info('Getting snapshot schedules list ')
+ LOG.info('Getting snapshot policies list ')
if filter_dict:
- snapshot_schedules = \
+ snapshot_policies = \
self.powerflex_conn.snapshot_policy.get(
filter_fields=filter_dict)
else:
- snapshot_schedules = \
+ snapshot_policies = \
self.powerflex_conn.snapshot_policy.get()
- return result_list(snapshot_schedules)
+ if snapshot_policies:
+ statistics_map = self.powerflex_conn.utility.get_statistics_for_all_snapshot_policies()
+ list_of_snap_pol_ids_in_statistics = statistics_map.keys()
+ for item in snapshot_policies:
+ item['statistics'] = statistics_map[item['id']] if item['id'] in list_of_snap_pol_ids_in_statistics else {}
+ return result_list(snapshot_policies)
except Exception as e:
- msg = 'Get snapshot schedules list from powerflex array failed ' \
+ msg = 'Get snapshot policies list from powerflex array failed ' \
'with error %s' % (str(e))
LOG.error(msg)
self.module.fail_json(msg=msg)
@@ -1431,6 +2060,114 @@ class PowerFlexInfo(object):
LOG.error(msg)
self.module.fail_json(msg=msg)
+ def get_fault_sets_list(self, filter_dict=None):
+ """ Get the list of fault sets on a given PowerFlex storage
+ system """
+
+ try:
+ LOG.info('Getting fault set list ')
+ filter_pd = []
+ if filter_dict:
+ if 'protectionDomainName' in filter_dict.keys():
+ filter_pd = filter_dict['protectionDomainName']
+ del filter_dict['protectionDomainName']
+ fault_sets = self.powerflex_conn.fault_set.get(filter_fields=filter_dict)
+ else:
+ fault_sets = self.powerflex_conn.fault_set.get()
+
+ fault_set_final = []
+ if fault_sets:
+ for fault_set in fault_sets:
+ fault_set['protectionDomainName'] = Configuration(self.powerflex_conn, self.module).get_protection_domain(
+ protection_domain_id=fault_set["protectionDomainId"])["name"]
+ fault_set["SDS"] = Configuration(self.powerflex_conn, self.module).get_associated_sds(
+ fault_set_id=fault_set['id'])
+ fault_set_final.append(fault_set)
+ fault_sets = []
+ for fault_set in fault_set_final:
+ if fault_set['protectionDomainName'] in filter_pd:
+ fault_sets.append(fault_set)
+ if len(filter_pd) != 0:
+ return result_list(fault_sets)
+ return result_list(fault_set_final)
+
+ except Exception as e:
+ msg = 'Get fault set list from powerflex array failed ' \
+ 'with error %s' % (str(e))
+ LOG.error(msg)
+ self.module.fail_json(msg=msg)
+
+ def get_managed_devices_list(self):
+ """ Get the list of managed devices on a given PowerFlex Manager system """
+ try:
+ LOG.info('Getting managed devices list ')
+ devices = self.powerflex_conn.managed_device.get(filters=self.populate_filter_list(),
+ limit=self.get_param_value('limit'),
+ offset=self.get_param_value('offset'),
+ sort=self.get_param_value('sort'))
+ return devices
+ except Exception as e:
+ msg = f'Get managed devices from PowerFlex Manager failed with error {str(e)}'
+ return self.handle_error_exit(msg)
+
+ def get_deployments_list(self):
+ """ Get the list of deployments on a given PowerFlex Manager system """
+ try:
+ LOG.info('Getting deployments list ')
+ deployments = self.powerflex_conn.deployment.get(filters=self.populate_filter_list(),
+ sort=self.get_param_value('sort'),
+ limit=self.get_param_value('limit'),
+ offset=self.get_param_value('offset'),
+ include_devices=self.get_param_value('include_devices'),
+ include_template=self.get_param_value('include_template'),
+ full=self.get_param_value('full'))
+ return deployments
+ except Exception as e:
+ msg = f'Get deployments from PowerFlex Manager failed with error {str(e)}'
+ return self.handle_error_exit(msg)
+
+ def get_service_templates_list(self):
+ """ Get the list of service templates on a given PowerFlex Manager system """
+ try:
+ LOG.info('Getting service templates list ')
+ service_templates = self.powerflex_conn.service_template.get(filters=self.populate_filter_list(),
+ sort=self.get_param_value('sort'),
+ offset=self.get_param_value('offset'),
+ limit=self.get_param_value('limit'),
+ full=self.get_param_value('full'),
+ include_attachments=self.get_param_value('include_attachments'))
+ return service_templates
+ except Exception as e:
+ msg = f'Get service templates from PowerFlex Manager failed with error {str(e)}'
+ return self.handle_error_exit(msg)
+
+ def handle_error_exit(self, detailed_message):
+ match = re.search(r"displayMessage=([^']+)", detailed_message)
+ error_message = match.group(1) if match else detailed_message
+ LOG.error(error_message)
+ if re.search(ERROR_CODES, detailed_message):
+ return []
+ self.module.fail_json(msg=error_message)
+
+ def get_param_value(self, param):
+ """
+ Get the value of the given parameter.
+ Args:
+ param (str): The parameter to get the value for.
+ Returns:
+ The value of the parameter if it is different from the default value,
+ The value of the parameter if int and greater than 0
+ otherwise None.
+ """
+ if param in ('sort', 'offset', 'limit') and len(self.module.params.get('gather_subset')) > 1:
+ return None
+
+ default_value = self.module_params.get(param).get('default')
+ param_value = self.module.params.get(param)
+ if (default_value != param_value) and (param_value >= 0 if isinstance(param_value, int) else True):
+ return param_value
+ return None
+
def validate_filter(self, filter_dict):
""" Validate given filter_dict """
@@ -1447,6 +2184,16 @@ class PowerFlexInfo(object):
LOG.error(msg)
self.module.fail_json(msg=msg)
+ def populate_filter_list(self):
+ """Populate the filter list"""
+ if len(self.module.params.get('gather_subset')) > 1:
+ return []
+ filters = self.module.params.get('filters') or []
+ return [
+ f'{self.filter_mapping.get(filter_dict["filter_operator"])},{filter_dict["filter_key"]},{filter_dict["filter_value"]}'
+ for filter_dict in filters
+ ]
+
def get_filters(self, filters):
"""Get the filters to be applied"""
@@ -1454,7 +2201,7 @@ class PowerFlexInfo(object):
for item in filters:
self.validate_filter(item)
f_op = item['filter_operator']
- if self.filter_mapping.get(f_op):
+ if self.filter_mapping.get(f_op) == self.filter_mapping.get("equal"):
f_key = item['filter_key']
f_val = item['filter_value']
if f_key in filter_dict:
@@ -1468,15 +2215,12 @@ class PowerFlexInfo(object):
filter_dict[f_key] = [filter_dict[f_key], f_val]
else:
filter_dict[f_key] = f_val
- else:
- msg = "Given filter operator '{0}' is not supported." \
- "supported operators are : '{1}'".format(
- f_op,
- list(self.filter_mapping.keys()))
- LOG.error(msg)
- self.module.fail_json(msg=msg)
return filter_dict
+ def validate_subset(self, api_version, subset):
+ if float(api_version) < MIN_SUPPORTED_POWERFLEX_MANAGER_VERSION and subset and set(subset).issubset(POWERFLEX_MANAGER_GATHER_SUBSET):
+ self.module.exit_json(msg=UNSUPPORTED_SUBSET_FOR_VERSION, skipped=True)
+
def perform_module_operation(self):
""" Perform different actions on info based on user input
in the playbook """
@@ -1498,8 +2242,13 @@ class PowerFlexInfo(object):
device = []
rcgs = []
replication_pair = []
+ fault_sets = []
+ service_template = []
+ managed_device = []
+ deployment = []
subset = self.module.params['gather_subset']
+ self.validate_subset(api_version, subset)
if subset is not None:
if 'sdc' in subset:
sdc = self.get_sdc_list(filter_dict=filter_dict)
@@ -1519,6 +2268,14 @@ class PowerFlexInfo(object):
rcgs = self.get_replication_consistency_group_list(filter_dict=filter_dict)
if 'replication_pair' in subset:
replication_pair = self.get_replication_pair_list(filter_dict=filter_dict)
+ if 'fault_set' in subset:
+ fault_sets = self.get_fault_sets_list(filter_dict=filter_dict)
+ if 'managed_device' in subset:
+ managed_device = self.get_managed_devices_list()
+ if 'service_template' in subset:
+ service_template = self.get_service_templates_list()
+ if 'deployment' in subset:
+ deployment = self.get_deployments_list()
self.module.exit_json(
Array_Details=array_details,
@@ -1531,7 +2288,11 @@ class PowerFlexInfo(object):
Protection_Domains=protection_domain,
Devices=device,
Replication_Consistency_Groups=rcgs,
- Replication_Pairs=replication_pair
+ Replication_Pairs=replication_pair,
+ Fault_Sets=fault_sets,
+ ManagedDevices=managed_device,
+ ServiceTemplates=service_template,
+ Deployments=deployment
)
@@ -1556,15 +2317,24 @@ def get_powerflex_info_parameters():
return dict(
gather_subset=dict(type='list', required=False, elements='str',
choices=['vol', 'storage_pool',
- 'protection_domain', 'sdc', 'sds',
- 'snapshot_policy', 'device', 'rcg', 'replication_pair']),
+ 'protection_domain', 'sdc', 'sds', 'snapshot_policy',
+ 'device', 'rcg', 'replication_pair', 'fault_set',
+ 'service_template', 'managed_device', 'deployment']),
filters=dict(type='list', required=False, elements='dict',
options=dict(filter_key=dict(type='str', required=True, no_log=False),
filter_operator=dict(
type='str', required=True,
- choices=['equal']),
+ choices=['equal', 'contains']),
filter_value=dict(type='str', required=True)
- )))
+ )),
+ sort=dict(type='str'),
+ limit=dict(type='int', default=50),
+ offset=dict(type='int', default=0),
+ include_devices=dict(type='bool', default=True),
+ include_template=dict(type='bool', default=True),
+ full=dict(type='bool', default=False),
+ include_attachments=dict(type='bool', default=True)
+ )
def main():
diff --git a/ansible_collections/dellemc/powerflex/plugins/modules/mdm_cluster.py b/ansible_collections/dellemc/powerflex/plugins/modules/mdm_cluster.py
index 084666bc3..90e0bcad0 100644
--- a/ansible_collections/dellemc/powerflex/plugins/modules/mdm_cluster.py
+++ b/ansible_collections/dellemc/powerflex/plugins/modules/mdm_cluster.py
@@ -145,7 +145,7 @@ notes:
interfaces.
- Parameters I(mdm_name) or I(mdm_id) are not required while modifying performance
profile.
- - For change MDM cluster ownership operation, only changed as True will be
+ - For change MDM cluster ownership operation, only changed as true will be
returned and for idempotency case MDM cluster details will be returned.
- Reinstall all SDC after changing ownership to some newly added MDM.
- To add manager standby MDM, MDM package must be installed with manager
@@ -229,7 +229,7 @@ EXAMPLES = r'''
validate_certs: "{{validate_certs}}"
port: "{{port}}"
mdm_name: "mdm_2"
- is_primary: True
+ is_primary: true
state: "present"
- name: Modify performance profile
@@ -262,7 +262,7 @@ EXAMPLES = r'''
port: "{{port}}"
mdm_name: "mdm_1"
virtual_ip_interface:
- - "ens224"
+ - "ens224"
state: "present"
- name: Clear virtual IP interface of the MDM
@@ -273,7 +273,7 @@ EXAMPLES = r'''
validate_certs: "{{validate_certs}}"
port: "{{port}}"
mdm_name: "mdm_1"
- clear_interfaces: True
+ clear_interfaces: true
state: "present"
'''
@@ -1052,6 +1052,12 @@ class PowerFlexMdmCluster(object):
if resp is not None:
mdm_cluster_details['perfProfile'] = resp['perfProfile']
+ # Append list of configured MDM IP addresses
+ gateway_configuration_details = self.powerflex_conn.system.\
+ get_gateway_configuration_details()
+ if gateway_configuration_details is not None:
+ mdm_cluster_details['mdmAddresses'] = gateway_configuration_details['mdmAddresses']
+
return mdm_cluster_details
except Exception as e:
@@ -1063,30 +1069,32 @@ class PowerFlexMdmCluster(object):
def check_ip_in_secondarys(self, standby_ip, cluster_details):
"""whether standby IPs present in secondary MDMs"""
- for secondary_mdm in cluster_details['slaves']:
- current_secondary_ips = secondary_mdm['ips']
- for ips in standby_ip:
- if ips in current_secondary_ips:
- LOG.info(self.exist_msg)
- return False
+ if 'slaves' in cluster_details:
+ for secondary_mdm in cluster_details['slaves']:
+ current_secondary_ips = secondary_mdm['ips']
+ for ips in standby_ip:
+ if ips in current_secondary_ips:
+ LOG.info(self.exist_msg)
+ return False
return True
def check_ip_in_tbs(self, standby_ip, cluster_details):
"""whether standby IPs present in tie-breaker MDMs"""
- for tb_mdm in cluster_details['tieBreakers']:
- current_tb_ips = tb_mdm['ips']
- for ips in standby_ip:
- if ips in current_tb_ips:
- LOG.info(self.exist_msg)
- return False
+ if 'tieBreakers' in cluster_details:
+ for tb_mdm in cluster_details['tieBreakers']:
+ current_tb_ips = tb_mdm['ips']
+ for ips in standby_ip:
+ if ips in current_tb_ips:
+ LOG.info(self.exist_msg)
+ return False
return True
def check_ip_in_standby(self, standby_ip, cluster_details):
"""whether standby IPs present in standby MDMs"""
if 'standbyMDMs' in cluster_details:
- for stb_mdm in cluster_details['tieBreakers']:
+ for stb_mdm in cluster_details['standbyMDMs']:
current_stb_ips = stb_mdm['ips']
for ips in standby_ip:
if ips in current_stb_ips:
diff --git a/ansible_collections/dellemc/powerflex/plugins/modules/protection_domain.py b/ansible_collections/dellemc/powerflex/plugins/modules/protection_domain.py
index 5ffdc6b63..18cb952f0 100644
--- a/ansible_collections/dellemc/powerflex/plugins/modules/protection_domain.py
+++ b/ansible_collections/dellemc/powerflex/plugins/modules/protection_domain.py
@@ -537,6 +537,14 @@ class PowerFlexProtectionDomain(object):
err_msg = msg.format(n_item)
self.module.fail_json(msg=err_msg)
+ if self.module.params['network_limits'] is not None:
+ if self.module.params['network_limits']['overall_limit'] is not None and \
+ self.module.params['network_limits']['overall_limit'] < 0:
+ error_msg = "Overall limit cannot be negative. " \
+ "Provide a valid value "
+ LOG.info(error_msg)
+ self.module.fail_json(msg=error_msg)
+
def is_id_or_new_name_in_create(self):
"""Checking if protection domain id or new names present in create """
diff --git a/ansible_collections/dellemc/powerflex/plugins/modules/replication_consistency_group.py b/ansible_collections/dellemc/powerflex/plugins/modules/replication_consistency_group.py
index 94ec651c3..b106dfbdc 100644
--- a/ansible_collections/dellemc/powerflex/plugins/modules/replication_consistency_group.py
+++ b/ansible_collections/dellemc/powerflex/plugins/modules/replication_consistency_group.py
@@ -16,7 +16,8 @@ short_description: Manage replication consistency groups on Dell PowerFlex
description:
- Managing replication consistency groups on PowerFlex storage system includes
getting details, creating, modifying, creating snapshots, pause, resume, freeze, unfreeze,
- activate, inactivate and deleting a replication consistency group.
+ activate, failover, reverse, restore, sync, switchover,
+ inactivate and deleting a replication consistency group.
author:
- Trisha Datta (@Trisha-Datta) <ansible.team@dell.com>
- Jennifer John (@Jennifer-John) <ansible.team@dell.com>
@@ -61,15 +62,35 @@ options:
pause:
description:
- Pause or resume the RCG.
+ - This parameter is deprecated. Use rcg_state instead.
+ type: bool
+ rcg_state:
+ description:
+ - Specify an action for RCG.
+ - Failover the RCG.
+ - Reverse the RCG.
+ - Restore the RCG.
+ - Switchover the RCG.
+ - Pause or resume the RCG.
+ - Freeze or unfreeze the RCG.
+ - Synchronize the RCG.
+ choices: ['failover', 'reverse', 'restore',
+ 'switchover', 'sync', 'pause',
+ 'resume', 'freeze', 'unfreeze']
+ type: str
+ force:
+ description:
+ - Force switchover the RCG.
type: bool
freeze:
description:
- Freeze or unfreeze the RCG.
+ - This parameter is deprecated. Use rcg_state instead.
type: bool
pause_mode:
description:
- Pause mode.
- - It is required if pause is set as True.
+ - It is required if pause is set as true.
choices: ['StopDataTransfer', 'OnlyTrackChanges']
type: str
target_volume_access_mode:
@@ -150,7 +171,7 @@ notes:
- Idempotency is not supported for create snapshot operation.
- There is a delay in reflection of final state of RCG after few update operations on RCG.
- In 3.6 and above, the replication consistency group will return back to consistent mode on changing to inconsistent mode
- if consistence barrier arrives. Hence idempotency on setting to inconsistent mode will return changed as True.
+ if consistence barrier arrives. Hence idempotency on setting to inconsistent mode will return changed as true.
'''
EXAMPLES = r'''
@@ -172,7 +193,7 @@ EXAMPLES = r'''
validate_certs: "{{validate_certs}}"
port: "{{port}}"
rcg_id: "{{rcg_id}}"
- create_snapshot: True
+ create_snapshot: true
state: "present"
- name: Create a replication consistency group
@@ -205,7 +226,7 @@ EXAMPLES = r'''
rpo: 60
target_volume_access_mode: "ReadOnly"
activity_mode: "Inactive"
- is_consistent: True
+ is_consistent: true
- name: Rename replication consistency group
dellemc.powerflex.replication_consistency_group:
@@ -225,7 +246,7 @@ EXAMPLES = r'''
validate_certs: "{{validate_certs}}"
port: "{{port}}"
rcg_name: "rcg_test"
- pause: True
+ rcg_state: "pause"
pause_mode: "StopDataTransfer"
- name: Resume replication consistency group
@@ -236,7 +257,7 @@ EXAMPLES = r'''
validate_certs: "{{validate_certs}}"
port: "{{port}}"
rcg_name: "rcg_test"
- pause: False
+ rcg_state: "resume"
- name: Freeze replication consistency group
dellemc.powerflex.replication_consistency_group:
@@ -246,7 +267,7 @@ EXAMPLES = r'''
validate_certs: "{{validate_certs}}"
port: "{{port}}"
rcg_name: "rcg_test"
- freeze: True
+ rcg_state: "freeze"
- name: UnFreeze replication consistency group
dellemc.powerflex.replication_consistency_group:
@@ -256,7 +277,57 @@ EXAMPLES = r'''
validate_certs: "{{validate_certs}}"
port: "{{port}}"
rcg_name: "rcg_test"
- freeze: False
+ rcg_state: "unfreeze"
+
+- name: Failover replication consistency group
+ dellemc.powerflex.replication_consistency_group:
+ hostname: "{{hostname}}"
+ username: "{{username}}"
+ password: "{{password}}"
+ validate_certs: "{{validate_certs}}"
+ port: "{{port}}"
+ rcg_name: "rcg_test"
+ rcg_state: "failover"
+
+- name: Reverse replication consistency group
+ dellemc.powerflex.replication_consistency_group:
+ hostname: "{{hostname}}"
+ username: "{{username}}"
+ password: "{{password}}"
+ validate_certs: "{{validate_certs}}"
+ port: "{{port}}"
+ rcg_name: "rcg_test"
+ rcg_state: "reverse"
+
+- name: Restore replication consistency group
+ dellemc.powerflex.replication_consistency_group:
+ hostname: "{{hostname}}"
+ username: "{{username}}"
+ password: "{{password}}"
+ validate_certs: "{{validate_certs}}"
+ port: "{{port}}"
+ rcg_name: "rcg_test"
+ rcg_state: "restore"
+
+- name: Switchover replication consistency group
+ dellemc.powerflex.replication_consistency_group:
+ hostname: "{{hostname}}"
+ username: "{{username}}"
+ password: "{{password}}"
+ validate_certs: "{{validate_certs}}"
+ port: "{{port}}"
+ rcg_name: "rcg_test"
+ rcg_state: "switchover"
+
+- name: Synchronize replication consistency group
+ dellemc.powerflex.replication_consistency_group:
+ hostname: "{{hostname}}"
+ username: "{{username}}"
+ password: "{{password}}"
+ validate_certs: "{{validate_certs}}"
+ port: "{{port}}"
+ rcg_name: "rcg_test"
+ rcg_state: "sync"
- name: Delete replication consistency group
dellemc.powerflex.replication_consistency_group:
@@ -442,8 +513,8 @@ class PowerFlexReplicationConsistencyGroup(object):
def get_rcg(self, rcg_name=None, rcg_id=None):
"""Get rcg details
- :param rcg_name: Name of the rcg
- :param rcg_id: ID of the rcg
+ :param rcg_name: Name of the RCG
+ :param rcg_id: ID of the RCG
:return: RCG details
"""
name_or_id = rcg_id if rcg_id else rcg_name
@@ -585,22 +656,22 @@ class PowerFlexReplicationConsistencyGroup(object):
:param rcg_details: RCG details.
:param pause: Pause or resume RCG.
:param pause_mode: Specifies the pause mode if pause is True.
- :return: Boolean indicates if rcg action is successful
+ :return: Boolean indicates if RCG action is successful
"""
if pause and rcg_details['pauseMode'] == 'None':
if not pause_mode:
self.module.fail_json(msg="Specify pause_mode to perform pause on replication consistency group.")
return self.pause(rcg_id, pause_mode)
- if not pause and rcg_details['pauseMode'] != 'None':
+ if not pause and (rcg_details['pauseMode'] != 'None' or rcg_details['failoverType'] in ['Failover', 'Switchover']):
return self.resume(rcg_id)
def freeze_or_unfreeze_rcg(self, rcg_id, rcg_details, freeze):
- """Perform specified rcg action
+ """Perform specified RCG action
:param rcg_id: Unique identifier of the RCG.
:param rcg_details: RCG details.
:param freeze: Freeze or unfreeze RCG.
- :return: Boolean indicates if rcg action is successful
+ :return: Boolean indicates if RCG action is successful
"""
if freeze and rcg_details['freezeState'].lower() == 'unfrozen':
return self.freeze(rcg_id)
@@ -648,6 +719,98 @@ class PowerFlexReplicationConsistencyGroup(object):
LOG.error(errormsg)
self.module.fail_json(msg=errormsg)
+ def failover(self, rcg_id):
+ """Perform failover
+ :param rcg_id: Unique identifier of the RCG.
+ :return: Boolean indicates if RCG failover is successful
+ """
+ try:
+ if not self.module.check_mode:
+ self.powerflex_conn.replication_consistency_group.failover(rcg_id)
+ return True
+ except Exception as e:
+ errormsg = f"Failover replication consistency group {rcg_id} failed with error {e}"
+ LOG.error(errormsg)
+ self.module.fail_json(msg=errormsg)
+
+ def reverse(self, rcg_id):
+ """Perform reverse
+ :param rcg_id: Unique identifier of the RCG.
+ :return: Boolean indicates if RCG reverse is successful
+ """
+ try:
+ if not self.module.check_mode:
+ self.powerflex_conn.replication_consistency_group.reverse(rcg_id)
+ return True
+ except Exception as e:
+ errormsg = f"Reverse replication consistency group {rcg_id} failed with error {e}"
+ LOG.error(errormsg)
+ self.module.fail_json(msg=errormsg)
+
+ def restore(self, rcg_id):
+ """Perform restore
+ :param rcg_id: Unique identifier of the RCG.
+ :return: Boolean indicates if RCG restore is successful
+ """
+ try:
+ if not self.module.check_mode:
+ self.powerflex_conn.replication_consistency_group.restore(rcg_id)
+ return True
+ except Exception as e:
+ errormsg = f"Restore replication consistency group {rcg_id} failed with error {e}"
+ LOG.error(errormsg)
+ self.module.fail_json(msg=errormsg)
+
+ def switchover(self, rcg_id, force):
+ """Perform switchover
+ :param rcg_id: Unique identifier of the RCG.
+ :param force: Force switchover.
+ :return: Boolean indicates if RCG switchover is successful
+ """
+ try:
+ if not self.module.check_mode:
+ self.powerflex_conn.replication_consistency_group.switchover(rcg_id, force)
+ return True
+ except Exception as e:
+ errormsg = f"Switchover replication consistency group {rcg_id} failed with error {e}"
+ LOG.error(errormsg)
+ self.module.fail_json(msg=errormsg)
+
+ def perform_rcg_action(self, rcg_id, rcg_details):
+ """Perform failover, reverse, restore or switchover
+ :param rcg_id: Unique identifier of the RCG.
+ :param rcg_details: RCG details.
+ :return: Boolean indicates if RCG action is successful
+ """
+ rcg_state = self.module.params['rcg_state']
+ force = self.module.params['force']
+
+ if rcg_state == 'failover' and rcg_details['failoverType'] != 'Failover':
+ return self.failover(rcg_id)
+
+ if rcg_state == 'switchover' and rcg_details['failoverType'] != 'Switchover':
+ return self.switchover(rcg_id, force)
+
+ if rcg_state == 'reverse' and rcg_details['failoverType']:
+ return self.reverse(rcg_id)
+
+ if rcg_state == 'restore' and rcg_details['failoverType'] != 'None':
+ return self.restore(rcg_id)
+
+ def sync(self, rcg_id):
+ """Perform sync
+ :param rcg_id: Unique identifier of the RCG.
+ :return: Boolean indicates if RCG sync is successful
+ """
+ try:
+ if not self.module.check_mode:
+ self.powerflex_conn.replication_consistency_group.sync(rcg_id)
+ return True
+ except Exception as e:
+ errormsg = f"Synchronization of replication consistency group {rcg_id} failed with error {e}"
+ LOG.error(errormsg)
+ self.module.fail_json(msg=errormsg)
+
def set_consistency(self, rcg_id, rcg_details, is_consistent):
"""Set rcg to specified mode
:param rcg_id: Unique identifier of the RCG.
@@ -689,7 +852,7 @@ class PowerFlexReplicationConsistencyGroup(object):
def delete_rcg(self, rcg_id):
"""Delete RCG
:param rcg_id: Unique identifier of the RCG.
- :return: Boolean indicates if delete rcg operation is successful
+ :return: Boolean indicates if delete RCG operation is successful
"""
try:
if not self.module.check_mode:
@@ -753,17 +916,55 @@ class PowerFlexReplicationConsistencyGroup(object):
rcg_params['remote_peer']['protection_domain_name'] is not None):
self.module.fail_json(msg='Enter remote protection_domain_name or protection_domain_id to create replication consistency group')
+ def get_pause_and_freeze_value(self):
+ """
+ Get Pause and Freeze values
+ :return: Boolean for pause and freeze
+ :rtype: (bool,bool)
+ """
+ rcg_state = self.module.params['rcg_state']
+ pause = self.module.params['pause']
+ freeze = self.module.params['freeze']
+
+ if pause is not None:
+ self.module.deprecate(
+ msg="Use 'rcg_state' param instead of 'pause'",
+ version="3.0.0",
+ collection_name="dellemc.powerflex"
+ )
+
+ if freeze is not None:
+ self.module.deprecate(
+ msg="Use 'rcg_state' param instead of 'freeze'",
+ version="3.0.0",
+ collection_name="dellemc.powerflex"
+ )
+
+ if rcg_state == 'pause':
+ pause = True
+ if rcg_state == 'resume':
+ pause = False
+ if rcg_state == 'freeze':
+ freeze = True
+ if rcg_state == 'unfreeze':
+ freeze = False
+
+ if self.module.params['pause_mode'] and not pause:
+ self.module.fail_json(msg="Specify rcg_state as 'pause' to pause replication consistency group")
+
+ return pause, freeze
+
def modify_rcg(self, rcg_id, rcg_details):
+ rcg_state = self.module.params['rcg_state']
create_snapshot = self.module.params['create_snapshot']
rpo = self.module.params['rpo']
target_volume_access_mode = self.module.params['target_volume_access_mode']
- pause = self.module.params['pause']
- freeze = self.module.params['freeze']
is_consistent = self.module.params['is_consistent']
activity_mode = self.module.params['activity_mode']
new_rcg_name = self.module.params['new_rcg_name']
changed = False
+ pause, freeze = self.get_pause_and_freeze_value()
if create_snapshot is True:
changed = self.create_rcg_snapshot(rcg_id)
if rpo and rcg_details['rpoInSeconds'] and \
@@ -788,6 +989,11 @@ class PowerFlexReplicationConsistencyGroup(object):
changed = True
if new_rcg_name and self.rename_rcg(rcg_id, rcg_details, new_rcg_name):
changed = True
+ if rcg_state == 'sync' and self.sync(rcg_id):
+ changed = True
+
+ rcg_action_status = self.perform_rcg_action(rcg_id, rcg_details)
+ changed = changed or rcg_action_status
return changed
@@ -800,8 +1006,6 @@ class PowerFlexReplicationConsistencyGroup(object):
for param in params:
if rcg_params[param] and utils.is_invalid_name(rcg_params[param]):
self.module.fail_json(msg='Enter a valid %s' % param)
- if rcg_params['pause_mode'] and rcg_params['pause'] is None:
- self.module.fail_json(msg='Specify pause as True to pause replication consistency group')
except Exception as e:
error_msg = "Validating input parameters failed with " \
"error '%s'" % (str(e))
@@ -879,7 +1083,13 @@ def get_powerflex_replication_consistency_group_parameters():
rpo=dict(type='int'), protection_domain_id=dict(),
protection_domain_name=dict(), new_rcg_name=dict(),
activity_mode=dict(choices=['Active', 'Inactive']),
- pause=dict(type='bool'), freeze=dict(type='bool'),
+ pause=dict(type='bool', removed_in_version='3.0.0', removed_from_collection='dellemc.powerflex'),
+ freeze=dict(type='bool', removed_in_version='3.0.0', removed_from_collection='dellemc.powerflex'),
+ force=dict(type='bool'),
+ rcg_state=dict(choices=['failover', 'reverse',
+ 'restore', 'switchover',
+ 'sync', 'pause', 'resume',
+ 'freeze', 'unfreeze']),
pause_mode=dict(choices=['StopDataTransfer', 'OnlyTrackChanges']),
target_volume_access_mode=dict(choices=['ReadOnly', 'NoAccess']),
is_consistent=dict(type='bool'),
diff --git a/ansible_collections/dellemc/powerflex/plugins/modules/replication_pair.py b/ansible_collections/dellemc/powerflex/plugins/modules/replication_pair.py
index c95455023..1bd69f225 100644
--- a/ansible_collections/dellemc/powerflex/plugins/modules/replication_pair.py
+++ b/ansible_collections/dellemc/powerflex/plugins/modules/replication_pair.py
@@ -1,6 +1,6 @@
#!/usr/bin/python
-# 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)
""" Ansible module for managing replication pairs on Dell Technologies (Dell) PowerFlex"""
@@ -77,7 +77,7 @@ options:
- Copy type.
choices: ['Identical', 'OnlineCopy', 'OnlineHashCopy', 'OfflineCopy']
type: str
- required: True
+ required: true
name:
description:
- Name of replication pair.
@@ -138,7 +138,6 @@ notes:
'''
EXAMPLES = r'''
-
- name: Get replication pair details
dellemc.powerflex.replication_pair:
hostname: "{{hostname}}"
@@ -176,11 +175,11 @@ EXAMPLES = r'''
copy_type: "OnlineCopy"
name: "pair1"
remote_peer:
- hostname: "{{hostname}}"
- username: "{{username}}"
- password: "{{password}}"
- validate_certs: "{{validate_certs}}"
- port: "{{port}}"
+ hostname: "{{hostname}}"
+ username: "{{username}}"
+ password: "{{password}}"
+ validate_certs: "{{validate_certs}}"
+ port: "{{port}}"
- name: Pause replication pair
dellemc.powerflex.replication_pair:
@@ -190,7 +189,7 @@ EXAMPLES = r'''
validate_certs: "{{validate_certs}}"
port: "{{port}}"
pair_name: "pair1"
- pause: True
+ pause: true
- name: Resume replication pair
dellemc.powerflex.replication_pair:
@@ -200,7 +199,7 @@ EXAMPLES = r'''
validate_certs: "{{validate_certs}}"
port: "{{port}}"
pair_name: "pair1"
- pause: False
+ pause: false
- name: Delete replication pair
dellemc.powerflex.replication_pair:
@@ -596,7 +595,7 @@ class PowerFlexReplicationPair(object):
def validate_pause(self, params):
if params['pause'] is not None and (not params['pair_id'] and not params['pair_name']):
- self.module.fail_json(msg='Specify either pair_id or pair_name to perform pause or resume of inital copy')
+ self.module.fail_json(msg='Specify either pair_id or pair_name to perform pause or resume of initial copy')
def validate_pause_or_resume(self, pause, replication_pair_details, pair_id):
if not replication_pair_details:
diff --git a/ansible_collections/dellemc/powerflex/plugins/modules/sdc.py b/ansible_collections/dellemc/powerflex/plugins/modules/sdc.py
index a2f05a31b..bb13a19a2 100644
--- a/ansible_collections/dellemc/powerflex/plugins/modules/sdc.py
+++ b/ansible_collections/dellemc/powerflex/plugins/modules/sdc.py
@@ -46,6 +46,12 @@ options:
description:
- New name of the SDC. Used to rename the SDC.
type: str
+ performance_profile:
+ description:
+ - Define the performance profile as I(Compact) or I(HighPerformance).
+ - The high performance profile configures a predefined set of parameters for very high performance use cases.
+ choices: ['Compact', 'HighPerformance']
+ type: str
state:
description:
- State of the SDC.
@@ -75,6 +81,25 @@ EXAMPLES = r'''
sdc_name: "centos_sdc"
sdc_new_name: "centos_sdc_renamed"
state: "present"
+
+- name: Modify performance profile of SDC using SDC name
+ dellemc.powerflex.sdc:
+ hostname: "{{hostname}}"
+ username: "{{username}}"
+ password: "{{password}}"
+ validate_certs: "{{validate_certs}}"
+ sdc_name: "centos_sdc"
+ performance_profile: "Compact"
+ state: "present"
+
+- name: Remove SDC using SDC name
+ dellemc.powerflex.sdc:
+ hostname: "{{hostname}}"
+ username: "{{username}}"
+ password: "{{password}}"
+ validate_certs: "{{validate_certs}}"
+ sdc_name: "centos_sdc"
+ state: "absent"
'''
RETURN = r'''
@@ -274,16 +299,54 @@ class PowerFlexSdc(object):
def validate_parameters(self, sdc_name=None, sdc_id=None, sdc_ip=None):
"""Validate the input parameters"""
- if all(param is None for param in [sdc_name, sdc_id, sdc_ip]):
- self.module.fail_json(msg="Please provide sdc_name/sdc_id/sdc_ip "
- "with valid input.")
-
sdc_identifiers = ['sdc_name', 'sdc_id', 'sdc_ip']
for param in sdc_identifiers:
if self.module.params[param] is not None and \
len(self.module.params[param].strip()) == 0:
- error_msg = "Please provide valid %s" % param
- self.module.fail_json(msg=error_msg)
+ msg = f"Please provide valid {param}"
+ LOG.error(msg)
+ self.module.fail_json(msg=msg)
+
+ def remove(self, sdc_id):
+ """Remove the SDC"""
+ try:
+ LOG.info(msg=f"Removing SDC {sdc_id}")
+ self.powerflex_conn.sdc.delete(sdc_id)
+ return True
+ except Exception as e:
+ errormsg = f"Removing SDC {sdc_id} failed with error {str(e)}"
+ LOG.error(errormsg)
+ self.module.fail_json(msg=errormsg)
+
+ def set_performance_profile(self, sdc_id, performance_profile):
+ """Set performance profile of SDC"""
+ try:
+ LOG.info(msg=f"Setting performance profile of SDC {sdc_id}")
+ self.powerflex_conn.sdc.set_performance_profile(sdc_id, performance_profile)
+ return True
+ except Exception as e:
+ errormsg = f"Modifying performance profile of SDC {sdc_id} failed with error {str(e)}"
+ LOG.error(errormsg)
+ self.module.fail_json(msg=errormsg)
+
+ def validate_input(self, sdc_details, sdc_new_name, state, id_ip_name):
+ if state == 'present' and not sdc_details:
+ error_msg = 'Could not find any SDC instance with ' \
+ 'identifier %s.' % id_ip_name
+ LOG.error(error_msg)
+ self.module.fail_json(msg=error_msg)
+
+ if sdc_new_name and len(sdc_new_name.strip()) == 0:
+ self.module.fail_json(msg="Provide valid SDC name to rename to.")
+
+ def perform_modify(self, sdc_details, sdc_new_name, performance_profile):
+ changed = False
+ if sdc_new_name is not None and sdc_new_name != sdc_details['name']:
+ changed = self.rename_sdc(sdc_details['id'], sdc_new_name)
+
+ if performance_profile and performance_profile != sdc_details['perfProfile']:
+ changed = self.set_performance_profile(sdc_details['id'], performance_profile)
+ return changed
def perform_module_operation(self):
"""
@@ -294,6 +357,7 @@ class PowerFlexSdc(object):
sdc_id = self.module.params['sdc_id']
sdc_ip = self.module.params['sdc_ip']
sdc_new_name = self.module.params['sdc_new_name']
+ performance_profile = self.module.params['performance_profile']
state = self.module.params['state']
# result is a dictionary to contain end state and SDC details
@@ -304,40 +368,22 @@ class PowerFlexSdc(object):
)
self.validate_parameters(sdc_name, sdc_id, sdc_ip)
-
sdc_details = self.get_sdc(sdc_name=sdc_name, sdc_id=sdc_id,
sdc_ip=sdc_ip)
- if sdc_name:
- id_ip_name = sdc_name
- elif sdc_ip:
- id_ip_name = sdc_ip
- else:
- id_ip_name = sdc_id
+ id_ip_name = sdc_name or sdc_ip or sdc_id
- if state == 'present' and not sdc_details:
- error_msg = 'Could not find any SDC instance with ' \
- 'identifier %s.' % id_ip_name
- LOG.error(error_msg)
- self.module.fail_json(msg=error_msg)
+ self.validate_input(sdc_details, sdc_new_name, state, id_ip_name)
if state == 'absent' and sdc_details:
- error_msg = 'Removal of SDC is not allowed through Ansible ' \
- 'module.'
- LOG.error(error_msg)
- self.module.fail_json(msg=error_msg)
-
- if state == 'present' and sdc_details and sdc_new_name is not None:
- if len(sdc_new_name.strip()) == 0:
- self.module.fail_json(msg="Please provide valid SDC name.")
-
- changed = self.rename_sdc(sdc_details['id'], sdc_new_name)
+ changed = self.remove(sdc_details['id'])
- if changed:
- sdc_name = sdc_new_name
+ if state == 'present' and sdc_details:
+ changed = self.perform_modify(sdc_details, sdc_new_name, performance_profile)
- if state == 'present':
- result['sdc_details'] = self.get_sdc(sdc_name=sdc_name,
- sdc_id=sdc_id, sdc_ip=sdc_ip)
+ if changed:
+ sdc_details = self.get_sdc(sdc_name=sdc_new_name or sdc_name,
+ sdc_id=sdc_id, sdc_ip=sdc_ip)
+ result['sdc_details'] = sdc_details
result['changed'] = changed
self.module.exit_json(**result)
@@ -349,7 +395,7 @@ def get_powerflex_sdc_parameters():
sdc_id=dict(),
sdc_ip=dict(),
sdc_name=dict(),
- sdc_new_name=dict(),
+ sdc_new_name=dict(), performance_profile=dict(choices=['Compact', 'HighPerformance']),
state=dict(required=True, type='str', choices=['present', 'absent'])
)
diff --git a/ansible_collections/dellemc/powerflex/plugins/modules/sds.py b/ansible_collections/dellemc/powerflex/plugins/modules/sds.py
index 91c287769..b0d3045ec 100644
--- a/ansible_collections/dellemc/powerflex/plugins/modules/sds.py
+++ b/ansible_collections/dellemc/powerflex/plugins/modules/sds.py
@@ -1,6 +1,6 @@
#!/usr/bin/python
-# Copyright: (c) 2021, 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)
""" Ansible module for managing SDS on Dell Technologies (Dell) PowerFlex"""
@@ -19,6 +19,7 @@ description:
modifying attributes of SDS, and deleting SDS.
author:
- Rajshree Khare (@khareRajshree) <ansible.team@dell.com>
+- Trisha Datta (@trisha-dell) <ansible.team@dell.com>
extends_documentation_fragment:
- dellemc.powerflex.powerflex
options:
@@ -96,6 +97,16 @@ options:
- Default value by API is C(HighPerformance).
choices: ['Compact', 'HighPerformance']
type: str
+ fault_set_name:
+ description:
+ - Name of the fault set.
+ - Mutually exclusive with I(fault_set_id).
+ type: str
+ fault_set_id:
+ description:
+ - Unique identifier of the fault set.
+ - Mutually exclusive with I(fault_set_name).
+ type: str
state:
description:
- State of the SDS.
@@ -114,7 +125,7 @@ notes:
'sdsOnly').
- SDS can be created with RF cache disabled, but, be aware that the RF cache
is not always updated. In this case, the user should re-try the operation.
- - The I(check_mode) is not supported.
+ - The I(check_mode) is supported.
'''
EXAMPLES = r'''
@@ -142,6 +153,7 @@ EXAMPLES = r'''
port: "{{port}}"
sds_name: "node1"
protection_domain_name: "domain1"
+ fault_set_name: "faultset1"
sds_ip_list:
- ip: "198.10.xxx.xxx"
role: "sdcOnly"
@@ -479,12 +491,16 @@ sds_details:
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.dellemc.powerflex.plugins.module_utils.storage.dell\
import utils
+from ansible_collections.dellemc.powerflex.plugins.module_utils.storage.dell.libraries.powerflex_base \
+ import PowerFlexBase
+from ansible_collections.dellemc.powerflex.plugins.module_utils.storage.dell.libraries.configuration \
+ import Configuration
import copy
LOG = utils.get_logger('sds')
-class PowerFlexSDS(object):
+class PowerFlexSDS(PowerFlexBase):
"""Class with SDS operations"""
def __init__(self):
@@ -493,29 +509,27 @@ class PowerFlexSDS(object):
self.module_params.update(get_powerflex_sds_parameters())
mut_ex_args = [['sds_name', 'sds_id'],
- ['protection_domain_name', 'protection_domain_id']]
+ ['protection_domain_name', 'protection_domain_id'],
+ ['fault_set_name', 'fault_set_id']]
required_together_args = [['sds_ip_list', 'sds_ip_state']]
required_one_of_args = [['sds_name', 'sds_id']]
# initialize the Ansible module
- self.module = AnsibleModule(
- argument_spec=self.module_params,
- supports_check_mode=False,
- mutually_exclusive=mut_ex_args,
- required_together=required_together_args,
- required_one_of=required_one_of_args)
-
- utils.ensure_required_libs(self.module)
-
- try:
- self.powerflex_conn = utils.get_powerflex_gateway_host_connection(
- self.module.params)
- LOG.info("Got the PowerFlex system connection object instance")
- except Exception as e:
- LOG.error(str(e))
- self.module.fail_json(msg=str(e))
+ ansible_module_params = {
+ 'argument_spec': get_powerflex_sds_parameters(),
+ 'supports_check_mode': True,
+ 'mutually_exclusive': mut_ex_args,
+ 'required_one_of': required_one_of_args,
+ 'required_together': required_together_args
+ }
+ super().__init__(AnsibleModule, ansible_module_params)
+
+ self.result = dict(
+ changed=False,
+ sds_details={}
+ )
def validate_rmcache_size_parameter(self, rmcache_enabled, rmcache_size):
"""Validate the input parameters"""
@@ -571,40 +585,24 @@ class PowerFlexSDS(object):
LOG.error(error_msg)
self.module.fail_json(msg=error_msg)
- def get_protection_domain(self, protection_domain_name=None,
- protection_domain_id=None):
- """Get protection domain details
- :param protection_domain_name: Name of the protection domain
+ def get_protection_domain(
+ self, protection_domain_name=None, protection_domain_id=None
+ ):
+ """Get the details of a protection domain in a given PowerFlex storage
+ system"""
+ return Configuration(self.powerflex_conn, self.module).get_protection_domain(
+ protection_domain_name=protection_domain_name, protection_domain_id=protection_domain_id)
+
+ def get_fault_set(self, fault_set_name=None, fault_set_id=None, protection_domain_id=None):
+ """Get fault set details
+ :param fault_set_name: Name of the fault set
+ :param fault_set_id: Id of the fault set
:param protection_domain_id: ID of the protection domain
- :return: Protection domain details
+ :return: Fault set details
:rtype: dict
"""
- name_or_id = protection_domain_id if protection_domain_id \
- else protection_domain_name
- try:
- pd_details = None
- if protection_domain_id:
- pd_details = self.powerflex_conn.protection_domain.get(
- filter_fields={'id': protection_domain_id})
-
- if protection_domain_name:
- pd_details = self.powerflex_conn.protection_domain.get(
- filter_fields={'name': protection_domain_name})
-
- if not pd_details:
- error_msg = "Unable to find the protection domain with " \
- "'%s'. Please enter a valid protection domain " \
- "name/id." % name_or_id
- LOG.error(error_msg)
- self.module.fail_json(msg=error_msg)
-
- return pd_details[0]
-
- except Exception as e:
- error_msg = "Failed to get the protection domain '%s' with " \
- "error '%s'" % (name_or_id, str(e))
- LOG.error(error_msg)
- self.module.fail_json(msg=error_msg)
+ return Configuration(self.powerflex_conn, self.module).get_fault_set(
+ fault_set_name=fault_set_name, fault_set_id=fault_set_id, protection_domain_id=protection_domain_id)
def restructure_ip_role_dict(self, sds_ip_list):
"""Restructure IP role dict
@@ -619,8 +617,41 @@ class PowerFlexSDS(object):
new_sds_ip_list.append({"SdsIp": item})
return new_sds_ip_list
- def create_sds(self, protection_domain_id, sds_ip_list, sds_ip_state,
- sds_name, rmcache_enabled=None, rmcache_size=None):
+ def validate_create(self, protection_domain_id, sds_ip_list, sds_ip_state, sds_name,
+ sds_id, sds_new_name, rmcache_enabled=None, rmcache_size=None,
+ fault_set_id=None):
+
+ if sds_name is None or len(sds_name.strip()) == 0:
+ error_msg = "Please provide valid sds_name value for " \
+ "creation of SDS."
+ LOG.error(error_msg)
+ self.module.fail_json(msg=error_msg)
+
+ if protection_domain_id is None:
+ error_msg = "Protection Domain is a mandatory parameter " \
+ "for creating an SDS. Please enter a valid value."
+ LOG.error(error_msg)
+ self.module.fail_json(msg=error_msg)
+
+ if sds_ip_list is None or len(sds_ip_list) == 0:
+ error_msg = "Please provide valid sds_ip_list values for " \
+ "creation of SDS."
+ LOG.error(error_msg)
+ self.module.fail_json(msg=error_msg)
+
+ if sds_ip_state is not None and sds_ip_state != "present-in-sds":
+ error_msg = "Incorrect IP state given for creation of SDS."
+ LOG.error(error_msg)
+ self.module.fail_json(msg=error_msg)
+
+ if sds_id:
+ error_msg = "Creation of SDS is allowed using sds_name " \
+ "only, sds_id given."
+ LOG.info(error_msg)
+ self.module.fail_json(msg=error_msg)
+
+ def create_sds(self, protection_domain_id, sds_ip_list, sds_ip_state, sds_name,
+ sds_id, sds_new_name, rmcache_enabled=None, rmcache_size=None, fault_set_id=None):
"""Create SDS
:param protection_domain_id: ID of the Protection Domain
:type protection_domain_id: str
@@ -636,62 +667,53 @@ class PowerFlexSDS(object):
:type rmcache_enabled: bool
:param rmcache_size: Read RAM cache size (in MB)
:type rmcache_size: int
+ :param fault_set_id: ID of the Fault Set
+ :type fault_set_id: str
:return: Boolean indicating if create operation is successful
"""
try:
- if sds_name is None or len(sds_name.strip()) == 0:
- error_msg = "Please provide valid sds_name value for " \
- "creation of SDS."
- LOG.error(error_msg)
- self.module.fail_json(msg=error_msg)
- if protection_domain_id is None:
- error_msg = "Protection Domain is a mandatory parameter " \
- "for creating a SDS. Please enter a valid value."
- LOG.error(error_msg)
- self.module.fail_json(msg=error_msg)
-
- if sds_ip_list is None or len(sds_ip_list) == 0:
- error_msg = "Please provide valid sds_ip_list values for " \
- "creation of SDS."
- LOG.error(error_msg)
- self.module.fail_json(msg=error_msg)
+ # Restructure IP-role parameter format
+ self.validate_create(protection_domain_id=protection_domain_id,
+ sds_ip_list=sds_ip_list, sds_ip_state=sds_ip_state,
+ sds_name=sds_name, sds_id=sds_id, sds_new_name=sds_new_name,
+ rmcache_enabled=rmcache_enabled, rmcache_size=rmcache_size,
+ fault_set_id=fault_set_id)
- if sds_ip_state is not None and sds_ip_state != "present-in-sds":
- error_msg = "Incorrect IP state given for creation of SDS."
- LOG.error(error_msg)
- self.module.fail_json(msg=error_msg)
+ self.validate_ip_parameter(sds_ip_list)
- # Restructure IP-role parameter format
- if sds_ip_list and sds_ip_state == "present-in-sds":
- sds_ip_list = self.restructure_ip_role_dict(sds_ip_list)
-
- if rmcache_size is not None:
- self.validate_rmcache_size_parameter(rmcache_enabled,
- rmcache_size)
- # set rmcache size in KB
- rmcache_size = rmcache_size * 1024
-
- create_params = ("protection_domain_id: %s,"
- " sds_ip_list: %s,"
- " sds_name: %s,"
- " rmcache_enabled: %s, "
- " rmcache_size_KB: %s"
- % (protection_domain_id, sds_ip_list,
- sds_name, rmcache_enabled, rmcache_size))
- LOG.info("Creating SDS with params: %s", create_params)
-
- self.powerflex_conn.sds.create(
- protection_domain_id=protection_domain_id,
- sds_ips=sds_ip_list,
- name=sds_name,
- rmcache_enabled=rmcache_enabled,
- rmcache_size_in_kb=rmcache_size)
- return True
+ if not self.module.check_mode:
+ if sds_ip_list and sds_ip_state == "present-in-sds":
+ sds_ip_list = self.restructure_ip_role_dict(sds_ip_list)
+
+ if rmcache_size is not None:
+ self.validate_rmcache_size_parameter(rmcache_enabled=rmcache_enabled,
+ rmcache_size=rmcache_size)
+ # set rmcache size in KB
+ rmcache_size = rmcache_size * 1024
+
+ create_params = ("protection_domain_id: %s,"
+ " sds_ip_list: %s,"
+ " sds_name: %s,"
+ " rmcache_enabled: %s, "
+ " rmcache_size_KB: %s, "
+ " fault_set_id: %s"
+ % (protection_domain_id, sds_ip_list,
+ sds_name, rmcache_enabled, rmcache_size,
+ fault_set_id))
+ LOG.info("Creating SDS with params: %s", create_params)
+
+ self.powerflex_conn.sds.create(
+ protection_domain_id=protection_domain_id,
+ sds_ips=sds_ip_list,
+ name=sds_name,
+ rmcache_enabled=rmcache_enabled,
+ rmcache_size_in_kb=rmcache_size,
+ fault_set_id=fault_set_id)
+ return self.get_sds_details(sds_name=sds_name)
except Exception as e:
- error_msg = "Create SDS '%s' operation failed with error '%s'" \
- % (sds_name, str(e))
+ error_msg = f"Create SDS {sds_name} operation failed with error {str(e)}"
LOG.error(error_msg)
self.module.fail_json(msg=error_msg)
@@ -716,21 +738,20 @@ class PowerFlexSDS(object):
"""
modify_dict = {}
- if sds_new_name is not None:
- if len(sds_new_name.strip()) == 0:
- error_msg = "Please provide valid SDS name."
- LOG.error(error_msg)
- self.module.fail_json(msg=error_msg)
- if sds_new_name != sds_details['name']:
- modify_dict['name'] = sds_new_name
+ if sds_new_name is not None and \
+ sds_new_name != sds_details['name']:
+ modify_dict['name'] = sds_new_name
- if rfcache_enabled is not None and \
- sds_details['rfcacheEnabled'] != rfcache_enabled:
- modify_dict['rfcacheEnabled'] = rfcache_enabled
+ param_input = dict()
+ param_input['rfcacheEnabled'] = rfcache_enabled
+ param_input['rmcacheEnabled'] = rmcache_enabled
+ param_input['perfProfile'] = performance_profile
- if rmcache_enabled is not None and \
- sds_details['rmcacheEnabled'] != rmcache_enabled:
- modify_dict['rmcacheEnabled'] = rmcache_enabled
+ param_list = ['rfcacheEnabled', 'rmcacheEnabled', 'perfProfile']
+ for param in param_list:
+ if param_input[param] is not None and \
+ sds_details[param] != param_input[param]:
+ modify_dict[param] = param_input[param]
if rmcache_size is not None:
self.validate_rmcache_size_parameter(rmcache_enabled,
@@ -748,10 +769,6 @@ class PowerFlexSDS(object):
LOG.error(error_msg)
self.module.fail_json(msg=error_msg)
- if performance_profile is not None and \
- sds_details['perfProfile'] != performance_profile:
- modify_dict['perfProfile'] = performance_profile
-
return modify_dict
def modify_sds_attributes(self, sds_id, modify_dict,
@@ -772,41 +789,42 @@ class PowerFlexSDS(object):
" updated is '%s'." % (str(modify_dict))
LOG.info(msg)
- if 'name' in modify_dict:
- self.powerflex_conn.sds.rename(sds_id, modify_dict['name'])
- msg = "The name of the SDS is updated to '%s' successfully." \
- % modify_dict['name']
- LOG.info(msg)
+ if not self.module.check_mode:
+ if 'name' in modify_dict:
+ self.powerflex_conn.sds.rename(sds_id, modify_dict['name'])
+ msg = "The name of the SDS is updated to '%s' successfully." \
+ % modify_dict['name']
+ LOG.info(msg)
- if 'rfcacheEnabled' in modify_dict:
- self.powerflex_conn.sds.set_rfcache_enabled(
- sds_id, modify_dict['rfcacheEnabled'])
- msg = "The use RFcache is updated to '%s' successfully." \
- % modify_dict['rfcacheEnabled']
- LOG.info(msg)
+ if 'rfcacheEnabled' in modify_dict:
+ self.powerflex_conn.sds.set_rfcache_enabled(
+ sds_id, modify_dict['rfcacheEnabled'])
+ msg = "The use RFcache is updated to '%s' successfully." \
+ % modify_dict['rfcacheEnabled']
+ LOG.info(msg)
- if 'rmcacheEnabled' in modify_dict:
- self.powerflex_conn.sds.set_rmcache_enabled(
- sds_id, modify_dict['rmcacheEnabled'])
- msg = "The use RMcache is updated to '%s' successfully." \
- % modify_dict['rmcacheEnabled']
- LOG.info(msg)
+ if 'rmcacheEnabled' in modify_dict:
+ self.powerflex_conn.sds.set_rmcache_enabled(
+ sds_id, modify_dict['rmcacheEnabled'])
+ msg = "The use RMcache is updated to '%s' successfully." \
+ % modify_dict['rmcacheEnabled']
+ LOG.info(msg)
- if 'rmcacheSizeInMB' in modify_dict:
- self.powerflex_conn.sds.set_rmcache_size(
- sds_id, modify_dict['rmcacheSizeInMB'])
- msg = "The size of RMcache is updated to '%s' successfully." \
- % modify_dict['rmcacheSizeInMB']
- LOG.info(msg)
+ if 'rmcacheSizeInMB' in modify_dict:
+ self.powerflex_conn.sds.set_rmcache_size(
+ sds_id, modify_dict['rmcacheSizeInMB'])
+ msg = "The size of RMcache is updated to '%s' successfully." \
+ % modify_dict['rmcacheSizeInMB']
+ LOG.info(msg)
- if 'perfProfile' in modify_dict:
- self.powerflex_conn.sds.set_performance_parameters(
- sds_id, modify_dict['perfProfile'])
- msg = "The performance profile is updated to '%s'" \
- % modify_dict['perfProfile']
- LOG.info(msg)
+ if 'perfProfile' in modify_dict:
+ self.powerflex_conn.sds.set_performance_parameters(
+ sds_id, modify_dict['perfProfile'])
+ msg = "The performance profile is updated to '%s'" \
+ % modify_dict['perfProfile']
+ LOG.info(msg)
- return True
+ return self.get_sds_details(sds_id=sds_id)
except Exception as e:
if create_flag:
error_msg = "Create SDS is successful, but failed to update" \
@@ -818,50 +836,39 @@ class PowerFlexSDS(object):
LOG.error(error_msg)
self.module.fail_json(msg=error_msg)
- def identify_ip_role(self, sds_ip_list, sds_details, sds_ip_state):
- """Identify IPs before addition/removal
- :param sds_ip_list: List of one or more IP addresses and
- their roles
- :type sds_ip_list: list[dict]
- :param sds_details: SDS details
- :type sds_details: dict
- :param sds_ip_state: State of IP in SDS
- :type sds_ip_state: str
- :return: List containing the key-value pairs of IP-role for an
- SDS
- :rtype: list[dict]
- """
+ def identify_ip_role_add(self, sds_ip_list, sds_details, sds_ip_state):
+ # identify IPs to add or roles to update
+
existing_ip_role_list = sds_details['ipList']
+ update_role = []
+ ips_to_add = []
+
+ # identify IPs to add
+ existing_ip_list = []
+ if existing_ip_role_list:
+ for ip in existing_ip_role_list:
+ existing_ip_list.append(ip['ip'])
+ for given_ip in sds_ip_list:
+ ip = given_ip['ip']
+ if ip not in existing_ip_list:
+ ips_to_add.append(given_ip)
+ LOG.info("IP(s) to be added: %s", ips_to_add)
+
+ if len(ips_to_add) != 0:
+ for ip in ips_to_add:
+ sds_ip_list.remove(ip)
+
+ # identify IPs whose role needs to be updated
+ update_role = [ip for ip in sds_ip_list
+ if ip not in existing_ip_role_list]
+ LOG.info("Role update needed for: %s", update_role)
+ return ips_to_add, update_role
+
+ def identify_ip_role_remove(self, sds_ip_list, sds_details, sds_ip_state):
+ # identify IPs to remove
- # identify IPs to add or roles to update
- if sds_ip_state == "present-in-sds":
- update_role = []
- ips_to_add = []
-
- # identify IPs to add
- existing_ip_list = []
- if existing_ip_role_list:
- for ip in existing_ip_role_list:
- existing_ip_list.append(ip['ip'])
- for given_ip in sds_ip_list:
- ip = given_ip['ip']
- if ip not in existing_ip_list:
- ips_to_add.append(given_ip)
- LOG.info("IP(s) to be added: %s", ips_to_add)
-
- if len(ips_to_add) != 0:
- for ip in ips_to_add:
- sds_ip_list.remove(ip)
-
- # identify IPs whose role needs to be updated
- update_role = [ip for ip in sds_ip_list
- if ip not in existing_ip_role_list]
- LOG.info("Role update needed for: %s", update_role)
-
- return ips_to_add, update_role
-
- elif sds_ip_state == "absent-in-sds":
- # identify IPs to remove
+ existing_ip_role_list = sds_details['ipList']
+ if sds_ip_state == "absent-in-sds":
ips_to_remove = [ip for ip in existing_ip_role_list
if ip in sds_ip_list]
if len(ips_to_remove) != 0:
@@ -869,7 +876,7 @@ class PowerFlexSDS(object):
return ips_to_remove
else:
LOG.info("IP(s) do not exists.")
- return False, None
+ return []
def add_ip(self, sds_id, sds_ip_list):
"""Add IP to SDS
@@ -881,10 +888,11 @@ class PowerFlexSDS(object):
:return: Boolean indicating if add IP operation is successful
"""
try:
- for ip in sds_ip_list:
- LOG.info("IP to add: %s", ip)
- self.powerflex_conn.sds.add_ip(sds_id=sds_id, sds_ip=ip)
- LOG.info("IP added successfully.")
+ if not self.module.check_mode:
+ for ip in sds_ip_list:
+ LOG.info("IP to add: %s", ip)
+ self.powerflex_conn.sds.add_ip(sds_id=sds_id, sds_ip=ip)
+ LOG.info("IP added successfully.")
return True
except Exception as e:
error_msg = "Add IP to SDS '%s' operation failed with " \
@@ -902,15 +910,16 @@ class PowerFlexSDS(object):
:return: Boolean indicating if add IP operation is successful
"""
try:
- LOG.info("Role updates for: %s", sds_ip_list)
- if len(sds_ip_list) != 0:
- for ip in sds_ip_list:
- LOG.info("ip-role: %s", ip)
- self.powerflex_conn.sds.set_ip_role(sds_id, ip['ip'],
- ip['role'])
- msg = "The role '%s' for IP '%s' is updated " \
- "successfully." % (ip['role'], ip['ip'])
- LOG.info(msg)
+ if not self.module.check_mode:
+ LOG.info("Role updates for: %s", sds_ip_list)
+ if len(sds_ip_list) != 0:
+ for ip in sds_ip_list:
+ LOG.info("ip-role: %s", ip)
+ self.powerflex_conn.sds.set_ip_role(sds_id, ip['ip'],
+ ip['role'])
+ msg = "The role '%s' for IP '%s' is updated " \
+ "successfully." % (ip['role'], ip['ip'])
+ LOG.info(msg)
return True
except Exception as e:
error_msg = "Update role of IP for SDS '%s' operation failed " \
@@ -928,10 +937,11 @@ class PowerFlexSDS(object):
:return: Boolean indicating if remove IP operation is successful
"""
try:
- for ip in sds_ip_list:
- LOG.info("IP to remove: %s", ip)
- self.powerflex_conn.sds.remove_ip(sds_id=sds_id, ip=ip['ip'])
- LOG.info("IP removed successfully.")
+ if not self.module.check_mode:
+ for ip in sds_ip_list:
+ LOG.info("IP to remove: %s", ip)
+ self.powerflex_conn.sds.remove_ip(sds_id=sds_id, ip=ip['ip'])
+ LOG.info("IP removed successfully.")
return True
except Exception as e:
error_msg = "Remove IP from SDS '%s' operation failed with " \
@@ -946,145 +956,16 @@ class PowerFlexSDS(object):
:return: Boolean indicating if delete operation is successful
"""
try:
- self.powerflex_conn.sds.delete(sds_id)
- return True
+ if not self.module.check_mode:
+ self.powerflex_conn.sds.delete(sds_id)
+ return None
+ return self.get_sds_details(sds_id=sds_id)
except Exception as e:
error_msg = "Delete SDS '%s' operation failed with error '%s'" \
% (sds_id, str(e))
LOG.error(error_msg)
self.module.fail_json(msg=error_msg)
- def perform_module_operation(self):
- """
- Perform different actions on SDS based on parameters passed in
- the playbook
- """
- sds_name = self.module.params['sds_name']
- sds_id = self.module.params['sds_id']
- sds_new_name = self.module.params['sds_new_name']
- protection_domain_name = self.module.params['protection_domain_name']
- protection_domain_id = self.module.params['protection_domain_id']
- rfcache_enabled = self.module.params['rfcache_enabled']
- rmcache_enabled = self.module.params['rmcache_enabled']
- rmcache_size = self.module.params['rmcache_size']
- sds_ip_list = copy.deepcopy(self.module.params['sds_ip_list'])
- sds_ip_state = self.module.params['sds_ip_state']
- performance_profile = self.module.params['performance_profile']
- state = self.module.params['state']
-
- # result is a dictionary to contain end state and SDS details
- changed = False
- result = dict(
- changed=False,
- sds_details={}
- )
-
- # get SDS details
- sds_details = self.get_sds_details(sds_name, sds_id)
- if sds_details:
- sds_id = sds_details['id']
- msg = "Fetched the SDS details %s" % (str(sds_details))
- LOG.info(msg)
-
- # get Protection Domain ID from name
- if protection_domain_name:
- pd_details = self.get_protection_domain(protection_domain_name)
- if pd_details:
- protection_domain_id = pd_details['id']
- msg = "Fetched the protection domain details with id '%s', " \
- "name '%s'" % (protection_domain_id, protection_domain_name)
- LOG.info(msg)
-
- # create operation
- create_changed = False
- if state == 'present' and not sds_details:
- if sds_id:
- error_msg = "Creation of SDS is allowed using sds_name " \
- "only, sds_id given."
- LOG.info(error_msg)
- self.module.fail_json(msg=error_msg)
-
- if sds_new_name:
- error_msg = "sds_new_name parameter is not supported " \
- "during creation of a SDS. Try renaming the " \
- "SDS after the creation."
- LOG.info(error_msg)
- self.module.fail_json(msg=error_msg)
-
- self.validate_ip_parameter(sds_ip_list)
-
- create_changed = self.create_sds(protection_domain_id,
- sds_ip_list, sds_ip_state,
- sds_name, rmcache_enabled,
- rmcache_size)
- if create_changed:
- sds_details = self.get_sds_details(sds_name)
- sds_id = sds_details['id']
- msg = "SDS created successfully, fetched SDS details %s"\
- % (str(sds_details))
- LOG.info(msg)
-
- # checking if basic SDS parameters are modified or not
- modify_dict = {}
- if sds_details and state == 'present':
- modify_dict = self.to_modify(sds_details, sds_new_name,
- rfcache_enabled, rmcache_enabled,
- rmcache_size, performance_profile)
- msg = "Parameters to be modified are as follows: %s"\
- % (str(modify_dict))
- LOG.info(msg)
-
- # modify operation
- modify_changed = False
- if modify_dict and state == 'present':
- LOG.info("Modify SDS params.")
- modify_changed = self.modify_sds_attributes(sds_id, modify_dict,
- create_changed)
-
- # get updated SDS details
- sds_details = self.get_sds_details(sds_id=sds_id)
-
- # add IPs to SDS
- # update IP's role for an SDS
- add_ip_changed = False
- update_role_changed = False
- if sds_details and state == 'present' \
- and sds_ip_state == "present-in-sds":
- self.validate_ip_parameter(sds_ip_list)
- ips_to_add, roles_to_update = self.identify_ip_role(
- sds_ip_list, sds_details, sds_ip_state)
- if ips_to_add:
- add_ip_changed = self.add_ip(sds_id, ips_to_add)
- if roles_to_update:
- update_role_changed = self.update_role(sds_id,
- roles_to_update)
-
- # remove IPs from SDS
- remove_ip_changed = False
- if sds_details and state == 'present' \
- and sds_ip_state == "absent-in-sds":
- self.validate_ip_parameter(sds_ip_list)
- ips_to_remove = self.identify_ip_role(sds_ip_list, sds_details,
- sds_ip_state)
- if ips_to_remove:
- remove_ip_changed = self.remove_ip(sds_id, ips_to_remove)
-
- # delete operation
- delete_changed = False
- if sds_details and state == 'absent':
- delete_changed = self.delete_sds(sds_id)
-
- if create_changed or modify_changed or add_ip_changed \
- or update_role_changed or remove_ip_changed or delete_changed:
- changed = True
-
- # Returning the updated SDS details
- if state == 'present':
- sds_details = self.show_output(sds_id)
- result['sds_details'] = sds_details
- result['changed'] = changed
- self.module.exit_json(**result)
-
def show_output(self, sds_id):
"""Show SDS details
:param sds_id: ID of the SDS
@@ -1115,6 +996,14 @@ class PowerFlexSDS(object):
rmcache_size_mb = sds_details[0]['rmcacheSizeInKb'] / 1024
sds_details[0]['rmcacheSizeInMb'] = int(rmcache_size_mb)
+ # Append fault set name
+ if 'faultSetId' in sds_details[0] \
+ and sds_details[0]['faultSetId']:
+ fs_details = self.get_fault_set(
+ fault_set_id=sds_details[0]['faultSetId'],
+ protection_domain_id=sds_details[0]['protectionDomainId'])
+ sds_details[0]['faultSetName'] = fs_details['name']
+
return sds_details[0]
except Exception as e:
@@ -1123,6 +1012,15 @@ class PowerFlexSDS(object):
LOG.error(error_msg)
self.module.fail_json(msg=error_msg)
+ def validate_parameters(self, sds_params):
+ params = [sds_params['sds_name'], sds_params['sds_new_name']]
+ for param in params:
+ if param is not None and len(param.strip()) == 0:
+ error_msg = "Provide valid value for name for the " \
+ "creation/modification of the SDS."
+ LOG.error(error_msg)
+ self.module.fail_json(msg=error_msg)
+
def get_powerflex_sds_parameters():
"""This method provide parameter required for the SDS module on
@@ -1145,15 +1043,137 @@ def get_powerflex_sds_parameters():
rmcache_enabled=dict(type='bool'),
rmcache_size=dict(type='int'),
performance_profile=dict(choices=['Compact', 'HighPerformance']),
+ fault_set_name=dict(),
+ fault_set_id=dict(),
state=dict(required=True, type='str', choices=['present', 'absent'])
)
+class SDSExitHandler():
+ def handle(self, sds_obj, sds_details):
+ if sds_details:
+ sds_obj.result["sds_details"] = sds_obj.show_output(sds_id=sds_details['id'])
+ else:
+ sds_obj.result["sds_details"] = None
+ sds_obj.module.exit_json(**sds_obj.result)
+
+
+class SDSDeleteHandler():
+ def handle(self, sds_obj, sds_params, sds_details):
+ if sds_params['state'] == 'absent' and sds_details:
+ sds_details = sds_obj.delete_sds(sds_details['id'])
+ sds_obj.result['changed'] = True
+
+ SDSExitHandler().handle(sds_obj, sds_details)
+
+
+class SDSRemoveIPHandler():
+ def handle(self, sds_obj, sds_params, sds_details, sds_ip_list):
+ if sds_params['state'] == 'present' and sds_details:
+ # remove IPs from SDS
+ remove_ip_changed = False
+ if sds_params['sds_ip_state'] == "absent-in-sds":
+ sds_obj.validate_ip_parameter(sds_ip_list)
+ ips_to_remove = sds_obj.identify_ip_role_remove(sds_ip_list, sds_details,
+ sds_params['sds_ip_state'])
+ if ips_to_remove:
+ remove_ip_changed = sds_obj.remove_ip(sds_details['id'], ips_to_remove)
+
+ if remove_ip_changed:
+ sds_obj.result['changed'] = True
+
+ SDSDeleteHandler().handle(sds_obj, sds_params, sds_details)
+
+
+class SDSAddIPHandler():
+ def handle(self, sds_obj, sds_params, sds_details, sds_ip_list):
+ if sds_params['state'] == 'present' and sds_details:
+ # add IPs to SDS
+ # update IP's role for an SDS
+ add_ip_changed = False
+ update_role_changed = False
+ if sds_params['sds_ip_state'] == "present-in-sds":
+ sds_obj.validate_ip_parameter(sds_ip_list)
+ ips_to_add, roles_to_update = sds_obj.identify_ip_role_add(
+ sds_ip_list, sds_details, sds_params['sds_ip_state'])
+ if ips_to_add:
+ add_ip_changed = sds_obj.add_ip(sds_details['id'], ips_to_add)
+ if roles_to_update:
+ update_role_changed = sds_obj.update_role(sds_details['id'],
+ roles_to_update)
+
+ if add_ip_changed or update_role_changed:
+ sds_obj.result['changed'] = True
+
+ SDSRemoveIPHandler().handle(sds_obj, sds_params, sds_details, sds_ip_list)
+
+
+class SDSModifyHandler():
+ def handle(self, sds_obj, sds_params, sds_details, create_flag, sds_ip_list):
+ if sds_params['state'] == 'present' and sds_details:
+ modify_dict = sds_obj.to_modify(sds_details=sds_details,
+ sds_new_name=sds_params['sds_new_name'],
+ rfcache_enabled=sds_params['rfcache_enabled'],
+ rmcache_enabled=sds_params['rmcache_enabled'],
+ rmcache_size=sds_params['rmcache_size'],
+ performance_profile=sds_params['performance_profile'])
+ if modify_dict:
+ sds_details = sds_obj.modify_sds_attributes(sds_id=sds_details['id'],
+ modify_dict=modify_dict,
+ create_flag=create_flag)
+ sds_obj.result['changed'] = True
+
+ SDSAddIPHandler().handle(sds_obj, sds_params, sds_details, sds_ip_list)
+
+
+class SDSCreateHandler():
+ def handle(self, sds_obj, sds_params, sds_details, protection_domain_id, fault_set_id):
+ create_flag = False
+ sds_ip_list = copy.deepcopy(sds_params['sds_ip_list'])
+ if sds_params['state'] == 'present' and not sds_details:
+ sds_details = sds_obj.create_sds(sds_name=sds_params['sds_name'],
+ sds_id=sds_params['sds_id'],
+ sds_new_name=sds_params['sds_new_name'],
+ protection_domain_id=protection_domain_id,
+ sds_ip_list=sds_ip_list,
+ sds_ip_state=sds_params['sds_ip_state'],
+ rmcache_enabled=sds_params['rmcache_enabled'],
+ rmcache_size=sds_params['rmcache_size'],
+ fault_set_id=fault_set_id)
+ sds_obj.result['changed'] = True
+ create_flag = True
+
+ SDSModifyHandler().handle(sds_obj, sds_params, sds_details, create_flag, sds_ip_list)
+
+
+class SDSHandler():
+ def handle(self, sds_obj, sds_params):
+ sds_details = sds_obj.get_sds_details(sds_params['sds_name'], sds_params['sds_id'])
+ sds_obj.validate_parameters(sds_params=sds_params)
+ protection_domain_id = None
+ if sds_params['protection_domain_id'] or sds_params['protection_domain_name']:
+ protection_domain_id = sds_obj.get_protection_domain(
+ protection_domain_id=sds_params['protection_domain_id'],
+ protection_domain_name=sds_params['protection_domain_name'])['id']
+ fault_set_id = None
+ if sds_params['fault_set_name'] or sds_params['fault_set_id']:
+ fault_set_details = sds_obj.get_fault_set(fault_set_name=sds_params['fault_set_name'],
+ fault_set_id=sds_params['fault_set_id'],
+ protection_domain_id=protection_domain_id)
+ if fault_set_details is None:
+ error_msg = "The specified Fault set is not in the specified Protection Domain."
+ LOG.error(error_msg)
+ sds_obj.module.fail_json(msg=error_msg)
+ else:
+ fault_set_id = fault_set_details['id']
+ SDSCreateHandler().handle(sds_obj, sds_params, sds_details, protection_domain_id, fault_set_id)
+
+
def main():
- """ Create PowerFlex SDS object and perform actions on it
- based on user input from playbook"""
+ """ Create PowerFlex SDS object and perform action on it
+ based on user input from playbook."""
obj = PowerFlexSDS()
- obj.perform_module_operation()
+ SDSHandler().handle(obj, obj.module.params)
if __name__ == '__main__':
diff --git a/ansible_collections/dellemc/powerflex/plugins/modules/snapshot.py b/ansible_collections/dellemc/powerflex/plugins/modules/snapshot.py
index 69caea075..0cc41c50e 100644
--- a/ansible_collections/dellemc/powerflex/plugins/modules/snapshot.py
+++ b/ansible_collections/dellemc/powerflex/plugins/modules/snapshot.py
@@ -150,7 +150,7 @@ EXAMPLES = r'''
validate_certs: "{{validate_certs}}"
snapshot_name: "ansible_snapshot"
vol_name: "ansible_volume"
- read_only: False
+ read_only: false
desired_retention: 2
state: "present"
@@ -171,9 +171,9 @@ EXAMPLES = r'''
validate_certs: "{{validate_certs}}"
snapshot_id: "fe6cb28200000007"
sdc:
- - sdc_ip: "198.10.xxx.xxx"
- - sdc_id: "663ac0d200000001"
- allow_multiple_mappings: True
+ - sdc_ip: "198.10.xxx.xxx"
+ - sdc_id: "663ac0d200000001"
+ allow_multiple_mappings: true
sdc_state: "mapped"
state: "present"
@@ -185,13 +185,13 @@ EXAMPLES = r'''
validate_certs: "{{validate_certs}}"
snapshot_id: "fe6cb28200000007"
sdc:
- - sdc_ip: "198.10.xxx.xxx"
- iops_limit: 11
- bandwidth_limit: 4096
- - sdc_id: "663ac0d200000001"
- iops_limit: 20
- bandwidth_limit: 2048
- allow_multiple_mappings: True
+ - sdc_ip: "198.10.xxx.xxx"
+ iops_limit: 11
+ bandwidth_limit: 4096
+ - sdc_id: "663ac0d200000001"
+ iops_limit: 20
+ bandwidth_limit: 2048
+ allow_multiple_mappings: true
sdc_state: "mapped"
state: "present"
diff --git a/ansible_collections/dellemc/powerflex/plugins/modules/snapshot_policy.py b/ansible_collections/dellemc/powerflex/plugins/modules/snapshot_policy.py
new file mode 100644
index 000000000..af2084e55
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/plugins/modules/snapshot_policy.py
@@ -0,0 +1,828 @@
+#!/usr/bin/python
+
+# Copyright: (c) 2023, Dell Technologies
+# Apache License version 2.0 (see MODULE-LICENSE or http://www.apache.org/licenses/LICENSE-2.0.txt)
+
+""" Ansible module for managing snapshot policies on Dell Technologies (Dell) PowerFlex"""
+
+from __future__ import (absolute_import, division, print_function)
+
+__metaclass__ = type
+
+DOCUMENTATION = r'''
+module: snapshot_policy
+version_added: '1.7.0'
+short_description: Manage snapshot policies on Dell PowerFlex
+description:
+- Managing snapshot policies on PowerFlex storage system includes
+ creating, getting details, modifying attributes, adding a source volume,
+ removing a source volume and deleting a snapshot policy.
+author:
+- Trisha Datta (@trisha-dell) <ansible.team@dell.com>
+extends_documentation_fragment:
+ - dellemc.powerflex.powerflex
+options:
+ snapshot_policy_name:
+ description:
+ - The name of the snapshot policy.
+ - It is unique across the PowerFlex array.
+ - Mutually exclusive with I(snapshot_policy_id).
+ type: str
+ snapshot_policy_id:
+ description:
+ - The unique identifier of the snapshot policy.
+ - Except create operation, all other operations can be performed
+ using I(snapshot_policy_id).
+ - Mutually exclusive with I(snapshot_policy_name).
+ type: str
+ auto_snapshot_creation_cadence:
+ description:
+ - The auto snapshot creation cadence of the snapshot policy.
+ type: dict
+ suboptions:
+ time:
+ description:
+ - The time between creation of two snapshots.
+ type: int
+ unit:
+ description:
+ - The unit of the auto snapshot creation cadence.
+ type: str
+ choices: ["Minute", "Hour", "Day", "Week"]
+ default: "Minute"
+ num_of_retained_snapshots_per_level:
+ description:
+ - Number of retained snapshots per level.
+ type: list
+ elements: int
+ new_name:
+ description:
+ - New name of the snapshot policy.
+ type: str
+ access_mode:
+ description:
+ - Access mode of the snapshot policy.
+ choices: ['READ_WRITE', 'READ_ONLY']
+ type: str
+ secure_snapshots:
+ description:
+ - Whether to secure snapshots or not.
+ - Used only in the create operation.
+ type: bool
+ source_volume:
+ description:
+ - The source volume details to be added or removed.
+ type: list
+ elements: dict
+ suboptions:
+ id:
+ description:
+ - The unique identifier of the source volume
+ to be added or removed.
+ - Mutually exclusive with I(name).
+ type: str
+ name:
+ description:
+ - The name of the source volume to be added or removed.
+ - Mutually exclusive with I(id).
+ type: str
+ auto_snap_removal_action:
+ description:
+ - Ways to handle the snapshots created by the policy (auto snapshots).
+ - Must be provided when I(state) is set to C('absent').
+ choices: ['Remove', 'Detach']
+ type: str
+ detach_locked_auto_snapshots:
+ description:
+ - Whether to detach the locked auto snapshots during removal of source volume.
+ type: bool
+ state:
+ description:
+ - The state of the source volume.
+ - When C(present), source volume will be added to the snapshot policy.
+ - When C(absent), source volume will be removed from the snapshot policy.
+ type: str
+ choices: ['present', 'absent']
+ default: 'present'
+ pause:
+ description:
+ - Whether to pause or resume the snapshot policy.
+ type: bool
+ state:
+ description:
+ - State of the snapshot policy.
+ choices: ['present', 'absent']
+ default: 'present'
+ type: str
+notes:
+ - The I(check_mode) is supported.
+'''
+
+EXAMPLES = r'''
+- name: Create a snapshot policy
+ dellemc.powerflex.snapshot_policy:
+ hostname: "{{hostname}}"
+ username: "{{username}}"
+ password: "{{password}}"
+ validate_certs: "{{validate_certs}}"
+ snapshot_policy_name: "snapshot_policy_name_1"
+ access_mode: "READ_WRITE"
+ secure_snapshots: false
+ auto_snapshot_creation_cadence:
+ time: 1
+ unit: "Hour"
+ num_of_retained_snapshots_per_level:
+ - 20
+ state: "present"
+
+- name: Get snapshot policy details using name
+ dellemc.powerflex.snapshot_policy:
+ hostname: "{{hostname}}"
+ username: "{{username}}"
+ password: "{{password}}"
+ validate_certs: "{{validate_certs}}"
+ snapshot_policy_name: "snapshot_policy_name_1"
+
+- name: Get snapshot policy details using id
+ dellemc.powerflex.snapshot_policy:
+ hostname: "{{hostname}}"
+ username: "{{username}}"
+ password: "{{password}}"
+ validate_certs: "{{validate_certs}}"
+ snapshot_policy_id: "snapshot_policy_id_1"
+
+- name: Modify a snapshot policy
+ dellemc.powerflex.snapshot_policy:
+ hostname: "{{hostname}}"
+ username: "{{username}}"
+ password: "{{password}}"
+ validate_certs: "{{validate_certs}}"
+ snapshot_policy_name: "snapshot_policy_name_1"
+ auto_snapshot_creation_cadence:
+ time: 2
+ unit: "Hour"
+ num_of_retained_snapshots_per_level:
+ - 40
+
+- name: Rename a snapshot policy
+ dellemc.powerflex.snapshot_policy:
+ hostname: "{{hostname}}"
+ username: "{{username}}"
+ password: "{{password}}"
+ validate_certs: "{{validate_certs}}"
+ snapshot_policy_name: "snapshot_policy_name_1"
+ new_name: "snapshot_policy_name_1_new"
+
+- name: Add source volume
+ dellemc.powerflex.snapshot_policy:
+ hostname: "{{hostname}}"
+ username: "{{username}}"
+ password: "{{password}}"
+ validate_certs: "{{validate_certs}}"
+ snapshot_policy_name: "snapshot_policy_name_1"
+ source_volume:
+ - name: "source_volume_name_1"
+ - id: "source_volume_id_2"
+ state: "present"
+
+- name: Remove source volume
+ dellemc.powerflex.snapshot_policy:
+ hostname: "{{hostname}}"
+ username: "{{username}}"
+ password: "{{password}}"
+ validate_certs: "{{validate_certs}}"
+ snapshot_policy_name: "{{snapshot_policy_name}}"
+ source_volume:
+ - name: "source_volume_name_1"
+ auto_snap_removal_action: 'Remove'
+ state: "absent"
+ - id: "source_volume_id_2"
+ auto_snap_removal_action: 'Remove'
+ detach_locked_auto_snapshots: true
+ state: "absent"
+
+- name: Pause a snapshot policy
+ dellemc.powerflex.snapshot_policy:
+ hostname: "{{hostname}}"
+ username: "{{username}}"
+ password: "{{password}}"
+ validate_certs: "{{validate_certs}}"
+ snapshot_policy_name: "{{snapshot_policy_name}}"
+ pause: true
+
+- name: Resume a snapshot policy
+ dellemc.powerflex.snapshot_policy:
+ hostname: "{{hostname}}"
+ username: "{{username}}"
+ password: "{{password}}"
+ validate_certs: "{{validate_certs}}"
+ snapshot_policy_name: "{{snapshot_policy_name}}"
+ pause: false
+
+- name: Delete a snapshot policy
+ dellemc.powerflex.snapshot_policy:
+ hostname: "{{hostname}}"
+ username: "{{username}}"
+ password: "{{password}}"
+ validate_certs: "{{validate_certs}}"
+ snapshot_policy_name: "snapshot_policy_name"
+ state: "absent"
+'''
+
+RETURN = r'''
+changed:
+ description: Whether or not the resource has changed.
+ returned: always
+ type: bool
+ sample: 'false'
+snapshot_policy_details:
+ description: Details of the snapshot policy.
+ returned: When snapshot policy exists
+ type: dict
+ contains:
+ autoSnapshotCreationCadenceInMin:
+ description: The snapshot rule of the snapshot policy.
+ type: int
+ id:
+ description: The ID of the snapshot policy.
+ type: str
+ lastAutoSnapshotCreationFailureReason:
+ description: The reason for the failure of last auto snapshot creation .
+ type: str
+ name:
+ description: Name of the snapshot policy.
+ type: str
+ lastAutoSnapshotFailureInFirstLevel:
+ description: Whether the last auto snapshot in first level failed.
+ type: bool
+ maxVTreeAutoSnapshots:
+ description: Maximum number of VTree auto snapshots.
+ type: int
+ nextAutoSnapshotCreationTime:
+ description: The time of creation of the next auto snapshot.
+ type: int
+ numOfAutoSnapshots:
+ description: Number of auto snapshots.
+ type: int
+ numOfCreationFailures:
+ description: Number of creation failures.
+ type: int
+ numOfExpiredButLockedSnapshots:
+ description: Number of expired but locked snapshots.
+ type: int
+ numOfLockedSnapshots:
+ description: Number of locked snapshots.
+ type: int
+ numOfRetainedSnapshotsPerLevel:
+ description: Number of snapshots retained per level
+ type: list
+ numOfSourceVolumes:
+ description: Number of source volumes.
+ type: int
+ secureSnapshots:
+ description: Whether the snapshots are secured.
+ type: bool
+ snapshotAccessMode:
+ description: Access mode of the snapshots.
+ type: str
+ snapshotPolicyState:
+ description: State of the snapshot policy.
+ type: str
+ systemId:
+ description: Unique identifier of the PowerFlex system.
+ type: str
+ timeOfLastAutoSnapshot:
+ description: Time of the last auto snapshot creation.
+ type: str
+ timeOfLastAutoSnapshotCreationFailure:
+ description: Time of the failure of the last auto snapshot creation.
+ type: str
+ statistics:
+ description: Statistics details of the snapshot policy.
+ type: dict
+ contains:
+ autoSnapshotVolIds:
+ description: Volume Ids of all the auto snapshots.
+ type: list
+ expiredButLockedSnapshotsIds:
+ description: Ids of expired but locked snapshots.
+ type: list
+ numOfAutoSnapshots:
+ description: Number of auto snapshots.
+ type: int
+ numOfExpiredButLockedSnapshots:
+ description: Number of expired but locked snapshots.
+ type: int
+ numOfSrcVols:
+ description: Number of source volumes.
+ type: int
+ srcVolIds:
+ description: Ids of the source volumes.
+ type: list
+
+ sample: {
+ "autoSnapshotCreationCadenceInMin": 120,
+ "id": "15ae842800000004",
+ "lastAutoSnapshotCreationFailureReason": "NR",
+ "lastAutoSnapshotFailureInFirstLevel": false,
+ "links": [
+ {
+ "href": "/api/instances/SnapshotPolicy::15ae842800000004",
+ "rel": "self"
+ },
+ {
+ "href": "/api/instances/SnapshotPolicy::15ae842800000004/relationships/Statistics",
+ "rel": "/api/SnapshotPolicy/relationship/Statistics"
+ },
+ {
+ "href": "/api/instances/SnapshotPolicy::15ae842800000004/relationships/SourceVolume",
+ "rel": "/api/SnapshotPolicy/relationship/SourceVolume"
+ },
+ {
+ "href": "/api/instances/SnapshotPolicy::15ae842800000004/relationships/AutoSnapshotVolume",
+ "rel": "/api/SnapshotPolicy/relationship/AutoSnapshotVolume"
+ },
+ {
+ "href": "/api/instances/System::0e7a082862fedf0f",
+ "rel": "/api/parent/relationship/systemId"
+ }
+ ],
+ "maxVTreeAutoSnapshots": 40,
+ "name": "Sample_snapshot_policy_1",
+ "nextAutoSnapshotCreationTime": 1683709201,
+ "numOfAutoSnapshots": 0,
+ "numOfCreationFailures": 0,
+ "numOfExpiredButLockedSnapshots": 0,
+ "numOfLockedSnapshots": 0,
+ "numOfRetainedSnapshotsPerLevel": [
+ 40
+ ],
+ "numOfSourceVolumes": 0,
+ "secureSnapshots": false,
+ "snapshotAccessMode": "ReadWrite",
+ "snapshotPolicyState": "Active",
+ "statistics": {
+ "autoSnapshotVolIds": [],
+ "expiredButLockedSnapshotsIds": [],
+ "numOfAutoSnapshots": 0,
+ "numOfExpiredButLockedSnapshots": 0,
+ "numOfSrcVols": 0,
+ "srcVolIds": []
+ },
+ "systemId": "0e7a082862fedf0f",
+ "timeOfLastAutoSnapshot": 0,
+ "timeOfLastAutoSnapshotCreationFailure": 0
+ }
+'''
+
+from ansible.module_utils.basic import AnsibleModule
+from ansible_collections.dellemc.powerflex.plugins.module_utils.storage.dell \
+ import utils
+
+LOG = utils.get_logger('snapshot_policy')
+
+
+class PowerFlexSnapshotPolicy(object):
+ """Class with snapshot policies operations"""
+
+ def __init__(self):
+ """ Define all parameters required by this module"""
+ self.module_params = utils.get_powerflex_gateway_host_parameters()
+ self.module_params.update(get_powerflex_snapshot_policy_parameters())
+
+ mut_ex_args = [['snapshot_policy_name', 'snapshot_policy_id']]
+
+ # initialize the Ansible module
+ self.module = AnsibleModule(
+ argument_spec=self.module_params,
+ supports_check_mode=True,
+ mutually_exclusive=mut_ex_args)
+
+ utils.ensure_required_libs(self.module)
+
+ self.result = dict(
+ changed=False,
+ snapshot_policy_details={}
+ )
+
+ try:
+ self.powerflex_conn = utils.get_powerflex_gateway_host_connection(
+ self.module.params)
+ LOG.info("Got the PowerFlex system connection object instance")
+ except Exception as e:
+ LOG.error(str(e))
+ self.module.fail_json(msg=str(e))
+
+ def get_snapshot_policy(self, snap_pol_id=None, snap_pol_name=None):
+ """Get snapshot policy details
+ :param snap_pol_name: Name of the snapshot policy.
+ :param snap_pol_id: ID of the snapshot policy.
+ :return: snapshot policy details
+ """
+ try:
+ snap_pol_details = None
+ if snap_pol_id:
+ snap_pol_details = self.powerflex_conn.snapshot_policy.get(
+ filter_fields={'id': snap_pol_id})
+
+ if snap_pol_name:
+ snap_pol_details = self.powerflex_conn.snapshot_policy.get(
+ filter_fields={'name': snap_pol_name})
+
+ if not snap_pol_details:
+ msg = "Unable to find the snapshot policy."
+ LOG.info(msg)
+ return None
+
+ # Append statistics
+ statistics = self.powerflex_conn.snapshot_policy.get_statistics(snap_pol_details[0]['id'])
+ snap_pol_details[0]['statistics'] = statistics if statistics else {}
+ return snap_pol_details[0]
+
+ except Exception as e:
+ errormsg = f'Failed to get the snapshot policy with error {str(e)}'
+ LOG.error(errormsg)
+ self.module.fail_json(msg=errormsg)
+
+ def create_snapshot_policy(self, auto_snapshot_creation_cadence_in_min, num_of_retained_snapshots_per_level,
+ access_mode, secure_snapshots, snapshot_policy_name=None):
+ """Create snapshot_policy
+ :param auto_snapshot_creation_cadence_in_min: The auto snapshot creation cadence of the snapshot policy.
+ :param num_of_retained_snapshots_per_level: Number of retained snapshots per level.
+ :param access_mode: Access mode of the snapshot policy.
+ :param secure_snapshots: Whether to secure snapshots or not.
+ :param snapshot_policy_name: Name of the snapshot policy.
+ :return: Id of the snapshot policy, if created.
+ """
+ try:
+ if not self.module.check_mode:
+ policy_id = self.powerflex_conn.snapshot_policy.create(
+ auto_snap_creation_cadence_in_min=auto_snapshot_creation_cadence_in_min,
+ retained_snaps_per_level=num_of_retained_snapshots_per_level, name=snapshot_policy_name,
+ snapshotAccessMode=access_mode, secureSnapshots=secure_snapshots)
+ return policy_id
+
+ except Exception as e:
+ errormsg = f'Creation of snapshot policy failed with error {str(e)}'
+ LOG.error(errormsg)
+ self.module.fail_json(msg=errormsg)
+
+ def delete_snapshot_policy(self, snap_pol_id):
+ """Delete snapshot policy
+ :param snap_pol_id: The unique identifier of the snapshot policy.
+ :return: Details of the snapshot policy.
+ """
+
+ try:
+ if not self.module.check_mode:
+ self.powerflex_conn.snapshot_policy.delete(snap_pol_id)
+ return self.get_snapshot_policy(snap_pol_id=snap_pol_id)
+
+ except Exception as e:
+ errormsg = (f'Deletion of snapshot policy {snap_pol_id} '
+ f'failed with error {str(e)}')
+ LOG.error(errormsg)
+ self.module.fail_json(msg=errormsg)
+
+ def get_volume(self, vol_name=None, vol_id=None):
+ """Get volume details
+ :param vol_name: Name of the volume
+ :param vol_id: ID of the volume
+ :return: Details of volume if exist.
+ """
+
+ id_or_name = vol_id if vol_id else vol_name
+
+ try:
+ if vol_name:
+ volume_details = self.powerflex_conn.volume.get(
+ filter_fields={'name': vol_name})
+ else:
+ volume_details = self.powerflex_conn.volume.get(
+ filter_fields={'id': vol_id})
+
+ if len(volume_details) == 0:
+ error_msg = f"Volume with identifier {id_or_name} not found"
+ LOG.error(error_msg)
+ self.module.fail_json(msg=error_msg)
+
+ # Append snapshot policy name and id
+ if volume_details[0]['snplIdOfSourceVolume'] is not None:
+ snap_policy_id = volume_details[0]['snplIdOfSourceVolume']
+ volume_details[0]['snapshotPolicyId'] = snap_policy_id
+ volume_details[0]['snapshotPolicyName'] = \
+ self.get_snapshot_policy(snap_policy_id)['name']
+
+ return volume_details[0]
+
+ except Exception as e:
+ error_msg = (f"Failed to get the volume {id_or_name}"
+ f" with error {str(e)}")
+ LOG.error(error_msg)
+ self.module.fail_json(msg=error_msg)
+
+ def manage_source_volume(self, snap_pol_details, vol_details, source_volume_element):
+ """Adding or removing a source volume
+ :param snap_pol_details: Details of the snapshot policy details.
+ :param vol_details: Details of the volume.
+ :param source_volume_element: The index of the source volume in the
+ list of volumes to be added/removed.
+ :return: Boolean indicating whether volume is added/removed.
+ """
+ try:
+ if self.module.params['source_volume'][source_volume_element]['state'] == 'present' and \
+ vol_details['snplIdOfSourceVolume'] != snap_pol_details['id']:
+ if not self.module.check_mode:
+ snap_pol_details = self.powerflex_conn.snapshot_policy.add_source_volume(
+ snapshot_policy_id=snap_pol_details['id'],
+ volume_id=vol_details['id'])
+ LOG.info("Source volume successfully added")
+ return True
+
+ elif self.module.params['source_volume'][source_volume_element]['state'] == 'absent' and \
+ vol_details['snplIdOfSourceVolume'] == snap_pol_details['id']:
+ if not self.module.check_mode:
+ snap_pol_details = self.powerflex_conn.snapshot_policy.remove_source_volume(
+ snapshot_policy_id=snap_pol_details['id'],
+ volume_id=vol_details['id'],
+ auto_snap_removal_action=self.module.params['source_volume'][source_volume_element]['auto_snap_removal_action'],
+ detach_locked_auto_snaps=self.module.params['source_volume'][source_volume_element]['detach_locked_auto_snapshots'])
+ LOG.info("Source volume successfully removed")
+ return True
+
+ except Exception as e:
+ error_msg = f"Failed to manage the source volume {vol_details['id']} with error {str(e)}"
+ LOG.error(error_msg)
+ self.module.fail_json(msg=error_msg)
+
+ def pause_snapshot_policy(self, snap_pol_details):
+ """Pausing or resuming a snapshot policy.
+ :param snap_pol_details: Details of the snapshot policy details.
+ :return: Boolean indicating whether snapshot policy is paused/removed or not.
+ """
+ try:
+ if self.module.params['pause'] and \
+ snap_pol_details['snapshotPolicyState'] != "Paused":
+ if not self.module.check_mode:
+ self.powerflex_conn.snapshot_policy.pause(
+ snapshot_policy_id=snap_pol_details['id'])
+ LOG.info("Snapshot policy successfully paused.")
+ return True
+
+ elif not self.module.params['pause'] and \
+ snap_pol_details['snapshotPolicyState'] == "Paused":
+ if not self.module.check_mode:
+ self.powerflex_conn.snapshot_policy.resume(
+ snapshot_policy_id=snap_pol_details['id'])
+ LOG.info("Snapshot policy successfully resumed.")
+ return True
+
+ except Exception as e:
+ error_msg = f"Failed to pause/resume {snap_pol_details['id']} with error {str(e)}"
+ LOG.error(error_msg)
+ self.module.fail_json(msg=error_msg)
+
+ def to_modify(self, snap_pol_details, auto_snapshot_creation_cadence_in_min, num_of_retained_snapshots_per_level, new_name):
+ """Whether to modify the snapshot policy or not
+ :param snap_pol_details: Details of the snapshot policy.
+ :param auto_snapshot_creation_cadence_in_min: Snapshot rule of the policy.
+ :param num_of_retained_snapshots_per_level: Retention rule of the policy.
+ :param new_name: The new name of the snapshot policy.
+ :return: Dictionary containing the attributes of
+ snapshot policy which are to be updated
+ """
+ modify_dict = {}
+
+ if self.module_params['auto_snapshot_creation_cadence'] is not None and \
+ snap_pol_details['autoSnapshotCreationCadenceInMin'] != auto_snapshot_creation_cadence_in_min:
+ modify_dict['auto_snapshot_creation_cadence_in_min'] = auto_snapshot_creation_cadence_in_min
+
+ if num_of_retained_snapshots_per_level is not None and \
+ snap_pol_details['numOfRetainedSnapshotsPerLevel'] != num_of_retained_snapshots_per_level:
+ modify_dict['num_of_retained_snapshots_per_level'] = num_of_retained_snapshots_per_level
+
+ if new_name is not None:
+ if len(new_name.strip()) == 0:
+ self.module.fail_json(
+ msg="Provide valid volume name.")
+ if new_name != snap_pol_details['name']:
+ modify_dict['new_name'] = new_name
+
+ return modify_dict
+
+ def modify_snapshot_policy(self, snap_pol_details, modify_dict):
+ """
+ Modify the snapshot policy attributes
+ :param snap_pol_details: Details of the snapshot policy
+ :param modify_dict: Dictionary containing the attributes of
+ snapshot policy which are to be updated
+ :return: True, if the operation is successful
+ """
+ try:
+ msg = (f"Dictionary containing attributes which are to be"
+ f" updated is {str(modify_dict)}.")
+ LOG.info(msg)
+ if not self.module.check_mode:
+ if 'new_name' in modify_dict:
+ self.powerflex_conn.snapshot_policy.rename(snap_pol_details['id'],
+ modify_dict['new_name'])
+ msg = (f"The name of the volume is updated"
+ f" to {modify_dict['new_name']} sucessfully.")
+ LOG.info(msg)
+
+ if 'auto_snapshot_creation_cadence_in_min' in modify_dict and \
+ 'num_of_retained_snapshots_per_level' not in modify_dict:
+ self.powerflex_conn.snapshot_policy.modify(
+ snapshot_policy_id=snap_pol_details['id'],
+ auto_snap_creation_cadence_in_min=modify_dict['auto_snapshot_creation_cadence_in_min'],
+ retained_snaps_per_level=snap_pol_details['numOfRetainedSnapshotsPerLevel'])
+ msg = f"The snapshot rule is updated to {modify_dict['auto_snapshot_creation_cadence_in_min']}"
+ LOG.info(msg)
+
+ elif 'auto_snapshot_creation_cadence_in_min' not in modify_dict and 'num_of_retained_snapshots_per_level' in modify_dict:
+ self.powerflex_conn.snapshot_policy.modify(
+ snapshot_policy_id=snap_pol_details['id'],
+ auto_snap_creation_cadence_in_min=snap_pol_details['autoSnapshotCreationCadenceInMin'],
+ retained_snaps_per_level=modify_dict['num_of_retained_snapshots_per_level'])
+ msg = f"The retention rule is updated to {modify_dict['num_of_retained_snapshots_per_level']}"
+ LOG.info(msg)
+
+ elif 'auto_snapshot_creation_cadence_in_min' in modify_dict and 'num_of_retained_snapshots_per_level' in modify_dict:
+ self.powerflex_conn.snapshot_policy.modify(
+ snapshot_policy_id=snap_pol_details['id'],
+ auto_snap_creation_cadence_in_min=modify_dict['auto_snapshot_creation_cadence_in_min'],
+ retained_snaps_per_level=modify_dict['num_of_retained_snapshots_per_level'])
+ msg = (f"The snapshot rule is updated to {modify_dict['auto_snapshot_creation_cadence_in_min']}"
+ f" and the retention rule is updated to {modify_dict['num_of_retained_snapshots_per_level']}")
+ LOG.info(msg)
+
+ return True
+
+ except Exception as e:
+ err_msg = (f"Failed to update the snapshot policy {snap_pol_details['id']}"
+ f" with error {str(e)}")
+ LOG.error(err_msg)
+ self.module.fail_json(msg=err_msg)
+
+
+def get_access_mode(access_mode):
+ """
+ :param access_mode: Access mode of the snapshot policy
+ :return: The enum for the access mode
+ """
+
+ access_mode_dict = {
+ "READ_WRITE": "ReadWrite",
+ "READ_ONLY": "ReadOnly"
+ }
+ return access_mode_dict.get(access_mode)
+
+
+def get_powerflex_snapshot_policy_parameters():
+ """This method provide parameter required for the snapshot
+ policy module on PowerFlex"""
+ return dict(
+ snapshot_policy_name=dict(), snapshot_policy_id=dict(),
+ new_name=dict(),
+ access_mode=dict(choices=['READ_WRITE', 'READ_ONLY']),
+ secure_snapshots=dict(type='bool'),
+ auto_snapshot_creation_cadence=dict(type='dict', options=dict(
+ time=dict(type='int'),
+ unit=dict(choices=['Minute', 'Hour', 'Day', 'Week'],
+ default='Minute'))),
+ num_of_retained_snapshots_per_level=dict(type='list', elements='int'),
+ source_volume=dict(type='list', elements='dict', options=dict(
+ id=dict(), name=dict(),
+ auto_snap_removal_action=dict(choices=['Remove', 'Detach']),
+ detach_locked_auto_snapshots=dict(type='bool'),
+ state=dict(default='present', choices=['present', 'absent']))),
+ pause=dict(type='bool'),
+ state=dict(default='present', choices=['present', 'absent'])
+ )
+
+
+class SnapshotPolicyCreateHandler():
+ def handle(self, con_object, con_params, snapshot_policy_details, access_mode, auto_snapshot_creation_cadence_in_min):
+ if con_params['state'] == 'present' and not snapshot_policy_details:
+ if con_params['snapshot_policy_id']:
+ con_object.module.fail_json(msg="Creation of snapshot "
+ "policy is allowed "
+ "using snapshot_policy_name only, "
+ "snapshot_policy_id given.")
+
+ snap_pol_id = con_object.create_snapshot_policy(snapshot_policy_name=con_params['snapshot_policy_name'],
+ access_mode=access_mode,
+ secure_snapshots=con_params['secure_snapshots'],
+ auto_snapshot_creation_cadence_in_min=auto_snapshot_creation_cadence_in_min,
+ num_of_retained_snapshots_per_level=con_params['num_of_retained_snapshots_per_level'])
+ con_object.result['changed'] = True
+ if snap_pol_id:
+ snapshot_policy_details = con_object.get_snapshot_policy(snap_pol_name=con_params['snapshot_policy_name'],
+ snap_pol_id=con_params['snapshot_policy_id'])
+
+ msg = (f"snapshot policy created successfully, fetched "
+ f"snapshot_policy details {str(snapshot_policy_details)}")
+ LOG.info(msg)
+
+ SnapshotPolicyModifyHandler().handle(con_object, con_params, snapshot_policy_details,
+ auto_snapshot_creation_cadence_in_min)
+
+
+class SnapshotPolicyModifyHandler():
+ def handle(self, con_object, con_params, snapshot_policy_details, auto_snapshot_creation_cadence_in_min):
+ modify_dict = {}
+ if con_params['state'] == 'present' and snapshot_policy_details:
+ modify_dict = con_object.to_modify(
+ snap_pol_details=snapshot_policy_details, new_name=con_params['new_name'],
+ auto_snapshot_creation_cadence_in_min=auto_snapshot_creation_cadence_in_min,
+ num_of_retained_snapshots_per_level=con_params['num_of_retained_snapshots_per_level'])
+ msg = (f"Parameters to be modified are as"
+ f" follows: {str(modify_dict)}")
+ LOG.info(msg)
+ if modify_dict and con_params['state'] == 'present':
+ con_object.result['changed'] = con_object.modify_snapshot_policy(snap_pol_details=snapshot_policy_details,
+ modify_dict=modify_dict)
+ snapshot_policy_details = con_object.get_snapshot_policy(snap_pol_id=snapshot_policy_details.get("id"))
+ SnapshotPolicySourceVolumeHandler().handle(con_object, con_params, snapshot_policy_details)
+
+
+class SnapshotPolicySourceVolumeHandler():
+ def handle(self, con_object, con_params, snapshot_policy_details):
+ if snapshot_policy_details and con_params['state'] == 'present' and con_params['source_volume'] is not None:
+ for source_volume_element in range(len(con_params['source_volume'])):
+ if not (con_params['source_volume'][source_volume_element]['id'] or
+ con_params['source_volume'][source_volume_element]['name']):
+ con_object.module.fail_json(
+ msg="Either id or name of source volume needs to be "
+ "passed with state of source volume")
+
+ elif con_params['source_volume'][source_volume_element]['id'] and \
+ con_params['source_volume'][source_volume_element]['name']:
+ con_object.module.fail_json(
+ msg="id and name of source volume are mutually exclusive")
+
+ elif con_params['source_volume'][source_volume_element]['id'] or \
+ con_params['source_volume'][source_volume_element]['name']:
+ volume_details = con_object.get_volume(vol_id=con_params['source_volume'][source_volume_element]['id'],
+ vol_name=con_params['source_volume'][source_volume_element]['name'])
+ con_object.result['changed'] = con_object.manage_source_volume(snap_pol_details=snapshot_policy_details,
+ vol_details=volume_details,
+ source_volume_element=source_volume_element)
+ snapshot_policy_details = con_object.get_snapshot_policy(snap_pol_name=con_params['snapshot_policy_name'],
+ snap_pol_id=con_params['snapshot_policy_id'])
+
+ SnapshotPolicyPauseHandler().handle(con_object, con_params, snapshot_policy_details)
+
+
+class SnapshotPolicyPauseHandler():
+ def handle(self, con_object, con_params, snapshot_policy_details):
+ if con_params["state"] == "present" and con_params["pause"] is not None:
+ con_object.result['changed'] = \
+ con_object.pause_snapshot_policy(snap_pol_details=snapshot_policy_details)
+ snapshot_policy_details = \
+ con_object.get_snapshot_policy(snap_pol_name=con_params['snapshot_policy_name'],
+ snap_pol_id=con_params['snapshot_policy_id'])
+ SnapshotPolicyDeleteHandler().handle(con_object, con_params, snapshot_policy_details)
+
+
+class SnapshotPolicyDeleteHandler():
+ def handle(self, con_object, con_params, snapshot_policy_details):
+ if con_params['state'] == 'absent' and snapshot_policy_details:
+ snapshot_policy_details = con_object.delete_snapshot_policy(
+ snap_pol_id=snapshot_policy_details.get("id"))
+ con_object.result['changed'] = True
+ SnapshotPolicyExitHandler().handle(con_object, snapshot_policy_details)
+
+
+class SnapshotPolicyExitHandler():
+ def handle(self, con_object, snapshot_policy_details):
+ con_object.result['snapshot_policy_details'] = snapshot_policy_details
+ con_object.module.exit_json(**con_object.result)
+
+
+class SnapshotPolicyHandler():
+ def handle(self, con_object, con_params):
+ access_mode = get_access_mode(con_params['access_mode'])
+ snapshot_policy_details = con_object.get_snapshot_policy(snap_pol_name=con_params['snapshot_policy_name'],
+ snap_pol_id=con_params['snapshot_policy_id'])
+ auto_snapshot_creation_cadence_in_min = None
+ if snapshot_policy_details:
+ auto_snapshot_creation_cadence_in_min = snapshot_policy_details['autoSnapshotCreationCadenceInMin']
+ msg = f"Fetched the snapshot policy details {str(snapshot_policy_details)}"
+ LOG.info(msg)
+ if con_params['auto_snapshot_creation_cadence'] is not None:
+ auto_snapshot_creation_cadence_in_min = utils.get_time_minutes(time=con_params['auto_snapshot_creation_cadence']['time'],
+ time_unit=con_params['auto_snapshot_creation_cadence']['unit'])
+ SnapshotPolicyCreateHandler().handle(con_object, con_params, snapshot_policy_details,
+ access_mode, auto_snapshot_creation_cadence_in_min)
+
+
+def main():
+ """ Create PowerFlex snapshot policy object and perform action on it
+ based on user input from playbook"""
+ obj = PowerFlexSnapshotPolicy()
+ SnapshotPolicyHandler().handle(obj, obj.module.params)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/ansible_collections/dellemc/powerflex/plugins/modules/storagepool.py b/ansible_collections/dellemc/powerflex/plugins/modules/storagepool.py
index ca343212d..9c8bb1d4a 100644
--- a/ansible_collections/dellemc/powerflex/plugins/modules/storagepool.py
+++ b/ansible_collections/dellemc/powerflex/plugins/modules/storagepool.py
@@ -88,7 +88,6 @@ notes:
'''
EXAMPLES = r'''
-
- name: Get the details of storage pool by name
dellemc.powerflex.storagepool:
hostname: "{{hostname}}"
@@ -127,8 +126,8 @@ EXAMPLES = r'''
validate_certs: "{{validate_certs}}"
storage_pool_name: "ansible_test_pool"
protection_domain_id: "1c957da800000000"
- use_rmcache: True
- use_rfcache: True
+ use_rmcache: true
+ use_rfcache: true
state: "present"
- name: Rename storage pool by id
diff --git a/ansible_collections/dellemc/powerflex/plugins/modules/volume.py b/ansible_collections/dellemc/powerflex/plugins/modules/volume.py
index 9c1e1cd29..0fc301831 100644
--- a/ansible_collections/dellemc/powerflex/plugins/modules/volume.py
+++ b/ansible_collections/dellemc/powerflex/plugins/modules/volume.py
@@ -126,7 +126,7 @@ options:
description:
- Specifies whether to allow or not allow multiple mappings.
- If the volume is mapped to one SDC then for every new mapping
- I(allow_multiple_mappings) has to be passed as True.
+ I(allow_multiple_mappings) has to be passed as true.
type: bool
sdc:
description:
@@ -175,10 +175,10 @@ options:
type: str
delete_snapshots:
description:
- - If C(True), the volume and all its dependent snapshots will be deleted.
- - If C(False), only the volume will be deleted.
+ - If C(true), the volume and all its dependent snapshots will be deleted.
+ - If C(false), only the volume will be deleted.
- It can be specified only when the I(state) is C(absent).
- - It defaults to C(False), if not specified.
+ - It defaults to C(false), if not specified.
type: bool
state:
description:
@@ -203,7 +203,7 @@ EXAMPLES = r'''
protection_domain_name: "pd_1"
vol_type: "THICK_PROVISIONED"
compression_type: "NORMAL"
- use_rmcache: True
+ use_rmcache: true
size: 16
state: "present"
@@ -215,7 +215,7 @@ EXAMPLES = r'''
validate_certs: "{{validate_certs}}"
port: "{{port}}"
vol_name: "sample_volume"
- allow_multiple_mappings: True
+ allow_multiple_mappings: true
sdc:
- sdc_id: "92A304DB-EFD7-44DF-A07E-D78134CC9764"
access_mode: "READ_WRITE"
@@ -251,7 +251,7 @@ EXAMPLES = r'''
iops_limit: 20
- sdc_ip: "198.10.xxx.xxx"
access_mode: "READ_ONLY"
- allow_multiple_mappings: True
+ allow_multiple_mappings: true
sdc_state: "mapped"
state: "present"
@@ -286,7 +286,7 @@ EXAMPLES = r'''
validate_certs: "{{validate_certs}}"
port: "{{port}}"
vol_name: "sample_volume"
- delete_snapshots: False
+ delete_snapshots: false
state: "absent"
- name: Delete the Volume and all its dependent snapshots
@@ -297,7 +297,7 @@ EXAMPLES = r'''
validate_certs: "{{validate_certs}}"
port: "{{port}}"
vol_name: "sample_volume"
- delete_snapshots: True
+ delete_snapshots: true
state: "absent"
'''
diff --git a/ansible_collections/dellemc/powerflex/requirements.txt b/ansible_collections/dellemc/powerflex/requirements.txt
index d0fb0f636..863b61efb 100644
--- a/ansible_collections/dellemc/powerflex/requirements.txt
+++ b/ansible_collections/dellemc/powerflex/requirements.txt
@@ -1,4 +1,2 @@
PyPowerFlex
-requests>=2.23.0
-python-dateutil>=2.8.0
-setuptools
+python-dateutil>=2.8.2
diff --git a/ansible_collections/dellemc/powerflex/roles/README.md b/ansible_collections/dellemc/powerflex/roles/README.md
new file mode 100644
index 000000000..e6f8c426a
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/roles/README.md
@@ -0,0 +1,117 @@
+## Supported Roles
+
+#dellemc.powerflex roles directory
+
+Here is the list of supported roles.
+
+```
+.
+├── powerflex_common
+├── powerflex_mdm
+├── powerflex_gateway
+├── powerflex_sdc
+├── powerflex_sdr
+├── powerflex_lia
+├── powerflex_tb
+├── powerflex_sds
+├── powerflex_config
+├── powerflex_activeMQ
+
+```
+
+## Role Descriptions
+
+Below is the brief description of each role
+
+<table>
+<thead>
+ <tr>
+ <th>Role Name</th>
+ <th>Description</th>
+ </tr>
+</thead>
+<tbody>
+ <tr>
+ <td>powerflex_common</td>
+ <td>Role to manage the common operations of Powerflex.</td>
+ </tr>
+ <tr>
+ <td>powerflex_gateway</td>
+ <td>Role to manage the installation and uninstallation of Powerflex Gateway.</td>
+ </tr>
+ <tr>
+ <td>powerflex_lia</td>
+ <td>Role to manage the installation and uninstallation of Powerflex LIA.</td>
+ </tr>
+ <tr>
+ <td>powerflex_mdm</td>
+ <td>Role to manage the installation and uninstallation of Powerflex MDM.</td>
+ </tr>
+ <tr>
+ <td>powerflex_sdc</td>
+ <td>Role to manage the installation and uninstallation of Powerflex SDC.</td>
+ </tr>
+ <tr>
+ <td>powerflex_sdr</td>
+ <td>Role to manage installation and uninstallation PowerFlex SDR.</td>
+ </tr>
+ <tr>
+ <td>powerflex_sds</td>
+ <td>Role to manage the installation and uninstallation of Powerflex SDS.</td>
+ </tr>
+ <tr>
+ <td>powerflex_tb</td>
+ <td>Role to manage the installation and uninstallation of Powerflex TB.</td>
+ </tr>
+ <tr>
+ <td>powerflex_webui</td>
+ <td>Role to manage the installation and uninstallation of Powerflex Web UI.</td>
+ </tr>
+ <tr>
+ <td>powerflex_config</td>
+ <td>Role to configure the protection domain, fault set and storage pool.</td>
+ </tr>
+ <tr>
+ <td>powerflex_activeMQ</td>
+ <td>Role to manage the installation and uninstallation of Powerflex ActiveMQ.</td>
+ </tr>
+</tbody>
+</table>
+
+## Acronyms and their full forms
+Below is the list of full form of the acronyms which are refered throughout the documentation
+
+<table>
+<thead>
+ <tr>
+ <th>Acronym</th>
+ <th>Full Form</th>
+ </tr>
+</thead>
+<tbody>
+ <tr>
+ <td>LIA</td>
+ <td>Lightweight Installation Agent.</td>
+ </tr>
+ <tr>
+ <td>MDM</td>
+ <td>Metadata Manager.</td>
+ </tr>
+ <tr>
+ <td>SDC</td>
+ <td>Storage Data Client.</td>
+ </tr>
+ <tr>
+ <td>SDR</td>
+ <td>Storage Data Replicator.</td>
+ </tr>
+ <tr>
+ <td>SDS</td>
+ <td>Storage Data Server.</td>
+ </tr>
+ <tr>
+ <td>TB</td>
+ <td>Tie Breaker.</td>
+ </tr>
+</tbody>
+</table>
diff --git a/ansible_collections/dellemc/powerflex/roles/molecule.yml b/ansible_collections/dellemc/powerflex/roles/molecule.yml
new file mode 100644
index 000000000..425cea9d1
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/roles/molecule.yml
@@ -0,0 +1,25 @@
+---
+dependency:
+ name: galaxy
+driver:
+ name: docker
+platforms:
+ - name: centos
+ image: quay.io/centos/centos:stream8
+ pre_build_image: true
+ volumes:
+ - /tmp:/tmp
+provisioner:
+ name: ansible
+ inventory:
+ links:
+ hosts: ../../../../playbooks/roles/inventory
+ group_vars: ../../../../playbooks/roles/group_vars/
+ host_vars: ../../../../playbooks/roles/host_vars/
+verifier:
+ name: ansible
+scenario:
+ test_sequence:
+ - check
+ - converge
+ - idempotence
diff --git a/ansible_collections/dellemc/powerflex/roles/powerflex_activemq/README.md b/ansible_collections/dellemc/powerflex/roles/powerflex_activemq/README.md
new file mode 100644
index 000000000..bdaf5efac
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/roles/powerflex_activemq/README.md
@@ -0,0 +1,155 @@
+# powerflex_activemq
+
+Role to manage the installation and uninstallation of Powerflex ActiveMQ.
+
+## Table of contents
+
+* [Requirements](#requirements)
+* [Ansible collections](#ansible-collections)
+* [Role Variables](#role-variables)
+* [Examples](#examples)
+* [Notes](#notes)
+* [Usage instructions](#usage-instructions)
+* [Author Information](#author-information)
+
+## Requirements
+
+```
+ansible
+python
+```
+
+## Ansible collections
+
+Collections required to use the role.
+
+```
+dellemc.powerflex
+```
+
+## Role Variables
+
+<table>
+<thead>
+ <tr>
+ <th>Name</th>
+ <th>Required</th>
+ <th>Description</th>
+ <th>Choices</th>
+ <th>Type</th>
+ <th>Default Value</th>
+ </tr>
+</thead>
+<tbody>
+ <tr>
+ <td>hostname</td>
+ <td>true</td>
+ <td>IP or FQDN of the PowerFlex host.</td>
+ <td></td>
+ <td>str</td>
+ <td></td>
+ </tr>
+ <tr>
+ <td>username</td>
+ <td>true</td>
+ <td>The username of the PowerFlex host.</td>
+ <td></td>
+ <td>str</td>
+ <td></td>
+ </tr>
+ <tr>
+ <td>password</td>
+ <td>true</td>
+ <td>The password of the PowerFlex host.</td>
+ <td></td>
+ <td>str</td>
+ <td></td>
+ </tr>
+ <tr>
+ <td>port</td>
+ <td>false</td>
+ <td>The port of the PowerFlex host.</td>
+ <td></td>
+ <td>int</td>
+ <td>443</td>
+ </tr>
+ <tr>
+ <td>validate_certs</td>
+ <td>false</td>
+ <td>If C(false), the SSL certificates will not be validated.<br>Configure C(false) only on personally controlled sites where self-signed certificates are used.</td>
+ <td></td>
+ <td>bool</td>
+ <td>false</td>
+ </tr>
+ <tr>
+ <td>timeout</td>
+ <td>false</td>
+ <td>Time after which connection will get terminated.</td>
+ <td></td>
+ <td>int</td>
+ <td>120</td>
+ </tr>
+ <tr>
+ <td>powerflex_common_file_install_location</td>
+ <td>true</td>
+ <td>Location of installation and rpm gpg files to be installed.
+ <br>The required, compatible installation software package based on the operating system of the node. The files can be downloaded from the Dell Product support page for PowerFlex software.</td>
+ <td></td>
+ <td>path</td>
+ <td>/var/tmp</td>
+ </tr>
+ <tr>
+ <td>powerflex_activemq_state</td>
+ <td>false</td>
+ <td>Specify state of ActiveMQ.
+ <br>present will install the ActiveMQ and absent will uninstall the ActiveMQ.</td>
+ <td>absent, present</td>
+ <td>str</td>
+ <td>present</td>
+ </tr>
+</tbody>
+</table>
+
+## Examples
+----
+```
+ - name: Install and configure PowerFlex ActiveMQ
+ ansible.builtin.import_role:
+ name: powerflex_activemq
+ vars:
+ hostname: "{{ hostname }}"
+ username: "{{ username }}"
+ password: "{{ password }}"
+ validate_certs: "{{ validate_certs }}"
+ port: "{{ port }}"
+ powerflex_activemq_state: present
+
+ - name: Uninstall powerflex ActiveMQ
+ ansible.builtin.import_role:
+ name: powerflex_activemq
+ vars:
+ powerflex_activemq_state: absent
+
+```
+## Notes
+- Supported in PowerFlex version 4.x and above
+
+## Usage instructions
+----
+### To install all dependency packages, including ActiveMQ, on node:
+ ```
+ ansible-playbook -i inventory site_powerflex45.yml
+ ```
+
+### To uninstall ActiveMQ:
+ ```
+ ansible-playbook -i inventory uninstall_powerflex45.yml
+ ```
+
+Sample playbooks and inventory can be found in the playbooks directory.
+
+## Author Information
+------------------
+
+Dell Technologies<br>
+Pavan Mudunuri (ansible.team@Dell.com) 2023
diff --git a/ansible_collections/dellemc/powerflex/roles/powerflex_activemq/defaults/main.yml b/ansible_collections/dellemc/powerflex/roles/powerflex_activemq/defaults/main.yml
new file mode 100644
index 000000000..646a6bf28
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/roles/powerflex_activemq/defaults/main.yml
@@ -0,0 +1,6 @@
+---
+file_glob_name: activemq
+file_gpg_name: RPM-GPG-KEY-ScaleIO
+powerflex_role_environment:
+ MDM_IP: "{{ powerflex_activemq_mdm_ips }}"
+powerflex_activemq_state: 'present'
diff --git a/ansible_collections/dellemc/powerflex/roles/powerflex_activemq/meta/argument_specs.yml b/ansible_collections/dellemc/powerflex/roles/powerflex_activemq/meta/argument_specs.yml
new file mode 100644
index 000000000..4a793e5e9
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/roles/powerflex_activemq/meta/argument_specs.yml
@@ -0,0 +1,48 @@
+---
+argument_specs:
+ main:
+ short_description: Role to manage the installation and uninstallation of Powerflex ActiveMQ.
+ description:
+ - Role to manage the installation and uninstallation of Powerflex ActiveMQ.
+ options:
+ hostname:
+ required: true
+ type: str
+ description: IP or FQDN of the PowerFlex host.
+ username:
+ required: true
+ type: str
+ description: The username of the PowerFlex host.
+ password:
+ required: true
+ type: str
+ description: The password of the PowerFlex host.
+ port:
+ type: int
+ description: Port of the PowerFlex host.
+ default: 443
+ validate_certs:
+ description:
+ - If C(false), the SSL certificates will not be validated.
+ - Configure C(false) only on personally controlled sites where self-signed certificates are used.
+ type: bool
+ default: false
+ timeout:
+ description: Time after which connection will get terminated.
+ type: int
+ default: 120
+ powerflex_common_file_install_location:
+ description:
+ - Location of installation, compatible installation software package
+ based on the operating system of the node.
+ - The files can be downloaded from the Dell Product support page for PowerFlex software.
+ type: path
+ default: /var/tmp
+ powerflex_activemq_state:
+ description:
+ - Specifies the state of the ActiveMQ.
+ - present will install the ActiveMQ if not already installed.
+ - absent will uninstall the ActiveMQ if installed.
+ type: str
+ choices: ['absent', 'present']
+ default: present
diff --git a/ansible_collections/dellemc/powerflex/roles/powerflex_activemq/meta/main.yml b/ansible_collections/dellemc/powerflex/roles/powerflex_activemq/meta/main.yml
new file mode 100644
index 000000000..7d16f052e
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/roles/powerflex_activemq/meta/main.yml
@@ -0,0 +1,27 @@
+---
+galaxy_info:
+ author: Pavan Mudunuri
+ description: Role to manage the installation and uninstallation of Powerflex ActiveMQ.
+ company: Dell Technologies
+ role_name: powerflex_activemq
+ namespace: dellemc
+
+ license: GPL-3.0-only
+
+ min_ansible_version: "2.14.0"
+
+ platforms:
+ - name: EL
+ versions:
+ - "9"
+ - "8"
+ - name: Ubuntu
+ versions:
+ - jammy
+
+ - name: SLES
+ versions:
+ - "15SP3"
+ - "15SP4"
+
+ galaxy_tags: []
diff --git a/ansible_collections/dellemc/powerflex/roles/powerflex_activemq/molecule/activemq_install/converge.yml b/ansible_collections/dellemc/powerflex/roles/powerflex_activemq/molecule/activemq_install/converge.yml
new file mode 100644
index 000000000..be4d31ed7
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/roles/powerflex_activemq/molecule/activemq_install/converge.yml
@@ -0,0 +1,31 @@
+---
+- name: Molecule Test for installation of ActiveMQ
+ hosts: activemq
+ vars_files:
+ - ../../../../playbooks/roles/vars_files/connection.yml
+
+ tasks:
+ - name: Install and configure Powerflex ActiveMQ
+ ansible.builtin.import_role:
+ name: powerflex_activemq
+ vars:
+ powerflex_activemq_state: present
+
+ - name: Verifying install package in check mode
+ ansible.builtin.assert:
+ that:
+ - powerflex_common_install_package_output.msg == "Check mode: No changes made"
+ when: ansible_check_mode
+
+ - name: Verifying installation package in normal mode
+ ansible.builtin.assert:
+ that:
+ - " 'Installed' in powerflex_common_install_package_output.results[0]"
+ when: not ansible_check_mode and powerflex_common_install_package_output.changed
+
+ - name: Verifying installation package in Idempotency mode
+ ansible.builtin.assert:
+ that: >
+ "'Nothing to do' in powerflex_common_install_package_output.results[0]" or
+ "'is already installed' in powerflex_common_install_package_output.results[0]"
+ when: not ansible_check_mode and not powerflex_common_install_package_output.changed
diff --git a/ansible_collections/dellemc/powerflex/roles/powerflex_activemq/molecule/activemq_install/molecule.yml b/ansible_collections/dellemc/powerflex/roles/powerflex_activemq/molecule/activemq_install/molecule.yml
new file mode 100644
index 000000000..ed97d539c
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/roles/powerflex_activemq/molecule/activemq_install/molecule.yml
@@ -0,0 +1 @@
+---
diff --git a/ansible_collections/dellemc/powerflex/roles/powerflex_activemq/molecule/activemq_uninstallation/converge.yml b/ansible_collections/dellemc/powerflex/roles/powerflex_activemq/molecule/activemq_uninstallation/converge.yml
new file mode 100644
index 000000000..3507299d0
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/roles/powerflex_activemq/molecule/activemq_uninstallation/converge.yml
@@ -0,0 +1,67 @@
+---
+- name: Molecule Test for uninstallation of ActiveMQ
+ hosts: activemq
+ vars_files:
+ - ../../../../playbooks/roles/vars_files/connection.yml
+
+ tasks:
+ - name: Uninstall powerflex ActiveMQ
+ ansible.builtin.import_role:
+ name: powerflex_activemq
+ vars:
+ powerflex_activemq_state: absent
+
+ - name: Verifying uninstall package in normal mode
+ ansible.builtin.assert:
+ that:
+ - "'Removed:' in powerflex_activemq_uninstall_output.results[0].results[0]"
+ when:
+ - not ansible_check_mode
+ - powerflex_activemq_uninstall_output.changed
+ - ansible_distribution in ("RedHat", "CentOS", "SLES", "Rocky")
+
+ - name: Verifying uninstall package in check mode
+ ansible.builtin.assert:
+ that:
+ - powerflex_activemq_uninstall_output.msg == "Check mode: No changes made"
+ when:
+ - ansible_check_mode
+ - ansible_distribution in ("RedHat", "CentOS", "SLES", "Rocky")
+
+ - name: Verifying uninstall package in Idempotency
+ ansible.builtin.assert:
+ that: >
+ "'Nothing to do' in powerflex_activemq_uninstall_output.results[0].msg" or
+ "'EMC-ScaleIO-activemq is not installed' in powerflex_activemq_uninstall_output.results[0].results[0]"
+ when:
+ - not ansible_check_mode
+ - not powerflex_activemq_uninstall_output.changed
+ - ansible_distribution in ("RedHat", "CentOS", "SLES", "Rocky")
+
+ - name: Verifying uninstall package in check mode for ubuntu
+ ansible.builtin.assert:
+ that:
+ - powerflex_activemq_uninstall_deb_output.results[0].msg == "Check mode: No changes made"
+ - powerflex_activemq_uninstall_deb_output.changed
+ when:
+ - ansible_check_mode
+ - ansible_distribution == "Ubuntu"
+
+ - name: Verifying uninstall package in normal mode for ubuntu
+ ansible.builtin.assert:
+ that:
+ - "'Removed:' in powerflex_activemq_uninstall_deb_output.results[0].results[0]"
+ when:
+ - not ansible_check_mode
+ - powerflex_activemq_uninstall_deb_output.changed
+ - ansible_distribution == "Ubuntu"
+
+ - name: Verifying uninstall package in Idempotency for ubuntu
+ ansible.builtin.assert:
+ that: >
+ "'Nothing to do' in powerflex_activemq_uninstall_deb_output.results[0].msg" or
+ "'is not installed' in powerflex_activemq_uninstall_deb_output.results[0].results[0]"
+ when:
+ - not ansible_check_mode
+ - not powerflex_activemq_uninstall_deb_output.changed
+ - ansible_distribution == "Ubuntu"
diff --git a/ansible_collections/dellemc/powerflex/roles/powerflex_activemq/molecule/activemq_uninstallation/molecule.yml b/ansible_collections/dellemc/powerflex/roles/powerflex_activemq/molecule/activemq_uninstallation/molecule.yml
new file mode 100644
index 000000000..ed97d539c
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/roles/powerflex_activemq/molecule/activemq_uninstallation/molecule.yml
@@ -0,0 +1 @@
+---
diff --git a/ansible_collections/dellemc/powerflex/roles/powerflex_activemq/tasks/install_activemq.yml b/ansible_collections/dellemc/powerflex/roles/powerflex_activemq/tasks/install_activemq.yml
new file mode 100644
index 000000000..8b0dd44d7
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/roles/powerflex_activemq/tasks/install_activemq.yml
@@ -0,0 +1,30 @@
+---
+- name: Set facts for MDM counts
+ ansible.builtin.set_fact:
+ mdm_count: "{{ groups['mdm'] | length }}"
+
+- name: Set facts for powerflex_activemq_primary_ip
+ ansible.builtin.set_fact:
+ powerflex_activemq_primary_ip: "{{ hostvars[groups['mdm'][0]]['ansible_host'] }}"
+
+- name: Set facts for powerflex_activemq_secondary_ip
+ ansible.builtin.set_fact:
+ powerflex_activemq_secondary_ip: "{{ hostvars[groups['mdm'][1]]['ansible_host'] }}"
+
+- name: Set facts for powerflex_activemq_tertiary_ip
+ ansible.builtin.set_fact:
+ powerflex_activemq_tertiary_ip: "{{ hostvars[groups['mdm'][2]]['ansible_host'] }}"
+ when: mdm_count | int > 2
+
+- name: Set facts for powerflex_activemq_mdm_ips if mdm_count is 2
+ ansible.builtin.set_fact:
+ powerflex_activemq_mdm_ips: "{{ powerflex_activemq_primary_ip }},{{ powerflex_activemq_secondary_ip }}"
+ when: mdm_count | int == 2
+
+- name: Set facts for powerflex_activemq_mdm_ips if mdm_count is more than 2
+ ansible.builtin.set_fact:
+ powerflex_activemq_mdm_ips: "{{ powerflex_activemq_primary_ip }},{{ powerflex_activemq_secondary_ip }},{{ powerflex_activemq_tertiary_ip }}"
+ when: mdm_count | int > 2
+
+- name: Include install_powerflex.yml
+ ansible.builtin.include_tasks: ../../powerflex_common/tasks/install_powerflex.yml
diff --git a/ansible_collections/dellemc/powerflex/roles/powerflex_activemq/tasks/main.yml b/ansible_collections/dellemc/powerflex/roles/powerflex_activemq/tasks/main.yml
new file mode 100644
index 000000000..1f3a708b6
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/roles/powerflex_activemq/tasks/main.yml
@@ -0,0 +1,8 @@
+---
+- name: Install ActiveMQ
+ ansible.builtin.include_tasks: install_activemq.yml
+ when: powerflex_activemq_state == 'present'
+
+- name: Uninstall ActiveMQ
+ ansible.builtin.include_tasks: uninstall_activemq.yml
+ when: powerflex_activemq_state == 'absent'
diff --git a/ansible_collections/dellemc/powerflex/roles/powerflex_activemq/tasks/uninstall_activemq.yml b/ansible_collections/dellemc/powerflex/roles/powerflex_activemq/tasks/uninstall_activemq.yml
new file mode 100644
index 000000000..1f45e94c0
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/roles/powerflex_activemq/tasks/uninstall_activemq.yml
@@ -0,0 +1,20 @@
+---
+- name: Uninstall package
+ register: powerflex_activemq_uninstall_output
+ environment:
+ I_AM_SURE: "{{ i_am_sure | int }}"
+ ansible.builtin.package:
+ name: "{{ item }}"
+ state: "absent"
+ with_items:
+ - EMC-ScaleIO-activemq
+ when: ansible_distribution in ("RedHat", "CentOS", "SLES", "Rocky")
+
+- name: Uninstall deb package
+ register: powerflex_activemq_uninstall_deb_output
+ ansible.builtin.apt:
+ name: "{{ item }}"
+ state: absent
+ with_items:
+ - emc-scaleio-activemq
+ when: ansible_distribution == "Ubuntu"
diff --git a/ansible_collections/dellemc/powerflex/roles/powerflex_activemq/vars/main.yml b/ansible_collections/dellemc/powerflex/roles/powerflex_activemq/vars/main.yml
new file mode 100644
index 000000000..ed97d539c
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/roles/powerflex_activemq/vars/main.yml
@@ -0,0 +1 @@
+---
diff --git a/ansible_collections/dellemc/powerflex/roles/powerflex_common/README.md b/ansible_collections/dellemc/powerflex/roles/powerflex_common/README.md
new file mode 100644
index 000000000..f681f6ac4
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/roles/powerflex_common/README.md
@@ -0,0 +1,3 @@
+# powerflex_common
+
+Role to manage the common operations of Powerflex.
diff --git a/ansible_collections/dellemc/powerflex/roles/powerflex_common/defaults/main.yml b/ansible_collections/dellemc/powerflex/roles/powerflex_common/defaults/main.yml
new file mode 100644
index 000000000..b0ea37ec6
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/roles/powerflex_common/defaults/main.yml
@@ -0,0 +1,4 @@
+---
+powerflex_common_file_install_location: "/var/tmp"
+powerflex_common_esxi_files_location: "/tmp/"
+powerflex_common_win_package_location: "C:\\Windows\\Temp"
diff --git a/ansible_collections/dellemc/powerflex/roles/powerflex_common/meta/main.yml b/ansible_collections/dellemc/powerflex/roles/powerflex_common/meta/main.yml
new file mode 100644
index 000000000..bbe74f9e0
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/roles/powerflex_common/meta/main.yml
@@ -0,0 +1,41 @@
+---
+galaxy_info:
+ author: Jennifer John
+ description: The role helps to manage the common functions of PowerFlex.
+ company: Dell Technologies
+ role_name: powerflex_common
+ namespace: dellemc
+ # If the issue tracker for your role is not on github, uncomment the
+ # next line and provide a value
+ # issue_tracker_url: http://example.com/issue/tracker
+
+ # Choose a valid license ID from https://spdx.org - some suggested licenses:
+ # - BSD-3-Clause (default)
+ # - MIT
+ # - GPL-2.0-or-later
+ # - GPL-3.0-only
+ # - Apache-2.0
+ # - CC-BY-4.0
+ license: GPL-3.0-only
+
+ min_ansible_version: "2.14.0"
+ platforms:
+ - name: EL
+ versions:
+ - "9"
+ - "8"
+ - name: Ubuntu
+ versions:
+ - jammy
+ - name: SLES
+ versions:
+ - "15SP3"
+ - "15SP4"
+
+ galaxy_tags: []
+ # List tags for your role here, one per line. A tag is a keyword that describes
+ # and categorizes the role. Users find roles by searching for tags. Be sure to
+ # remove the '[]' above, if you add tags to this list.
+ #
+ # NOTE: A tag is limited to a single word comprised of alphanumeric characters.
+ # Maximum 20 tags per role.
diff --git a/ansible_collections/dellemc/powerflex/roles/powerflex_common/tasks/install_java_CentOS.yml b/ansible_collections/dellemc/powerflex/roles/powerflex_common/tasks/install_java_CentOS.yml
new file mode 100644
index 000000000..33922039b
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/roles/powerflex_common/tasks/install_java_CentOS.yml
@@ -0,0 +1,5 @@
+---
+- name: Install pre-requisite java
+ ansible.builtin.package:
+ name: "java-1.8.0-openjdk-devel"
+ state: "present"
diff --git a/ansible_collections/dellemc/powerflex/roles/powerflex_common/tasks/install_java_RedHat.yml b/ansible_collections/dellemc/powerflex/roles/powerflex_common/tasks/install_java_RedHat.yml
new file mode 100644
index 000000000..33922039b
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/roles/powerflex_common/tasks/install_java_RedHat.yml
@@ -0,0 +1,5 @@
+---
+- name: Install pre-requisite java
+ ansible.builtin.package:
+ name: "java-1.8.0-openjdk-devel"
+ state: "present"
diff --git a/ansible_collections/dellemc/powerflex/roles/powerflex_common/tasks/install_java_Rocky.yml b/ansible_collections/dellemc/powerflex/roles/powerflex_common/tasks/install_java_Rocky.yml
new file mode 100644
index 000000000..33922039b
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/roles/powerflex_common/tasks/install_java_Rocky.yml
@@ -0,0 +1,5 @@
+---
+- name: Install pre-requisite java
+ ansible.builtin.package:
+ name: "java-1.8.0-openjdk-devel"
+ state: "present"
diff --git a/ansible_collections/dellemc/powerflex/roles/powerflex_common/tasks/install_java_SLES.yml b/ansible_collections/dellemc/powerflex/roles/powerflex_common/tasks/install_java_SLES.yml
new file mode 100644
index 000000000..484286982
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/roles/powerflex_common/tasks/install_java_SLES.yml
@@ -0,0 +1,5 @@
+---
+- name: Install pre-requisite java
+ ansible.builtin.package:
+ name: "java-11-openjdk-headless"
+ state: "present"
diff --git a/ansible_collections/dellemc/powerflex/roles/powerflex_common/tasks/install_java_Ubuntu.yml b/ansible_collections/dellemc/powerflex/roles/powerflex_common/tasks/install_java_Ubuntu.yml
new file mode 100644
index 000000000..97de45222
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/roles/powerflex_common/tasks/install_java_Ubuntu.yml
@@ -0,0 +1,10 @@
+---
+- name: Install pre-requisite binutils
+ ansible.builtin.package:
+ name: "binutils"
+ state: "present"
+
+- name: Install pre-requisite java
+ ansible.builtin.package:
+ name: "openjdk-8-jdk"
+ state: "present"
diff --git a/ansible_collections/dellemc/powerflex/roles/powerflex_common/tasks/install_packages_CentOS.yml b/ansible_collections/dellemc/powerflex/roles/powerflex_common/tasks/install_packages_CentOS.yml
new file mode 100644
index 000000000..35d08f875
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/roles/powerflex_common/tasks/install_packages_CentOS.yml
@@ -0,0 +1,36 @@
+---
+- name: Copy files
+ ansible.builtin.copy:
+ src: "{{ item }}"
+ dest: "/var/tmp/"
+ mode: "0644"
+ register: powerflex_common_file
+ with_fileglob:
+ - "{{ powerflex_common_file_install_location }}/*{{ file_glob_name }}*"
+ - "{{ powerflex_common_file_install_location }}/{{ file_gpg_name }}*"
+
+- name: List the rpm file
+ register: powerflex_common_package_file
+ ansible.builtin.find:
+ paths: "/var/tmp/"
+ patterns: "*{{ file_glob_name }}*.rpm"
+
+- name: List the rpm gpg file
+ register: powerflex_common_package_gpg
+ ansible.builtin.find:
+ paths: "/var/tmp/"
+ patterns: "{{ file_gpg_name }}*"
+
+- name: Import gpg key
+ ansible.builtin.rpm_key:
+ state: present
+ key: "{{ powerflex_common_package_gpg.files[0].path }}"
+ when: powerflex_common_package_gpg.files | length > 0
+
+- name: Install package
+ register: powerflex_common_install_package_output
+ environment: "{{ powerflex_role_environment }}"
+ ansible.builtin.package:
+ name: "{{ powerflex_common_package_file.files[0].path }}"
+ state: "present"
+ disable_gpg_check: "{{ powerflex_gateway_disable_gpg_check | default(false) }}"
diff --git a/ansible_collections/dellemc/powerflex/roles/powerflex_common/tasks/install_packages_RedHat.yml b/ansible_collections/dellemc/powerflex/roles/powerflex_common/tasks/install_packages_RedHat.yml
new file mode 100644
index 000000000..380c9e81d
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/roles/powerflex_common/tasks/install_packages_RedHat.yml
@@ -0,0 +1,36 @@
+---
+- name: Copy files
+ ansible.builtin.copy:
+ src: "{{ item }}"
+ dest: "/var/tmp/"
+ mode: "0644"
+ register: powerflex_common_file
+ with_fileglob:
+ - "{{ powerflex_common_file_install_location }}/*{{ file_glob_name }}*.rpm"
+ - "{{ powerflex_common_file_install_location }}/{{ file_gpg_name }}*"
+
+- name: List the rpm file
+ register: powerflex_common_package_file
+ ansible.builtin.find:
+ paths: "/var/tmp/"
+ patterns: "*{{ file_glob_name }}*.rpm"
+
+- name: List the rpm gpg file
+ register: powerflex_common_package_gpg
+ ansible.builtin.find:
+ paths: "/var/tmp/"
+ patterns: "{{ file_gpg_name }}*"
+
+- name: Import gpg key
+ ansible.builtin.rpm_key:
+ state: present
+ key: "{{ powerflex_common_package_gpg.files[0].path }}"
+ when: powerflex_common_package_gpg.files | length > 0
+
+- name: Install package
+ register: powerflex_common_install_package_output
+ environment: "{{ powerflex_role_environment }}"
+ ansible.builtin.package:
+ name: "{{ powerflex_common_package_file.files[0].path }}"
+ state: "present"
+ disable_gpg_check: "{{ powerflex_gateway_disable_gpg_check | default(false) }}"
diff --git a/ansible_collections/dellemc/powerflex/roles/powerflex_common/tasks/install_packages_Rocky.yml b/ansible_collections/dellemc/powerflex/roles/powerflex_common/tasks/install_packages_Rocky.yml
new file mode 100644
index 000000000..35d08f875
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/roles/powerflex_common/tasks/install_packages_Rocky.yml
@@ -0,0 +1,36 @@
+---
+- name: Copy files
+ ansible.builtin.copy:
+ src: "{{ item }}"
+ dest: "/var/tmp/"
+ mode: "0644"
+ register: powerflex_common_file
+ with_fileglob:
+ - "{{ powerflex_common_file_install_location }}/*{{ file_glob_name }}*"
+ - "{{ powerflex_common_file_install_location }}/{{ file_gpg_name }}*"
+
+- name: List the rpm file
+ register: powerflex_common_package_file
+ ansible.builtin.find:
+ paths: "/var/tmp/"
+ patterns: "*{{ file_glob_name }}*.rpm"
+
+- name: List the rpm gpg file
+ register: powerflex_common_package_gpg
+ ansible.builtin.find:
+ paths: "/var/tmp/"
+ patterns: "{{ file_gpg_name }}*"
+
+- name: Import gpg key
+ ansible.builtin.rpm_key:
+ state: present
+ key: "{{ powerflex_common_package_gpg.files[0].path }}"
+ when: powerflex_common_package_gpg.files | length > 0
+
+- name: Install package
+ register: powerflex_common_install_package_output
+ environment: "{{ powerflex_role_environment }}"
+ ansible.builtin.package:
+ name: "{{ powerflex_common_package_file.files[0].path }}"
+ state: "present"
+ disable_gpg_check: "{{ powerflex_gateway_disable_gpg_check | default(false) }}"
diff --git a/ansible_collections/dellemc/powerflex/roles/powerflex_common/tasks/install_packages_SLES.yml b/ansible_collections/dellemc/powerflex/roles/powerflex_common/tasks/install_packages_SLES.yml
new file mode 100644
index 000000000..380c9e81d
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/roles/powerflex_common/tasks/install_packages_SLES.yml
@@ -0,0 +1,36 @@
+---
+- name: Copy files
+ ansible.builtin.copy:
+ src: "{{ item }}"
+ dest: "/var/tmp/"
+ mode: "0644"
+ register: powerflex_common_file
+ with_fileglob:
+ - "{{ powerflex_common_file_install_location }}/*{{ file_glob_name }}*.rpm"
+ - "{{ powerflex_common_file_install_location }}/{{ file_gpg_name }}*"
+
+- name: List the rpm file
+ register: powerflex_common_package_file
+ ansible.builtin.find:
+ paths: "/var/tmp/"
+ patterns: "*{{ file_glob_name }}*.rpm"
+
+- name: List the rpm gpg file
+ register: powerflex_common_package_gpg
+ ansible.builtin.find:
+ paths: "/var/tmp/"
+ patterns: "{{ file_gpg_name }}*"
+
+- name: Import gpg key
+ ansible.builtin.rpm_key:
+ state: present
+ key: "{{ powerflex_common_package_gpg.files[0].path }}"
+ when: powerflex_common_package_gpg.files | length > 0
+
+- name: Install package
+ register: powerflex_common_install_package_output
+ environment: "{{ powerflex_role_environment }}"
+ ansible.builtin.package:
+ name: "{{ powerflex_common_package_file.files[0].path }}"
+ state: "present"
+ disable_gpg_check: "{{ powerflex_gateway_disable_gpg_check | default(false) }}"
diff --git a/ansible_collections/dellemc/powerflex/roles/powerflex_common/tasks/install_packages_Ubuntu.yml b/ansible_collections/dellemc/powerflex/roles/powerflex_common/tasks/install_packages_Ubuntu.yml
new file mode 100644
index 000000000..77f794572
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/roles/powerflex_common/tasks/install_packages_Ubuntu.yml
@@ -0,0 +1,51 @@
+---
+- name: Copy files
+ ansible.builtin.unarchive:
+ src: "{{ item }}"
+ dest: "/var/tmp/"
+ mode: "0644"
+ register: powerflex_common_ubuntu_tar_file
+ with_fileglob:
+ - "{{ powerflex_common_file_install_location }}/*{{ file_glob_name }}*.tar"
+
+- name: Get powerflex_common_siob_file # noqa: no-handler
+ ansible.builtin.find:
+ paths: "/var/tmp/"
+ patterns: "*{{ file_glob_name }}*.siob"
+ register: powerflex_common_siob_file
+ when: powerflex_common_ubuntu_tar_file.changed
+
+- name: Execute chmod siob_extract # noqa: no-handler
+ ansible.builtin.file:
+ path: "/var/tmp/siob_extract"
+ mode: "0755"
+ when: powerflex_common_ubuntu_tar_file.changed
+
+- name: Execute the siob_extract # noqa: no-handler
+ ansible.builtin.command: /var/tmp/siob_extract "{{ powerflex_common_siob_file.files[0].path }}" chdir="/var/tmp"
+ when: powerflex_common_ubuntu_tar_file.changed
+ register: powerflex_common_siob_extract_output
+ changed_when: powerflex_common_siob_extract_output.rc == 0
+
+- name: Copy deb file
+ ansible.builtin.copy:
+ src: "{{ item }}"
+ dest: "/var/tmp"
+ mode: "0644"
+ with_fileglob:
+ - "{{ powerflex_common_file_install_location }}/*{{ file_glob_name }}*.deb"
+ when: powerflex_common_ubuntu_tar_file.skipped
+
+- name: List the deb file
+ register: powerflex_common_package_file
+ ansible.builtin.find:
+ paths: "/var/tmp/"
+ patterns: "*{{ file_glob_name }}*.deb"
+
+- name: Install package
+ register: powerflex_common_install_package_output
+ environment: "{{ powerflex_role_environment }}"
+ ansible.builtin.package:
+ deb: "{{ powerflex_common_package_file.files[0].path }}"
+ state: "present"
+ disable_gpg_check: "{{ powerflex_gateway_disable_gpg_check | default(false) }}"
diff --git a/ansible_collections/dellemc/powerflex/roles/powerflex_common/tasks/install_packages_VMkernel.yml b/ansible_collections/dellemc/powerflex/roles/powerflex_common/tasks/install_packages_VMkernel.yml
new file mode 100644
index 000000000..3291fe8f6
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/roles/powerflex_common/tasks/install_packages_VMkernel.yml
@@ -0,0 +1,50 @@
+---
+- name: Get the acceptance level of the ESXi node
+ register: powerflex_common_get_acceptance_output
+ ansible.builtin.shell: >
+ esxcli software acceptance get
+ changed_when: powerflex_common_get_acceptance_output.stdout != 'PartnerSupported'
+
+- name: Set the acceptance level to PartnerSupported
+ register: powerflex_common_set_acceptance_output
+ ansible.builtin.shell: >
+ esxcli software acceptance set --level=PartnerSupported
+ when: powerflex_common_get_acceptance_output.stdout != 'PartnerSupported'
+ changed_when: powerflex_common_get_acceptance_output.stdout != 'PartnerSupported'
+
+- name: Copy Esxi component and rpm files
+ ansible.builtin.copy:
+ src: "{{ item }}"
+ dest: "{{ powerflex_common_esxi_files_location }}"
+ mode: "0644"
+ register: powerflex_common_file
+ with_fileglob:
+ - "{{ powerflex_common_file_install_location }}/*{{ file_glob_name }}*"
+ - "{{ powerflex_common_file_install_location }}/{{ file_gpg_name }}*"
+
+- name: List the zip file
+ register: powerflex_common_package_file
+ ansible.builtin.find:
+ paths: "{{ powerflex_common_esxi_files_location }}"
+ patterns: "*{{ file_glob_name }}*.zip"
+
+- name: Install SDC package for ESXi
+ register: powerflex_common_install_package_output
+ ansible.builtin.shell: >
+ esxcli software vib install -d {{ powerflex_common_package_file.files[0].path }} --no-sig-check
+ ignore_errors: true
+ changed_when: "'Reboot Required: true' in powerflex_common_install_package_output.stdout"
+
+- name: Reboot ESXi host
+ register: powerflex_common_reboot_output
+ ansible.builtin.reboot:
+ reboot_timeout: 500
+ msg: "Rebooting the ESXi host."
+ when: "'Reboot Required: true' in powerflex_common_install_package_output.stdout"
+ changed_when: powerflex_common_reboot_output.rebooted
+
+- name: Ensure the driver is loaded for SDC after reboot
+ register: powerflex_common_loaded_driver_output
+ ansible.builtin.shell: >
+ set -o pipefail && esxcli software vib list | grep sdc
+ changed_when: powerflex_common_loaded_driver_output.stdout_lines | length == 0
diff --git a/ansible_collections/dellemc/powerflex/roles/powerflex_common/tasks/install_packages_WindowsOS.yml b/ansible_collections/dellemc/powerflex/roles/powerflex_common/tasks/install_packages_WindowsOS.yml
new file mode 100644
index 000000000..6b8e4b8e8
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/roles/powerflex_common/tasks/install_packages_WindowsOS.yml
@@ -0,0 +1,20 @@
+---
+- name: Copy files
+ ansible.windows.win_copy:
+ src: "{{ item }}"
+ dest: "{{ powerflex_common_win_package_location }}"
+ register: powerflex_common_file
+ with_fileglob:
+ - "{{ powerflex_common_file_install_location }}/*{{ file_glob_name }}*.msi"
+
+- name: List the msi file
+ ansible.windows.win_find:
+ paths: "{{ powerflex_common_win_package_location }}"
+ patterns: "*{{ file_glob_name }}*.msi"
+ register: powerflex_common_package_file
+
+- name: Install package
+ register: powerflex_common_install_package_output
+ ansible.windows.win_command: >
+ msiexec.exe /i "{{ powerflex_common_package_file.files[0].path }}" MDM_IP="{{ powerflex_role_environment.MDM_IP }}" /q
+ ignore_errors: true
diff --git a/ansible_collections/dellemc/powerflex/roles/powerflex_common/tasks/install_powerflex.yml b/ansible_collections/dellemc/powerflex/roles/powerflex_common/tasks/install_powerflex.yml
new file mode 100644
index 000000000..98ae4b0c9
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/roles/powerflex_common/tasks/install_powerflex.yml
@@ -0,0 +1,8 @@
+---
+- name: Set fact # noqa var-naming[no-role-prefix]
+ ansible.builtin.set_fact:
+ ansible_distribution: "WindowsOS"
+ when: " 'Windows' in ansible_distribution"
+
+- name: Include installation file based on distribution
+ ansible.builtin.include_tasks: "install_packages_{{ ansible_distribution }}.yml"
diff --git a/ansible_collections/dellemc/powerflex/roles/powerflex_common/tasks/main.yml b/ansible_collections/dellemc/powerflex/roles/powerflex_common/tasks/main.yml
new file mode 100644
index 000000000..b83b1c148
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/roles/powerflex_common/tasks/main.yml
@@ -0,0 +1,14 @@
+---
+- name: Set fact # noqa var-naming[no-role-prefix]
+ ansible.builtin.set_fact:
+ ansible_distribution: "WindowsOS"
+ when: " 'Windows' in ansible_distribution"
+
+- name: Include vars
+ ansible.builtin.include_vars: "../vars/{{ ansible_distribution }}.yml"
+
+- name: Install required packages # noqa package-latest
+ ansible.builtin.package:
+ name: "{{ item }}"
+ state: latest
+ with_items: "{{ powerflex_common_packages }}"
diff --git a/ansible_collections/dellemc/powerflex/roles/powerflex_common/vars/CentOS.yml b/ansible_collections/dellemc/powerflex/roles/powerflex_common/vars/CentOS.yml
new file mode 100644
index 000000000..cadab0571
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/roles/powerflex_common/vars/CentOS.yml
@@ -0,0 +1,4 @@
+---
+powerflex_common_packages:
+ - numactl
+ - libaio
diff --git a/ansible_collections/dellemc/powerflex/roles/powerflex_common/vars/RedHat.yml b/ansible_collections/dellemc/powerflex/roles/powerflex_common/vars/RedHat.yml
new file mode 100644
index 000000000..03cedf354
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/roles/powerflex_common/vars/RedHat.yml
@@ -0,0 +1,6 @@
+---
+powerflex_common_packages:
+ - numactl
+ - libaio
+ - python3
+ - binutils
diff --git a/ansible_collections/dellemc/powerflex/roles/powerflex_common/vars/Rocky.yml b/ansible_collections/dellemc/powerflex/roles/powerflex_common/vars/Rocky.yml
new file mode 100644
index 000000000..03cedf354
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/roles/powerflex_common/vars/Rocky.yml
@@ -0,0 +1,6 @@
+---
+powerflex_common_packages:
+ - numactl
+ - libaio
+ - python3
+ - binutils
diff --git a/ansible_collections/dellemc/powerflex/roles/powerflex_common/vars/SLES.yml b/ansible_collections/dellemc/powerflex/roles/powerflex_common/vars/SLES.yml
new file mode 100644
index 000000000..0f15a62a6
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/roles/powerflex_common/vars/SLES.yml
@@ -0,0 +1,4 @@
+---
+powerflex_common_packages:
+ - python3
+ - libapr1
diff --git a/ansible_collections/dellemc/powerflex/roles/powerflex_common/vars/Ubuntu.yml b/ansible_collections/dellemc/powerflex/roles/powerflex_common/vars/Ubuntu.yml
new file mode 100644
index 000000000..6c5f371ec
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/roles/powerflex_common/vars/Ubuntu.yml
@@ -0,0 +1,5 @@
+---
+powerflex_common_packages:
+ - numactl
+ - libaio1
+ - ldap-utils
diff --git a/ansible_collections/dellemc/powerflex/roles/powerflex_common/vars/VMkernel.yml b/ansible_collections/dellemc/powerflex/roles/powerflex_common/vars/VMkernel.yml
new file mode 100644
index 000000000..3cc003a2a
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/roles/powerflex_common/vars/VMkernel.yml
@@ -0,0 +1,2 @@
+---
+powerflex_common_packages: []
diff --git a/ansible_collections/dellemc/powerflex/roles/powerflex_common/vars/WindowsOS.yml b/ansible_collections/dellemc/powerflex/roles/powerflex_common/vars/WindowsOS.yml
new file mode 100644
index 000000000..3cc003a2a
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/roles/powerflex_common/vars/WindowsOS.yml
@@ -0,0 +1,2 @@
+---
+powerflex_common_packages: []
diff --git a/ansible_collections/dellemc/powerflex/roles/powerflex_config/README.md b/ansible_collections/dellemc/powerflex/roles/powerflex_config/README.md
new file mode 100644
index 000000000..987773d88
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/roles/powerflex_config/README.md
@@ -0,0 +1,165 @@
+# powerflex_config
+
+Role to configure the protection domain, fault set and storage pool.
+
+## Table of contents
+
+* [Requirements](#requirements)
+* [Ansible collections](#ansible-collections)
+* [Role Variables](#role-variables)
+* [Examples](#examples)
+* [Usage instructions](#usage-instructions)
+* [Notes](#notes)
+* [Author Information](#author-information)
+
+## Requirements
+
+```
+ansible
+python
+```
+
+## Ansible collections
+
+Collections required to use the role.
+
+```
+dellemc.powerflex
+```
+
+## Role Variables
+
+<table>
+<thead>
+ <tr>
+ <th>Name</th>
+ <th>Required</th>
+ <th>Description</th>
+ <th>Choices</th>
+ <th>Type</th>
+ <th>Default Value</th>
+ </tr>
+</thead>
+<tbody>
+ <tr>
+ <td>hostname</td>
+ <td>true</td>
+ <td>IP or FQDN of the PowerFlex host.</td>
+ <td></td>
+ <td>str</td>
+ <td></td>
+ </tr>
+ <tr>
+ <td>username</td>
+ <td>true</td>
+ <td>The username of the PowerFlex host.</td>
+ <td></td>
+ <td>str</td>
+ <td></td>
+ </tr>
+ <tr>
+ <td>password</td>
+ <td>true</td>
+ <td>The password of the PowerFlex host.</td>
+ <td></td>
+ <td>str</td>
+ <td></td>
+ </tr>
+ <tr>
+ <td>port</td>
+ <td>false</td>
+ <td>Port of the PowerFlex host.</td>
+ <td></td>
+ <td>int</td>
+ <td>443</td>
+ </tr>
+ <tr>
+ <td>validate_certs</td>
+ <td>false</td>
+ <td>If C(false), the SSL certificates will not be validated.<br>Configure C(false) only on personally controlled sites where self-signed certificates are used.</td>
+ <td></td>
+ <td>bool</td>
+ <td>false</td>
+ </tr>
+ <tr>
+ <td>powerflex_protection_domain_name</td>
+ <td>false</td>
+ <td>Name of the protection domain.<br></td>
+ <td></td>
+ <td>str</td>
+ <td>config_protection_domain</td>
+ </tr>
+ <tr>
+ <td>powerflex_fault_sets</td>
+ <td>false</td>
+ <td>List of fault sets.<br></td>
+ <td></td>
+ <td>list</td>
+ <td>['fs1','fs2','fs3']</td>
+ </tr>
+ <tr>
+ <td>powerflex_media_type</td>
+ <td>false</td>
+ <td>Media type of the storage pool.<br></td>
+ <td>'SSD', 'HDD', 'TRANSITIONAL'</td>
+ <td>str</td>
+ <td>SSD</td>>
+ </tr>
+ <tr>
+ <td>powerflex_storage_pool_name</td>
+ <td>false</td>
+ <td>Name of the storage pool.<br></td>
+ <td></td>
+ <td>str</td>
+ <td>config_storage_pool</td>
+ </tr>
+</tbody>
+</table>
+
+## Examples
+----
+```
+ - name: Configuration of protection domain, fault set and storage pool.
+ ansible.builtin.import_role:
+ name: "powerflex_config"
+ vars:
+ hostname: "{{ hostname }}"
+ username: "{{ username }}"
+ password: "{{ password }}"
+ validate_certs: "{{ validate_certs }}"
+ port: "{{ port }}"
+ powerflex_protection_domain_name: "protection_domain"
+ powerflex_fault_sets:
+ - 'fs1'
+ - 'fs2'
+ - 'fs3'
+ powerflex_media_type: 'SSD'
+ powerflex_storage_pool_name: "storage_pool"
+
+```
+
+## Usage instructions
+----
+### To configure the protection domain and storage pool:
+- PowerFlex 3.6:
+ ```
+ ansible-playbook -i inventory site.yml
+ ```
+- PowerFlex 4.5:
+ ```
+ ansible-playbook -i inventory site_powerflex45.yml
+ ```
+
+Sample playbooks and inventory can be found in the playbooks directory.
+
+## Notes
+----
+
+- As a pre-requisite, the Gateway must be installed.
+- TRANSITIONAL media type is supported only during modification.
+
+## Author Information
+------------------
+
+Dell Technologies </br>
+Felix Stephen A (ansible.team@Dell.com) 2023
diff --git a/ansible_collections/dellemc/powerflex/roles/powerflex_config/defaults/main.yml b/ansible_collections/dellemc/powerflex/roles/powerflex_config/defaults/main.yml
new file mode 100644
index 000000000..0cb687ecb
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/roles/powerflex_config/defaults/main.yml
@@ -0,0 +1,6 @@
+---
+# defaults file for powerflex_config
+powerflex_protection_domain_name: "domain1"
+powerflex_fault_sets: ['fs1', 'fs2', 'fs3']
+powerflex_media_type: 'SSD' # When version is R3
+powerflex_storage_pool_name: "pool1"
diff --git a/ansible_collections/dellemc/powerflex/roles/powerflex_config/meta/argument_specs.yml b/ansible_collections/dellemc/powerflex/roles/powerflex_config/meta/argument_specs.yml
new file mode 100644
index 000000000..bd94c306f
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/roles/powerflex_config/meta/argument_specs.yml
@@ -0,0 +1,50 @@
+---
+argument_specs:
+ main:
+ short_description: Role to configure the protection domain, fault set and storage pool.
+ description:
+ - Role to configure the protection domain, fault set and storage pool.
+ options:
+ hostname:
+ required: true
+ type: str
+ description: IP or FQDN of the PowerFlex gateway.
+ username:
+ required: true
+ type: str
+ description: The username of the PowerFlex gateway.
+ password:
+ required: true
+ type: str
+ description: The password of the PowerFlex gateway.
+ port:
+ type: int
+ description: Port of the PowerFlex gateway.
+ default: 443
+ validate_certs:
+ description:
+ - If C(false), the SSL certificates will not be validated.
+ - Configure C(false) only on personally controlled sites where self-signed certificates are used.
+ type: bool
+ default: false
+ timeout:
+ description: Timeout.
+ type: int
+ default: 120
+ powerflex_protection_domain_name:
+ type: str
+ description: Name of the protection domain.
+ default: 'config_protection_domain'
+ powerflex_fault_sets:
+ description: List of fault sets.
+ type: list
+ default: ['fs1', 'fs2', 'fs3']
+ powerflex_media_type:
+ description: Media type of the storage pool.
+ type: str
+ choices: ['SSD', 'HDD', 'TRANSITIONAL']
+ default: 'SSD'
+ powerflex_storage_pool_name:
+ description: Name of the storage pool.
+ type: str
+ default: 'config_storage_pool'
diff --git a/ansible_collections/dellemc/powerflex/roles/powerflex_config/meta/main.yml b/ansible_collections/dellemc/powerflex/roles/powerflex_config/meta/main.yml
new file mode 100644
index 000000000..9924409c8
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/roles/powerflex_config/meta/main.yml
@@ -0,0 +1,25 @@
+---
+galaxy_info:
+ author: Felix Stephen A
+ description: Role to configure the protection domain, fault set and storage pool.
+ company: Dell Technologies
+ license: GPL-3.0-only
+ role_name: powerflex_config
+ namespace: dellemc
+
+ min_ansible_version: "2.14.0"
+ platforms:
+ - name: EL
+ versions:
+ - "9"
+ - "8"
+ - name: Ubuntu
+ versions:
+ - jammy
+
+ - name: SLES
+ versions:
+ - "15SP3"
+ - "15SP4"
+
+ galaxy_tags: []
diff --git a/ansible_collections/dellemc/powerflex/roles/powerflex_config/molecule/configure_protection_domain/converge.yml b/ansible_collections/dellemc/powerflex/roles/powerflex_config/molecule/configure_protection_domain/converge.yml
new file mode 100644
index 000000000..1fb1c2425
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/roles/powerflex_config/molecule/configure_protection_domain/converge.yml
@@ -0,0 +1,47 @@
+---
+- name: Molecule Test for Configuring Protection Domain
+ hosts: mdm
+ vars_files:
+ - ../../../../playbooks/roles/vars_files/connection.yml
+
+ tasks:
+ - name: Run Config role
+ ansible.builtin.import_role:
+ name: "powerflex_config"
+ register: powerflex_config_result
+
+ - name: "Verifying protection domain creation"
+ ansible.builtin.assert:
+ that:
+ - powerflex_config_add_pd_output.protection_domain_details.name == powerflex_protection_domain_name
+ when: not ansible_check_mode and powerflex_config_add_pd_output.changed
+
+ - name: "Verifying storage pool R2 creation"
+ ansible.builtin.assert:
+ that:
+ - powerflex_config_storage_pool_output.storage_pool_details.name == powerflex_storage_pool_name
+ when: not ansible_check_mode and powerflex_config_storage_pool_output.changed
+
+ - name: "Verifying storage pool R3 creation"
+ ansible.builtin.assert:
+ that:
+ - powerflex_config_storage_pool_output.storage_pool_details.name == powerflex_storage_pool_name
+ when: not ansible_check_mode and powerflex_config_storage_pool_output.changed and powerflex_media_type is not none
+
+ - name: "Verifying protection domain creation in Idempotency"
+ ansible.builtin.assert:
+ that:
+ - powerflex_config_add_pd_output.protection_domain_details.name == powerflex_protection_domain_name
+ when: not ansible_check_mode and powerflex_config_add_pd_output.changed
+
+ - name: "Verifying storage pool R2 creation in Idempotency"
+ ansible.builtin.assert:
+ that:
+ powerflex_config_storage_pool_output.storage_pool_details.name == powerflex_storage_pool_name
+ when: not ansible_check_mode and powerflex_config_storage_pool_output.changed
+
+ - name: "Verifying storage pool R3 creation in Idempotency"
+ ansible.builtin.assert:
+ that:
+ - powerflex_config_storage_pool_output.storage_pool_details.name == powerflex_storage_pool_name
+ when: not ansible_check_mode and powerflex_config_storage_pool_output.changed and powerflex_media_type is not none
diff --git a/ansible_collections/dellemc/powerflex/roles/powerflex_config/molecule/configure_protection_domain/molecule.yml b/ansible_collections/dellemc/powerflex/roles/powerflex_config/molecule/configure_protection_domain/molecule.yml
new file mode 100644
index 000000000..ed97d539c
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/roles/powerflex_config/molecule/configure_protection_domain/molecule.yml
@@ -0,0 +1 @@
+---
diff --git a/ansible_collections/dellemc/powerflex/roles/powerflex_config/tasks/main.yml b/ansible_collections/dellemc/powerflex/roles/powerflex_config/tasks/main.yml
new file mode 100644
index 000000000..f9340f0fd
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/roles/powerflex_config/tasks/main.yml
@@ -0,0 +1,79 @@
+---
+- name: Get configured MDM IP addresses
+ dellemc.powerflex.mdm_cluster:
+ hostname: "{{ hostname }}"
+ username: "{{ username }}"
+ password: "{{ password }}"
+ validate_certs: "{{ validate_certs }}"
+ state: "present"
+ register: powerflex_config_mdm_ip_result
+ delegate_to: "{{ lookup('ansible.builtin.env', 'RUNON', default='localhost') }}"
+
+- name: Set fact - PowerFlex version and MDM primary hostname
+ ansible.builtin.set_fact:
+ powerflex_config_array_version: "{{ powerflex_config_mdm_ip_result.mdm_cluster_details.master.versionInfo[1] }}"
+ powerflex_config_mdm_primary_hostname: "{{ hostvars[groups['mdm'][0]]['inventory_hostname'] }}"
+
+- name: Login to primary MDM of PowerFlex 3.6
+ ansible.builtin.command: scli --login --username {{ username }} --password "{{ password }}"
+ run_once: true
+ register: powerflex_config_login_output
+ changed_when: powerflex_config_login_output.rc == 0
+ delegate_to: "{{ powerflex_config_mdm_primary_hostname }}"
+ when: powerflex_config_array_version == '3'
+
+- name: Login to primary MDM of PowerFlex 4.5
+ ansible.builtin.command: scli --login --username {{ username }} --management_system_ip {{ hostname }} --password "{{ password }}"
+ run_once: true
+ register: powerflex_config_login_output
+ changed_when: powerflex_config_login_output.rc == 0
+ delegate_to: "{{ powerflex_config_mdm_primary_hostname }}"
+ when: powerflex_config_array_version == '4'
+
+- name: Create the protection domain
+ dellemc.powerflex.protection_domain:
+ hostname: "{{ hostname }}"
+ username: "{{ username }}"
+ password: "{{ password }}"
+ validate_certs: "{{ validate_certs }}"
+ port: "{{ port }}"
+ protection_domain_name: "{{ powerflex_protection_domain_name }}"
+ is_active: "{{ is_active | default(omit) }}"
+ network_limits: "{{ network_limits | default(omit) }}"
+ rf_cache_limits: "{{ rf_cache_limits | default(omit) }}"
+ state: "present"
+ register: powerflex_config_add_pd_output
+ delegate_to: "{{ lookup('ansible.builtin.env', 'RUNON', default='localhost') }}"
+
+- name: Add fault set
+ ansible.builtin.command: scli --add_fault_set --protection_domain_name "{{ powerflex_protection_domain_name }}" --fault_set_name "{{ item }}"
+ with_items: "{{ powerflex_fault_sets }}"
+ run_once: true
+ delegate_to: "{{ powerflex_config_mdm_primary_hostname }}"
+ register: powerflex_config_add_fs_output
+ ignore_errors: true
+ changed_when: powerflex_config_add_fs_output.rc == 0
+ when:
+ - powerflex_fault_sets is defined
+
+- name: Create a new storage pool.
+ dellemc.powerflex.storagepool:
+ hostname: "{{ hostname }}"
+ username: "{{ username }}"
+ password: "{{ password }}"
+ validate_certs: "{{ validate_certs }}"
+ storage_pool_name: "{{ powerflex_storage_pool_name }}"
+ protection_domain_name: "{{ powerflex_protection_domain_name }}"
+ media_type: "{{ powerflex_media_type | default(omit) }}"
+ state: "present"
+ register: powerflex_config_storage_pool_output
+ delegate_to: "{{ lookup('ansible.builtin.env', 'RUNON', default='localhost') }}"
+
+- name: Enable zero-padding
+ ansible.builtin.command: |
+ scli --modify_zero_padding_policy --protection_domain_name
+ {{ powerflex_protection_domain_name }} --storage_pool_name {{ powerflex_storage_pool_name }} --enable_zero_padding
+ run_once: true
+ register: powerflex_config_enable_zero_padding_output
+ changed_when: powerflex_config_enable_zero_padding_output.rc == 0
+ delegate_to: "{{ powerflex_config_mdm_primary_hostname }}"
diff --git a/ansible_collections/dellemc/powerflex/roles/powerflex_config/vars/main.yml b/ansible_collections/dellemc/powerflex/roles/powerflex_config/vars/main.yml
new file mode 100644
index 000000000..ac3d9b959
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/roles/powerflex_config/vars/main.yml
@@ -0,0 +1,2 @@
+---
+# vars file for powerflex_config
diff --git a/ansible_collections/dellemc/powerflex/roles/powerflex_gateway/README.md b/ansible_collections/dellemc/powerflex/roles/powerflex_gateway/README.md
new file mode 100644
index 000000000..eac43ab78
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/roles/powerflex_gateway/README.md
@@ -0,0 +1,160 @@
+# powerflex_gateway
+
+Role to manage the installation and uninstallation of Powerflex Gateway.
+
+## Table of contents
+
+* [Requirements](#requirements)
+* [Ansible collections](#ansible-collections)
+* [Role Variables](#role-variables)
+* [Examples](#examples)
+* [Notes](#notes)
+* [Usage instructions](#usage-instructions)
+* [Author Information](#author-information)
+
+## Requirements
+
+```
+ansible
+python
+```
+
+## Ansible collections
+
+Collections required to use the role.
+
+```
+dellemc.powerflex
+```
+
+## Role Variables
+
+<table>
+<thead>
+ <tr>
+ <th>Name</th>
+ <th>Required</th>
+ <th>Description</th>
+ <th>Choices</th>
+ <th>Type</th>
+ <th>Default Value</th>
+ </tr>
+</thead>
+<tbody>
+ <tr>
+ <td>powerflex_common_file_install_location</td>
+ <td>false</td>
+ <td>Location of installation, compatible installation software package based on the operating system of the node.
+ <br> The files can be downloaded from the Dell Product support page for PowerFlex software.</td>
+ <td></td>
+ <td>path</td>
+ <td>/var/tmp</td>
+ </tr>
+ <tr>
+ <td>powerflex_gateway_is_redundant</td>
+ <td>false</td>
+ <td>Is the gateway redundant (will install keepalived).<br></td>
+ <td></td>
+ <td>bool</td>
+ <td>false</td>
+ </tr>
+ <tr>
+ <td>powerflex_gateway_admin_password</td>
+ <td>true</td>
+ <td>Admin password for the Powerflex gateway.<br></td>
+ <td></td>
+ <td>str</td>
+ <td></td>
+ </tr>
+ <tr>
+ <td>powerflex_gateway_http_port</td>
+ <td>false</td>
+ <td>Powerflex gateway HTTP port.<br></td>
+ <td></td>
+ <td>int</td>
+ <td>80</td>
+ </tr>
+ <tr>
+ <td>powerflex_gateway_https_port</td>
+ <td>false</td>
+ <td>Powerflex gateway HTTPS port.<br></td>
+ <td></td>
+ <td>int</td>
+ <td>443</td>
+ </tr>
+ <tr>
+ <td>powerflex_gateway_virtual_ip</td>
+ <td>false</td>
+ <td>Virtual IP address of Powerflex gateway.<br></td>
+ <td></td>
+ <td>str</td>
+ <td></td>
+ </tr>
+ <tr>
+ <td>powerflex_gateway_virtual_interface</td>
+ <td>false</td>
+ <td>Virtual interface of Powerflex gateway.<br></td>
+ <td></td>
+ <td>str</td>
+ <td></td>
+ </tr>
+ <tr>
+ <td>powerflex_gateway_state</td>
+ <td>false</td>
+ <td>Specify state of gateway.<br></td>
+ <td>absent, present</td>
+ <td>str</td>
+ <td>present</td>
+ </tr>
+ <tr>
+ <td>powerflex_gateway_skip_java</td>
+ <td>false</td>
+ <td>Specify whether to install the java or not.<br></td>
+ <td></td>
+ <td>bool</td>
+ <td>false</td>
+ </tr>
+</tbody>
+</table>
+
+## Examples
+----
+```
+ - name: Install and configure powerflex gateway
+ ansible.builtin.import_role:
+ name: powerflex_gateway
+ vars:
+ powerflex_common_file_install_location: "/opt/scaleio/rpm"
+ powerflex_gateway_admin_password: password
+ powerflex_gateway_state: present
+
+ - name: Uninstall powerflex gateway
+ ansible.builtin.import_role:
+ name: powerflex_gateway
+ vars:
+ powerflex_gateway_state: absent
+
+```
+
+## Notes
+- Supported only in PowerFlex version 3.6.
+
+## Usage instructions
+----
+### To install all dependency packages, including gateway, on node:
+ ```
+ ansible-playbook -i inventory site.yml
+ ```
+
+### To uninstall gateway:
+ ```
+ ansible-playbook -i inventory uninstall_powerflex.yml
+ ```
+
+Sample playbooks and inventory can be found in the playbooks directory.
+
+## Author Information
+------------------
+
+Dell Technologies <br>
+Bhavneet Sharma (ansible.team@Dell.com) 2023
diff --git a/ansible_collections/dellemc/powerflex/roles/powerflex_gateway/defaults/main.yml b/ansible_collections/dellemc/powerflex/roles/powerflex_gateway/defaults/main.yml
new file mode 100644
index 000000000..011911837
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/roles/powerflex_gateway/defaults/main.yml
@@ -0,0 +1,17 @@
+---
+# Is the gateway redundant (will install keepalived)
+powerflex_gateway_is_redundant: false
+# Virtual IP if redundant
+powerflex_gateway_virtual_ip: ''
+# Interface to attach the virtual ip
+powerflex_gateway_virtual_interface: eth1
+# Skip Java installation for PowerFlex (assume it's on the system)
+powerflex_gateway_skip_java: false
+# Disable GPG check to install PowerFlex gateway
+powerflex_gateway_disable_gpg_check: true
+powerflex_gateway_user_properties_file: /opt/emc/scaleio/gateway/webapps/ROOT/WEB-INF/classes/gatewayUser.properties
+powerflex_gateway_catalina_properties_file: /opt/emc/scaleio/gateway/conf/catalina.properties
+file_glob_name: gateway
+powerflex_role_environment:
+ MDM_IP: "{{ powerflex_mdm_ips }}"
+ GATEWAY_ADMIN_PASSWORD: "{{ powerflex_gateway_admin_password }}"
diff --git a/ansible_collections/dellemc/powerflex/roles/powerflex_gateway/handlers/main.yml b/ansible_collections/dellemc/powerflex/roles/powerflex_gateway/handlers/main.yml
new file mode 100644
index 000000000..ae7f1163f
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/roles/powerflex_gateway/handlers/main.yml
@@ -0,0 +1,6 @@
+---
+- name: Restart keepalived
+ ansible.builtin.service:
+ name: "keepalived"
+ state: "restarted"
+ enabled: true
diff --git a/ansible_collections/dellemc/powerflex/roles/powerflex_gateway/meta/argument_specs.yml b/ansible_collections/dellemc/powerflex/roles/powerflex_gateway/meta/argument_specs.yml
new file mode 100644
index 000000000..2fe8f150e
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/roles/powerflex_gateway/meta/argument_specs.yml
@@ -0,0 +1,47 @@
+---
+argument_specs:
+ main:
+ short_description: Role to manage the installation and uninstallation of
+ PowerFlex gateway
+ description:
+ - Role to manage the installation and uninstallation of PowerFlex gateway.
+ options:
+ powerflex_common_file_install_location:
+ type: path
+ description:
+ - Location of installation, compatible installation software package
+ based on the operating system of the node.
+ - The files can be downloaded from the Dell Product support page for
+ the PowerFlex software.
+ default: '/var/tmp'
+ powerflex_gateway_is_redundant:
+ type: bool
+ description: Is the gateway redundant (will install keepalived)
+ default: false
+ powerflex_gateway_admin_password:
+ required: true
+ type: str
+ description: Admin password for the PowerFlex gateway.
+ powerflex_gateway_http_port:
+ type: int
+ description: PowerFlex gateway HTTP port.
+ default: 80
+ powerflex_gateway_https_port:
+ type: int
+ description: PowerFlex gateway HTTPS port.
+ default: 443
+ powerflex_gateway_virtual_ip:
+ type: str
+ description: Virtual IP address of PowerFlex gateway.
+ powerflex_gateway_virtual_interface:
+ type: str
+ description: Virtual interface of PowerFlex gateway.
+ powerflex_gateway_state:
+ type: str
+ description: State of the PowerFlex gateway.
+ choices: ['present', 'absent']
+ default: 'present'
+ powerflex_gateway_skip_java:
+ type: bool
+ description: Specifies whether to install java or not.
+ default: false
diff --git a/ansible_collections/dellemc/powerflex/roles/powerflex_gateway/meta/main.yml b/ansible_collections/dellemc/powerflex/roles/powerflex_gateway/meta/main.yml
new file mode 100644
index 000000000..11db01ec5
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/roles/powerflex_gateway/meta/main.yml
@@ -0,0 +1,28 @@
+---
+galaxy_info:
+ role_name: powerflex_gateway
+ namespace: dellemc
+ author: Bhavneet Sharma
+ description: Role to manage the installation and uninstallation of Powerflex gateway
+ company: Dell Technologies
+ license: GPL-3.0-only
+ min_ansible_version: "2.14.0"
+ platforms:
+ - name: EL
+ versions:
+ - "9"
+ - "8"
+
+ - name: Ubuntu
+ versions:
+ - jammy
+
+ - name: SLES
+ versions:
+ - "15SP3"
+ - "15SP4"
+
+ galaxy_tags: []
+
+dependencies:
+ - role: powerflex_common
diff --git a/ansible_collections/dellemc/powerflex/roles/powerflex_gateway/molecule/gateway_installation/converge.yml b/ansible_collections/dellemc/powerflex/roles/powerflex_gateway/molecule/gateway_installation/converge.yml
new file mode 100644
index 000000000..92183f3dc
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/roles/powerflex_gateway/molecule/gateway_installation/converge.yml
@@ -0,0 +1,29 @@
+---
+- name: Install gateway
+ hosts: gateway
+ gather_facts: true
+ tasks:
+ - name: Install and configure powerflex gateway
+ ansible.builtin.import_role:
+ name: "powerflex_gateway"
+ vars:
+ powerflex_gateway_state: present
+ register: powerflex_gateway_result_molecule
+
+ - name: Verifying installation package in check mode
+ ansible.builtin.assert:
+ that:
+ - " 'No changes made, but would have if not in check mode' in powerflex_common_install_package_output.msg"
+ when: ansible_check_mode
+
+ - name: Verifying installation package
+ ansible.builtin.assert:
+ that:
+ - " 'Installed' in powerflex_common_install_package_output.results[0]"
+ when: not ansible_check_mode and powerflex_common_install_package_output.changed
+
+ - name: Verifying installation package in idempotency
+ ansible.builtin.assert:
+ that:
+ - " 'Nothing to do' in powerflex_common_install_package_output.msg"
+ when: not ansible_check_mode and not powerflex_common_install_package_output.changed
diff --git a/ansible_collections/dellemc/powerflex/roles/powerflex_gateway/molecule/gateway_installation/molecule.yml b/ansible_collections/dellemc/powerflex/roles/powerflex_gateway/molecule/gateway_installation/molecule.yml
new file mode 100644
index 000000000..ed97d539c
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/roles/powerflex_gateway/molecule/gateway_installation/molecule.yml
@@ -0,0 +1 @@
+---
diff --git a/ansible_collections/dellemc/powerflex/roles/powerflex_gateway/molecule/gateway_installation_invalid_path_rpm/converge.yml b/ansible_collections/dellemc/powerflex/roles/powerflex_gateway/molecule/gateway_installation_invalid_path_rpm/converge.yml
new file mode 100644
index 000000000..258173d9a
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/roles/powerflex_gateway/molecule/gateway_installation_invalid_path_rpm/converge.yml
@@ -0,0 +1,32 @@
+---
+- name: Install gateway with invalid rpm path
+ hosts: gateway
+ gather_facts: true
+ tasks:
+ - name: Install and configure powerflex gateway with no rpm
+ ansible.builtin.import_role:
+ name: "powerflex_gateway"
+ vars:
+ powerflex_common_file_install_location: "/opt/empty"
+ powerflex_gateway_state: present
+ ignore_errors: true
+ register: powerflex_gateway_install_config_no_rpm_result
+
+ - name: Verifying failure of install package with respect to no rpm file
+ ansible.builtin.assert:
+ that:
+ - powerflex_common_package_file.files | length == 0
+
+ - name: Install and configure powerflex gateway with wrong file path
+ ansible.builtin.import_role:
+ name: "powerflex_gateway"
+ vars:
+ powerflex_common_file_install_location: "/opt/aaab"
+ powerflex_gateway_state: present
+ ignore_errors: true
+ register: powerflex_gateway_install_config_wrong_path_result
+
+ - name: Verifying failure of install package with wrong file path
+ ansible.builtin.assert:
+ that:
+ - powerflex_common_package_file.files | length == 0
diff --git a/ansible_collections/dellemc/powerflex/roles/powerflex_gateway/molecule/gateway_installation_invalid_path_rpm/molecule.yml b/ansible_collections/dellemc/powerflex/roles/powerflex_gateway/molecule/gateway_installation_invalid_path_rpm/molecule.yml
new file mode 100644
index 000000000..93cad84c9
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/roles/powerflex_gateway/molecule/gateway_installation_invalid_path_rpm/molecule.yml
@@ -0,0 +1,4 @@
+---
+scenario:
+ test_sequence:
+ - converge
diff --git a/ansible_collections/dellemc/powerflex/roles/powerflex_gateway/molecule/gateway_uninstallation/converge.yml b/ansible_collections/dellemc/powerflex/roles/powerflex_gateway/molecule/gateway_uninstallation/converge.yml
new file mode 100644
index 000000000..6342d5f25
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/roles/powerflex_gateway/molecule/gateway_uninstallation/converge.yml
@@ -0,0 +1,48 @@
+---
+- name: Uninstall gateway
+ hosts: gateway
+ gather_facts: true
+ tasks:
+ - name: Uninstall powerflex gateway
+ ansible.builtin.import_role:
+ name: "powerflex_gateway"
+ vars:
+ powerflex_gateway_state: 'absent'
+
+ - name: Verifying uninstall package in check mode
+ ansible.builtin.assert:
+ that:
+ - powerflex_gateway_uninstall_output.msg == "Check mode: No changes made, but would have if not in check mode"
+ - powerflex_gateway_uninstall_output.changed is true
+ when: ansible_check_mode and ansible_distribution in ("RedHat", "CentOS", "SLES")
+
+ - name: Verifying uninstall package in converge
+ ansible.builtin.assert:
+ that:
+ - " 'Removed:' in powerflex_gateway_uninstall_output.results[0].results[0]"
+ when: not ansible_check_mode and powerflex_gateway_uninstall_output.changed and ansible_distribution in ("RedHat", "CentOS", "SLES")
+
+ - name: Verifying uninstall package in Idempotency
+ ansible.builtin.assert:
+ that:
+ - powerflex_gateway_uninstall_output.results[0].msg == 'Nothing to do'
+ when: not ansible_check_mode and not powerflex_gateway_uninstall_output.changed and ansible_distribution in ("RedHat", "CentOS", "SLES")
+
+ - name: Verifying uninstall package in check mode
+ ansible.builtin.assert:
+ that:
+ - powerflex_gateway_uninstall_deb_output.msg == "Check mode: No changes made, but would have if not in check mode"
+ - powerflex_gateway_uninstall_deb_output.changed is true
+ when: ansible_check_mode and ansible_distribution == "Ubuntu"
+
+ - name: Verifying uninstall package in converge
+ ansible.builtin.assert:
+ that:
+ - " 'Removed:' in powerflex_gateway_uninstall_deb_output.results[0].results[0]"
+ when: not ansible_check_mode and powerflex_gateway_uninstall_deb_output.changed and ansible_distribution == "Ubuntu"
+
+ - name: Verifying uninstall package in Idempotency
+ ansible.builtin.assert:
+ that:
+ - powerflex_gateway_uninstall_deb_output.results[0].msg == 'Nothing to do'
+ when: not ansible_check_mode and not powerflex_gateway_uninstall_deb_output.changed and ansible_distribution == "Ubuntu"
diff --git a/ansible_collections/dellemc/powerflex/roles/powerflex_gateway/molecule/gateway_uninstallation/molecule.yml b/ansible_collections/dellemc/powerflex/roles/powerflex_gateway/molecule/gateway_uninstallation/molecule.yml
new file mode 100644
index 000000000..ed97d539c
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/roles/powerflex_gateway/molecule/gateway_uninstallation/molecule.yml
@@ -0,0 +1 @@
+---
diff --git a/ansible_collections/dellemc/powerflex/roles/powerflex_gateway/tasks/install_gateway.yml b/ansible_collections/dellemc/powerflex/roles/powerflex_gateway/tasks/install_gateway.yml
new file mode 100644
index 000000000..58bbd1a08
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/roles/powerflex_gateway/tasks/install_gateway.yml
@@ -0,0 +1,52 @@
+---
+- name: Set Fact the powerflex_mdm_ips
+ ansible.builtin.set_fact:
+ powerflex_mdm_ips: "{{ hostvars[groups['mdm'][0]]['ansible_host'] }},{{ hostvars[groups['mdm'][1]]['ansible_host'] }}"
+
+- name: Install java
+ ansible.builtin.include_tasks: "../../powerflex_common/tasks/install_java_{{ ansible_distribution }}.yml"
+ when: not powerflex_gateway_skip_java
+
+- name: Set gateway admin password
+ ansible.builtin.set_fact:
+ token: "{{ powerflex_gateway_admin_password }}"
+
+- name: Include install_powerflex.yml
+ ansible.builtin.include_tasks: ../../powerflex_common/tasks/install_powerflex.yml
+
+- name: Include install_keepalived.yml
+ ansible.builtin.include_tasks: install_keepalived.yml
+ when: powerflex_gateway_is_redundant == "true"
+
+- name: Configure gateway with MDM addresses
+ ansible.builtin.lineinfile:
+ name: "{{ powerflex_gateway_user_properties_file }}"
+ regexp: '^mdm.ip.addresses'
+ line: "mdm.ip.addresses={{ powerflex_mdm_ips }}"
+ ignore_errors: "{{ ansible_check_mode }}"
+
+- name: Configure gateway to accept certificates
+ ansible.builtin.lineinfile:
+ name: "{{ powerflex_gateway_user_properties_file }}"
+ regexp: '^security.bypass_certificate_check'
+ line: "security.bypass_certificate_check=true"
+ ignore_errors: "{{ ansible_check_mode }}"
+
+- name: Configure gateway http port
+ ansible.builtin.lineinfile:
+ name: "{{ powerflex_gateway_catalina_properties_file }}"
+ regexp: '^http.port'
+ line: "http.port={{ powerflex_gateway_http_port }}"
+ ignore_errors: "{{ ansible_check_mode }}"
+
+- name: Configure gateway https port
+ ansible.builtin.lineinfile:
+ name: "{{ powerflex_gateway_catalina_properties_file }}"
+ regexp: '^ssl.port'
+ line: "ssl.port={{ powerflex_gateway_https_port }}"
+ ignore_errors: "{{ ansible_check_mode }}"
+
+- name: Restart service PowerFlex Gateway
+ ansible.builtin.service:
+ name: scaleio-gateway.service
+ state: restarted
diff --git a/ansible_collections/dellemc/powerflex/roles/powerflex_gateway/tasks/install_keepalived.yml b/ansible_collections/dellemc/powerflex/roles/powerflex_gateway/tasks/install_keepalived.yml
new file mode 100644
index 000000000..df6fd9dac
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/roles/powerflex_gateway/tasks/install_keepalived.yml
@@ -0,0 +1,28 @@
+---
+- name: Include vars
+ ansible.builtin.include_vars: "../vars/{{ ansible_distribution }}.yml"
+
+- name: Install required packages
+ ansible.builtin.package:
+ name: "{{ item }}"
+ state: present
+ with_items: "{{ keepalived_packages }}"
+
+- name: Set the priority of keepalived
+ ansible.builtin.set_fact:
+ keepalived_priority: 100
+ run_once: true
+
+- name: Set the priority of keepalived if not defined
+ ansible.builtin.set_fact:
+ keepalived_priority: 101
+ when: keepalived_priority is not defined
+
+- name: Configure keepalived
+ ansible.builtin.template:
+ src: keepalived.conf.j2
+ dest: "{{ keepalived_config_file_location }}/keepalived.conf"
+ mode: '0600'
+ owner: root
+ group: root
+ notify: restart keepalived
diff --git a/ansible_collections/dellemc/powerflex/roles/powerflex_gateway/tasks/main.yml b/ansible_collections/dellemc/powerflex/roles/powerflex_gateway/tasks/main.yml
new file mode 100644
index 000000000..80af948e2
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/roles/powerflex_gateway/tasks/main.yml
@@ -0,0 +1,8 @@
+---
+- name: Install Powerflex gateway
+ ansible.builtin.include_tasks: install_gateway.yml
+ when: powerflex_gateway_state == "present"
+
+- name: Uninstall Powerflex gateway
+ ansible.builtin.include_tasks: uninstall_gateway.yml
+ when: powerflex_gateway_state == "absent"
diff --git a/ansible_collections/dellemc/powerflex/roles/powerflex_gateway/tasks/uninstall_gateway.yml b/ansible_collections/dellemc/powerflex/roles/powerflex_gateway/tasks/uninstall_gateway.yml
new file mode 100644
index 000000000..39645d5a3
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/roles/powerflex_gateway/tasks/uninstall_gateway.yml
@@ -0,0 +1,20 @@
+---
+- name: Uninstall package
+ register: powerflex_gateway_uninstall_output
+ environment:
+ I_AM_SURE: "{{ i_am_sure | int }}"
+ ansible.builtin.package:
+ name: "{{ item }}"
+ state: "absent"
+ with_items:
+ - EMC-ScaleIO-gateway
+ when: ansible_distribution in ("RedHat", "CentOS", "SLES")
+
+- name: Uninstall deb package
+ register: powerflex_gateway_uninstall_deb_output
+ ansible.builtin.apt:
+ name: "{{ item }}"
+ state: absent
+ with_items:
+ - emc-scaleio-gateway
+ when: ansible_distribution == "Ubuntu"
diff --git a/ansible_collections/dellemc/powerflex/roles/powerflex_gateway/templates/keepalived.conf.j2 b/ansible_collections/dellemc/powerflex/roles/powerflex_gateway/templates/keepalived.conf.j2
new file mode 100644
index 000000000..b9896823e
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/roles/powerflex_gateway/templates/keepalived.conf.j2
@@ -0,0 +1,18 @@
+vrrp_script chk_gateway { # Requires keepalived-1.1.13
+ script "killall -0 scaleio-gateway-wd.bash" # cheaper than pidof
+ interval 2 # check every 2 seconds
+ weight 2 # add 2 points of prio if OK
+}
+
+vrrp_instance gateway_vi_1 {
+ interface {{ powerflex_gateway_virtual_interface }}
+ state MASTER
+ virtual_router_id 51
+ priority {{ keepalived_priority }} # 101 on master, 100 on backup
+ virtual_ipaddress {
+ {{ powerflex_gateway_virtual_ip }}
+ }
+ track_script {
+ chk_gateway
+ }
+}
diff --git a/ansible_collections/dellemc/powerflex/roles/powerflex_gateway/vars/CentOS.yml b/ansible_collections/dellemc/powerflex/roles/powerflex_gateway/vars/CentOS.yml
new file mode 100644
index 000000000..cbb2739c1
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/roles/powerflex_gateway/vars/CentOS.yml
@@ -0,0 +1,4 @@
+---
+keepalived_packages:
+ - keepalived
+keepalived_config_file_location: /etc/keepalived
diff --git a/ansible_collections/dellemc/powerflex/roles/powerflex_gateway/vars/RedHat.yml b/ansible_collections/dellemc/powerflex/roles/powerflex_gateway/vars/RedHat.yml
new file mode 100644
index 000000000..cbb2739c1
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/roles/powerflex_gateway/vars/RedHat.yml
@@ -0,0 +1,4 @@
+---
+keepalived_packages:
+ - keepalived
+keepalived_config_file_location: /etc/keepalived
diff --git a/ansible_collections/dellemc/powerflex/roles/powerflex_gateway/vars/SLES.yml b/ansible_collections/dellemc/powerflex/roles/powerflex_gateway/vars/SLES.yml
new file mode 100644
index 000000000..cbb2739c1
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/roles/powerflex_gateway/vars/SLES.yml
@@ -0,0 +1,4 @@
+---
+keepalived_packages:
+ - keepalived
+keepalived_config_file_location: /etc/keepalived
diff --git a/ansible_collections/dellemc/powerflex/roles/powerflex_gateway/vars/Ubuntu.yml b/ansible_collections/dellemc/powerflex/roles/powerflex_gateway/vars/Ubuntu.yml
new file mode 100644
index 000000000..cbb2739c1
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/roles/powerflex_gateway/vars/Ubuntu.yml
@@ -0,0 +1,4 @@
+---
+keepalived_packages:
+ - keepalived
+keepalived_config_file_location: /etc/keepalived
diff --git a/ansible_collections/dellemc/powerflex/roles/powerflex_gateway/vars/main.yml b/ansible_collections/dellemc/powerflex/roles/powerflex_gateway/vars/main.yml
new file mode 100644
index 000000000..6de60b1d3
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/roles/powerflex_gateway/vars/main.yml
@@ -0,0 +1,6 @@
+---
+file_glob_name: gateway
+file_gpg_name: RPM-GPG-KEY-ScaleIO
+powerflex_role_environment:
+ MDM_IP: "{{ powerflex_mdm_ips }}"
+ GATEWAY_ADMIN_PASSWORD: "{{ powerflex_gateway_admin_password }}"
diff --git a/ansible_collections/dellemc/powerflex/roles/powerflex_lia/README.md b/ansible_collections/dellemc/powerflex/roles/powerflex_lia/README.md
new file mode 100644
index 000000000..2ae19c9a1
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/roles/powerflex_lia/README.md
@@ -0,0 +1,170 @@
+# powerflex_lia
+
+Role to manage the installation and uninstallation of Powerflex LIA.
+
+## Table of contents
+
+* [Requirements](#requirements)
+* [Ansible collections](#ansible-collections)
+* [Role Variables](#role-variables)
+* [Examples](#examples)
+* [Usage instructions](#usage-instructions)
+* [Author Information](#author-information)
+
+## Requirements
+
+```
+ansible
+python
+```
+
+## Ansible collections
+
+Collections required to use the role.
+
+```
+dellemc.powerflex
+```
+
+## Role Variables
+
+<table>
+<thead>
+ <tr>
+ <th>Name</th>
+ <th>Required</th>
+ <th>Description</th>
+ <th>Choices</th>
+ <th>Type</th>
+ <th>Default Value</th>
+ </tr>
+</thead>
+<tbody>
+ <tr>
+ <td>hostname</td>
+ <td>true</td>
+ <td>IP or FQDN of the PowerFlex gateway.</td>
+ <td></td>
+ <td>str</td>
+ <td>10.1.1.1</td>
+ </tr>
+ <tr>
+ <td>username</td>
+ <td>true</td>
+ <td>The username of the PowerFlex gateway.</td>
+ <td></td>
+ <td>str</td>
+ <td>admin</td>
+ </tr>
+ <tr>
+ <td>password</td>
+ <td>true</td>
+ <td>The password of the PowerFlex gateway.</td>
+ <td></td>
+ <td>str</td>
+ <td>password</td>
+ </tr>
+ <tr>
+ <td>port</td>
+ <td>false</td>
+ <td>Port</td>
+ <td></td>
+ <td>int</td>
+ <td>443</td>
+ </tr>
+ <tr>
+ <td>validate_certs</td>
+ <td>false</td>
+ <td>If C(false), the SSL certificates will not be validated.<br>Configure C(false) only on personally controlled sites where self-signed certificates are used.</td>
+ <td></td>
+ <td>bool</td>
+ <td>false</td>
+ </tr>
+ <tr>
+ <td>timeout</td>
+ <td>false</td>
+ <td>Timeout</td>
+ <td></td>
+ <td>int</td>
+ <td>120</td>
+ </tr>
+ <tr>
+ <td>powerflex_common_file_install_location</td>
+ <td>true</td>
+ <td>Location of installation and rpm gpg files to be installed.
+ <br>The required, compatible installation software package based on the operating system of the node. The files can be downloaded from the Dell Product support page for PowerFlex software.</td>
+ <td></td>
+ <td>str</td>
+ <td>/var/tmp</td>
+ </tr>
+ <tr>
+ <td>powerflex_lia_token</td>
+ <td>true</td>
+ <td>Lia password for node management.</td>
+ <td></td>
+ <td>str</td>
+ <td>Cluster1!</td>
+ </tr>
+ <tr>
+ <td>powerflex_lia_state</td>
+ <td>false</td>
+ <td>Specify state of LIA.<br></td>
+ <td>absent, present</td>
+ <td>str</td>
+ <td>present</td>
+ </tr>
+</tbody>
+</table>
+
+## Examples
+----
+```
+ - name: Install and configure PowerFlex LIA
+ ansible.builtin.import_role:
+ name: powerflex_lia
+ vars:
+ hostname: "{{ hostname }}"
+ username: "{{ username }}"
+ password: "{{ password }}"
+ validate_certs: "{{ validate_certs }}"
+ port: "{{ port }}"
+ powerflex_lia_token: "Cluster1!"
+ powerflex_lia_state: present
+
+ - name: Uninstall powerflex LIA
+ ansible.builtin.import_role:
+ name: powerflex_lia
+ vars:
+ powerflex_lia_state: absent
+
+```
+
+## Usage instructions
+----
+### To install all dependency packages, including LIA, on node:
+- PowerFlex 3.6:
+ ```
+ ansible-playbook -i inventory site.yml
+ ```
+- PowerFlex 4.5:
+ ```
+ ansible-playbook -i inventory site_powerflex45.yml
+ ```
+
+### To uninstall LIA:
+- PowerFlex 3.6:
+ ```
+ ansible-playbook -i inventory uninstall_powerflex.yml
+ ```
+- PowerFlex 4.5:
+ ```
+ ansible-playbook -i inventory uninstall_powerflex45.yml
+ ```
+
+Sample playbooks and inventory can be found in the playbooks directory.
+
+## Author Information
+------------------
+
+Dell Technologies
+Pavan Mudunuri (ansible.team@Dell.com) 2023
diff --git a/ansible_collections/dellemc/powerflex/roles/powerflex_lia/defaults/main.yml b/ansible_collections/dellemc/powerflex/roles/powerflex_lia/defaults/main.yml
new file mode 100644
index 000000000..d69a82e0a
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/roles/powerflex_lia/defaults/main.yml
@@ -0,0 +1,5 @@
+---
+file_glob_name: lia
+powerflex_role_environment:
+ MDM_IP: "{{ powerflex_lia_mdm_ips }}"
+ TOKEN: "{{ powerflex_lia_token }}"
diff --git a/ansible_collections/dellemc/powerflex/roles/powerflex_lia/meta/main.yml b/ansible_collections/dellemc/powerflex/roles/powerflex_lia/meta/main.yml
new file mode 100644
index 000000000..ea089a471
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/roles/powerflex_lia/meta/main.yml
@@ -0,0 +1,27 @@
+---
+galaxy_info:
+ author: Pavan Mudunuri
+ description: Role to manage the installation and uninstallation of Powerflex LIA.
+ company: Dell Technologies
+ role_name: powerflex_lia
+ namespace: dellemc
+
+ license: GPL-3.0-only
+
+ min_ansible_version: "2.14.0"
+
+ platforms:
+ - name: EL
+ versions:
+ - "9"
+ - "8"
+ - name: Ubuntu
+ versions:
+ - jammy
+
+ - name: SLES
+ versions:
+ - "15SP3"
+ - "15SP4"
+
+ galaxy_tags: []
diff --git a/ansible_collections/dellemc/powerflex/roles/powerflex_lia/molecule/lia_install/converge.yml b/ansible_collections/dellemc/powerflex/roles/powerflex_lia/molecule/lia_install/converge.yml
new file mode 100644
index 000000000..7774d935b
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/roles/powerflex_lia/molecule/lia_install/converge.yml
@@ -0,0 +1,30 @@
+---
+- name: Molecule Test for installation of LIA
+ hosts: lia
+ vars_files:
+ - ../../../../playbooks/roles/vars_files/connection.yml
+
+ tasks:
+ - name: Install and configure Powerflex LIA
+ ansible.builtin.import_role:
+ name: powerflex_lia
+ vars:
+ powerflex_lia_state: present
+
+ - name: Verifying install package in check mode
+ ansible.builtin.assert:
+ that:
+ - powerflex_common_install_package_output.msg == "Check mode: No changes made"
+ when: ansible_check_mode
+
+ - name: Verifying installation package in normal mode
+ ansible.builtin.assert:
+ that:
+ - " 'Installed' in powerflex_common_install_package_output.results[0]"
+ when: not ansible_check_mode and powerflex_common_install_package_output.changed
+
+ - name: Verifying installation package in Idempotency mode
+ ansible.builtin.assert:
+ that:
+ - "'Nothing to do' in powerflex_common_install_package_output.msg"
+ when: not ansible_check_mode and not powerflex_common_install_package_output.changed
diff --git a/ansible_collections/dellemc/powerflex/roles/powerflex_lia/molecule/lia_install/molecule.yml b/ansible_collections/dellemc/powerflex/roles/powerflex_lia/molecule/lia_install/molecule.yml
new file mode 100644
index 000000000..ed97d539c
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/roles/powerflex_lia/molecule/lia_install/molecule.yml
@@ -0,0 +1 @@
+---
diff --git a/ansible_collections/dellemc/powerflex/roles/powerflex_lia/molecule/lia_installation_invalid_path_rpm/converge.yml b/ansible_collections/dellemc/powerflex/roles/powerflex_lia/molecule/lia_installation_invalid_path_rpm/converge.yml
new file mode 100644
index 000000000..d517ac2b6
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/roles/powerflex_lia/molecule/lia_installation_invalid_path_rpm/converge.yml
@@ -0,0 +1,34 @@
+---
+- name: Molecule Test for installation of LIA with invalid rpm path, rpm file
+ hosts: lia
+ vars_files:
+ - ../../../../playbooks/roles/vars_files/connection.yml
+
+ tasks:
+ - name: Install and configure powerflex LIA with no rpm
+ ansible.builtin.import_role:
+ name: powerflex_lia
+ vars:
+ powerflex_common_file_install_location: "/opt/empty"
+ powerflex_lia_state: present
+ register: powerflex_lia_no_rpm_result
+ ignore_errors: true
+
+ - name: Verifying failure of install package with respect to no rpm file in normal mode
+ ansible.builtin.assert:
+ that:
+ - powerflex_common_package_file.files | length | int == 0
+
+ - name: Install and configure powerflex lia with wrong file path
+ ansible.builtin.import_role:
+ name: powerflex_lia
+ vars:
+ powerflex_common_file_install_location: "/opt/aaab"
+ powerflex_lia_state: present
+ register: powerflex_lia_wrong_file_path_result
+ ignore_errors: true
+
+ - name: Verifying failure of install package with wrong file path in normal mode
+ ansible.builtin.assert:
+ that:
+ - powerflex_common_package_file.files | length | int == 0
diff --git a/ansible_collections/dellemc/powerflex/roles/powerflex_lia/molecule/lia_installation_invalid_path_rpm/molecule.yml b/ansible_collections/dellemc/powerflex/roles/powerflex_lia/molecule/lia_installation_invalid_path_rpm/molecule.yml
new file mode 100644
index 000000000..93cad84c9
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/roles/powerflex_lia/molecule/lia_installation_invalid_path_rpm/molecule.yml
@@ -0,0 +1,4 @@
+---
+scenario:
+ test_sequence:
+ - converge
diff --git a/ansible_collections/dellemc/powerflex/roles/powerflex_lia/molecule/lia_uninstallation/converge.yml b/ansible_collections/dellemc/powerflex/roles/powerflex_lia/molecule/lia_uninstallation/converge.yml
new file mode 100644
index 000000000..531359673
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/roles/powerflex_lia/molecule/lia_uninstallation/converge.yml
@@ -0,0 +1,30 @@
+---
+- name: Molecule Test for uninstallation of LIA
+ hosts: lia
+ vars_files:
+ - ../../../../playbooks/roles/vars_files/connection.yml
+
+ tasks:
+ - name: Uninstall powerflex lia
+ ansible.builtin.import_role:
+ name: powerflex_lia
+ vars:
+ powerflex_lia_state: 'absent'
+
+ - name: Verifying uninstall package in check mode
+ ansible.builtin.assert:
+ that:
+ - powerflex_lia_uninstall_output.msg == "Check mode: No changes made
+ when: ansible_check_mode
+
+ - name: Verifying uninstall package in normal mode
+ ansible.builtin.assert:
+ that:
+ - "'Removed: EMC-ScaleIO-lia' in powerflex_lia_uninstall_output.results[0].results[0]"
+ when: not ansible_check_mode and powerflex_lia_uninstall_output.changed
+
+ - name: Verifying uninstall package in Idempotency
+ ansible.builtin.assert:
+ that:
+ - "'Nothing to do' in powerflex_lia_uninstall_output.results[0].msg"
+ when: not ansible_check_mode and not powerflex_lia_uninstall_output.changed
diff --git a/ansible_collections/dellemc/powerflex/roles/powerflex_lia/molecule/lia_uninstallation/molecule.yml b/ansible_collections/dellemc/powerflex/roles/powerflex_lia/molecule/lia_uninstallation/molecule.yml
new file mode 100644
index 000000000..ed97d539c
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/roles/powerflex_lia/molecule/lia_uninstallation/molecule.yml
@@ -0,0 +1 @@
+---
diff --git a/ansible_collections/dellemc/powerflex/roles/powerflex_lia/tasks/install_lia.yml b/ansible_collections/dellemc/powerflex/roles/powerflex_lia/tasks/install_lia.yml
new file mode 100644
index 000000000..4b987b80e
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/roles/powerflex_lia/tasks/install_lia.yml
@@ -0,0 +1,22 @@
+---
+- name: Set facts for MDM counts
+ ansible.builtin.set_fact:
+ mdm_count: "{{ groups['mdm'] | length }}"
+
+- name: Set facts for powerflex_lia_tertiary_ip
+ ansible.builtin.set_fact:
+ powerflex_lia_tertiary_ip: "{{ hostvars[groups['mdm'][2]]['ansible_host'] }}"
+ when: mdm_count | int > 2
+
+- name: Set facts for powerflex_lia_mdm_ips if mdm_count is 2
+ ansible.builtin.set_fact:
+ powerflex_lia_mdm_ips: "{{ hostvars[groups['mdm'][0]]['ansible_host'] }},{{ hostvars[groups['mdm'][1]]['ansible_host'] }}"
+ when: mdm_count | int == 2
+
+- name: Set facts for powerflex_lia_mdm_ips if mdm_count is more than 2
+ ansible.builtin.set_fact:
+ powerflex_lia_mdm_ips: "{{ hostvars[groups['mdm'][0]]['ansible_host'] }},{{ hostvars[groups['mdm'][1]]['ansible_host'] }},{{ powerflex_lia_tertiary_ip }}"
+ when: mdm_count | int > 2
+
+- name: Include install_powerflex.yml
+ ansible.builtin.include_tasks: ../../powerflex_common/tasks/install_powerflex.yml
diff --git a/ansible_collections/dellemc/powerflex/roles/powerflex_lia/tasks/main.yml b/ansible_collections/dellemc/powerflex/roles/powerflex_lia/tasks/main.yml
new file mode 100644
index 000000000..f04a3ff75
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/roles/powerflex_lia/tasks/main.yml
@@ -0,0 +1,8 @@
+---
+- name: Install LIA
+ ansible.builtin.include_tasks: install_lia.yml
+ when: powerflex_lia_state == 'present'
+
+- name: Uninstall LIA
+ ansible.builtin.include_tasks: uninstall_lia.yml
+ when: powerflex_lia_state == 'absent'
diff --git a/ansible_collections/dellemc/powerflex/roles/powerflex_lia/tasks/uninstall_lia.yml b/ansible_collections/dellemc/powerflex/roles/powerflex_lia/tasks/uninstall_lia.yml
new file mode 100644
index 000000000..3cac9b41d
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/roles/powerflex_lia/tasks/uninstall_lia.yml
@@ -0,0 +1,19 @@
+---
+- name: Uninstall package
+ register: powerflex_lia_uninstall_output
+ environment:
+ I_AM_SURE: "{{ i_am_sure | int }}"
+ ansible.builtin.package:
+ name: "{{ item }}"
+ state: "absent"
+ with_items:
+ - EMC-ScaleIO-lia
+ when: ansible_distribution in ("RedHat", "CentOS", "SLES")
+
+- name: Uninstall deb package
+ ansible.builtin.apt:
+ name: "{{ item }}"
+ state: absent
+ with_items:
+ - emc-scaleio-lia
+ when: ansible_distribution == "Ubuntu"
diff --git a/ansible_collections/dellemc/powerflex/roles/powerflex_lia/vars/main.yml b/ansible_collections/dellemc/powerflex/roles/powerflex_lia/vars/main.yml
new file mode 100644
index 000000000..6877f3723
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/roles/powerflex_lia/vars/main.yml
@@ -0,0 +1,6 @@
+---
+file_glob_name: lia
+file_gpg_name: RPM-GPG-KEY-ScaleIO
+powerflex_role_environment:
+ MDM_IP: "{{ powerflex_lia_mdm_ips }}"
+ TOKEN: "{{ powerflex_lia_token }}"
diff --git a/ansible_collections/dellemc/powerflex/roles/powerflex_mdm/README.md b/ansible_collections/dellemc/powerflex/roles/powerflex_mdm/README.md
new file mode 100644
index 000000000..aa54a27a0
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/roles/powerflex_mdm/README.md
@@ -0,0 +1,142 @@
+# powerflex_mdm
+
+Role to manage the installation and uninstallation of Powerflex MDM.
+
+## Table of contents
+
+* [Requirements](#requirements)
+* [Ansible collections](#ansible-collections)
+* [Role Variables](#role-variables)
+* [Examples](#examples)
+* [Usage instructions](#usage-instructions)
+* [Notes](#notes)
+* [Author Information](#author-information)
+
+## Requirements
+
+```
+ansible
+python
+```
+
+## Ansible collections
+
+Collections required to use the role.
+
+```
+dellemc.powerflex
+ansible.posix
+community.general
+```
+
+## Role Variables
+
+<table>
+<thead>
+ <tr>
+ <th>Name</th>
+ <th>Required</th>
+ <th>Description</th>
+ <th>Choices</th>
+ <th>Type</th>
+ <th>Default Value</th>
+ </tr>
+</thead>
+<tbody>
+ <tr>
+ <td>powerflex_common_file_install_location</td>
+ <td>true</td>
+ <td>Location of installation and rpm gpg files to be installed.
+ <br> The required, compatible installation software package based on the operating system of the node.
+ <br> The files can be downloaded from the Dell Product support page for PowerFlex software.</td>
+ <td></td>
+ <td>str</td>
+ <td>/var/tmp</td>
+ </tr>
+ <tr>
+ <td>powerflex_mdm_password</td>
+ <td>true</td>
+ <td>Password for mdm cluster.<br></td>
+ <td></td>
+ <td>str</td>
+ <td>Password123</td>
+ </tr>
+ <tr>
+ <td>powerflex_mdm_state</td>
+ <td>false</td>
+ <td>Specify state of MDM.<br></td>
+ <td>absent, present</td>
+ <td>str</td>
+ <td>present</td>
+ </tr>
+ <tr>
+ <td>powerflex_mdm_virtual_ip</td>
+ <td>false</td>
+ <td>Virtual IP address of MDM.<br></td>
+ <td></td>
+ <td>str</td>
+ <td></td>
+ </tr>
+ <tr>
+ <td>powerflex_mdm_cert_password</td>
+ <td>false</td>
+ <td>The password to generate the certificate cli.
+ <br>Required while installing MDM for Powerlex 4.x.<br></td>
+ <td></td>
+ <td>str</td>
+ <td>Password123!</td>
+ </tr>
+</tbody>
+</table>
+
+## Examples
+----
+```
+ - name: "Install and configure powerflex mdm"
+ ansible.builtin.import_role:
+ name: "powerflex_mdm"
+ vars:
+ powerflex_common_file_install_location: "/opt/scaleio/rpm"
+ powerflex_mdm_password: password
+ powerflex_mdm_state: present
+
+ - name: "Uninstall powerflex mdm"
+ ansible.builtin.import_role:
+ name: "powerflex_mdm"
+ vars:
+ powerflex_mdm_state: absent
+
+```
+
+## Usage instructions
+----
+### To install all dependency packages, including mdm, on node:
+- PowerFlex 3.6:
+ ```
+ ansible-playbook -i inventory site.yml
+ ```
+- PowerFlex 4.5:
+ ```
+ ansible-playbook -i inventory site_powerflex45.yml
+ ```
+
+### To uninstall mdm:
+- PowerFlex 3.6:
+ ```
+ ansible-playbook -i inventory uninstall_powerflex.yml
+ ```
+- PowerFlex 4.5:
+ ```
+ ansible-playbook -i inventory uninstall_powerflex45.yml
+ ```
+
+Sample playbooks and inventory can be found in the playbooks directory.
+
+## Notes
+- The ```community.general``` collection must be installed for MDM installation on SLES OS.
+
+## Author Information
+------------------
+
+Dell Technologies <br>
+Bhavneet Sharma (ansible.team@Dell.com) 2023
diff --git a/ansible_collections/dellemc/powerflex/roles/powerflex_mdm/defaults/main.yml b/ansible_collections/dellemc/powerflex/roles/powerflex_mdm/defaults/main.yml
new file mode 100644
index 000000000..340da8bb8
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/roles/powerflex_mdm/defaults/main.yml
@@ -0,0 +1,9 @@
+---
+# Skip Java installation for powerflex (assume it's on the system)
+powerflex_skip_java: false
+powerflex_mdm_primary_hostname: ''
+powerflex_mdm_primary_ip: ''
+powerflex_role_environment:
+ MDM_IP: "{{ powerflex_mdm_ips }}"
+ MDM_ROLE_IS_MANAGER: 1
+file_glob_name: mdm
diff --git a/ansible_collections/dellemc/powerflex/roles/powerflex_mdm/meta/argument_spec.yml b/ansible_collections/dellemc/powerflex/roles/powerflex_mdm/meta/argument_spec.yml
new file mode 100644
index 000000000..be80c1970
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/roles/powerflex_mdm/meta/argument_spec.yml
@@ -0,0 +1,28 @@
+---
+argument_specs:
+ main:
+ version_added: "1.8.0"
+ short_description: Role to manage the installation and uninstallation of Powerflex MDM
+ description:
+ - Role to manage the installation and uninstallation of Powerflex MDM.
+ options:
+ powerflex_mdm_state:
+ required: true
+ type: str
+ description: State of the Powerflex MDM.
+ powerflex_mdm_password:
+ required: true
+ type: str
+ description: Password for the Powerflex MDM.
+ powerflex_common_file_install_location:
+ required: true
+ type: str
+ description: Common file installation location.
+ powerflex_mdm_virtual_ip:
+ type: str
+ description: Virtual IP address of MDM.
+ powerflex_mdm_cert_password:
+ type: str
+ description:
+ - Password to generate cli certificate for MDM.
+ - Required while installing MDM for Powerlex 4.x.
diff --git a/ansible_collections/dellemc/powerflex/roles/powerflex_mdm/meta/main.yml b/ansible_collections/dellemc/powerflex/roles/powerflex_mdm/meta/main.yml
new file mode 100644
index 000000000..889114f46
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/roles/powerflex_mdm/meta/main.yml
@@ -0,0 +1,29 @@
+---
+galaxy_info:
+ author: Bhavneet Sharma
+ description: Role to manage the installation and uninstallation of Powerflex MDM.
+ company: Dell Technologies
+ role_name: powerflex_mdm
+ namespace: dellemc
+
+ license: GPL-3.0-only
+
+ min_ansible_version: "2.14.0"
+
+ platforms:
+ - name: EL
+ versions:
+ - "9"
+ - "8"
+ - name: Ubuntu
+ versions:
+ - jammy
+
+ - name: SLES
+ versions:
+ - "15SP3"
+ - "15SP4"
+
+ galaxy_tags: []
+dependencies:
+ - role: powerflex_common
diff --git a/ansible_collections/dellemc/powerflex/roles/powerflex_mdm/molecule/mdm_installation/converge.yml b/ansible_collections/dellemc/powerflex/roles/powerflex_mdm/molecule/mdm_installation/converge.yml
new file mode 100644
index 000000000..707de56fe
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/roles/powerflex_mdm/molecule/mdm_installation/converge.yml
@@ -0,0 +1,63 @@
+---
+- name: MDM installation
+ hosts: mdm
+ vars_files:
+ - ../../../../playbooks/roles/vars_files/connection.yml
+ tasks:
+ - name: Install common packages
+ ansible.builtin.import_role:
+ name: powerflex_common
+
+ - name: Install and configure Powerflex MDM
+ ansible.builtin.import_role:
+ name: powerflex_mdm
+ vars:
+ powerflex_mdm_state: present
+
+ - name: Verifying install package in check mode
+ ansible.builtin.assert:
+ that:
+ - powerflex_common_install_package_output.msg == "Check mode: No changes made"
+ when: ansible_check_mode
+
+ - name: Verifying installation package in normal mode
+ ansible.builtin.assert:
+ that:
+ - "'Installed' in powerflex_common_install_package_output.results[0]"
+ when: not ansible_check_mode and powerflex_common_install_package_output.changed
+
+ - name: Verifying add primary mdm in normal mode
+ ansible.builtin.assert:
+ that:
+ - "'Successfully created the MDM Cluster' in powerflex_mdm_add_primary_output.stdout"
+ when: not ansible_check_mode and powerflex_mdm_add_primary_output.changed
+
+ - name: Verifying add secondary mdm in normal mode
+ ansible.builtin.assert:
+ that:
+ - "'Successfully added a standby MDM' in powerflex_mdm_add_secondary_output.stdout"
+ when: not ansible_check_mode and powerflex_mdm_add_secondary_output.changed
+
+ - name: Verifying add tertiary mdm in normal mode
+ ansible.builtin.assert:
+ that:
+ - "'Successfully added a standby MDM' in powerflex_mdm_add_tertiary_output.stdout"
+ when: not ansible_check_mode and powerflex_mdm_add_tertiary_output.changed
+
+ - name: Verifying primary mdm configuration in Idempotency
+ ansible.builtin.assert:
+ that:
+ - "'The Primary MDM is already configured' in powerflex_mdm_add_primary_output.stderr_lines[0]"
+ when: not ansible_check_mode and not powerflex_mdm_add_primary_output.changed
+
+ - name: Verifying secondary mdm configuration in Idempotency
+ ansible.builtin.assert:
+ that:
+ - "'An MDM with the same name already exists' in powerflex_mdm_add_secondary_output.stderr_lines[0]"
+ when: not ansible_check_mode and not powerflex_mdm_add_secondary_output.changed
+
+ - name: Verifying tertiary mdm configuration in Idempotency
+ ansible.builtin.assert:
+ that:
+ - "'An MDM with the same name already exists' in powerflex_mdm_add_tertiary_output.stderr_lines[0]"
+ when: not ansible_check_mode and powerflex_mdm_tertiary_ip is defined and not powerflex_mdm_add_tertiary_output.changed
diff --git a/ansible_collections/dellemc/powerflex/roles/powerflex_mdm/molecule/mdm_installation/molecule.yml b/ansible_collections/dellemc/powerflex/roles/powerflex_mdm/molecule/mdm_installation/molecule.yml
new file mode 100644
index 000000000..ed97d539c
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/roles/powerflex_mdm/molecule/mdm_installation/molecule.yml
@@ -0,0 +1 @@
+---
diff --git a/ansible_collections/dellemc/powerflex/roles/powerflex_mdm/molecule/mdm_uninstallation/converge.yml b/ansible_collections/dellemc/powerflex/roles/powerflex_mdm/molecule/mdm_uninstallation/converge.yml
new file mode 100644
index 000000000..ede8baf5a
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/roles/powerflex_mdm/molecule/mdm_uninstallation/converge.yml
@@ -0,0 +1,53 @@
+---
+- name: MDM uninstallation
+ hosts: mdm
+ vars_files:
+ - ../../../../playbooks/roles/vars_files/connection.yml
+ tasks:
+ - name: Uninstall powerflex MDM
+ ansible.builtin.import_role:
+ name: powerflex_mdm
+ vars:
+ powerflex_mdm_state: 'absent'
+
+ - name: Verifying uninstall package in check mode
+ ansible.builtin.assert:
+ that:
+ - powerflex_mdm_uninstall_output.msg == "Check mode: No changes made"
+ when: ansible_check_mode
+
+ - name: Verifying remove secondary mdm in normal mode
+ ansible.builtin.assert:
+ that:
+ - "'Successfully removed the standby MDM' in powerflex_mdm_remove_secondary.stdout"
+ when: not ansible_check_mode and powerflex_mdm_remove_secondary.changed
+
+ - name: Verifying remove tertiary mdm in normal mode
+ ansible.builtin.assert:
+ that:
+ - "'Successfully removed the standby MDM' in powerflex_mdm_remove_tertiary.stdout"
+ when: not ansible_check_mode and powerflex_mdm_tertiary_ip is defined and powerflex_mdm_remove_tertiary.changed
+
+ - name: Verifying uninstall package in normal mode
+ ansible.builtin.assert:
+ that:
+ - "'Removed: EMC-ScaleIO-mdm' in powerflex_mdm_uninstall_output.results[0].results[0]"
+ when: not ansible_check_mode and powerflex_mdm_uninstall_output.changed
+
+ - name: Verifying remove secondary mdm in Idempotency
+ ansible.builtin.assert:
+ that:
+ - "'No such file or directory' in powerflex_mdm_remove_secondary.msg"
+ when: not ansible_check_mode and not powerflex_mdm_remove_secondary.changed
+
+ - name: Verifying remove tertiary mdm in Idempotency
+ ansible.builtin.assert:
+ that:
+ - "'No such file or directory' in powerflex_mdm_remove_tertiary.msg"
+ when: not ansible_check_mode and powerflex_mdm_tertiary_ip is defined and not powerflex_mdm_remove_tertiary.changed
+
+ - name: Verifying uninstall package in Idempotency
+ ansible.builtin.assert:
+ that:
+ - "'Nothing to do' in powerflex_mdm_uninstall_output.results[0].msg"
+ when: not ansible_check_mode and not powerflex_mdm_uninstall_output.changed
diff --git a/ansible_collections/dellemc/powerflex/roles/powerflex_mdm/molecule/mdm_uninstallation/molecule.yml b/ansible_collections/dellemc/powerflex/roles/powerflex_mdm/molecule/mdm_uninstallation/molecule.yml
new file mode 100644
index 000000000..ed97d539c
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/roles/powerflex_mdm/molecule/mdm_uninstallation/molecule.yml
@@ -0,0 +1 @@
+---
diff --git a/ansible_collections/dellemc/powerflex/roles/powerflex_mdm/tasks/add_certs.yml b/ansible_collections/dellemc/powerflex/roles/powerflex_mdm/tasks/add_certs.yml
new file mode 100644
index 000000000..f517d5229
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/roles/powerflex_mdm/tasks/add_certs.yml
@@ -0,0 +1,168 @@
+---
+- name: Generate CA certificate
+ register: powerflex_mdm_generate_mgmt_ca_cert
+ ansible.builtin.command: python3 certificate_generator_MDM_USER.py --generate_ca mgmt_ca.pem
+ args:
+ chdir: /opt/emc/scaleio/mdm/cfg
+ delegate_to: "{{ powerflex_mdm_primary_hostname }}"
+ changed_when: powerflex_mdm_generate_mgmt_ca_cert.rc == 0
+
+- name: Create CLI certificate
+ register: powerflex_mdm_generate_cli_cert
+ ansible.builtin.command: >
+ python3 certificate_generator_MDM_USER.py --generate_cli cli_certificate.p12 -CA mgmt_ca.pem --password {{ powerflex_mdm_cert_password }}
+ delegate_to: "{{ powerflex_mdm_primary_hostname }}"
+ args:
+ chdir: /opt/emc/scaleio/mdm/cfg
+ changed_when: powerflex_mdm_generate_cli_cert.rc == 0
+
+- name: Create MDM certificate
+ register: powerflex_mdm_generate_mdm_cert
+ ansible.builtin.command: python3 certificate_generator_MDM_USER.py --generate_mdm mdm_certificate.pem -CA mgmt_ca.pem
+ delegate_to: "{{ powerflex_mdm_primary_hostname }}"
+ args:
+ chdir: /opt/emc/scaleio/mdm/cfg
+ changed_when: powerflex_mdm_generate_mdm_cert.rc == 0
+
+- name: Create additional MDM certificates
+ register: powerflex_mdm_generate_additional_mdm_cert
+ ansible.builtin.command: python3 certificate_generator_MDM_USER.py --generate_mdm sec_mdm_certificate.pem -CA mgmt_ca.pem
+ delegate_to: "{{ powerflex_mdm_primary_hostname }}"
+ args:
+ chdir: /opt/emc/scaleio/mdm/cfg
+ changed_when: powerflex_mdm_generate_additional_mdm_cert.rc == 0
+
+- name: Fetch all certs to localhost
+ register: powerflex_mdm_fetch_certs
+ ansible.builtin.fetch:
+ src: /opt/emc/scaleio/mdm/cfg/{{ item }}
+ dest: /tmp/
+ flat: true
+ delegate_to: "{{ powerflex_mdm_primary_hostname }}"
+ with_items:
+ - sec_mdm_certificate.pem
+ - cli_certificate.p12
+ - mgmt_ca.pem
+
+- name: Copy MDM certificates to Secondary manager MDM node
+ register: powerflex_mdm_copy_additional_certs_to_secondary
+ ansible.builtin.copy:
+ src: /tmp/sec_mdm_certificate.pem
+ dest: /opt/emc/scaleio/mdm/cfg/mdm_certificate.pem
+ mode: preserve
+ delegate_to: "{{ powerflex_mdm_secondary_hostname }}"
+ when: powerflex_mdm_secondary_ip is defined
+
+- name: Copy CLI certificates to Secondary manager MDM node
+ register: powerflex_mdm_copy_cli_certs_to_secondary
+ ansible.builtin.copy:
+ src: /tmp/cli_certificate.p12
+ dest: /opt/emc/scaleio/mdm/cfg/cli_certificate.p12
+ mode: preserve
+ delegate_to: "{{ powerflex_mdm_secondary_hostname }}"
+ when: powerflex_mdm_secondary_ip is defined
+
+- name: Copy mgmt_ca.pem certificates to Secondary manager MDM node
+ register: powerflex_mdm_copy_mgmt_certs_to_secondary
+ ansible.builtin.copy:
+ src: /tmp/mgmt_ca.pem
+ dest: /opt/emc/scaleio/mdm/cfg/mgmt_ca.pem
+ mode: preserve
+ delegate_to: "{{ powerflex_mdm_secondary_hostname }}"
+ when: powerflex_mdm_secondary_ip is defined
+
+- name: Copy MDM certificates to Tertiary manager MDM node
+ register: powerflex_mdm_copy_additional_certs_to_tertiary
+ ansible.builtin.copy:
+ src: /tmp/sec_mdm_certificate.pem
+ dest: /opt/emc/scaleio/mdm/cfg/mdm_certificate.pem
+ mode: preserve
+ delegate_to: "{{ powerflex_mdm_tertiary_hostname }}"
+ when: powerflex_mdm_tertiary_ip is defined
+
+- name: Copy CLI certificates to Tertiary manager MDM node
+ register: powerflex_mdm_copy_cli_certs_to_tertiary
+ ansible.builtin.copy:
+ src: /tmp/cli_certificate.p12
+ dest: /opt/emc/scaleio/mdm/cfg/cli_certificate.p12
+ mode: preserve
+ delegate_to: "{{ powerflex_mdm_tertiary_hostname }}"
+ when: powerflex_mdm_tertiary_ip is defined
+
+- name: Copy mgmt_ca.pem certificates to Tertiary manager MDM node
+ register: powerflex_mdm_copy_mgmt_certs_to_tertiary
+ ansible.builtin.copy:
+ src: /tmp/mgmt_ca.pem
+ dest: /opt/emc/scaleio/mdm/cfg/mgmt_ca.pem
+ mode: preserve
+ delegate_to: "{{ powerflex_mdm_tertiary_hostname }}"
+ when: powerflex_mdm_tertiary_ip is defined
+
+- name: Add CA certificate on primary MDM
+ register: powerflex_mdm_add_mgmt_cert_to_ca_primary
+ ansible.builtin.command: scli --add_certificate --certificate_file mgmt_ca.pem
+ delegate_to: "{{ powerflex_mdm_primary_hostname }}"
+ args:
+ chdir: /opt/emc/scaleio/mdm/cfg
+ changed_when: powerflex_mdm_add_mgmt_cert_to_ca_primary.rc == 0
+
+- name: Add CA certificate on secondary MDM
+ register: powerflex_mdm_add_mgmt_cert_to_ca_secondary
+ ansible.builtin.command: scli --add_certificate --certificate_file mgmt_ca.pem
+ delegate_to: "{{ powerflex_mdm_secondary_hostname }}"
+ when: powerflex_mdm_secondary_ip is defined
+ args:
+ chdir: /opt/emc/scaleio/mdm/cfg
+ changed_when: powerflex_mdm_add_mgmt_cert_to_ca_secondary.rc == 0
+
+- name: Add CA certificate on tertiary MDM
+ register: powerflex_mdm_add_mgmt_cert_to_ca_tertiary
+ ansible.builtin.command: scli --add_certificate --certificate_file mgmt_ca.pem
+ delegate_to: "{{ powerflex_mdm_tertiary_hostname }}"
+ when: powerflex_mdm_tertiary_ip is defined
+ args:
+ chdir: /opt/emc/scaleio/mdm/cfg
+ changed_when: powerflex_mdm_add_mgmt_cert_to_ca_tertiary.rc == 0
+
+- name: Start MDM service on primary MDM
+ register: powerflex_mdm_start_service_primary
+ ansible.builtin.service:
+ name: "mdm.service"
+ state: "restarted"
+ enabled: true
+ delegate_to: "{{ powerflex_mdm_primary_hostname }}"
+
+- name: Start MDM service on secondary MDM
+ register: powerflex_mdm_start_service_secondary
+ ansible.builtin.service:
+ name: "mdm.service"
+ state: "restarted"
+ enabled: true
+ delegate_to: "{{ powerflex_mdm_secondary_hostname }}"
+ when: powerflex_mdm_secondary_ip is defined
+
+- name: Start MDM service on tertiary MDM
+ register: powerflex_mdm_start_service_tertiary
+ ansible.builtin.service:
+ name: "mdm.service"
+ state: "restarted"
+ enabled: true
+ delegate_to: "{{ powerflex_mdm_tertiary_hostname }}"
+ when: powerflex_mdm_tertiary_ip is defined
+
+- name: Check MDM service status
+ register: powerflex_mdm_check_service
+ ansible.builtin.command: systemctl status mdm.service
+ delegate_to: "{{ powerflex_mdm_primary_hostname }}"
+ changed_when: powerflex_mdm_check_service.rc == 0
+
+- name: Delete certificates from localhost
+ register: powerflex_mdm_delete_localhost_certs
+ ansible.builtin.file:
+ path: /tmp/{{ item }}
+ state: absent
+ with_items:
+ - sec_mdm_certificate.pem
+ - cli_certificate.p12
+ - mgmt_ca.pem
+ delegate_to: "{{ lookup('ansible.builtin.env', 'RUNON', default='localhost') }}"
diff --git a/ansible_collections/dellemc/powerflex/roles/powerflex_mdm/tasks/install_mdm.yml b/ansible_collections/dellemc/powerflex/roles/powerflex_mdm/tasks/install_mdm.yml
new file mode 100644
index 000000000..76bbba5a6
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/roles/powerflex_mdm/tasks/install_mdm.yml
@@ -0,0 +1,32 @@
+---
+- name: Include the mdm_set_facts.yml
+ ansible.builtin.include_tasks: "mdm_set_facts.yml"
+
+- name: Include vars
+ ansible.builtin.include_vars: "../vars/{{ ansible_distribution }}.yml"
+
+- name: Pre-requisite on rhel6 based os
+ ansible.posix.sysctl:
+ name: kernel.shmmax
+ value: 209715200
+ when: ansible_os_family == "RedHat" and ansible_distribution_major_version == "6"
+
+- name: List the rpm file
+ register: powerflex_mdm_package_file_version
+ ansible.builtin.find:
+ paths: "{{ powerflex_common_file_install_location }}"
+ patterns: "*{{ file_glob_name }}*.rpm"
+ delegate_to: "{{ lookup('ansible.builtin.env', 'RUNON', default='localhost') }}"
+
+- name: Extract file versions
+ ansible.builtin.set_fact:
+ version: "{{ powerflex_mdm_package_file_version.files[0].path | regex_search('mdm-(\\d+)', '\\1') }}"
+ when: powerflex_mdm_package_file_version.files | length > 0
+
+- name: Install MDM for PowerFlex below 4.x
+ ansible.builtin.include_tasks: install_powerflex3x_mdm.yml
+ when: version[0] < "4"
+
+- name: Install MDM for PowerFlex 4.x
+ ansible.builtin.include_tasks: install_powerflex4x_mdm.yml
+ when: version[0] >= "4"
diff --git a/ansible_collections/dellemc/powerflex/roles/powerflex_mdm/tasks/install_powerflex3x_mdm.yml b/ansible_collections/dellemc/powerflex/roles/powerflex_mdm/tasks/install_powerflex3x_mdm.yml
new file mode 100644
index 000000000..178bd8696
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/roles/powerflex_mdm/tasks/install_powerflex3x_mdm.yml
@@ -0,0 +1,128 @@
+---
+- name: Include install_powerflex.yml
+ ansible.builtin.include_tasks: ../../powerflex_common/tasks/install_powerflex.yml
+
+- name: Wait for MDM to be active
+ ansible.builtin.wait_for:
+ port: 9011
+ state: started
+ run_once: true
+ delegate_to: "{{ powerflex_mdm_primary_hostname }}"
+
+- name: Add primary MDM with virtual ip
+ ansible.builtin.command: >
+ scli --create_mdm_cluster
+ --master_mdm_ip {{ powerflex_mdm_primary_ip }}
+ --master_mdm_management_ip {{ powerflex_mdm_primary_ip }}
+ --master_mdm_name {{ powerflex_mdm_primary_hostname }}
+ --master_mdm_virtual_ip_interface {{ ansible_default_ipv4.interface }}
+ --cluster_virtual_ip {{ powerflex_mdm_virtual_ip }}
+ --accept_license --approve_certificate
+ run_once: true
+ register: powerflex_mdm_add_primary_output
+ delegate_to: "{{ powerflex_mdm_primary_hostname }}"
+ until: ("{{ powerflex_mdm_add_primary_output.rc }} == 0") or ("{{ powerflex_mdm_add_primary_output.rc }} == 7")
+ ignore_errors: true
+ when:
+ - powerflex_mdm_virtual_ip is defined
+ - powerflex_mdm_virtual_ip | length > 0
+ changed_when: powerflex_mdm_add_primary_output.rc == 0
+
+- name: Add primary MDM without virtual ip
+ ansible.builtin.command: >
+ scli --create_mdm_cluster
+ --master_mdm_ip {{ powerflex_mdm_primary_ip }}
+ --master_mdm_management_ip {{ powerflex_mdm_primary_ip }}
+ --master_mdm_name {{ powerflex_mdm_primary_hostname }}
+ --master_mdm_virtual_ip_interface {{ ansible_default_ipv4.interface }}
+ --accept_license --approve_certificate
+ run_once: true
+ delegate_to: "{{ powerflex_mdm_primary_hostname }}"
+ until: ("{{ powerflex_mdm_add_primary_output.rc }} == 0") or ("{{ powerflex_mdm_add_primary_output.rc }} == 7")
+ register: powerflex_mdm_add_primary_output
+ ignore_errors: true
+ when:
+ - powerflex_mdm_virtual_ip | length == 0
+ changed_when: powerflex_mdm_add_primary_output.rc == 0
+
+- name: Wait for MDM to be active
+ ansible.builtin.wait_for:
+ port: 6611
+ state: started
+ run_once: true
+ delegate_to: "{{ powerflex_mdm_primary_hostname }}"
+
+- name: Initial login to primary MDM
+ ansible.builtin.command: scli --login --username admin --password admin
+ run_once: true
+ delegate_to: "{{ powerflex_mdm_primary_hostname }}"
+ ignore_errors: true
+ register: powerflex_mdm_initial_login
+ changed_when: powerflex_mdm_initial_login.rc == 0
+
+- name: Login with new password primary MDM
+ ansible.builtin.command: >
+ scli --login --username admin --password "{{ powerflex_mdm_password }}"
+ run_once: true
+ delegate_to: "{{ powerflex_mdm_primary_hostname }}"
+ when: powerflex_mdm_initial_login.rc == 7
+ changed_when: powerflex_mdm_initial_login.rc == 0
+
+- name: Set password for MDM cluster
+ ansible.builtin.command: >
+ scli --set_password --old_password admin
+ --new_password "{{ powerflex_mdm_password }}"
+ run_once: true
+ delegate_to: "{{ powerflex_mdm_primary_hostname }}"
+ when: powerflex_mdm_initial_login.rc == 0
+ changed_when: powerflex_mdm_initial_login.rc == 0
+
+- name: Secondary node login
+ ansible.builtin.command: >
+ scli --login --mdm_ip {{ powerflex_mdm_primary_ip }}
+ --username admin --password {{ powerflex_mdm_password }} --approve_certificate
+ run_once: true
+ register: powerflex_mdm_secondary_login
+ changed_when: powerflex_mdm_secondary_login.rc == 0
+ delegate_to: "{{ powerflex_mdm_primary_hostname }}"
+
+- name: Add secondary MDM
+ ansible.builtin.command: >
+ scli --add_standby_mdm
+ --new_mdm_ip {{ powerflex_mdm_secondary_ip }}
+ --mdm_role manager
+ --new_mdm_name {{ powerflex_mdm_secondary_hostname }}
+ --new_mdm_management_ip {{ powerflex_mdm_secondary_ip }}
+ --new_mdm_virtual_ip_interface {{ ansible_default_ipv4.interface }}
+ --approve_certificate
+ run_once: true
+ register: powerflex_mdm_add_secondary_output
+ delegate_to: "{{ powerflex_mdm_primary_hostname }}"
+ ignore_errors: true
+ changed_when: powerflex_mdm_add_secondary_output.rc == 0
+
+- name: Tertiary node login
+ ansible.builtin.command: >
+ scli --login --mdm_ip {{ powerflex_mdm_primary_ip }} --username admin
+ --password {{ powerflex_mdm_password }} --approve_certificate
+ run_once: true
+ delegate_to: "{{ powerflex_mdm_tertiary_hostname }}"
+ when: powerflex_mdm_tertiary_ip is defined
+ register: powerflex_mdm_tertiary_login
+ changed_when: powerflex_mdm_tertiary_login.rc == 0
+
+- name: Add tertiary MDM
+ ansible.builtin.command: >
+ scli --add_standby_mdm
+ --new_mdm_ip {{ powerflex_mdm_tertiary_ip }}
+ --mdm_role manager
+ --new_mdm_name {{ powerflex_mdm_tertiary_hostname }}
+ --new_mdm_management_ip {{ powerflex_mdm_tertiary_ip }}
+ --new_mdm_virtual_ip_interface {{ ansible_default_ipv4.interface }}
+ --approve_certificate
+ run_once: true
+ register: powerflex_mdm_add_tertiary_output
+ delegate_to: "{{ powerflex_mdm_primary_hostname }}"
+ ignore_errors: true
+ when: powerflex_mdm_tertiary_ip is defined
+ changed_when: powerflex_mdm_add_tertiary_output.rc == 0
diff --git a/ansible_collections/dellemc/powerflex/roles/powerflex_mdm/tasks/install_powerflex4x_mdm.yml b/ansible_collections/dellemc/powerflex/roles/powerflex_mdm/tasks/install_powerflex4x_mdm.yml
new file mode 100644
index 000000000..67164337d
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/roles/powerflex_mdm/tasks/install_powerflex4x_mdm.yml
@@ -0,0 +1,101 @@
+---
+- name: Install java
+ ansible.builtin.include_tasks: "../../powerflex_common/tasks/install_java_{{ ansible_distribution }}.yml"
+
+- name: Include install_powerflex.yml
+ ansible.builtin.include_tasks: ../../powerflex_common/tasks/install_powerflex.yml
+
+- name: Include the add_certs.yml
+ ansible.builtin.include_tasks: add_certs.yml
+
+- name: Add primary MDM with virtual ip
+ ansible.builtin.command: >
+ scli --create_mdm_cluster
+ --primary_mdm_ip {{ powerflex_mdm_primary_ip }}
+ --primary_mdm_management_ip {{ powerflex_mdm_primary_ip }}
+ --primary_mdm_name {{ powerflex_mdm_primary_hostname }}
+ --primary_mdm_virtual_ip_interface {{ ansible_default_ipv4.interface }}
+ --cluster_virtual_ip {{ powerflex_mdm_virtual_ip }}
+ --accept_license --approve_certificate
+ run_once: true
+ register: powerflex_mdm_add_primary_output
+ delegate_to: "{{ powerflex_mdm_primary_hostname }}"
+ until: ("{{ powerflex_mdm_add_primary_output.rc }} == 0") or ("{{ powerflex_mdm_add_primary_output.rc }} == 7")
+ ignore_errors: true
+ when:
+ - powerflex_mdm_virtual_ip is defined
+ - powerflex_mdm_virtual_ip | length > 0
+ changed_when: powerflex_mdm_add_primary_output.rc == 0
+
+- name: Add primary MDM without virtual ip
+ ansible.builtin.command: >
+ scli --create_mdm_cluster
+ --primary_mdm_ip {{ powerflex_mdm_primary_ip }}
+ --primary_mdm_management_ip {{ powerflex_mdm_primary_ip }}
+ --primary_mdm_name {{ powerflex_mdm_primary_hostname }}
+ --primary_mdm_virtual_ip_interface {{ ansible_default_ipv4.interface }}
+ --accept_license --approve_certificate
+ run_once: true
+ delegate_to: "{{ powerflex_mdm_primary_hostname }}"
+ until: ("{{ powerflex_mdm_add_primary_output.rc }} == 0") or ("{{ powerflex_mdm_add_primary_output.rc }} == 7")
+ register: powerflex_mdm_add_primary_output
+ ignore_errors: true
+ when:
+ - powerflex_mdm_virtual_ip | length == 0
+ changed_when: powerflex_mdm_add_primary_output.rc == 0
+
+- name: Wait for MDM to be active
+ ansible.builtin.wait_for:
+ port: 8611
+ state: started
+ run_once: true
+ delegate_to: "{{ powerflex_mdm_primary_hostname }}"
+
+- name: Login to primary MDM node
+ register: powerflex_mdm_secondary_login
+ ansible.builtin.command: >
+ scli --login --p12_path /opt/emc/scaleio/mdm/cfg/cli_certificate.p12 --p12_password {{ powerflex_mdm_cert_password }}
+ delegate_to: "{{ powerflex_mdm_primary_hostname }}"
+ run_once: true
+ when: powerflex_mdm_secondary_ip is defined
+ changed_when: powerflex_mdm_secondary_login.rc == 0
+
+- name: Add secondary MDM
+ ansible.builtin.command: >
+ scli --add_standby_mdm
+ --new_mdm_ip {{ powerflex_mdm_secondary_ip }}
+ --mdm_role manager
+ --new_mdm_name {{ powerflex_mdm_secondary_hostname }}
+ --new_mdm_management_ip {{ powerflex_mdm_secondary_ip }}
+ --new_mdm_virtual_ip_interface {{ ansible_default_ipv4.interface }}
+ --approve_certificate
+ run_once: true
+ register: powerflex_mdm_add_secondary_output
+ delegate_to: "{{ powerflex_mdm_primary_hostname }}"
+ ignore_errors: true
+ changed_when: powerflex_mdm_add_secondary_output.rc == 0
+
+- name: Login to Primary MDM node
+ register: powerflex_mdm_tertiary_login
+ ansible.builtin.command: >
+ scli --login --p12_path /opt/emc/scaleio/mdm/cfg/cli_certificate.p12 --p12_password {{ powerflex_mdm_cert_password }}
+ delegate_to: "{{ powerflex_mdm_primary_hostname }}"
+ run_once: true
+ when: powerflex_mdm_tertiary_ip is defined
+ changed_when: powerflex_mdm_tertiary_login.rc == 0
+
+- name: Add tertiary MDM
+ ansible.builtin.command: >
+ scli --add_standby_mdm
+ --new_mdm_ip {{ powerflex_mdm_tertiary_ip }}
+ --mdm_role manager
+ --new_mdm_name {{ powerflex_mdm_tertiary_hostname }}
+ --new_mdm_management_ip {{ powerflex_mdm_tertiary_ip }}
+ --new_mdm_virtual_ip_interface {{ ansible_default_ipv4.interface }}
+ --approve_certificate
+ run_once: true
+ register: powerflex_mdm_add_tertiary_output
+ delegate_to: "{{ powerflex_mdm_primary_hostname }}"
+ ignore_errors: true
+ when: powerflex_mdm_tertiary_ip is defined
+ changed_when: powerflex_mdm_add_tertiary_output.rc == 0
diff --git a/ansible_collections/dellemc/powerflex/roles/powerflex_mdm/tasks/main.yml b/ansible_collections/dellemc/powerflex/roles/powerflex_mdm/tasks/main.yml
new file mode 100644
index 000000000..a5e2703ac
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/roles/powerflex_mdm/tasks/main.yml
@@ -0,0 +1,8 @@
+---
+- name: Install and Configure MDM cluster
+ ansible.builtin.include_tasks: install_mdm.yml
+ when: powerflex_mdm_state == "present"
+
+- name: Uninstall MDM cluster
+ ansible.builtin.include_tasks: remove_mdm.yml
+ when: powerflex_mdm_state == "absent"
diff --git a/ansible_collections/dellemc/powerflex/roles/powerflex_mdm/tasks/mdm_set_facts.yml b/ansible_collections/dellemc/powerflex/roles/powerflex_mdm/tasks/mdm_set_facts.yml
new file mode 100644
index 000000000..c24b889eb
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/roles/powerflex_mdm/tasks/mdm_set_facts.yml
@@ -0,0 +1,40 @@
+---
+- name: Set facts for MDM counts
+ ansible.builtin.set_fact:
+ mdm_count: "{{ groups['mdm'] | length }}"
+
+- name: Set facts for powerflex_mdm_primary_ip
+ ansible.builtin.set_fact:
+ powerflex_mdm_primary_ip: "{{ hostvars[groups['mdm'][0]]['ansible_host'] }}"
+
+- name: Set facts for powerflex_mdm_primary_hostname
+ ansible.builtin.set_fact:
+ powerflex_mdm_primary_hostname: "{{ hostvars[groups['mdm'][0]]['inventory_hostname'] }}"
+
+- name: Set facts for powerflex_mdm_secondary_ip
+ ansible.builtin.set_fact:
+ powerflex_mdm_secondary_ip: "{{ hostvars[groups['mdm'][1]]['ansible_host'] }}"
+
+- name: Set facts for powerflex_mdm_secondary_hostname
+ ansible.builtin.set_fact:
+ powerflex_mdm_secondary_hostname: "{{ hostvars[groups['mdm'][1]]['inventory_hostname'] }}"
+
+- name: Set facts for powerflex_mdm_tertiary_ip
+ ansible.builtin.set_fact:
+ powerflex_mdm_tertiary_ip: "{{ hostvars[groups['mdm'][2]]['ansible_host'] }}"
+ when: mdm_count | int > 2
+
+- name: Set facts for powerflex_mdm_tertiary_hostname
+ ansible.builtin.set_fact:
+ powerflex_mdm_tertiary_hostname: "{{ hostvars[groups['mdm'][2]]['inventory_hostname'] }}"
+ when: mdm_count | int > 2
+
+- name: Set facts for powerflex_mdm_ips if mdm_count is 2
+ ansible.builtin.set_fact:
+ powerflex_mdm_ips: "{{ powerflex_mdm_secondary_ip }},{{ powerflex_mdm_primary_ip }}"
+ when: mdm_count | int == 2
+
+- name: Set facts for powerflex_mdm_ips if mdm_count is more than 2
+ ansible.builtin.set_fact:
+ powerflex_mdm_ips: "{{ powerflex_mdm_secondary_ip }},{{ powerflex_mdm_primary_ip }},{{ powerflex_mdm_tertiary_ip }}"
+ when: mdm_count | int > 2
diff --git a/ansible_collections/dellemc/powerflex/roles/powerflex_mdm/tasks/remove_mdm.yml b/ansible_collections/dellemc/powerflex/roles/powerflex_mdm/tasks/remove_mdm.yml
new file mode 100644
index 000000000..5c200c5e3
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/roles/powerflex_mdm/tasks/remove_mdm.yml
@@ -0,0 +1,58 @@
+---
+- name: Include the mdm_set_facts.yml
+ ansible.builtin.include_tasks: mdm_set_facts.yml
+
+- name: List the rpm file
+ register: powerflex_mdm_package_file_version
+ ansible.builtin.find:
+ paths: "/var/tmp/"
+ patterns: "*{{ file_glob_name }}*.rpm"
+
+- name: Extract file versions
+ ansible.builtin.set_fact:
+ version: "{{ powerflex_mdm_package_file_version.files[0].path | regex_search('mdm-(\\d+)', '\\1') }}"
+ when: powerflex_mdm_package_file_version.files | length > 0
+
+- name: MDM Cluster login below PowerFlex 4.x
+ ansible.builtin.command: >
+ scli --login --mdm_ip {{ powerflex_mdm_primary_ip }}
+ --username admin --password {{ powerflex_mdm_password }} --approve_certificate
+ run_once: true
+ ignore_errors: true
+ register: powerflex_mdm_cluster_login
+ changed_when: powerflex_mdm_cluster_login.rc == 0
+ delegate_to: "{{ powerflex_mdm_primary_hostname }}"
+ when: version[0] < "4"
+
+- name: Login to primary MDM node for PowerFlex 4.x
+ register: powerflex_mdm_primary_login
+ ansible.builtin.command: >
+ scli --login --username {{ username }} --management_system_ip {{ hostname }} --password "{{ password }}"
+ delegate_to: "{{ powerflex_mdm_primary_hostname }}"
+ run_once: true
+ ignore_errors: true
+ changed_when: powerflex_mdm_primary_login.rc == 0
+ when: version[0] >= "4"
+
+- name: Remove secondary MDM
+ ansible.builtin.command: >
+ scli --remove_standby_mdm --remove_mdm_ip {{ powerflex_mdm_secondary_ip }}
+ run_once: true
+ register: powerflex_mdm_remove_secondary
+ delegate_to: "{{ powerflex_mdm_primary_hostname }}"
+ ignore_errors: true
+ when: powerflex_mdm_secondary_ip is defined
+ changed_when: powerflex_mdm_remove_secondary.rc == 0
+
+- name: Remove tertiary MDM
+ ansible.builtin.command: >
+ scli --remove_standby_mdm --remove_mdm_ip {{ powerflex_mdm_tertiary_ip }}
+ run_once: true
+ register: powerflex_mdm_remove_tertiary
+ delegate_to: "{{ powerflex_mdm_primary_hostname }}"
+ ignore_errors: true
+ when: powerflex_mdm_tertiary_ip is defined
+ changed_when: powerflex_mdm_remove_tertiary.rc == 0
+
+- name: Include uninstall_mdm.yml
+ ansible.builtin.include_tasks: uninstall_mdm.yml
diff --git a/ansible_collections/dellemc/powerflex/roles/powerflex_mdm/tasks/uninstall_mdm.yml b/ansible_collections/dellemc/powerflex/roles/powerflex_mdm/tasks/uninstall_mdm.yml
new file mode 100644
index 000000000..a4302cb6d
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/roles/powerflex_mdm/tasks/uninstall_mdm.yml
@@ -0,0 +1,19 @@
+---
+- name: Uninstall MDM package
+ register: powerflex_mdm_uninstall_output
+ environment:
+ I_AM_SURE: "{{ i_am_sure | int }}"
+ ansible.builtin.package:
+ name: "{{ item }}"
+ state: "absent"
+ with_items:
+ - EMC-ScaleIO-mdm
+ when: ansible_distribution in ("RedHat", "CentOS", "SLES")
+
+- name: Uninstall deb package
+ ansible.builtin.apt:
+ name: "{{ item }}"
+ state: absent
+ with_items:
+ - emc-scaleio-mdm
+ when: ansible_distribution == "Ubuntu"
diff --git a/ansible_collections/dellemc/powerflex/roles/powerflex_mdm/vars/CentOS.yml b/ansible_collections/dellemc/powerflex/roles/powerflex_mdm/vars/CentOS.yml
new file mode 100644
index 000000000..1f3617cf0
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/roles/powerflex_mdm/vars/CentOS.yml
@@ -0,0 +1,5 @@
+---
+powerflex_mdm_packages:
+ - bash-completion
+ - python2
+ - binutils
diff --git a/ansible_collections/dellemc/powerflex/roles/powerflex_mdm/vars/RedHat.yml b/ansible_collections/dellemc/powerflex/roles/powerflex_mdm/vars/RedHat.yml
new file mode 100644
index 000000000..1f3617cf0
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/roles/powerflex_mdm/vars/RedHat.yml
@@ -0,0 +1,5 @@
+---
+powerflex_mdm_packages:
+ - bash-completion
+ - python2
+ - binutils
diff --git a/ansible_collections/dellemc/powerflex/roles/powerflex_mdm/vars/SLES.yml b/ansible_collections/dellemc/powerflex/roles/powerflex_mdm/vars/SLES.yml
new file mode 100644
index 000000000..f71b51d9a
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/roles/powerflex_mdm/vars/SLES.yml
@@ -0,0 +1,3 @@
+---
+powerflex_mdm_packages:
+ - python3
diff --git a/ansible_collections/dellemc/powerflex/roles/powerflex_mdm/vars/Ubuntu.yml b/ansible_collections/dellemc/powerflex/roles/powerflex_mdm/vars/Ubuntu.yml
new file mode 100644
index 000000000..3d64b947c
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/roles/powerflex_mdm/vars/Ubuntu.yml
@@ -0,0 +1,4 @@
+---
+powerflex_mdm_packages:
+ - bash-completion
+ - python2.7
diff --git a/ansible_collections/dellemc/powerflex/roles/powerflex_mdm/vars/main.yml b/ansible_collections/dellemc/powerflex/roles/powerflex_mdm/vars/main.yml
new file mode 100644
index 000000000..94678a3cf
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/roles/powerflex_mdm/vars/main.yml
@@ -0,0 +1,6 @@
+---
+file_glob_name: mdm
+file_gpg_name: RPM-GPG-KEY-ScaleIO
+powerflex_role_environment:
+ MDM_IP: "{{ powerflex_mdm_ips }}"
+ MDM_ROLE_IS_MANAGER: 1
diff --git a/ansible_collections/dellemc/powerflex/roles/powerflex_sdc/README.md b/ansible_collections/dellemc/powerflex/roles/powerflex_sdc/README.md
new file mode 100644
index 000000000..5006cb6d4
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/roles/powerflex_sdc/README.md
@@ -0,0 +1,311 @@
+# powerflex_sdc
+
+Role to manage the installation and uninstallation of Powerflex SDC.
+
+## Table of contents
+
+* [Requirements](#requirements)
+* [Ansible collections](#ansible-collections)
+* [Role Variables](#role-variables)
+* [Examples](#examples)
+* [Notes](#notes)
+* [Usage instructions](#usage-instructions)
+* [Author Information](#author-information)
+
+## Requirements
+
+```
+ansible
+python
+pywinrm==0.4.3
+```
+
+## Ansible collections
+
+Collections required to use the role.
+
+```
+dellemc.powerflex
+ansible.windows
+```
+
+## Role Variables
+
+<table>
+<thead>
+ <tr>
+ <th>Name</th>
+ <th>Required</th>
+ <th>Description</th>
+ <th>Choices</th>
+ <th>Type</th>
+ <th>Default Value</th>
+ </tr>
+</thead>
+<tbody>
+ <tr>
+ <td>hostname</td>
+ <td>true</td>
+ <td>IP or FQDN of the PowerFlex gateway</td>
+ <td></td>
+ <td>str</td>
+ <td>10.1.1.1</td>
+ </tr>
+ <tr>
+ <td>username</td>
+ <td>true</td>
+ <td>The username of the PowerFlex gateway</td>
+ <td></td>
+ <td>str</td>
+ <td>admin</td>
+ </tr>
+ <tr>
+ <td>password</td>
+ <td>true</td>
+ <td>The password of the PowerFlex gateway</td>
+ <td></td>
+ <td>str</td>
+ <td>password</td>
+ </tr>
+ <tr>
+ <td>port</td>
+ <td>false</td>
+ <td>Port</td>
+ <td></td>
+ <td>int</td>
+ <td>443</td>
+ </tr>
+ <tr>
+ <td>validate_certs</td>
+ <td>false</td>
+ <td>If C(false), the SSL certificates will not be validated.<br>Configure C(false) only on personally controlled sites where self-signed certificates are used</td>
+ <td></td>
+ <td>bool</td>
+ <td>false</td>
+ </tr>
+ <tr>
+ <td>timeout</td>
+ <td>false</td>
+ <td>Timeout</td>
+ <td></td>
+ <td>int</td>
+ <td>120</td>
+ </tr>
+ <tr>
+ <td>powerflex_common_file_install_location</td>
+ <td>true</td>
+ <td>Location of installation and rpm gpg files to be installed.
+ <br>The required, compatible installation software package based on the operating system of the node.
+ <br>The files can be downloaded from the Dell Product support page for PowerFlex software.</td>
+ <td></td>
+ <td>str</td>
+ <td>/var/tmp</td>
+ </tr>
+ <tr>
+ <td>powerflex_common_win_package_location</td>
+ <td>false</td>
+ <td>Location of SDC windows package on the node.
+ <br>SDC windows package will be copied to this location on installation.</td>
+ <td></td>
+ <td>str</td>
+ <td>C:\\Windows\\Temp</td>
+ </tr>
+ <tr>
+ <td>powerflex_sdc_driver_sync_repo_address</td>
+ <td>false</td>
+ <td>Repository address for the kernel modules</td>
+ <td></td>
+ <td>str</td>
+ <td>ftp://ftp.emc.com/</td>
+ </tr>
+ <tr>
+ <td>powerflex_sdc_driver_sync_repo_user</td>
+ <td>false</td>
+ <td>Username for the repository</td>
+ <td></td>
+ <td>str</td>
+ <td>QNzgdxXix</td>
+ </tr>
+ <tr>
+ <td>powerflex_sdc_driver_sync_repo_password</td>
+ <td>false</td>
+ <td>Password for the repository</td>
+ <td></td>
+ <td>str</td>
+ <td>Aw3wFAwAq3</td>
+ </tr>
+ <tr>
+ <td>powerflex_sdc_driver_sync_repo_local_dir</td>
+ <td>false</td>
+ <td>Local cache of the repository</td>
+ <td></td>
+ <td>str</td>
+ <td>/bin/emc/scaleio/scini_sync/driver_cache/</td>
+ </tr>
+ <tr>
+ <td>powerflex_sdc_driver_sync_user_private_rsa_key_src</td>
+ <td>false</td>
+ <td>Private ssh RSA key source (if using sftp protocol)</td>
+ <td></td>
+ <td>str</td>
+ <td></td>
+ </tr>
+ <tr>
+ <td>powerflex_sdc_driver_sync_user_private_rsa_key_dest</td>
+ <td>false</td>
+ <td>Private ssh RSA key destination</td>
+ <td></td>
+ <td>str</td>
+ <td>/bin/emc/scaleio/scini_sync/scini_key</td>
+ </tr>
+ <tr>
+ <td>powerflex_sdc_driver_sync_repo_public_rsa_key_src</td>
+ <td>false</td>
+ <td>Public ssh USA key source (if using sftp protocol)</td>
+ <td></td>
+ <td>str</td>
+ <td></td>
+ </tr>
+ <tr>
+ <td>powerflex_sdc_driver_sync_repo_public_rsa_key_dest</td>
+ <td>false</td>
+ <td>Private ssh RSA key destination</td>
+ <td></td>
+ <td>str</td>
+ <td>/bin/emc/scaleio/scini_sync/scini_repo_key.pub</td>
+ </tr>
+ <tr>
+ <td>powerflex_sdc_driver_sync_module_sigcheck</td>
+ <td>false</td>
+ <td>If signature check is required</td>
+ <td></td>
+ <td>str</td>
+ <td>1</td>
+ </tr>
+ <tr>
+ <td>powerflex_sdc_driver_sync_emc_public_gpg_key_src</td>
+ <td>false</td>
+ <td>Location of the signature file</td>
+ <td></td>
+ <td>str</td>
+ <td>{{ powerflex_common_file_install_location }}/files/RPM-GPG-KEY-ScaleIO_2.0.*.0</td>
+ </tr>
+ <tr>
+ <td>powerflex_sdc_driver_sync_emc_public_gpg_key_dest</td>
+ <td>false</td>
+ <td>Destination of the signature file</td>
+ <td></td>
+ <td>str</td>
+ <td>/bin/emc/scaleio/scini_sync/emc_key.pub</td>
+ </tr>
+ <tr>
+ <td>powerflex_sdc_driver_sync_sync_pattern</td>
+ <td>false</td>
+ <td>Repo sync pattern</td>
+ <td></td>
+ <td>str</td>
+ <td>.*</td>
+ </tr>
+ <tr>
+ <td>powerflex_sdc_name</td>
+ <td>false</td>
+ <td>Name of SDC to rename to<br></td>
+ <td></td>
+ <td>str</td>
+ <td>sdc_test</td>
+ </tr>
+ <tr>
+ <td>powerflex_sdc_performance_profile</td>
+ <td>false</td>
+ <td>Performance profile of SDC<br></td>
+ <td></td>
+ <td>str</td>
+ <td>Compact</td>
+ </tr>
+ <tr>
+ <td>powerflex_sdc_esxi_guid</td>
+ <td>false</td>
+ <td>Specifies the unique GUID for the ESXi SDC node.
+ <br>It is required only with ESXi node.
+ <br>To configure ESXi node as SDC, generate one GUID per server.
+ <br>Tools that are freely available online can generate these strings.
+ <br>If the value is different, then update in main.yml of defaults.</td>
+ <td></td>
+ <td>str</td>
+ <td>d422ecab-af6f-4e0c-a059-333ac89cfb42</td>
+ </tr>
+ <tr>
+ <td>powerflex_sdc_state</td>
+ <td>false</td>
+ <td>Specify state of SDC<br></td>
+ <td>absent, present</td>
+ <td>str</td>
+ <td>present</td>
+ </tr>
+</tbody>
+</table>
+
+## Examples
+----
+```
+ - name: Install and configure powerflex SDC
+ ansible.builtin.import_role:
+ name: powerflex_sdc
+ vars:
+ hostname: "{{ hostname }}"
+ username: "{{ username }}"
+ password: "{{ password }}"
+ validate_certs: "{{ validate_certs }}"
+ port: "{{ port }}"
+ powerflex_common_file_install_location: "/opt/scaleio/rpm"
+ powerflex_sdc_name: sdc_test
+ powerflex_sdc_performance_profile: Compact
+ powerflex_sdc_state: present
+
+ - name: Uninstall powerflex SDC
+ ansible.builtin.import_role:
+ name: powerflex_sdc
+ vars:
+ hostname: "{{ hostname }}"
+ username: "{{ username }}"
+ password: "{{ password }}"
+ validate_certs: "{{ validate_certs }}"
+ port: "{{ port }}"
+ powerflex_sdc_state: absent
+
+```
+
+## Notes
+- Generate GUID using https://www.guidgenerator.com/online-guid-generator.aspx. Use the default GUID settings.
+- While adding ESXi server as SDC, this procedure requires two server reboots.
+
+## Usage instructions
+----
+### To install all dependency packages, including SDC, on node:
+- PowerFlex 3.6:
+ ```
+ ansible-playbook -i inventory site.yml
+ ```
+- PowerFlex 4.5:
+ ```
+ ansible-playbook -i inventory site_powerflex45.yml
+ ```
+
+### To uninstall SDC:
+- PowerFlex 3.6:
+ ```
+ ansible-playbook -i inventory uninstall_powerflex.yml
+ ```
+- PowerFlex 4.5:
+ ```
+ ansible-playbook -i inventory uninstall_powerflex45.yml
+ ```
+
+Sample playbooks and inventory can be found in the playbooks directory.
+
+## Author Information
+------------------
+
+Dell Technologies <br>
+Jennifer John (ansible.team@Dell.com) 2023 <br>
+Bhavneet Sharma (ansible.team@Dell.com) 2023
diff --git a/ansible_collections/dellemc/powerflex/roles/powerflex_sdc/defaults/main.yml b/ansible_collections/dellemc/powerflex/roles/powerflex_sdc/defaults/main.yml
new file mode 100644
index 000000000..5801c0ced
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/roles/powerflex_sdc/defaults/main.yml
@@ -0,0 +1,21 @@
+---
+powerflex_common_win_package_location: "C:\\Windows\\Temp"
+powerflex_sdc_driver_sync_repo_address: 'ftp://ftp.emc.com/'
+powerflex_sdc_driver_sync_repo_user: 'QNzgdxXix'
+powerflex_sdc_driver_sync_repo_password: 'Aw3wFAwAq3'
+powerflex_sdc_driver_sync_repo_local_dir: '/bin/emc/scaleio/scini_sync/driver_cache/'
+powerflex_sdc_driver_sync_user_private_rsa_key_src: ''
+powerflex_sdc_driver_sync_user_private_rsa_key_dest: '/bin/emc/scaleio/scini_sync/scini_key'
+powerflex_sdc_driver_sync_repo_public_rsa_key_src: ''
+powerflex_sdc_driver_sync_repo_public_rsa_key_dest: '/bin/emc/scaleio/scini_sync/scini_repo_key.pub'
+powerflex_sdc_driver_sync_module_sigcheck: 1
+powerflex_sdc_driver_sync_emc_public_gpg_key_src: ../../../files/RPM-GPG-KEY-powerflex_2.0.*.0
+powerflex_sdc_driver_sync_emc_public_gpg_key_dest: '/bin/emc/scaleio/scini_sync/emc_key.pub'
+powerflex_sdc_driver_sync_sync_pattern: .*
+powerflex_sdc_state: present
+powerflex_sdc_name: sdc_test
+powerflex_sdc_performance_profile: Compact
+file_glob_name: sdc
+i_am_sure: 1
+powerflex_sdc_esxi_guid: "d422ecab-af6f-4e0c-a059-333ac89cfb42"
+powerflex_role_environment:
diff --git a/ansible_collections/dellemc/powerflex/roles/powerflex_sdc/handlers/main.yml b/ansible_collections/dellemc/powerflex/roles/powerflex_sdc/handlers/main.yml
new file mode 100644
index 000000000..ecdcc3384
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/roles/powerflex_sdc/handlers/main.yml
@@ -0,0 +1,5 @@
+---
+- name: Restart scini
+ ansible.builtin.service:
+ name: "scini"
+ state: "restarted"
diff --git a/ansible_collections/dellemc/powerflex/roles/powerflex_sdc/meta/main.yml b/ansible_collections/dellemc/powerflex/roles/powerflex_sdc/meta/main.yml
new file mode 100644
index 000000000..2535678e4
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/roles/powerflex_sdc/meta/main.yml
@@ -0,0 +1,25 @@
+---
+galaxy_info:
+ author: Jennifer John
+ description: The role helps to manage the installation of SDC.
+ company: Dell Technologies
+ role_name: powerflex_sdc
+ namespace: dellemc
+
+ license: GPL-3.0-only
+
+ min_ansible_version: "2.14.0"
+ platforms:
+ - name: EL
+ versions:
+ - "9"
+ - "8"
+ - name: Ubuntu
+ versions:
+ - jammy
+ - name: SLES
+ versions:
+ - "15SP3"
+ - "15SP4"
+
+ galaxy_tags: []
diff --git a/ansible_collections/dellemc/powerflex/roles/powerflex_sdc/molecule/sdc_install_map_volume_uninstall_negative/converge.yml b/ansible_collections/dellemc/powerflex/roles/powerflex_sdc/molecule/sdc_install_map_volume_uninstall_negative/converge.yml
new file mode 100644
index 000000000..8f5049642
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/roles/powerflex_sdc/molecule/sdc_install_map_volume_uninstall_negative/converge.yml
@@ -0,0 +1,118 @@
+---
+- name: Volume creation
+ hosts: localhost
+ vars_files:
+ - ../var_values.yml
+ - ../../../../playbooks/roles/vars_files/connection.yml
+ tasks:
+ - name: Create a volume
+ dellemc.powerflex.volume:
+ hostname: "{{ hostname }}"
+ username: "{{ username }}"
+ password: "{{ password }}"
+ validate_certs: "{{ validate_certs }}"
+ port: "{{ port }}"
+ vol_name: "{{ vol_name }}"
+ storage_pool_name: "{{ storage_pool_name }}"
+ protection_domain_name: "{{ protection_domain_name }}"
+ size: "{{ vol_size }}"
+ state: "present"
+ register: volume_output
+
+- name: Install SDC
+ hosts: sdc
+ vars_files:
+ - ../var_values.yml
+ - ../../../../playbooks/roles/vars_files/connection.yml
+ tasks:
+ - name: "Install and configure powerflex SDC"
+ ansible.builtin.import_role:
+ name: "powerflex_sdc"
+ vars:
+ powerflex_sdc_name: "{{ sdc_name }}"
+ powerflex_sdc_state: present
+ register: powerflex_sdc_result_molecule
+
+- name: Mapping SDC to volume
+ hosts: localhost
+ vars_files:
+ - ../var_values.yml
+ - ../../../../playbooks/roles/vars_files/connection.yml
+ gather_facts: true
+ tasks:
+ - name: Map a SDC to volume
+ dellemc.powerflex.volume:
+ hostname: "{{ hostname }}"
+ username: "{{ username }}"
+ password: "{{ password }}"
+ validate_certs: "{{ validate_certs }}"
+ port: "{{ port }}"
+ vol_name: "{{ vol_name }}"
+ allow_multiple_mappings: true
+ sdc:
+ - sdc_name: "{{ sdc_name }}"
+ access_mode: "{{ access_mode }}"
+ sdc_state: "mapped"
+ state: "present"
+
+- name: Uninstall SDC
+ hosts: sdc
+ vars_files:
+ - ../var_values.yml
+ - ../../../../playbooks/roles/vars_files/connection.yml
+ gather_facts: true
+ tasks:
+ - name: "Uninstall powerflex SDC"
+ register: powerflex_sdc_uninstall_outputs
+ ansible.builtin.import_role:
+ name: "powerflex_sdc"
+ ignore_errors: true
+ vars:
+ powerflex_sdc_name: "{{ sdc_name }}"
+ powerflex_sdc_state: 'absent'
+
+ - name: "Verifying failure of sdc removal"
+ ansible.builtin.assert:
+ that:
+ - "'SDC has mapped volume(s)' in powerflex_sdc_remove_output.msg"
+
+- name: Unmapping and Removing SDC
+ hosts: localhost
+ vars_files:
+ - ../var_values.yml
+ - ../../../../playbooks/roles/vars_files/connection.yml
+ gather_facts: true
+ tasks:
+ - name: Unmap a SDC to volume
+ dellemc.powerflex.volume:
+ hostname: "{{ hostname }}"
+ username: "{{ username }}"
+ password: "{{ password }}"
+ validate_certs: "{{ validate_certs }}"
+ port: "{{ port }}"
+ vol_name: "{{ vol_name }}"
+ sdc:
+ - sdc_name: "{{ sdc_name }}"
+ sdc_state: "unmapped"
+ state: "present"
+
+ - name: Delete the Volume
+ dellemc.powerflex.volume:
+ hostname: "{{ hostname }}"
+ username: "{{ username }}"
+ password: "{{ password }}"
+ validate_certs: "{{ validate_certs }}"
+ port: "{{ port }}"
+ vol_name: "{{ vol_name }}"
+ delete_snapshots: false
+ state: "absent"
+
+ - name: Remove the SDC
+ dellemc.powerflex.sdc:
+ hostname: "{{ hostname }}"
+ username: "{{ username }}"
+ password: "{{ password }}"
+ validate_certs: "{{ validate_certs }}"
+ port: "{{ port }}"
+ sdc_name: "{{ sdc_name }}"
+ state: "absent"
diff --git a/ansible_collections/dellemc/powerflex/roles/powerflex_sdc/molecule/sdc_install_map_volume_uninstall_negative/molecule.yml b/ansible_collections/dellemc/powerflex/roles/powerflex_sdc/molecule/sdc_install_map_volume_uninstall_negative/molecule.yml
new file mode 100644
index 000000000..93cad84c9
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/roles/powerflex_sdc/molecule/sdc_install_map_volume_uninstall_negative/molecule.yml
@@ -0,0 +1,4 @@
+---
+scenario:
+ test_sequence:
+ - converge
diff --git a/ansible_collections/dellemc/powerflex/roles/powerflex_sdc/molecule/sdc_installation/converge.yml b/ansible_collections/dellemc/powerflex/roles/powerflex_sdc/molecule/sdc_installation/converge.yml
new file mode 100644
index 000000000..4687e5ffc
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/roles/powerflex_sdc/molecule/sdc_installation/converge.yml
@@ -0,0 +1,62 @@
+---
+- name: Converge
+ hosts: sdc
+ vars_files:
+ - ../../../../playbooks/roles/vars_files/connection.yml
+ gather_facts: true
+ tasks:
+ - name: "Install and configure powerflex SDC"
+ ansible.builtin.import_role:
+ name: "powerflex_sdc"
+ vars:
+ powerflex_sdc_state: present
+ register: powerflex_sdc_result_molecule
+
+ - name: "Verifying installation package"
+ ansible.builtin.assert:
+ that:
+ - " 'Installed' in powerflex_common_install_package_output.results[0]"
+ when:
+ - not ansible_check_mode and powerflex_common_install_package_output.changed
+ - " 'WindowsOS' not in ansible_distribution "
+ - " 'VMkernel' not in ansible_distribution "
+
+ - name: "Verifying installation package on windows"
+ ansible.builtin.assert:
+ that:
+ - powerflex_common_install_package_output.rc == 0
+ when:
+ - " 'WindowsOS' in ansible_distribution "
+ - not ansible_check_mode and powerflex_common_install_package_output.changed
+
+ - name: "Verifying installation package on ESXi"
+ ansible.builtin.assert:
+ that:
+ - powerflex_common_install_package_output.rc == 0
+ when:
+ - " 'VMkernel' in ansible_distribution "
+ - not ansible_check_mode and powerflex_common_install_package_output.changed
+
+ - name: "Verifying performance profile value"
+ ansible.builtin.assert:
+ that:
+ - powerflex_sdc_performance_profile_output.sdc_details.perfProfile == "{{ powerflex_sdc_performance_profile }}"
+ when: not ansible_check_mode and powerflex_sdc_performance_profile_output.changed
+
+ - name: "Verifying rename value"
+ ansible.builtin.assert:
+ that:
+ - powerflex_sdc_rename_output.sdc_details.name == "{{ powerflex_sdc_name }}"
+ when: not ansible_check_mode and powerflex_sdc_performance_profile_output.changed
+
+ - name: "Verifying performance profile value in Idempotency"
+ ansible.builtin.assert:
+ that:
+ - powerflex_sdc_performance_profile_output.sdc_details.perfProfile == "{{ powerflex_sdc_performance_profile }}"
+ when: not ansible_check_mode and not powerflex_sdc_performance_profile_output.changed
+
+ - name: "Verifying rename value in Idempotency"
+ ansible.builtin.assert:
+ that:
+ - powerflex_sdc_rename_output.sdc_details.name == '{{ powerflex_sdc_name }}'
+ when: not ansible_check_mode and not powerflex_sdc_performance_profile_output.changed
diff --git a/ansible_collections/dellemc/powerflex/roles/powerflex_sdc/molecule/sdc_installation/molecule.yml b/ansible_collections/dellemc/powerflex/roles/powerflex_sdc/molecule/sdc_installation/molecule.yml
new file mode 100644
index 000000000..ed97d539c
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/roles/powerflex_sdc/molecule/sdc_installation/molecule.yml
@@ -0,0 +1 @@
+---
diff --git a/ansible_collections/dellemc/powerflex/roles/powerflex_sdc/molecule/sdc_installation_invalid_path_rpm/converge.yml b/ansible_collections/dellemc/powerflex/roles/powerflex_sdc/molecule/sdc_installation_invalid_path_rpm/converge.yml
new file mode 100644
index 000000000..bb06afe18
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/roles/powerflex_sdc/molecule/sdc_installation_invalid_path_rpm/converge.yml
@@ -0,0 +1,54 @@
+---
+- name: Converge
+ hosts: sdc
+ vars_files:
+ - ../../../../playbooks/roles/vars_files/connection.yml
+ gather_facts: true
+ tasks:
+ - name: "Install and configure powerflex SDC with no rpm"
+ ansible.builtin.import_role:
+ name: "powerflex_sdc"
+ vars:
+ powerflex_common_file_install_location: "/opt/empty"
+ powerflex_sdc_state: present
+ register: powerflex_sdc_result_molecule
+ ignore_errors: true
+
+ - name: "Verifying failure of install package with respect to no rpm file"
+ ansible.builtin.assert:
+ that:
+ - powerflex_common_package_file.files | length == 0
+
+ - name: "Install and configure powerflex SDC with wrong file path"
+ ansible.builtin.import_role:
+ name: "powerflex_sdc"
+ vars:
+ powerflex_common_file_install_location: "/opt/aaab"
+ powerflex_sdc_state: present
+ register: powerflex_sdc_result_molecule
+ ignore_errors: true
+
+ - name: "Verifying failure of install package with wrong file path"
+ ansible.builtin.assert:
+ that:
+ - powerflex_common_package_file.files | length == 0
+
+ - name: "Install and configure powerflex SDC with wrong rpm version"
+ ansible.builtin.import_role:
+ name: "powerflex_sdc"
+ vars:
+ powerflex_common_file_install_location: "/opt/wrong_rpm"
+ powerflex_sdc_state: present
+ ignore_errors: true
+ register: powerflex_sdc_wrong_rpm_version
+
+ - name: "Verifying failure of install package with wrong rpm version"
+ ansible.builtin.assert:
+ that:
+ - " 'Depsolve Error occurred: ' in powerflex_common_install_package_output.msg"
+
+ - name: "Uninstall powerflex SDC"
+ ansible.builtin.import_role:
+ name: "powerflex_sdc"
+ vars:
+ powerflex_sdc_state: absent
diff --git a/ansible_collections/dellemc/powerflex/roles/powerflex_sdc/molecule/sdc_installation_invalid_path_rpm/molecule.yml b/ansible_collections/dellemc/powerflex/roles/powerflex_sdc/molecule/sdc_installation_invalid_path_rpm/molecule.yml
new file mode 100644
index 000000000..93cad84c9
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/roles/powerflex_sdc/molecule/sdc_installation_invalid_path_rpm/molecule.yml
@@ -0,0 +1,4 @@
+---
+scenario:
+ test_sequence:
+ - converge
diff --git a/ansible_collections/dellemc/powerflex/roles/powerflex_sdc/molecule/sdc_uninstallation/converge.yml b/ansible_collections/dellemc/powerflex/roles/powerflex_sdc/molecule/sdc_uninstallation/converge.yml
new file mode 100644
index 000000000..a11ce0ec4
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/roles/powerflex_sdc/molecule/sdc_uninstallation/converge.yml
@@ -0,0 +1,57 @@
+---
+- name: Converge
+ hosts: sdc
+ vars_files:
+ - ../../../../playbooks/roles/vars_files/connection.yml
+ gather_facts: true
+ tasks:
+ - name: "Uninstall powerflex SDC"
+ register: powerflex_sdc_uninstall_outputs
+ ansible.builtin.import_role:
+ name: "powerflex_sdc"
+ vars:
+ powerflex_sdc_state: 'absent'
+
+ - name: "Verifying uninstall package in check mode"
+ ansible.builtin.assert:
+ that:
+ - powerflex_sdc_uninstall_output.msg == "Check mode: No changes made, but would have if not in check mode"
+ - powerflex_sdc_uninstall_output.changed is true
+ when: ansible_check_mode
+
+ - name: "Verifying remove the sdc"
+ ansible.builtin.assert:
+ that:
+ - powerflex_sdc_remove_output.sdc_details is none
+ when: not ansible_check_mode and powerflex_sdc_remove_output.changed
+
+ - name: "Verifying uninstall package in Idempotency"
+ ansible.builtin.assert:
+ that:
+ - powerflex_sdc_uninstall_output.results[0].msg == 'Nothing to do'
+ when:
+ - not ansible_check_mode and not powerflex_sdc_uninstall_output.changed
+ - " 'WindowsOS' not in ansible_distribution "
+ - " 'VMkernel' not in ansible_distribution "
+
+ - name: "Verifying uninstall package in Idempotency for Windows node"
+ ansible.builtin.assert:
+ that:
+ - powerflex_sdc_uninstall_output.msg == 'All items skipped'
+ when:
+ - not ansible_check_mode and not powerflex_sdc_uninstall_output.changed
+ - " 'WindowsOS' in ansible_distribution "
+
+ - name: "Verifying uninstall package in Idempotency for ESXi node"
+ ansible.builtin.assert:
+ that:
+ - powerflex_sdc_uninstall_output.msg == 'All items skipped'
+ when:
+ - not ansible_check_mode and not powerflex_sdc_uninstall_output.changed
+ - " 'VMkernel' in ansible_distribution "
+
+ - name: "Verifying remove the sdc in Idempotency"
+ ansible.builtin.assert:
+ that:
+ - powerflex_sdc_remove_output.sdc_details is none
+ when: not ansible_check_mode and not powerflex_sdc_remove_output.changed and powerflex_sdc_remove_output.sdc_details is defined
diff --git a/ansible_collections/dellemc/powerflex/roles/powerflex_sdc/molecule/sdc_uninstallation/molecule.yml b/ansible_collections/dellemc/powerflex/roles/powerflex_sdc/molecule/sdc_uninstallation/molecule.yml
new file mode 100644
index 000000000..ed97d539c
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/roles/powerflex_sdc/molecule/sdc_uninstallation/molecule.yml
@@ -0,0 +1 @@
+---
diff --git a/ansible_collections/dellemc/powerflex/roles/powerflex_sdc/molecule/var_values.yml b/ansible_collections/dellemc/powerflex/roles/powerflex_sdc/molecule/var_values.yml
new file mode 100644
index 000000000..91e203e08
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/roles/powerflex_sdc/molecule/var_values.yml
@@ -0,0 +1,7 @@
+---
+vol_name: "sdc_test_vol_1"
+storage_pool_name: "pool1"
+protection_domain_name: "domain1"
+vol_size: 8
+sdc_name: sdc_test_demo_1
+access_mode: "READ_WRITE"
diff --git a/ansible_collections/dellemc/powerflex/roles/powerflex_sdc/molecule/wrong_sdc_credentials/converge.yml b/ansible_collections/dellemc/powerflex/roles/powerflex_sdc/molecule/wrong_sdc_credentials/converge.yml
new file mode 100644
index 000000000..86583fd34
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/roles/powerflex_sdc/molecule/wrong_sdc_credentials/converge.yml
@@ -0,0 +1,20 @@
+---
+- name: Converge
+ hosts: sdc
+ vars_files:
+ - ../../../../playbooks/roles/vars_files/connection.yml
+ gather_facts: false
+ tasks:
+ - name: "Install and configure powerflex SDC with wrong credentials"
+ ansible.builtin.import_role:
+ name: "powerflex_sdc"
+ vars:
+ powerflex_sdc_state: present
+ ignore_unreachable: true
+ ignore_errors: true
+ register: powerflex_sdc_wrong_credentials_output
+
+ - name: "Verifying failure of install package with wrong credentials"
+ ansible.builtin.assert:
+ that:
+ - " 'failed. The error was: error while evaluating conditional' in powerflex_sdc_driver_sync_output.msg"
diff --git a/ansible_collections/dellemc/powerflex/roles/powerflex_sdc/molecule/wrong_sdc_credentials/inventory b/ansible_collections/dellemc/powerflex/roles/powerflex_sdc/molecule/wrong_sdc_credentials/inventory
new file mode 100644
index 000000000..1974d7ad4
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/roles/powerflex_sdc/molecule/wrong_sdc_credentials/inventory
@@ -0,0 +1,4 @@
+node0 ansible_host=10.2.2.2 ansible_port=22 ansible_ssh_pass=wrongpassword ansible_user=root
+
+[sdc]
+node0
diff --git a/ansible_collections/dellemc/powerflex/roles/powerflex_sdc/molecule/wrong_sdc_credentials/molecule.yml b/ansible_collections/dellemc/powerflex/roles/powerflex_sdc/molecule/wrong_sdc_credentials/molecule.yml
new file mode 100644
index 000000000..805f92879
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/roles/powerflex_sdc/molecule/wrong_sdc_credentials/molecule.yml
@@ -0,0 +1,11 @@
+---
+provisioner:
+ name: ansible
+ inventory:
+ links:
+ hosts: inventory
+ group_vars: ../../../../playbooks/roles/group_vars/
+ host_vars: ../../../../playbooks/roles/host_vars/
+scenario:
+ test_sequence:
+ - converge
diff --git a/ansible_collections/dellemc/powerflex/roles/powerflex_sdc/molecule/wrong_sdc_ip/converge.yml b/ansible_collections/dellemc/powerflex/roles/powerflex_sdc/molecule/wrong_sdc_ip/converge.yml
new file mode 100644
index 000000000..1ebda8c05
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/roles/powerflex_sdc/molecule/wrong_sdc_ip/converge.yml
@@ -0,0 +1,20 @@
+---
+- name: Converge
+ hosts: sdc
+ vars_files:
+ - ../../../../playbooks/roles/vars_files/connection.yml
+ gather_facts: false
+ tasks:
+ - name: "Install and configure powerflex SDC with wrong SDC IP"
+ ansible.builtin.import_role:
+ name: "powerflex_sdc"
+ vars:
+ powerflex_sdc_state: present
+ ignore_unreachable: true
+ ignore_errors: true
+ register: powerflex_sdc_wrong_ip_output
+
+ - name: "Verifying failure of install package with wrong SDC IP"
+ ansible.builtin.assert:
+ that:
+ " 'failed. The error was: error while evaluating conditional' in powerflex_sdc_driver_sync_output.msg"
diff --git a/ansible_collections/dellemc/powerflex/roles/powerflex_sdc/molecule/wrong_sdc_ip/inventory b/ansible_collections/dellemc/powerflex/roles/powerflex_sdc/molecule/wrong_sdc_ip/inventory
new file mode 100644
index 000000000..845add1d5
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/roles/powerflex_sdc/molecule/wrong_sdc_ip/inventory
@@ -0,0 +1,4 @@
+node0 ansible_host=10.2.2.0 ansible_port=22 ansible_ssh_pass=password ansible_user=root
+
+[sdc]
+node0
diff --git a/ansible_collections/dellemc/powerflex/roles/powerflex_sdc/molecule/wrong_sdc_ip/molecule.yml b/ansible_collections/dellemc/powerflex/roles/powerflex_sdc/molecule/wrong_sdc_ip/molecule.yml
new file mode 100644
index 000000000..805f92879
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/roles/powerflex_sdc/molecule/wrong_sdc_ip/molecule.yml
@@ -0,0 +1,11 @@
+---
+provisioner:
+ name: ansible
+ inventory:
+ links:
+ hosts: inventory
+ group_vars: ../../../../playbooks/roles/group_vars/
+ host_vars: ../../../../playbooks/roles/host_vars/
+scenario:
+ test_sequence:
+ - converge
diff --git a/ansible_collections/dellemc/powerflex/roles/powerflex_sdc/tasks/configure_sdc.yml b/ansible_collections/dellemc/powerflex/roles/powerflex_sdc/tasks/configure_sdc.yml
new file mode 100644
index 000000000..453fe43d5
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/roles/powerflex_sdc/tasks/configure_sdc.yml
@@ -0,0 +1,28 @@
+---
+- name: Rename the SDC
+ dellemc.powerflex.sdc:
+ hostname: "{{ hostname }}"
+ username: "{{ username }}"
+ password: "{{ password }}"
+ validate_certs: "{{ validate_certs }}"
+ sdc_ip: "{{ ansible_host }}"
+ sdc_new_name: "{{ powerflex_sdc_name }}"
+ state: "present"
+ register: powerflex_sdc_rename_output
+ delegate_to: "{{ lookup('ansible.builtin.env', 'RUNON', default='localhost') }}"
+ when: powerflex_sdc_name is defined and powerflex_sdc_name | length > 0
+
+- name: Set performance profile of SDC
+ dellemc.powerflex.sdc:
+ hostname: "{{ hostname }}"
+ username: "{{ username }}"
+ password: "{{ password }}"
+ validate_certs: "{{ validate_certs }}"
+ sdc_ip: "{{ ansible_host }}"
+ performance_profile: "{{ powerflex_sdc_performance_profile }}"
+ state: "present"
+ register: powerflex_sdc_performance_profile_output
+ delegate_to: "{{ lookup('ansible.builtin.env', 'RUNON', default='localhost') }}"
+ when:
+ - powerflex_sdc_performance_profile is defined
+ - powerflex_sdc_performance_profile | length > 0
diff --git a/ansible_collections/dellemc/powerflex/roles/powerflex_sdc/tasks/install_sdc.yml b/ansible_collections/dellemc/powerflex/roles/powerflex_sdc/tasks/install_sdc.yml
new file mode 100644
index 000000000..9b75321c3
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/roles/powerflex_sdc/tasks/install_sdc.yml
@@ -0,0 +1,75 @@
+---
+- name: Get configured MDM IP addresses
+ dellemc.powerflex.mdm_cluster:
+ hostname: "{{ hostname }}"
+ username: "{{ username }}"
+ password: "{{ password }}"
+ validate_certs: "{{ validate_certs }}"
+ state: "present"
+ register: powerflex_sdc_mdm_ip_result
+ delegate_to: "{{ lookup('ansible.builtin.env', 'RUNON', default='localhost') }}"
+
+- name: Set fact - powerflex_sdc_mdm_ips
+ ansible.builtin.set_fact:
+ powerflex_sdc_mdm_ips: "{{ powerflex_sdc_mdm_ip_result.mdm_cluster_details.mdmAddresses | join(',') }}"
+
+- name: Include install_powerflex.yml
+ ansible.builtin.include_tasks: ../../powerflex_common/tasks/install_powerflex.yml
+
+- name: Register SDC and Set MDM IP addresses by register_esxi_sdc.yml
+ ansible.builtin.include_tasks: register_esxi_sdc.yml
+ when: ansible_distribution == "VMkernel"
+
+- name: Include configure_sdc.yml
+ ansible.builtin.include_tasks: configure_sdc.yml
+
+- name: Copy powerflex_sdc_driver_sync_user_private_rsa_key_src for driver_sync.conf
+ ansible.builtin.copy:
+ src: "{{ powerflex_sdc_driver_sync_user_private_rsa_key_src }}"
+ dest: "{{ powerflex_sdc_driver_sync_user_private_rsa_key_dest }}"
+ mode: "0600"
+ owner: "root"
+ group: "root"
+ when:
+ - powerflex_sdc_driver_sync_user_private_rsa_key_src is defined
+ - powerflex_sdc_driver_sync_user_private_rsa_key_src | length > 0
+ - " 'WindowsOS' not in ansible_distribution"
+
+- name: Copy powerflex_sdc_driver_sync_repo_public_rsa_key_src for driver_sync.conf
+ ansible.builtin.copy:
+ src: "{{ powerflex_sdc_driver_sync_repo_public_rsa_key_src }}"
+ dest: "{{ powerflex_sdc_driver_sync_repo_public_rsa_key_dest }}"
+ mode: "0600"
+ owner: "root"
+ group: "root"
+ when:
+ - powerflex_sdc_driver_sync_repo_public_rsa_key_src is defined
+ - powerflex_sdc_driver_sync_repo_public_rsa_key_src | length > 0
+ - " 'WindowsOS' not in ansible_distribution"
+
+- name: Copy powerflex_sdc_driver_sync_emc_public_gpg_key_src for driver_sync.conf
+ ansible.builtin.copy:
+ src: "{{ item }}"
+ dest: "{{ powerflex_sdc_driver_sync_emc_public_gpg_key_dest }}"
+ mode: "0600"
+ owner: "root"
+ group: "root"
+ with_fileglob:
+ - "{{ powerflex_sdc_driver_sync_emc_public_gpg_key_src }}"
+ when:
+ - powerflex_sdc_driver_sync_emc_public_gpg_key_src is defined
+ - powerflex_sdc_driver_sync_emc_public_gpg_key_src | length > 0
+ - " 'WindowsOS' not in ansible_distribution"
+
+- name: Copy driver_sync.conf template in place
+ register: powerflex_sdc_driver_sync_output
+ ansible.builtin.template:
+ src: "driver_sync.conf.j2"
+ dest: "/bin/emc/scaleio/scini_sync/driver_sync.conf"
+ mode: "0600"
+ owner: "root"
+ group: "root"
+ notify: restart scini
+ when:
+ - ansible_distribution != "VMkernel"
+ - " 'WindowsOS' not in ansible_distribution"
diff --git a/ansible_collections/dellemc/powerflex/roles/powerflex_sdc/tasks/main.yml b/ansible_collections/dellemc/powerflex/roles/powerflex_sdc/tasks/main.yml
new file mode 100644
index 000000000..ec81fb21f
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/roles/powerflex_sdc/tasks/main.yml
@@ -0,0 +1,8 @@
+---
+- name: Install SDC
+ ansible.builtin.include_tasks: install_sdc.yml
+ when: powerflex_sdc_state == 'present'
+
+- name: Remove SDC
+ ansible.builtin.include_tasks: remove_sdc.yml
+ when: powerflex_sdc_state == 'absent'
diff --git a/ansible_collections/dellemc/powerflex/roles/powerflex_sdc/tasks/register_esxi_sdc.yml b/ansible_collections/dellemc/powerflex/roles/powerflex_sdc/tasks/register_esxi_sdc.yml
new file mode 100644
index 000000000..0b06fc8cd
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/roles/powerflex_sdc/tasks/register_esxi_sdc.yml
@@ -0,0 +1,47 @@
+---
+- name: Register SDC and Set MDM IP addresses
+ register: powerflex_sdc_register_set_sdc_mdm
+ ansible.builtin.shell: >
+ esxcli system module parameters set -m scini -p "IoctlIniGuidStr={{ powerflex_sdc_esxi_guid }}
+ IoctlMdmIPStr={{ powerflex_sdc_mdm_ips }} bBlkDevIsPdlActive=1 blkDevPdlTimeoutMillis=60000"
+ changed_when: powerflex_sdc_register_set_sdc_mdm.rc == 0
+
+- name: Reboot ESXi host
+ register: powerflex_sdc_reboot_node
+ ansible.builtin.reboot:
+ reboot_timeout: 500
+ msg: "Rebooting the ESXi host."
+ when:
+ - powerflex_sdc_register_set_sdc_mdm.rc == 0
+ - "'Reboot Required: true' in powerflex_common_install_package_output.stdout"
+ changed_when: powerflex_sdc_reboot_node
+
+- name: Ensure the driver is loaded for SDC
+ register: powerflex_sdc_driver_loaded
+ ansible.builtin.shell: >
+ set -o pipefail && vmkload_mod -l | grep scini
+ changed_when: powerflex_sdc_driver_loaded.stdout_lines | length == 0
+
+- name: Verify ESXi SDC connection with MDMs
+ ansible.builtin.copy:
+ src: "{{ item }}"
+ dest: "{{ powerflex_common_esxi_files_location }}"
+ mode: "0755"
+ register: powerflex_sdc_drv_cfg_file_output
+ with_fileglob:
+ - "{{ powerflex_common_file_install_location }}/drv_cfg*"
+
+- name: List the drv_cfg file
+ register: powerflex_sdc_drv_cfg_file
+ ansible.builtin.find:
+ paths: "{{ powerflex_common_esxi_files_location }}"
+ patterns: "*drv_cfg*"
+
+- name: Execute drv_cfg command
+ register: powerflex_sdc_drv_cfg_output
+ ansible.builtin.command: ./drv_cfg --query_mdm
+ args:
+ chdir: "{{ powerflex_common_esxi_files_location }}"
+ when: powerflex_sdc_drv_cfg_file.files | length > 0
+ changed_when:
+ - "'Retrieved 1 mdm(s)' not in powerflex_sdc_drv_cfg_output.stdout"
diff --git a/ansible_collections/dellemc/powerflex/roles/powerflex_sdc/tasks/remove_sdc.yml b/ansible_collections/dellemc/powerflex/roles/powerflex_sdc/tasks/remove_sdc.yml
new file mode 100644
index 000000000..b8e9b5492
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/roles/powerflex_sdc/tasks/remove_sdc.yml
@@ -0,0 +1,20 @@
+---
+- name: Set fact # noqa var-naming[no-role-prefix]
+ ansible.builtin.set_fact:
+ ansible_distribution: "WindowsOS"
+ when: " 'Windows' in ansible_distribution"
+
+- name: Include uninstall_sdc.yml
+ ansible.builtin.include_tasks: uninstall_sdc.yml
+
+- name: Remove the SDC
+ dellemc.powerflex.sdc:
+ hostname: "{{ hostname }}"
+ username: "{{ username }}"
+ password: "{{ password }}"
+ validate_certs: "{{ validate_certs }}"
+ sdc_name: "{{ powerflex_sdc_name }}"
+ state: "absent"
+ register: powerflex_sdc_remove_output
+ delegate_to: "{{ lookup('ansible.builtin.env', 'RUNON', default='localhost') }}"
+ when: powerflex_sdc_name is defined and powerflex_sdc_name | length > 0
diff --git a/ansible_collections/dellemc/powerflex/roles/powerflex_sdc/tasks/uninstall_esxi_sdc.yml b/ansible_collections/dellemc/powerflex/roles/powerflex_sdc/tasks/uninstall_esxi_sdc.yml
new file mode 100644
index 000000000..b3c3c68df
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/roles/powerflex_sdc/tasks/uninstall_esxi_sdc.yml
@@ -0,0 +1,30 @@
+---
+- name: Get the name of SDC driver installed
+ register: powerflex_sdc_installed_driver_list
+ ansible.builtin.shell: >
+ set -o pipefail && esxcli software vib list | grep sdc
+ changed_when: powerflex_sdc_installed_driver_list.stdout_lines | length == 0
+ ignore_errors: true
+
+- name: Remove the SDC driver form the esxi host
+ register: powerflex_sdc_remove_driver_output
+ ansible.builtin.shell: >
+ esxcli software vib remove -n {{ powerflex_sdc_installed_driver_list.stdout.split()[0] }}
+ changed_when: "'Reboot Required: true' in powerflex_sdc_remove_driver_output.stdout"
+ when: powerflex_sdc_installed_driver_list.stdout_lines | length != 0
+
+- name: Reboot ESXi host
+ register: powerflex_sdc_remove_driver_reboot_output
+ ansible.builtin.reboot:
+ reboot_timeout: 450
+ msg: "Rebooting the ESXi host."
+ when:
+ - powerflex_sdc_installed_driver_list.stdout_lines | length != 0
+ changed_when: powerflex_sdc_remove_driver_reboot_output.rebooted
+
+- name: List the SDC driver installed
+ ansible.builtin.shell: >
+ set -o pipefail && esxcli software vib list | grep sdc
+ register: powerflex_sdc_drivers_list
+ changed_when: powerflex_sdc_drivers_list.stdout_lines | length != 0
+ failed_when: powerflex_sdc_drivers_list.stdout_lines | length != 0
diff --git a/ansible_collections/dellemc/powerflex/roles/powerflex_sdc/tasks/uninstall_sdc.yml b/ansible_collections/dellemc/powerflex/roles/powerflex_sdc/tasks/uninstall_sdc.yml
new file mode 100644
index 000000000..e46a32683
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/roles/powerflex_sdc/tasks/uninstall_sdc.yml
@@ -0,0 +1,40 @@
+---
+- name: Uninstall package
+ register: powerflex_sdc_uninstall_output
+ environment:
+ I_AM_SURE: "{{ i_am_sure | int }}"
+ ansible.builtin.package:
+ name: "{{ item }}"
+ state: "absent"
+ with_items:
+ - EMC-ScaleIO-sdc
+ when: ansible_distribution in ("RedHat", "CentOS", "SLES", "Rocky")
+
+- name: Uninstall deb package
+ register: powerflex_sdc_uninstall_deb_output
+ ansible.builtin.apt:
+ name: "{{ item }}"
+ state: absent
+ with_items:
+ - emc-scaleio-sdc
+ when: ansible_distribution == "Ubuntu"
+
+- name: Include uninstall_esxi_sdc.yml to uninstall the esxi sdc
+ ansible.builtin.include_tasks: uninstall_esxi_sdc.yml
+ when: ansible_distribution == "VMkernel"
+
+- name: List the msi files
+ ansible.windows.win_find:
+ paths: "{{ powerflex_common_win_package_location }}"
+ patterns: "*{{ file_glob_name }}*.msi"
+ register: powerflex_sdc_msi_package_files
+ when: ansible_distribution == "WindowsOS"
+
+- name: Uninstall win package
+ register: powerflex_sdc_uninstall_win_output
+ ansible.windows.win_package:
+ path: "{{ item.path }}"
+ state: absent
+ with_items:
+ - "{{ powerflex_sdc_msi_package_files.files }}"
+ when: ansible_distribution == "WindowsOS"
diff --git a/ansible_collections/dellemc/powerflex/roles/powerflex_sdc/templates/driver_sync.conf.j2 b/ansible_collections/dellemc/powerflex/roles/powerflex_sdc/templates/driver_sync.conf.j2
new file mode 100644
index 000000000..08541128e
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/roles/powerflex_sdc/templates/driver_sync.conf.j2
@@ -0,0 +1,31 @@
+###############################################################################
+# driver_sync Configuration file
+# Everything after a '#' until the end of the line is ignored
+###############################################################################
+
+# Repository address, prefixed by protocol
+repo_address = {{ powerflex_sdc_driver_sync_repo_address }}
+
+# Repository user (valid for ftp/sftp protocol)
+repo_user = {{ powerflex_sdc_driver_sync_repo_user }}
+
+# Repository password (valid for ftp protocol)
+repo_password = {{ powerflex_sdc_driver_sync_repo_password }}
+
+# Local directory for modules
+local_dir = {{ powerflex_sdc_driver_sync_repo_local_dir }}
+
+# User's RSA private key file (sftp protocol)
+user_private_rsa_key = {{ powerflex_sdc_driver_sync_user_private_rsa_key_dest }}
+
+# Repository host public key (sftp protocol)
+repo_public_rsa_key = {{ powerflex_sdc_driver_sync_repo_public_rsa_key_dest }}
+
+# Should the fetched modules' signatures be checked [0, 1]
+module_sigcheck = {{ powerflex_sdc_driver_sync_module_sigcheck }}
+
+# EMC public signature key (needed when module_sigcheck is 1)
+emc_public_gpg_key = {{ powerflex_sdc_driver_sync_emc_public_gpg_key_dest }}
+
+# Sync pattern (regular expression) for massive retrieve
+sync_pattern = {{ powerflex_sdc_driver_sync_sync_pattern }}
diff --git a/ansible_collections/dellemc/powerflex/roles/powerflex_sdc/vars/main.yml b/ansible_collections/dellemc/powerflex/roles/powerflex_sdc/vars/main.yml
new file mode 100644
index 000000000..ad6c0e4e2
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/roles/powerflex_sdc/vars/main.yml
@@ -0,0 +1,5 @@
+---
+file_glob_name: sdc
+file_gpg_name: RPM-GPG-KEY-ScaleIO
+powerflex_role_environment:
+ MDM_IP: "{{ powerflex_sdc_mdm_ips }}"
diff --git a/ansible_collections/dellemc/powerflex/roles/powerflex_sdr/README.md b/ansible_collections/dellemc/powerflex/roles/powerflex_sdr/README.md
new file mode 100644
index 000000000..e83491329
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/roles/powerflex_sdr/README.md
@@ -0,0 +1,145 @@
+# powerflex_sdr
+
+Role to manage installation and uninstallation PowerFlex SDR.
+
+## Table of contents
+
+* [Requirements](#requirements)
+* [Ansible collections](#ansible-collections)
+* [Role Variables](#role-variables)
+* [Examples](#examples)
+* [Usage instructions](#usage-instructions)
+* [Author Information](#author-information)
+
+## Requirements
+
+```
+ansible
+python
+```
+
+## Ansible collections
+
+Collections required to use the role.
+
+```
+dellemc.powerflex
+```
+
+## Role Variables
+
+<table>
+<thead>
+ <tr>
+ <th>Name</th>
+ <th>Required</th>
+ <th>Description</th>
+ <th>Choices</th>
+ <th>Type</th>
+ <th>Default Value</th>
+ </tr>
+</thead>
+<tbody>
+ <tr>
+ <td>powerflex_common_file_install_location</td>
+ <td>false</td>
+ <td>Location of required, compatible installation software package based on the operating system of the node.
+ <br>The files can be downloaded from the Dell Product support page for PowerFlex software.</td>
+ <td></td>
+ <td>path</td>
+ <td>/var/tmp</td>
+ </tr>
+ <tr>
+ <td>powerflex_protection_domain_name</td>
+ <td>false</td>
+ <td>The name of the protection domain to which the SDR will be added.</td>
+ <td></td>
+ <td>str</td>
+ <td></td>
+ </tr>
+ <tr>
+ <td>powerflex_storage_pool_name</td>
+ <td>false</td>
+ <td>The name of the storage pool to which the device will be added.</td>
+ <td></td>
+ <td>str</td>
+ <td></td>
+ </tr>
+ <tr>
+ <td>powerflex_sdr_repl_journal_capacity_max_ratio</td>
+ <td>false</td>
+ <td>Maximum capacity percentage to be allocated for journal capacity. Range is 0 to 100.</td>
+ <td></td>
+ <td>int</td>
+ <td>10</td>
+ </tr>
+ <tr>
+ <td>powerflex_mdm_password</td>
+ <td>true</td>
+ <td>Password for primary MDM node.</td>
+ <td></td>
+ <td>str</td>
+ <td></td>
+ </tr>
+<tr>
+ <td>powerflex_sdr_state</td>
+ <td>false</td>
+ <td>State of the SDR.</td>
+ <td>present, absent</td>
+ <td>str</td>
+ <td>present</td>
+ </tr>
+</tbody>
+</table>
+
+## Examples
+----
+```
+ - name: Install powerflex SDR
+ ansible.builtin.include_role:
+ name: powerflex_sdr
+ vars:
+ powerflex_protection_domain_name: domain1
+ powerflex_storage_pool_name: pool1
+ powerflex_sdr_repl_journal_capacity_max_ratio: 10
+ powerflex_sdr_state: present
+ powerflex_mdm_password: Password111
+
+ - name: Uninstall powerflex SDR
+ ansible.builtin.include_role:
+ name: powerflex_sdr
+ vars:
+ powerflex_mdm_password: Password111
+ powerflex_sdr_state: absent
+
+```
+
+## Usage instructions
+----
+### To install all dependency packages, including SDR, on node:
+- PowerFlex 3.6:
+ ```
+ ansible-playbook -i inventory site.yml
+ ```
+- PowerFlex 4.5:
+ ```
+ ansible-playbook -i inventory site_powerflex45.yml
+ ```
+
+### To uninstall SDR:
+- PowerFlex 3.6:
+ ```
+ ansible-playbook -i inventory uninstall_powerflex.yml
+ ```
+- PowerFlex 4.5:
+ ```
+ ansible-playbook -i inventory uninstall_powerflex45.yml
+ ```
+
+Sample playbooks and inventory can be found in the playbooks directory.
+
+## Author Information
+------------------
+
+Dell Technologies <br>
+Abhishek Sinha (ansible.team@Dell.com) 2023 \ No newline at end of file
diff --git a/ansible_collections/dellemc/powerflex/roles/powerflex_sdr/defaults/main.yml b/ansible_collections/dellemc/powerflex/roles/powerflex_sdr/defaults/main.yml
new file mode 100644
index 000000000..efae8a870
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/roles/powerflex_sdr/defaults/main.yml
@@ -0,0 +1,9 @@
+---
+# defaults file for powerflex_sdr
+file_glob_name: sdr
+file_gpg_name: RPM-GPG-KEY-ScaleIO
+powerflex_common_file_install_location: "/var/tmp"
+powerflex_sdr_repl_journal_capacity_max_ratio: 10
+powerflex_role_environment:
+ MDM_IP: "{{ powerflex_sdr_mdm_primary_ip }}"
+powerflex_sdr_state: present
diff --git a/ansible_collections/dellemc/powerflex/roles/powerflex_sdr/meta/argument_specs.yml b/ansible_collections/dellemc/powerflex/roles/powerflex_sdr/meta/argument_specs.yml
new file mode 100644
index 000000000..b730ebed4
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/roles/powerflex_sdr/meta/argument_specs.yml
@@ -0,0 +1,34 @@
+---
+argument_specs:
+ main:
+ short_description: Role to manage installation and uninstallation Powerflex SDR
+ description:
+ - Role to manage installation and uninstallation Powerflex SDR.
+ options:
+ powerflex_common_file_install_location:
+ description:
+ - Location of installation and rpm gpg files to be installed.
+ - The required, compatible installation software package based on the operating system of the node.
+ - The files can be downloaded from the Dell Product support page for PowerFlex software.
+ type: path
+ default: /var/tmp
+ powerflex_sdr_state:
+ description:
+ - Specifies the state of SDR.
+ type: str
+ choices: ['absent', 'present']
+ default: present
+ powerflex_protection_domain_name:
+ description: The name of the protection domain to which the SDR will be added.
+ type: str
+ powerflex_storage_pool_name:
+ description: The name of the storage pool to which the device will be added.
+ type: str
+ powerflex_sdr_repl_journal_capacity_max_ratio:
+ description: Maximum capacity percentage to be allocated for journal capacity.
+ type: int
+ default: 10
+ powerflex_mdm_password:
+ required: true
+ type: str
+ description: Password for the Powerflex MDM.
diff --git a/ansible_collections/dellemc/powerflex/roles/powerflex_sdr/meta/main.yml b/ansible_collections/dellemc/powerflex/roles/powerflex_sdr/meta/main.yml
new file mode 100644
index 000000000..e0280962c
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/roles/powerflex_sdr/meta/main.yml
@@ -0,0 +1,21 @@
+---
+galaxy_info:
+ role_name: powerflex_sdr
+ namespace: dellemc
+ author: Abhishek Sinha
+ description: The role to manage installation and uninstallation PowerFlex SDR.
+ company: Dell Technologies
+ license: GPL-3.0-only
+ min_ansible_version: "2.14.0"
+ platforms:
+ - name: EL
+ versions:
+ - "9"
+ - "8"
+ - name: Ubuntu
+ versions:
+ - jammy
+ - name: SLES
+ versions:
+ - "15SP3"
+ - "15SP4"
diff --git a/ansible_collections/dellemc/powerflex/roles/powerflex_sdr/molecule/sdr_installation/converge.yml b/ansible_collections/dellemc/powerflex/roles/powerflex_sdr/molecule/sdr_installation/converge.yml
new file mode 100644
index 000000000..3e4bb51fa
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/roles/powerflex_sdr/molecule/sdr_installation/converge.yml
@@ -0,0 +1,30 @@
+---
+- name: Molecule Test for installation of SDR
+ hosts: sdr
+ vars_files:
+ - ../../../../playbooks/roles/vars_files/connection.yml
+
+ tasks:
+ - name: Install and configure Powerflex SDR
+ ansible.builtin.import_role:
+ name: powerflex_sdr
+ vars:
+ powerflex_sdr_state: present
+
+ - name: Verifying install package in check mode
+ ansible.builtin.assert:
+ that:
+ - powerflex_common_install_package_output.msg == "Check mode: No changes made"
+ when: ansible_check_mode
+
+ - name: Verifying installation package in normal mode
+ ansible.builtin.assert:
+ that:
+ - " 'Installed' in powerflex_common_install_package_output.results[0]"
+ when: not ansible_check_mode and powerflex_common_install_package_output.changed
+
+ - name: Verifying installation package in Idempotency mode
+ ansible.builtin.assert:
+ that:
+ - "'Nothing to do' in powerflex_common_install_package_output.msg"
+ when: not ansible_check_mode and not powerflex_common_install_package_output.changed
diff --git a/ansible_collections/dellemc/powerflex/roles/powerflex_sdr/molecule/sdr_installation/molecule.yml b/ansible_collections/dellemc/powerflex/roles/powerflex_sdr/molecule/sdr_installation/molecule.yml
new file mode 100644
index 000000000..ed97d539c
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/roles/powerflex_sdr/molecule/sdr_installation/molecule.yml
@@ -0,0 +1 @@
+---
diff --git a/ansible_collections/dellemc/powerflex/roles/powerflex_sdr/molecule/sdr_installation_invalid_path_rpm/converge.yml b/ansible_collections/dellemc/powerflex/roles/powerflex_sdr/molecule/sdr_installation_invalid_path_rpm/converge.yml
new file mode 100644
index 000000000..bc5b5d3d5
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/roles/powerflex_sdr/molecule/sdr_installation_invalid_path_rpm/converge.yml
@@ -0,0 +1,34 @@
+---
+- name: Molecule Test for installation of SDR with invalid rpm path, rpm file
+ hosts: sdr
+ vars_files:
+ - ../../../../playbooks/roles/vars_files/connection.yml
+
+ tasks:
+ - name: Install and configure powerflex SDR with no rpm
+ ansible.builtin.import_role:
+ name: powerflex_sdr
+ vars:
+ powerflex_common_file_install_location: "/opt/empty"
+ powerflex_sdr_state: present
+ register: powerflex_sdr_no_rpm_result
+ ignore_errors: true
+
+ - name: Verifying failure of install package with respect to no rpm file in normal mode
+ ansible.builtin.assert:
+ that:
+ - powerflex_common_package_file.files | length | int == 0
+
+ - name: Install and configure powerflex SDR with wrong file path
+ ansible.builtin.import_role:
+ name: powerflex_sdr
+ vars:
+ powerflex_common_file_install_location: "/opt/aaab"
+ powerflex_sdr_state: present
+ ignore_errors: true
+ register: powerflex_sdr_wrong_path_result
+
+ - name: Verifying failure of install package with wrong file path
+ ansible.builtin.assert:
+ that:
+ - powerflex_common_package_file.files | length == 0
diff --git a/ansible_collections/dellemc/powerflex/roles/powerflex_sdr/molecule/sdr_installation_invalid_path_rpm/molecule.yml b/ansible_collections/dellemc/powerflex/roles/powerflex_sdr/molecule/sdr_installation_invalid_path_rpm/molecule.yml
new file mode 100644
index 000000000..93cad84c9
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/roles/powerflex_sdr/molecule/sdr_installation_invalid_path_rpm/molecule.yml
@@ -0,0 +1,4 @@
+---
+scenario:
+ test_sequence:
+ - converge
diff --git a/ansible_collections/dellemc/powerflex/roles/powerflex_sdr/molecule/sdr_installation_invalid_pd/converge.yml b/ansible_collections/dellemc/powerflex/roles/powerflex_sdr/molecule/sdr_installation_invalid_pd/converge.yml
new file mode 100644
index 000000000..17a448963
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/roles/powerflex_sdr/molecule/sdr_installation_invalid_pd/converge.yml
@@ -0,0 +1,20 @@
+---
+- name: Molecule Test for installation of SDR
+ hosts: sdr
+ vars_files:
+ - ../../../../playbooks/roles/vars_files/connection.yml
+
+ tasks:
+ - name: Install and configure powerflex SDR without protection domain
+ vars:
+ error_msg: "Please provide powerflex_protection_domain_name and powerflex_storage_pool_name in parameter for installing SDR."
+ block:
+ - name: Powerflex SDR
+ ansible.builtin.import_role:
+ name: dellemc.powerflex.powerflex_sdr
+ vars:
+ powerflex_sdr_state: present
+ rescue:
+ - name: Verifying failure of install package without protection domain
+ ansible.builtin.assert:
+ that: ansible_failed_result.ansible_facts.powerflex_add_sdr_output.msg == error_msg
diff --git a/ansible_collections/dellemc/powerflex/roles/powerflex_sdr/molecule/sdr_installation_invalid_pd/molecule.yml b/ansible_collections/dellemc/powerflex/roles/powerflex_sdr/molecule/sdr_installation_invalid_pd/molecule.yml
new file mode 100644
index 000000000..93cad84c9
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/roles/powerflex_sdr/molecule/sdr_installation_invalid_pd/molecule.yml
@@ -0,0 +1,4 @@
+---
+scenario:
+ test_sequence:
+ - converge
diff --git a/ansible_collections/dellemc/powerflex/roles/powerflex_sdr/molecule/sdr_uninstallation/converge.yml b/ansible_collections/dellemc/powerflex/roles/powerflex_sdr/molecule/sdr_uninstallation/converge.yml
new file mode 100644
index 000000000..cc28cae33
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/roles/powerflex_sdr/molecule/sdr_uninstallation/converge.yml
@@ -0,0 +1,66 @@
+---
+- name: SDR uninstallation
+ hosts: sdr
+ vars_files:
+ - ../../../../playbooks/roles/vars_files/connection.yml
+
+ tasks:
+ - name: Uninstall powerflex SDR
+ register: powerflex_sdr_uninstall_outputs
+ ansible.builtin.import_role:
+ name: powerflex_sdr
+ vars:
+ powerflex_sdr_state: absent
+
+ - name: Verifying uninstall package in converge
+ ansible.builtin.assert:
+ that:
+ - " 'Removed:' in powerflex_sdr_uninstall_output.results[0].results[0]"
+ when:
+ - not ansible_check_mode
+ - powerflex_sdr_uninstall_output.changed
+ - ansible_distribution in ("RedHat", "CentOS", "SLES")
+
+ - name: Verifying uninstall package in check mode
+ ansible.builtin.assert:
+ that:
+ - powerflex_sdr_uninstall_output.msg == "Check mode: No changes made"
+ - powerflex_sdr_uninstall_output.changed
+ - ansible_distribution in ("RedHat", "CentOS", "SLES")
+ when: ansible_check_mode
+
+ - name: Verifying remove the sdr in normal mode
+ ansible.builtin.assert:
+ that:
+ - powerflex_remove_sdr_output.sdr_details is None
+ when:
+ - not ansible_check_mode
+ - powerflex_remove_sdr_output.changed
+ - ansible_distribution == "Ubuntu"
+
+ - name: Verifying uninstall package in Idempotency
+ ansible.builtin.assert:
+ that:
+ - powerflex_sdr_uninstall_output.results[0].msg == 'Nothing to do'
+ - ansible_distribution in ("RedHat", "CentOS", "SLES")
+ when:
+ - not ansible_check_mode
+ - not powerflex_sdr_uninstall_output.changed
+ - ansible_distribution in ("RedHat", "CentOS", "SLES")
+
+ - name: Verifying remove the sdr in check mode
+ ansible.builtin.assert:
+ that:
+ - powerflex_remove_sdr_output.msg == "Check mode: No changes made"
+ - powerflex_remove_sdr_output.changed
+ - ansible_distribution == "Ubuntu"
+ when: ansible_check_mode
+
+ - name: Verifying remove the sdr in Idempotency
+ ansible.builtin.assert:
+ that:
+ - powerflex_remove_sdr_output.sdr_details is None
+ when:
+ - not ansible_check_mode
+ - not powerflex_remove_sdr_output.changed
+ - ansible_distribution == "Ubuntu"
diff --git a/ansible_collections/dellemc/powerflex/roles/powerflex_sdr/molecule/sdr_uninstallation/molecule.yml b/ansible_collections/dellemc/powerflex/roles/powerflex_sdr/molecule/sdr_uninstallation/molecule.yml
new file mode 100644
index 000000000..ed97d539c
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/roles/powerflex_sdr/molecule/sdr_uninstallation/molecule.yml
@@ -0,0 +1 @@
+---
diff --git a/ansible_collections/dellemc/powerflex/roles/powerflex_sdr/molecule/var_values.yml b/ansible_collections/dellemc/powerflex/roles/powerflex_sdr/molecule/var_values.yml
new file mode 100644
index 000000000..87df25556
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/roles/powerflex_sdr/molecule/var_values.yml
@@ -0,0 +1,3 @@
+---
+powerflex_protection_domain_name: domain1
+powerflex_storage_pool_name: pool1
diff --git a/ansible_collections/dellemc/powerflex/roles/powerflex_sdr/tasks/add_sdr.yml b/ansible_collections/dellemc/powerflex/roles/powerflex_sdr/tasks/add_sdr.yml
new file mode 100644
index 000000000..1af345276
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/roles/powerflex_sdr/tasks/add_sdr.yml
@@ -0,0 +1,142 @@
+---
+- name: Get configured MDM IP addresses
+ dellemc.powerflex.mdm_cluster:
+ hostname: "{{ hostname }}"
+ username: "{{ username }}"
+ password: "{{ password }}"
+ validate_certs: "{{ validate_certs }}"
+ state: "present"
+ register: powerflex_sdr_mdm_ip_result
+ delegate_to: "{{ lookup('ansible.builtin.env', 'RUNON', default='localhost') }}"
+
+- name: Set fact - PowerFlex version
+ ansible.builtin.set_fact:
+ powerflex_sdr_array_version: "{{ powerflex_sdr_mdm_ip_result.mdm_cluster_details.master.versionInfo[1] }}"
+
+- name: Checking powerflex_protection_domain_name and powerflex_storage_pool_name is provided
+ ansible.builtin.set_fact:
+ powerflex_add_sdr_output:
+ msg: Please provide powerflex_protection_domain_name and powerflex_storage_pool_name in parameter for installing SDR.
+ failed_when:
+ - powerflex_protection_domain_name is undefined or powerflex_storage_pool_name is undefined
+
+- name: Include the sdr_set_facts.yml
+ ansible.builtin.include_tasks: sdr_set_facts.yml
+
+- name: Include install_powerflex.yml
+ ansible.builtin.include_tasks: ../../powerflex_common/tasks/install_powerflex.yml
+
+- name: Login to mdm for PowerFlex version below 4.x
+ ansible.builtin.command: >
+ scli --login --mdm_ip {{ powerflex_sdr_mdm_primary_ip }}
+ --username admin
+ --password "{{ powerflex_mdm_password }}"
+ --approve_certificate
+ run_once: true
+ register: powerflex_initial_login
+ delegate_to: "{{ powerflex_sdr_mdm_primary_hostname }}"
+ changed_when: powerflex_initial_login.rc == 0
+ no_log: true
+ when: powerflex_sdr_array_version == "3"
+
+- name: Login to mdm for PowerFlex version 4.x
+ ansible.builtin.command: >
+ scli --login --management_system_ip {{ hostname }}
+ --username admin
+ --password "{{ password }}"
+ --approve_certificate
+ run_once: true
+ register: powerflex_initial_login
+ delegate_to: "{{ powerflex_sdr_mdm_primary_hostname }}"
+ changed_when: powerflex_initial_login.rc == 0
+ no_log: true
+ when: powerflex_sdr_array_version != "3"
+
+- name: Output msg of previous task login to mdm
+ ansible.builtin.debug:
+ var: powerflex_initial_login.stdout
+
+- name: Set replication capacity
+ ansible.builtin.command: >
+ scli --set_replication_journal_capacity
+ --protection_domain_name {{ powerflex_protection_domain_name }}
+ --storage_pool_name {{ powerflex_storage_pool_name }}
+ --replication_journal_capacity_max_ratio {{ powerflex_sdr_repl_journal_capacity_max_ratio }}
+ run_once: true
+ register: powerflex_set_replication_capacity
+ delegate_to: "{{ powerflex_sdr_mdm_primary_hostname }}"
+ changed_when: powerflex_set_replication_capacity.rc == 0
+
+- name: Wait for replication capacity to be created
+ ansible.builtin.pause:
+ seconds: 60
+ run_once: true
+ delegate_to: "{{ powerflex_sdr_mdm_primary_hostname }}"
+
+- name: Get replication capacity
+ ansible.builtin.command: >
+ scli --query_all_replication_journal_capacity
+ --protection_domain_name {{ powerflex_protection_domain_name }}
+ run_once: true
+ delegate_to: "{{ powerflex_sdr_mdm_primary_hostname }}"
+ register: powerflex_get_replication_capacity
+ changed_when: powerflex_get_replication_capacity.rc == 0
+
+- name: Checking if SDR already exists or not for PowerFlex version below 4.x
+ ansible.builtin.command: >
+ scli --mdm_ip {{ powerflex_sdr_mdm_primary_ip }}
+ --query_sdr
+ --sdr_name "{{ powerflex_sdr_hostname }}"
+ delegate_to: "{{ powerflex_sdr_mdm_primary_hostname }}"
+ register: check_sdr_exists
+ changed_when: check_sdr_exists.rc == 0
+ failed_when: false
+ when: powerflex_sdr_array_version == "3"
+
+- name: Checking if SDR already exists or not for PowerFlex version 4.x
+ ansible.builtin.command: >
+ scli --management_system_ip {{ hostname }}
+ --query_sdr
+ --sdr_name "{{ powerflex_sdr_hostname }}"
+ delegate_to: "{{ powerflex_sdr_mdm_primary_hostname }}"
+ register: check_sdr_exists
+ changed_when: check_sdr_exists.rc == 0
+ failed_when: false
+ when: powerflex_sdr_array_version != "3"
+
+- name: Skipping add SDR if already exists
+ ansible.builtin.debug:
+ msg: "SDR name {{ powerflex_sdr_hostname }} already exists, will skip adding SDR."
+ when: check_sdr_exists.rc == 0
+ delegate_to: "{{ powerflex_sdr_mdm_primary_hostname }}"
+
+- name: Adding SDR for PowerFlex version below 4.x
+ ansible.builtin.command: >
+ scli --add_sdr --mdm_ip {{ powerflex_sdr_mdm_primary_ip }}
+ --sdr_ip_role all
+ --sdr_ip {{ powerflex_sdr_ip }}
+ --sdr_name "{{ powerflex_sdr_hostname }}"
+ --protection_domain_name {{ powerflex_protection_domain_name }}
+ register: powerflex_add_sdr_output
+ delegate_to: "{{ powerflex_sdr_mdm_primary_hostname }}"
+ changed_when: powerflex_add_sdr_output.rc == 0
+ when: check_sdr_exists.rc == 7 and powerflex_sdr_array_version == "3"
+
+- name: Adding SDR for PowerFlex version 4.x
+ ansible.builtin.command: >
+ scli --add_sdr --management_system_ip {{ hostname }}
+ --sdr_ip_role all
+ --sdr_ip {{ powerflex_sdr_ip }}
+ --sdr_name "{{ powerflex_sdr_hostname }}"
+ --protection_domain_name {{ powerflex_protection_domain_name }}
+ register: powerflex_add_sdr_output
+ delegate_to: "{{ powerflex_sdr_mdm_primary_hostname }}"
+ changed_when: powerflex_add_sdr_output.rc == 0
+ when: check_sdr_exists.rc == 7 and powerflex_sdr_array_version != "3"
+
+- name: Logging out of the mdm
+ ansible.builtin.command: scli --logout
+ register: powerflex_mdm_logout
+ delegate_to: "{{ powerflex_sdr_mdm_primary_hostname }}"
+ changed_when: powerflex_mdm_logout.rc == 0
+ run_once: true
diff --git a/ansible_collections/dellemc/powerflex/roles/powerflex_sdr/tasks/main.yml b/ansible_collections/dellemc/powerflex/roles/powerflex_sdr/tasks/main.yml
new file mode 100644
index 000000000..440173b8d
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/roles/powerflex_sdr/tasks/main.yml
@@ -0,0 +1,8 @@
+---
+- name: Add SDR
+ ansible.builtin.include_tasks: add_sdr.yml
+ when: powerflex_sdr_state == 'present'
+
+- name: Remove SDR
+ ansible.builtin.include_tasks: remove_sdr.yml
+ when: powerflex_sdr_state == 'absent'
diff --git a/ansible_collections/dellemc/powerflex/roles/powerflex_sdr/tasks/remove_sdr.yml b/ansible_collections/dellemc/powerflex/roles/powerflex_sdr/tasks/remove_sdr.yml
new file mode 100644
index 000000000..3bf33b6ea
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/roles/powerflex_sdr/tasks/remove_sdr.yml
@@ -0,0 +1,120 @@
+---
+- name: Get configured MDM IP addresses
+ dellemc.powerflex.mdm_cluster:
+ hostname: "{{ hostname }}"
+ username: "{{ username }}"
+ password: "{{ password }}"
+ validate_certs: "{{ validate_certs }}"
+ state: "present"
+ register: powerflex_sdr_mdm_ip_result
+ delegate_to: "{{ lookup('ansible.builtin.env', 'RUNON', default='localhost') }}"
+
+- name: Set fact - PowerFlex version
+ ansible.builtin.set_fact:
+ powerflex_sdr_array_version: "{{ powerflex_sdr_mdm_ip_result.mdm_cluster_details.master.versionInfo[1] }}"
+
+- name: Include the sdr_set_facts.yml
+ ansible.builtin.include_tasks: sdr_set_facts.yml
+
+- name: Login to mdm for PowerFlex version below 4.x
+ ansible.builtin.command: >
+ scli --login --mdm_ip {{ powerflex_sdr_mdm_primary_ip }}
+ --username admin
+ --password "{{ powerflex_mdm_password }}"
+ --approve_certificate
+ run_once: true
+ register: powerflex_initial_login
+ delegate_to: "{{ powerflex_sdr_mdm_primary_hostname }}"
+ changed_when: powerflex_initial_login.rc == 0
+ no_log: true
+ when: powerflex_sdr_array_version == "3"
+
+- name: Login to mdm for PowerFlex version 4.x
+ ansible.builtin.command: >
+ scli --login --management_system_ip {{ hostname }}
+ --username admin
+ --password "{{ password }}"
+ --approve_certificate
+ run_once: true
+ register: powerflex_initial_login
+ delegate_to: "{{ powerflex_sdr_mdm_primary_hostname }}"
+ changed_when: powerflex_initial_login.rc == 0
+ no_log: true
+ when: powerflex_sdr_array_version != "3"
+
+- name: Output msg of previous task login to mdm
+ ansible.builtin.debug:
+ var: powerflex_initial_login.stdout
+ delegate_to: "{{ powerflex_sdr_mdm_primary_hostname }}"
+
+- name: Checking if SDR already exists or not for PowerFlex version below 4.x
+ ansible.builtin.command: >
+ scli --mdm_ip {{ powerflex_sdr_mdm_primary_ip }}
+ --query_sdr
+ --sdr_name "{{ powerflex_sdr_hostname }}"
+ delegate_to: "{{ powerflex_sdr_mdm_primary_hostname }}"
+ register: check_sdr_exists
+ failed_when: false
+ changed_when: check_sdr_exists.rc == 0
+ when: powerflex_sdr_array_version == "3"
+
+- name: Checking if SDR already exists or not for PowerFlex version 4.x
+ ansible.builtin.command: >
+ scli --management_system_ip {{ hostname }}
+ --query_sdr
+ --sdr_name "{{ powerflex_sdr_hostname }}"
+ delegate_to: "{{ powerflex_sdr_mdm_primary_hostname }}"
+ register: check_sdr_exists
+ failed_when: false
+ changed_when: check_sdr_exists.rc == 0
+ when: powerflex_sdr_array_version != "3"
+
+- name: Skipping remove SDR if does not exist
+ ansible.builtin.debug:
+ msg: "SDR name {{ powerflex_sdr_hostname }} does not exist, will skip removing SDR."
+ when: check_sdr_exists.rc == 7
+ delegate_to: "{{ powerflex_sdr_mdm_primary_hostname }}"
+
+- name: Removing SDR for PowerFlex version below 4.x
+ ansible.builtin.command: >
+ scli --remove_sdr --mdm_ip {{ powerflex_sdr_mdm_primary_ip }}
+ --sdr_name "{{ powerflex_sdr_hostname }}"
+ register: powerflex_remove_sdr_output
+ delegate_to: "{{ powerflex_sdr_mdm_primary_hostname }}"
+ changed_when: powerflex_remove_sdr_output.rc == 0
+ when: check_sdr_exists.rc == 0 and powerflex_sdr_array_version == "3"
+
+- name: Removing SDR for PowerFlex version 4.x
+ ansible.builtin.command: >
+ scli --remove_sdr --management_system_ip {{ hostname }}
+ --sdr_name "{{ powerflex_sdr_hostname }}"
+ register: powerflex_remove_sdr_output
+ delegate_to: "{{ powerflex_sdr_mdm_primary_hostname }}"
+ changed_when: powerflex_remove_sdr_output.rc == 0
+ when: check_sdr_exists.rc == 0 and powerflex_sdr_array_version != "3"
+
+- name: Uninstall package
+ register: powerflex_sdr_uninstall_output
+ environment:
+ I_AM_SURE: "{{ i_am_sure | int }}"
+ ansible.builtin.package:
+ name: "{{ item }}"
+ state: "absent"
+ with_items:
+ - EMC-ScaleIO-sdr
+ when: ansible_distribution in ("RedHat", "CentOS", "SLES")
+
+- name: Uninstall deb package
+ ansible.builtin.apt:
+ name: "{{ item }}"
+ state: absent
+ with_items:
+ - emc-scaleio-sdr
+ when: ansible_distribution == "Ubuntu"
+
+- name: Logging out of the mdm
+ ansible.builtin.command: scli --logout
+ register: powerflex_mdm_logout
+ run_once: true
+ delegate_to: "{{ powerflex_sdr_mdm_primary_hostname }}"
+ changed_when: powerflex_mdm_logout.rc == 0
diff --git a/ansible_collections/dellemc/powerflex/roles/powerflex_sdr/tasks/sdr_set_facts.yml b/ansible_collections/dellemc/powerflex/roles/powerflex_sdr/tasks/sdr_set_facts.yml
new file mode 100644
index 000000000..086b8b5bd
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/roles/powerflex_sdr/tasks/sdr_set_facts.yml
@@ -0,0 +1,7 @@
+---
+- name: Set facts for powerflex primary ip and hostname for mdm and sdr
+ ansible.builtin.set_fact:
+ powerflex_sdr_mdm_primary_ip: "{{ hostvars[groups['mdm'][0]]['ansible_host'] }}"
+ powerflex_sdr_mdm_primary_hostname: "{{ hostvars[groups['mdm'][0]]['inventory_hostname'] }}"
+ powerflex_sdr_ip: "{{ hostvars[inventory_hostname]['ansible_host'] }}"
+ powerflex_sdr_hostname: "{{ inventory_hostname }}"
diff --git a/ansible_collections/dellemc/powerflex/roles/powerflex_sdr/vars/main.yml b/ansible_collections/dellemc/powerflex/roles/powerflex_sdr/vars/main.yml
new file mode 100644
index 000000000..6a0f1ad81
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/roles/powerflex_sdr/vars/main.yml
@@ -0,0 +1,2 @@
+---
+# vars file for powerflex_sdr
diff --git a/ansible_collections/dellemc/powerflex/roles/powerflex_sds/README.md b/ansible_collections/dellemc/powerflex/roles/powerflex_sds/README.md
new file mode 100644
index 000000000..af7061108
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/roles/powerflex_sds/README.md
@@ -0,0 +1,243 @@
+# powerflex_sds
+
+Role to manage the installation and uninstallation of Powerflex SDS.
+
+## Table of contents
+
+* [Requirements](#requirements)
+* [Ansible collections](#ansible-collections)
+* [Role Variables](#role-variables)
+* [Examples](#examples)
+* [Usage instructions](#usage-instructions)
+* [Author Information](#author-information)
+
+## Requirements
+
+```
+ansible
+python
+```
+
+## Ansible collections
+
+Collections required to use the role.
+
+```
+dellemc.powerflex
+```
+
+## Role Variables
+
+<table>
+<thead>
+ <tr>
+ <th>Name</th>
+ <th>Required</th>
+ <th>Description</th>
+ <th>Choices</th>
+ <th>Type</th>
+ <th>Default Value</th>
+ </tr>
+</thead>
+<tbody>
+ <tr>
+ <td>hostname</td>
+ <td>true</td>
+ <td>IP or FQDN of the PowerFlex gateway.</td>
+ <td></td>
+ <td>str</td>
+ <td>10.1.1.1</td>
+ </tr>
+ <tr>
+ <td>username</td>
+ <td>true</td>
+ <td>The username of the PowerFlex gateway.</td>
+ <td></td>
+ <td>str</td>
+ <td>admin</td>
+ </tr>
+ <tr>
+ <td>password</td>
+ <td>true</td>
+ <td>The password of the PowerFlex gateway.</td>
+ <td></td>
+ <td>str</td>
+ <td>password</td>
+ </tr>
+ <tr>
+ <td>port</td>
+ <td>false</td>
+ <td>Port</td>
+ <td></td>
+ <td>int</td>
+ <td>443</td>
+ </tr>
+ <tr>
+ <td>validate_certs</td>
+ <td>false</td>
+ <td>If C(false), the SSL certificates will not be validated.<br>Configure C(false) only on personally controlled sites where self-signed certificates are used.</td>
+ <td></td>
+ <td>bool</td>
+ <td>false</td>
+ </tr>
+ <tr>
+ <td>timeout</td>
+ <td>false</td>
+ <td>Timeout</td>
+ <td></td>
+ <td>int</td>
+ <td>120</td>
+ </tr>
+ <tr>
+ <td>powerflex_common_file_install_location</td>
+ <td>true</td>
+ <td>Location of installation and rpm gpg files to be installed.
+ <br>The required, compatible installation software package based on the operating system of the node.
+ <br>The files can be downloaded from the Dell Product support page for PowerFlex software.</td>
+ <td></td>
+ <td>str</td>
+ <td>/var/tmp</td>
+ </tr>
+ <tr>
+ <td>powerflex_sds_protection_domain</td>
+ <td>true</td>
+ <td>The name of the protection domain to which the SDS will be added.
+ </td>
+ <td></td>
+ <td>str</td>
+ <td></td>
+ </tr>
+ <tr>
+ <td>powerflex_sds_storage_pool</td>
+ <td>true</td>
+ <td>The name of the storage pool to which the device will be added.
+ </td>
+ <td></td>
+ <td>str</td>
+ <td></td>
+ </tr>
+ <tr>
+ <td>powerflex_sds_disks</td>
+ <td>true</td>
+ <td>Disks for adding the device.</td>
+ <td></td>
+ <td>str</td>
+ <td></td>
+ </tr>
+ <tr>
+ <td>powerflex_sds_role</td>
+ <td>true</td>
+ <td>Role of the SDS.</td>
+ <td>'sdsOnly', 'sdcOnly', 'all'</td>
+ <td>str</td>
+ <td></td>
+ </tr>
+ <tr>
+ <td>powerflex_sds_device_media_type</td>
+ <td>true</td>
+ <td>Media type of the device.</td>
+ <td>'HDD', 'SSD', 'NVDIMM'</td>
+ <td>str</td>
+ <td></td>
+ </tr>
+ <tr>
+ <td>powerflex_sds_device_name</td>
+ <td>true</td>
+ <td>Name of the device added to the SDS.</td>
+ <td></td>
+ <td>str</td>
+ <td></td>
+ </tr>
+ <tr>
+ <td>powerflex_sds_external_acceleration_type</td>
+ <td>true</td>
+ <td>External acceleration type of the device added.</td>
+ <td>'Invalid', 'None', 'Read', 'Write', 'ReadAndWrite'</td>
+ <td>str</td>
+ <td></td>
+ </tr>
+ <tr>
+ <td>powerflex_sds_fault_set</td>
+ <td>false</td>
+ <td>Fault set to which the SDS will be added.</td>
+ <td></td>
+ <td>str</td>
+ <td></td>
+ </tr>
+<tr>
+ <td>powerflex_sds_state</td>
+ <td>false</td>
+ <td>State of the SDS.</td>
+ <td>present, absent</td>
+ <td>str</td>
+ <td>present</td>
+ </tr>
+</tbody>
+</table>
+
+## Examples
+----
+```
+ - name: "Install and configure powerflex SDS"
+ ansible.builtin.import_role:
+ name: "powerflex_sds"
+ vars:
+ hostname: "{{ hostname }}"
+ username: "{{ username }}"
+ password: "{{ password }}"
+ validate_certs: "{{ validate_certs }}"
+ port: "{{ port }}"
+ powerflex_sds_disks:
+ ansible_available_disks:
+ - '/dev/sdb'
+ powerflex_sds_disks_type: HDD
+ powerflex_sds_protection_domain: domain1
+ powerflex_sds_storage_pool: pool1
+ powerflex_sds_role: all
+ powerflex_sds_device_media_type: HDD
+ powerflex_sds_device_name: '/dev/sdb'
+ powerflex_sds_external_acceleration_type: ReadAndWrite
+ powerflex_sds_state: present
+
+ - name: "Uninstall powerflex SDS"
+ ansible.builtin.import_role:
+ name: "powerflex_sds"
+ vars:
+ hostname: "{{ hostname }}"
+ username: "{{ username }}"
+ password: "{{ password }}"
+ validate_certs: "{{ validate_certs }}"
+ port: "{{ port }}"
+ powerflex_sds_state: 'absent'
+
+```
+
+## Usage instructions
+----
+### To install all dependency packages, including SDS, on node:
+- PowerFlex 3.6:
+ ```
+ ansible-playbook -i inventory site.yml
+ ```
+- PowerFlex 4.5:
+ ```
+ ansible-playbook -i inventory site_powerflex45.yml
+ ```
+
+### To uninstall SDS:
+- PowerFlex 3.6:
+ ```
+ ansible-playbook -i inventory uninstall_powerflex.yml
+ ```
+- PowerFlex 4.5:
+ ```
+ ansible-playbook -i inventory uninstall_powerflex45.yml
+ ```
+
+Sample playbooks and inventory can be found in the playbooks directory.
+
+## Author Information
+------------------
+
+Dell Technologies
+Trisha Datta (ansible.team@Dell.com) 2023 \ No newline at end of file
diff --git a/ansible_collections/dellemc/powerflex/roles/powerflex_sds/defaults/main.yml b/ansible_collections/dellemc/powerflex/roles/powerflex_sds/defaults/main.yml
new file mode 100644
index 000000000..033c766b5
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/roles/powerflex_sds/defaults/main.yml
@@ -0,0 +1,4 @@
+---
+file_glob_name: sds
+i_am_sure: 1
+powerflex_sds_state: present
diff --git a/ansible_collections/dellemc/powerflex/roles/powerflex_sds/meta/argument_spec.yml b/ansible_collections/dellemc/powerflex/roles/powerflex_sds/meta/argument_spec.yml
new file mode 100644
index 000000000..9b5e44cbe
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/roles/powerflex_sds/meta/argument_spec.yml
@@ -0,0 +1,88 @@
+---
+argument_specs:
+ main:
+ short_description: Role to manage the installation and uninstallation of Powerflex SDS.
+ description:
+ - Role to manage the installation and uninstallation of Powerflex SDS.
+ options:
+ hostname:
+ required: true
+ type: str
+ description: IP or FQDN of the PowerFlex gateway.
+ username:
+ required: true
+ type: str
+ description: The username of the PowerFlex gateway.
+ password:
+ required: true
+ type: str
+ description: The password of the PowerFlex gateway.
+ port:
+ type: int
+ description: Port of the PowerFlex gateway.
+ default: 443
+ validate_certs:
+ description:
+ - If C(false), the SSL certificates will not be validated.
+ - Configure C(false) only on personally controlled sites where self-signed certificates are used.
+ type: bool
+ default: false
+ timeout:
+ description: Timeout.
+ type: int
+ default: 120
+ powerflex_common_file_install_location:
+ description:
+ - Location of installation and rpm gpg files to be installed.
+ - The required, compatible installation software package based on the operating system of the node.
+ - The files can be downloaded from the Dell Product support page for PowerFlex software.
+ type: str
+ default: /var/tmp
+ powerflex_sds_state:
+ description:
+ - Specifies the state of SDS.
+ type: str
+ choices: ['absent', 'present']
+ default: present
+ powerflex_sds_protection_domaine:
+ required: true
+ description:
+ - The name of the protection domain to which the SDS will be added.
+ type: str
+ powerflex_sds_storage_pool:
+ required: true
+ description:
+ - The name of the storage pool to which the device will be added.
+ type: str
+ powerflex_sds_disks:
+ required: true
+ description:
+ - Disks for adding the device.
+ type: str
+ powerflex_sds_role:
+ required: true
+ description:
+ - Role of the SDS
+ type: str
+ choices: ['sdsOnly', 'sdcOnly', 'all']
+ powerflex_sds_device_media_type:
+ required: true
+ description:
+ - Media type of the device.
+ type: str
+ choices: ['HDD', 'SSD', 'NVDIMM']
+ powerflex_sds_device_name:
+ required: true
+ description:
+ - Name of the SDS device.
+ type: str
+ powerflex_sds_external_acceleration_type:
+ required: true
+ description:
+ - External acceleration type of the device added.
+ type: str
+ choices: ['Invalid', 'None', 'Read', 'Write', 'ReadAndWrite']
+ powerflex_sds_fault_set:
+ description:
+ - Fault set to which the SDS will be added.
+ type: str
diff --git a/ansible_collections/dellemc/powerflex/roles/powerflex_sds/meta/main.yml b/ansible_collections/dellemc/powerflex/roles/powerflex_sds/meta/main.yml
new file mode 100644
index 000000000..999d95e6e
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/roles/powerflex_sds/meta/main.yml
@@ -0,0 +1,24 @@
+---
+galaxy_info:
+ author: Trisha Datta
+ description: The role helps to manage the installation of SDS.
+ company: Dell Technologies
+ license: GPL-3.0-only
+ role_name: powerflex_sds
+ namespace: dellemc
+
+ min_ansible_version: "2.14.0"
+ platforms:
+ - name: EL
+ versions:
+ - "9"
+ - "8"
+ - name: Ubuntu
+ versions:
+ - jammy
+ - name: SLES
+ versions:
+ - "15SP3"
+ - "15SP4"
+
+ galaxy_tags: []
diff --git a/ansible_collections/dellemc/powerflex/roles/powerflex_sds/molecule/sds_installation/converge.yml b/ansible_collections/dellemc/powerflex/roles/powerflex_sds/molecule/sds_installation/converge.yml
new file mode 100644
index 000000000..0dc711a2c
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/roles/powerflex_sds/molecule/sds_installation/converge.yml
@@ -0,0 +1,55 @@
+---
+- name: SDS installation
+ hosts: sds
+ vars_files:
+ - ../../../../playbooks/roles/vars_files/connection.yml
+ tasks:
+ - name: Install common packages
+ ansible.builtin.import_role:
+ name: powerflex_common
+ vars:
+ powerflex_sds_state: present
+
+ - name: Install and configure powerflex SDS
+ ansible.builtin.import_role:
+ name: powerflex_sds
+ vars:
+ powerflex_sds_state: present
+
+ - name: Verifying device in normal mode
+ ansible.builtin.assert:
+ that:
+ - powerflex_sds_add_device_output.device_details.mediaType == "{{ powerflex_sds_device_media_type }}"
+ - powerflex_sds_add_device_output.device_details.name == "{{ powerflex_sds_device_name }}"
+ - powerflex_sds_add_device_output.device_details.externalAccelerationType == "{{ powerflex_sds_external_acceleration_type }}"
+ - powerflex_sds_add_device_output.device_details.storagepoolPoolName == "{{ powerflex_sds_storage_pool }}"
+ - powerflex_sds_add_device_output.device_details.protectionDomainName == "{{ powerflex_sds_protection_domain }}"
+ when:
+ - not ansible_check_mode
+ - powerflex_sds_add_device_output.changed
+
+ - name: Verifying install package in check mode
+ ansible.builtin.assert:
+ that:
+ - powerflex_common_install_package_output.msg == "Check mode: No changes made"
+ when: ansible_check_mode
+
+ - name: Verifying installation package
+ ansible.builtin.assert:
+ that:
+ - " 'Installed' in powerflex_common_install_package_output.results[0]"
+ when:
+ - not ansible_check_mode
+ - powerflex_common_install_package_output.changed
+
+ - name: Verifying device in Idempotency
+ ansible.builtin.assert:
+ that:
+ - powerflex_sds_add_device_output.device_details.mediaType == "{{ powerflex_sds_device_media_type }}"
+ - powerflex_sds_add_device_output.device_details.name == "{{ powerflex_sds_device_name }}"
+ - powerflex_sds_add_device_output.device_details.externalAccelerationType == "{{ powerflex_sds_external_acceleration_type }}"
+ - powerflex_sds_add_device_output.device_details.storagepoolPoolName == "{{ powerflex_sds_storage_pool }}"
+ - powerflex_sds_add_device_output.device_details.protectionDomainName == "{{ powerflex_sds_protection_domain }}"
+ when:
+ - not ansible_check_mode
+ - not powerflex_sds_add_device_output.changed
diff --git a/ansible_collections/dellemc/powerflex/roles/powerflex_sds/molecule/sds_installation/molecule.yml b/ansible_collections/dellemc/powerflex/roles/powerflex_sds/molecule/sds_installation/molecule.yml
new file mode 100644
index 000000000..ed97d539c
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/roles/powerflex_sds/molecule/sds_installation/molecule.yml
@@ -0,0 +1 @@
+---
diff --git a/ansible_collections/dellemc/powerflex/roles/powerflex_sds/molecule/sds_uninstallation/converge.yml b/ansible_collections/dellemc/powerflex/roles/powerflex_sds/molecule/sds_uninstallation/converge.yml
new file mode 100644
index 000000000..fbb99419a
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/roles/powerflex_sds/molecule/sds_uninstallation/converge.yml
@@ -0,0 +1,50 @@
+---
+- name: SDS uninstallation
+ hosts: sds
+ vars_files:
+ - ../../../../playbooks/roles/vars_files/connection.yml
+ tasks:
+ - name: Uninstall powerflex SDS
+ register: powerflex_sds_uninstall_outputs
+ ansible.builtin.import_role:
+ name: powerflex_sds
+ vars:
+ powerflex_sds_state: absent
+
+ - name: Verifying uninstall package in check mode
+ ansible.builtin.assert:
+ that:
+ - powerflex_sds_uninstall_output.msg == "Check mode: No changes made"
+ - powerflex_sds_uninstall_output.changed
+ when: ansible_check_mode
+
+ - name: Verifying remove the sds in normal mode
+ ansible.builtin.assert:
+ that:
+ - powerflex_sds_remove_result.sds_details is None
+ when:
+ - not ansible_check_mode
+ - powerflex_sds_remove_result.changed
+
+ - name: Verifying uninstall package in Idempotency
+ ansible.builtin.assert:
+ that:
+ - powerflex_sds_uninstall_output.results[0].msg == 'Nothing to do'
+ when:
+ - not ansible_check_mode
+ - not powerflex_sds_uninstall_output.changed
+
+ - name: Verifying remove the sds in check mode
+ ansible.builtin.assert:
+ that:
+ - powerflex_sds_remove_result.msg == "Check mode: No changes made"
+ - powerflex_sds_remove_result.changed
+ when: ansible_check_mode
+
+ - name: Verifying remove the sds in Idempotency
+ ansible.builtin.assert:
+ that:
+ - powerflex_sds_remove_result.sds_details is None
+ when:
+ - not ansible_check_mode
+ - not powerflex_sds_remove_result.changed
diff --git a/ansible_collections/dellemc/powerflex/roles/powerflex_sds/molecule/sds_uninstallation/molecule.yml b/ansible_collections/dellemc/powerflex/roles/powerflex_sds/molecule/sds_uninstallation/molecule.yml
new file mode 100644
index 000000000..ed97d539c
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/roles/powerflex_sds/molecule/sds_uninstallation/molecule.yml
@@ -0,0 +1 @@
+---
diff --git a/ansible_collections/dellemc/powerflex/roles/powerflex_sds/molecule/var_values.yml b/ansible_collections/dellemc/powerflex/roles/powerflex_sds/molecule/var_values.yml
new file mode 100644
index 000000000..b8d4ddc74
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/roles/powerflex_sds/molecule/var_values.yml
@@ -0,0 +1,11 @@
+---
+powerflex_sds_disks:
+ansible_available_disks:
+ - '/dev/sdb'
+powerflex_sds_disks_type: HDD
+powerflex_sds_protection_domain: domain1
+powerflex_sds_storage_pool: pool1
+powerflex_sds_role: all
+powerflex_sds_device_media_type: HDD
+powerflex_sds_device_name: '/dev/sdb'
+powerflex_sds_external_acceleration_type: ReadAndWrite
diff --git a/ansible_collections/dellemc/powerflex/roles/powerflex_sds/tasks/install_sds.yml b/ansible_collections/dellemc/powerflex/roles/powerflex_sds/tasks/install_sds.yml
new file mode 100644
index 000000000..8887ff13c
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/roles/powerflex_sds/tasks/install_sds.yml
@@ -0,0 +1,112 @@
+---
+- name: Get configured MDM IP addresses
+ dellemc.powerflex.mdm_cluster:
+ hostname: "{{ hostname }}"
+ username: "{{ username }}"
+ password: "{{ password }}"
+ validate_certs: "{{ validate_certs }}"
+ state: "present"
+ register: powerflex_sds_mdm_ip_result
+ delegate_to: "{{ lookup('ansible.builtin.env', 'RUNON', default='localhost') }}"
+
+- name: Set fact - PowerFlex version
+ ansible.builtin.set_fact:
+ powerflex_sds_array_version: "{{ powerflex_sds_mdm_ip_result.mdm_cluster_details.master.versionInfo[1] }}"
+
+- name: Set fact - powerflex_sds_mdm_ips
+ ansible.builtin.set_fact:
+ powerflex_sds_mdm_ips: "{{ powerflex_sds_mdm_ip_result.mdm_cluster_details.mdmAddresses | join(',') }}"
+ powerflex_sds_primary_mdm_hostname: "{{ hostvars[groups['mdm'][0]]['inventory_hostname'] }}"
+
+- name: Include install_powerflex.yml
+ ansible.builtin.include_tasks: ../../powerflex_common/tasks/install_powerflex.yml
+
+- name: Collect only facts about hardware
+ ansible.builtin.setup:
+ gather_subset:
+ - hardware
+
+- name: Find disks
+ ansible.builtin.set_fact:
+ disks: hostvars[inventory_hostname].ansible_devices.keys() | list
+
+- name: Use local disk variable
+ when: powerflex_sds_disks
+ ansible.builtin.set_fact:
+ disks: "{{ powerflex_sds_disks }}"
+
+- name: Login to MDM for PowerFlex version 4.x
+ ansible.builtin.command: scli --login --management_system_ip {{ hostname }} --username {{ username }} --password {{ password }} --approve_certificate
+ run_once: true
+ register: powerflex_sds_login_output
+ changed_when: powerflex_sds_login_output.rc == 0
+ delegate_to: "{{ powerflex_sds_primary_mdm_hostname }}"
+ when: powerflex_sds_array_version != "3"
+
+- name: Login to MDM for PowerFlex version below 4.x
+ ansible.builtin.command: scli --login --username {{ username }} --password {{ password }} --approve_certificate
+ run_once: true
+ register: powerflex_sds_login_output
+ changed_when: powerflex_sds_login_output.rc == 0
+ delegate_to: "{{ powerflex_sds_primary_mdm_hostname }}"
+ when: powerflex_sds_array_version == "3"
+
+- name: Create SDS
+ dellemc.powerflex.sds:
+ hostname: "{{ hostname }}"
+ username: "{{ username }}"
+ password: "{{ password }}"
+ validate_certs: "{{ validate_certs }}"
+ sds_name: "{{ inventory_hostname }}"
+ protection_domain_name: "{{ powerflex_sds_protection_domain }}"
+ sds_ip_list:
+ - ip: "{{ hostvars[inventory_hostname]['ansible_default_ipv4']['address'] }}"
+ role: "{{ powerflex_sds_role }}"
+ sds_ip_state: "present-in-sds"
+ state: "present"
+ register: powerflex_sds_result
+ delegate_to: "{{ lookup('ansible.builtin.env', 'RUNON', default='localhost') }}"
+ when: powerflex_sds_fault_set is not defined
+
+- name: Add SDS with fault set for PowerFlex version 4.x
+ ansible.builtin.command:
+ scli --add_sds --management_system_ip {{ hostname }}
+ --sds_ip {{ hostvars[inventory_hostname]['ansible_default_ipv4']['address'] }}
+ --storage_pool_name {{ powerflex_sds_storage_pool }} --sds_name "{{ inventory_hostname }}"
+ --protection_domain_name {{ powerflex_sds_protection_domain }} --fault_set_name {{ powerflex_sds_fault_set }}
+ register: add_sds
+ changed_when: ('already in use' in add_sds.stderr) or (add_sds.rc == 0)
+ delegate_to: "{{ powerflex_sds_primary_mdm_hostname }}"
+ ignore_errors: true
+ when: powerflex_sds_fault_set is defined and powerflex_sds_array_version != "3"
+
+- name: Add SDS with fault set for PowerFlex version below 4.x
+ ansible.builtin.command:
+ scli --add_sds --mdm_ip {{ powerflex_sds_mdm_ips }}
+ --sds_ip {{ hostvars[inventory_hostname]['ansible_default_ipv4']['address'] }}
+ --storage_pool_name {{ powerflex_sds_storage_pool }} --sds_name "{{ inventory_hostname }}"
+ --protection_domain_name {{ powerflex_sds_protection_domain }} --fault_set_name {{ powerflex_sds_fault_set }}
+ register: add_sds
+ changed_when: ('already in use' in add_sds.stderr) or (add_sds.rc == 0)
+ delegate_to: "{{ powerflex_sds_primary_mdm_hostname }}"
+ ignore_errors: true
+ when: powerflex_sds_fault_set is defined and powerflex_sds_array_version == "3"
+
+- name: Add a device
+ dellemc.powerflex.device:
+ hostname: "{{ hostname }}"
+ username: "{{ username }}"
+ password: "{{ password }}"
+ validate_certs: "{{ validate_certs }}"
+ port: "{{ port }}"
+ current_pathname: "{{ disks.ansible_available_disks | join(',') }}"
+ sds_name: "{{ inventory_hostname }}"
+ media_type: "{{ powerflex_sds_device_media_type }}"
+ device_name: "{{ powerflex_sds_device_name }}"
+ storage_pool_name: "{{ powerflex_sds_storage_pool }}"
+ protection_domain_name: "{{ powerflex_sds_protection_domain }}"
+ external_acceleration_type: "{{ powerflex_sds_external_acceleration_type }}"
+ force: true
+ state: "present"
+ delegate_to: "{{ lookup('ansible.builtin.env', 'RUNON', default='localhost') }}"
+ when: disks.ansible_available_disks | length > 0
diff --git a/ansible_collections/dellemc/powerflex/roles/powerflex_sds/tasks/main.yml b/ansible_collections/dellemc/powerflex/roles/powerflex_sds/tasks/main.yml
new file mode 100644
index 000000000..04fe23630
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/roles/powerflex_sds/tasks/main.yml
@@ -0,0 +1,8 @@
+---
+- name: Install SDS
+ ansible.builtin.include_tasks: install_sds.yml
+ when: powerflex_sds_state == 'present'
+
+- name: Remove SDS
+ ansible.builtin.include_tasks: uninstall_sds.yml
+ when: powerflex_sds_state == 'absent'
diff --git a/ansible_collections/dellemc/powerflex/roles/powerflex_sds/tasks/uninstall_sds.yml b/ansible_collections/dellemc/powerflex/roles/powerflex_sds/tasks/uninstall_sds.yml
new file mode 100644
index 000000000..af5609e57
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/roles/powerflex_sds/tasks/uninstall_sds.yml
@@ -0,0 +1,31 @@
+---
+- name: Remove SDS
+ dellemc.powerflex.sds:
+ hostname: "{{ hostname }}"
+ username: "{{ username }}"
+ password: "{{ password }}"
+ validate_certs: "{{ validate_certs }}"
+ port: "{{ port }}"
+ sds_name: "{{ inventory_hostname }}"
+ state: "absent"
+ register: powerflex_sds_remove_result
+ delegate_to: "{{ lookup('ansible.builtin.env', 'RUNON', default='localhost') }}"
+
+- name: Uninstall package
+ register: powerflex_sds_uninstall_output
+ environment:
+ I_AM_SURE: "{{ i_am_sure | int }}"
+ ansible.builtin.package:
+ name: "{{ item }}"
+ state: "absent"
+ with_items:
+ - EMC-ScaleIO-sds
+ when: ansible_distribution in ("RedHat", "CentOS", "SLES")
+
+- name: Uninstall deb package
+ ansible.builtin.apt:
+ name: "{{ item }}"
+ state: absent
+ with_items:
+ - emc-scaleio-sds
+ when: ansible_distribution == "Ubuntu"
diff --git a/ansible_collections/dellemc/powerflex/roles/powerflex_sds/vars/main.yml b/ansible_collections/dellemc/powerflex/roles/powerflex_sds/vars/main.yml
new file mode 100644
index 000000000..af154c9d9
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/roles/powerflex_sds/vars/main.yml
@@ -0,0 +1,5 @@
+---
+file_glob_name: sds
+file_gpg_name: RPM-GPG-KEY-ScaleIO
+powerflex_role_environment:
+ MDM_IP: "{{ powerflex_sds_mdm_ips }}"
diff --git a/ansible_collections/dellemc/powerflex/roles/powerflex_tb/README.md b/ansible_collections/dellemc/powerflex/roles/powerflex_tb/README.md
new file mode 100644
index 000000000..dec61fec9
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/roles/powerflex_tb/README.md
@@ -0,0 +1,210 @@
+# powerflex_tb
+
+Role to manage the installation and uninstallation of Powerflex TB.
+
+## Table of contents
+
+* [Requirements](#requirements)
+* [Ansible collections](#ansible-collections)
+* [Role Variables](#role-variables)
+* [Examples](#examples)
+* [Notes](#notes)
+* [Usage instructions](#usage-instructions)
+* [Author Information](#author-information)
+
+## Requirements
+
+```
+ansible
+python
+```
+
+## Ansible collections
+
+Collections required to use the role.
+
+```
+dellemc.powerflex
+```
+
+## Role Variables
+
+<table>
+<thead>
+ <tr>
+ <th>Name</th>
+ <th>Required</th>
+ <th>Description</th>
+ <th>Choices</th>
+ <th>Type</th>
+ <th>Default Value</th>
+ </tr>
+</thead>
+<tbody>
+ <tr>
+ <td>hostname</td>
+ <td>true</td>
+ <td>IP or FQDN of the PowerFlex gateway.</td>
+ <td></td>
+ <td>str</td>
+ <td></td>
+ </tr>
+ <tr>
+ <td>username</td>
+ <td>true</td>
+ <td>The username of the PowerFlex gateway.</td>
+ <td></td>
+ <td>str</td>
+ <td></td>
+ </tr>
+ <tr>
+ <td>password</td>
+ <td>true</td>
+ <td>The password of the PowerFlex gateway.</td>
+ <td></td>
+ <td>str</td>
+ <td></td>
+ </tr>
+ <tr>
+ <td>port</td>
+ <td>false</td>
+ <td>Port of the PowerFlex gateway.</td>
+ <td></td>
+ <td>int</td>
+ <td>443</td>
+ </tr>
+ <tr>
+ <td>validate_certs</td>
+ <td>false</td>
+ <td>If C(false), the SSL certificates will not be validated.<br>Configure C(false) only on personally controlled sites where self-signed certificates are used.</td>
+ <td></td>
+ <td>bool</td>
+ <td>false</td>
+ </tr>
+ <tr>
+ <td>timeout</td>
+ <td>false</td>
+ <td>Timeout.</td>
+ <td></td>
+ <td>int</td>
+ <td>120</td>
+ </tr>
+ <tr>
+ <td>powerflex_common_file_install_location</td>
+ <td>false</td>
+ <td>Location of installation and rpm gpg files to be installed.
+ <br>The required, compatible installation software package based on the operating system of the node.
+ <br> The files can be downloaded from the Dell Product support page for PowerFlex software.</td>
+ <td></td>
+ <td>str</td>
+ <td>/var/tmp</td>
+ </tr>
+ <tr>
+ <td>powerflex_tb_state</td>
+ <td>false</td>
+ <td>Specify state of TB.<br></td>
+ <td>absent, present</td>
+ <td>str</td>
+ <td>present</td>
+ </tr>
+ <tr>
+ <td>powerflex_tb_primary_name</td>
+ <td>false</td>
+ <td>Name of the primary TB.<br></td>
+ <td></td>
+ <td>str</td>
+ <td>primary_tb</td>
+ </tr>
+ <tr>
+ <td>powerflex_tb_secondary_name</td>
+ <td>false</td>
+ <td>Name of the secondary TB.<br></td>
+ <td></td>
+ <td>str</td>
+ <td>secondary_tb</td>
+ </tr>
+ <tr>
+ <td>powerflex_tb_cluster_mode</td>
+ <td>false</td>
+ <td>Mode of the cluster.<br></td>
+ <td>ThreeNodes, FiveNodes</td>
+ <td>str</td>
+ <td>ThreeNodes</td>
+ </tr>
+ <tr>
+ <td>powerflex_tb_cert_password</td>
+ <td>false</td>
+ <td>The CLI certificate password for login to the primary MDM.<br></td>
+ <td></td>
+ <td>str</td>
+ <td></td>
+ </tr>
+</tbody>
+</table>
+
+## Examples
+----
+```
+ - name: Install and configure PowerFlex TB
+ ansible.builtin.import_role:
+ name: "powerflex_tb"
+ vars:
+ hostname: "{{ hostname }}"
+ username: "{{ username }}"
+ password: "{{ password }}"
+ validate_certs: "{{ validate_certs }}"
+ port: "{{ port }}"
+ powerflex_tb_primary_name: "primary_tb"
+ powerflex_tb_secondary_name: "secondary_tb"
+ powerflex_tb_cluster_mode: "ThreeNodes"
+ powerflex_common_file_install_location: "/var/tmp"
+ powerflex_tb_state: present
+
+ - name: Uninstall powerflex TB
+ ansible.builtin.import_role:
+ name: "powerflex_tb"
+ vars:
+ hostname: "{{ hostname }}"
+ username: "{{ username }}"
+ password: "{{ password }}"
+ validate_certs: "{{ validate_certs }}"
+ port: "{{ port }}"
+ powerflex_tb_state: 'absent'
+
+```
+
+## Notes
+----
+
+- As a pre-requisite for PowerFlex 3.6, the Gateway must be installed.
+- For PowerFlex 4.x, after installing the TB perform initial configuration steps on PowerFlex Manager GUI. These steps can be found in Install and Update of Dell PowerFlex 4.x from Dell Support page.
+
+## Usage instructions
+----
+### To install all dependency packages, including TB, on node:
+- PowerFlex 3.6:
+ ```
+ ansible-playbook -i inventory site.yml
+ ```
+- PowerFlex 4.5:
+ ```
+ ansible-playbook -i inventory site_powerflex45.yml
+ ```
+
+### To uninstall TB:
+- PowerFlex 3.6:
+ ```
+ ansible-playbook -i inventory uninstall_powerflex.yml
+ ```
+- PowerFlex 4.5:
+ ```
+ ansible-playbook -i inventory uninstall_powerflex45.yml
+ ```
+
+Sample playbooks and inventory can be found in the playbooks directory.
+
+## Author Information
+------------------
+
+Dell Technologies <br>
+Ananthu S Kuttattu (ansible.team@Dell.com) 2023
diff --git a/ansible_collections/dellemc/powerflex/roles/powerflex_tb/defaults/main.yml b/ansible_collections/dellemc/powerflex/roles/powerflex_tb/defaults/main.yml
new file mode 100644
index 000000000..b4b0b5a30
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/roles/powerflex_tb/defaults/main.yml
@@ -0,0 +1,8 @@
+---
+# defaults file for powerflex_tb
+powerflex_tb_primary_name: primary_tb
+powerflex_tb_secondary_name: secondary_tb
+powerflex_tb_cluster_mode: "ThreeNodes"
+file_glob_name: mdm
+powerflex_role_environment:
+ MDM_IP: "{{ powerflex_tb_mdm_ips }}"
diff --git a/ansible_collections/dellemc/powerflex/roles/powerflex_tb/meta/argument_spec.yml b/ansible_collections/dellemc/powerflex/roles/powerflex_tb/meta/argument_spec.yml
new file mode 100644
index 000000000..f3072df80
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/roles/powerflex_tb/meta/argument_spec.yml
@@ -0,0 +1,85 @@
+---
+argument_specs:
+ main:
+ short_description: Role to manage the installation and uninstallation of Powerflex TB.
+ description:
+ - Role to manage the installation and uninstallation of Powerflex TB.
+ options:
+ hostname:
+ required: true
+ type: str
+ description: IP or FQDN of the PowerFlex gateway.
+ username:
+ required: true
+ type: str
+ description: The username of the PowerFlex gateway.
+ password:
+ required: true
+ type: str
+ description: The password of the PowerFlex gateway.
+ port:
+ type: int
+ description: Port of the PowerFlex gateway.
+ default: 443
+ validate_certs:
+ description:
+ - If C(false), the SSL certificates will not be validated.
+ - Configure C(false) only on personally controlled sites where self-signed certificates are used.
+ type: bool
+ default: false
+ timeout:
+ description: Timeout.
+ type: int
+ default: 120
+ powerflex_common_file_install_location:
+ description:
+ - Location of installation and rpm gpg files to be installed.
+ - The required, compatible installation software package based on the operating system of the node.
+ type: str
+ default: /var/tmp
+ powerflex_tb_state:
+ description:
+ - Specify state of TB.
+ type: str
+ choices: ['absent', 'present']
+ default: present
+ powerflex_tb_primary_name:
+ required: true
+ description:
+ - Name of the primary TB.
+ type: str
+ default: 'primary_tb'
+ powerflex_tb_secondary_name:
+ required: true
+ description:
+ - Name of the secondary TB.
+ type: str
+ default: 'secondary_tb'
+ powerflex_tb_cluster_mode:
+ required: true
+ description:
+ - Mode of the cluster.
+ choices: ['ThreeNodes', 'FiveNodes']
+ type: str
+ default: 'ThreeNodes'
+ powerflex_protection_domain_name:
+ description:
+ - Name of the protection domain.
+ type: str
+ default: 'tb_protection_domain'
+ powerflex_fault_sets:
+ description:
+ - List of fault sets.
+ type: list
+ default: ['fs1', 'fs2', 'fs3']
+ powerflex_media_type:
+ description:
+ - Media type of the storage pool.
+ type: str
+ choices: ['SSD', 'HDD', 'TRANSITIONAL']
+ default: 'SSD'
+ powerflex_storage_pool_name:
+ description:
+ - Name of the storage pool.
+ type: str
+ default: 'tb_storage_pool'
diff --git a/ansible_collections/dellemc/powerflex/roles/powerflex_tb/meta/argument_specs.yml b/ansible_collections/dellemc/powerflex/roles/powerflex_tb/meta/argument_specs.yml
new file mode 100644
index 000000000..ac5a3e3ef
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/roles/powerflex_tb/meta/argument_specs.yml
@@ -0,0 +1,65 @@
+---
+argument_specs:
+ main:
+ short_description: Role to manage the installation and uninstallation of Powerflex TB.
+ description:
+ - Role to manage the installation and uninstallation of Powerflex TB.
+ options:
+ hostname:
+ required: true
+ type: str
+ description: IP or FQDN of the PowerFlex gateway.
+ username:
+ required: true
+ type: str
+ description: The username of the PowerFlex gateway.
+ password:
+ required: true
+ type: str
+ description: The password of the PowerFlex gateway.
+ port:
+ type: int
+ description: Port of the PowerFlex gateway.
+ default: 443
+ validate_certs:
+ description:
+ - If C(false), the SSL certificates will not be validated.
+ - Configure C(false) only on personally controlled sites where self-signed certificates are used.
+ type: bool
+ default: false
+ timeout:
+ description: Timeout.
+ type: int
+ default: 120
+ powerflex_common_file_install_location:
+ description:
+ - Location of installation and rpm gpg files to be installed.
+ - The required, compatible installation software package based on the operating system of the node.
+ type: str
+ default: /var/tmp
+ powerflex_tb_state:
+ description:
+ - Specify state of TB.
+ type: str
+ choices: ['absent', 'present']
+ default: present
+ powerflex_tb_primary_name:
+ description:
+ - Name of the primary TB.
+ type: str
+ default: 'primary_tb'
+ powerflex_tb_secondary_name:
+ description:
+ - Name of the secondary TB.
+ type: str
+ default: 'secondary_tb'
+ powerflex_tb_cluster_mode:
+ description:
+ - Mode of the cluster.
+ choices: ['ThreeNodes', 'FiveNodes']
+ type: str
+ default: 'ThreeNodes'
+ powerflex_tb_cert_password:
+ description:
+ - The CLI certificate password for login to the primary MDM.
+ type: str
diff --git a/ansible_collections/dellemc/powerflex/roles/powerflex_tb/meta/main.yml b/ansible_collections/dellemc/powerflex/roles/powerflex_tb/meta/main.yml
new file mode 100644
index 000000000..c3179cd1e
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/roles/powerflex_tb/meta/main.yml
@@ -0,0 +1,25 @@
+---
+galaxy_info:
+ author: Ananthu S Kuttattu
+ description: Role to manage the installation and uninstallation of Powerflex TB.
+ company: Dell Technologies
+ license: GPL-3.0-only
+ role_name: powerflex_tb
+ namespace: dellemc
+
+ min_ansible_version: "2.14.0"
+ platforms:
+ - name: EL
+ versions:
+ - "9"
+ - "8"
+ - name: Ubuntu
+ versions:
+ - jammy
+
+ - name: SLES
+ versions:
+ - "15SP3"
+ - "15SP4"
+
+ galaxy_tags: []
diff --git a/ansible_collections/dellemc/powerflex/roles/powerflex_tb/molecule/tb_installation/converge.yml b/ansible_collections/dellemc/powerflex/roles/powerflex_tb/molecule/tb_installation/converge.yml
new file mode 100644
index 000000000..77d00ec11
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/roles/powerflex_tb/molecule/tb_installation/converge.yml
@@ -0,0 +1,35 @@
+---
+- name: TB installation
+ hosts: tb
+ vars_files:
+ - ../../../../playbooks/roles/vars_files/connection.yml
+ gather_facts: true
+ tasks:
+ - name: Install common packages
+ ansible.builtin.import_role:
+ name: powerflex_common
+
+ - name: "Install PowerFlex TieBreaker"
+ ansible.builtin.import_role:
+ name: "powerflex_tb"
+ vars:
+ powerflex_tb_state: present
+ register: powerflex_tb_results
+
+ - name: "Verifying installation package"
+ ansible.builtin.assert:
+ that:
+ - " 'Installed' in powerflex_common_install_package_output.results[0]"
+ when: not ansible_check_mode and powerflex_common_install_package_output.changed
+
+ - name: "Verifying cluster mode switch from 1 node to 3 node MDM cluster"
+ ansible.builtin.assert:
+ that:
+ - powerflex_tb_cluster_to_three_output.stdout == "Successfully switched the cluster mode."
+ when: not ansible_check_mode and powerflex_tb_cluster_to_three_output.changed and powerflex_tb_cluster_mode == "ThreeNodes"
+
+ - name: "Verifying cluster mode switch from 1 node to 5 node MDM cluster"
+ ansible.builtin.assert:
+ that:
+ - powerflex_tb_cluster_to_five_output.stdout == "Successfully switched the cluster mode."
+ when: not ansible_check_mode and powerflex_tb_cluster_to_five_output.changed and powerflex_tb_cluster_mode == "FiveNodes"
diff --git a/ansible_collections/dellemc/powerflex/roles/powerflex_tb/molecule/tb_installation/molecule.yml b/ansible_collections/dellemc/powerflex/roles/powerflex_tb/molecule/tb_installation/molecule.yml
new file mode 100644
index 000000000..ed97d539c
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/roles/powerflex_tb/molecule/tb_installation/molecule.yml
@@ -0,0 +1 @@
+---
diff --git a/ansible_collections/dellemc/powerflex/roles/powerflex_tb/molecule/tb_uninstallation/converge.yml b/ansible_collections/dellemc/powerflex/roles/powerflex_tb/molecule/tb_uninstallation/converge.yml
new file mode 100644
index 000000000..986b270de
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/roles/powerflex_tb/molecule/tb_uninstallation/converge.yml
@@ -0,0 +1,19 @@
+---
+- name: TB uninstallation
+ hosts: tb
+ vars_files:
+ - ../../../../playbooks/roles/vars_files/connection.yml
+ gather_facts: true
+ tasks:
+ - name: "Uninstall powerflex TB"
+ register: powerflex_tb_uninstall_outputs
+ ansible.builtin.import_role:
+ name: "powerflex_tb"
+ vars:
+ powerflex_tb_state: 'absent'
+
+ - name: "Verifying remove the TB"
+ ansible.builtin.assert:
+ that:
+ - powerflex_tb_remove_primary_tb_output.stdout == "Successfully removed the standby MDM."
+ when: not ansible_check_mode and powerflex_tb_remove_primary_tb_output.changed
diff --git a/ansible_collections/dellemc/powerflex/roles/powerflex_tb/molecule/tb_uninstallation/molecule.yml b/ansible_collections/dellemc/powerflex/roles/powerflex_tb/molecule/tb_uninstallation/molecule.yml
new file mode 100644
index 000000000..ed97d539c
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/roles/powerflex_tb/molecule/tb_uninstallation/molecule.yml
@@ -0,0 +1 @@
+---
diff --git a/ansible_collections/dellemc/powerflex/roles/powerflex_tb/molecule/var_values.yml b/ansible_collections/dellemc/powerflex/roles/powerflex_tb/molecule/var_values.yml
new file mode 100644
index 000000000..01397a639
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/roles/powerflex_tb/molecule/var_values.yml
@@ -0,0 +1,7 @@
+---
+powerflex_tb_primary_name: primary_tb
+powerflex_tb_secondary_name: secondary_tb
+powerflex_tb_cluster_mode: "ThreeNodes"
+powerflex_protection_domain_name: "tb_protection_domain"
+powerflex_fault_sets: ['fs1', 'fs2', 'fs3']
+powerflex_storage_pool_name: "tb_storage_pool"
diff --git a/ansible_collections/dellemc/powerflex/roles/powerflex_tb/molecule/wrong_tb_credentials/converge.yml b/ansible_collections/dellemc/powerflex/roles/powerflex_tb/molecule/wrong_tb_credentials/converge.yml
new file mode 100644
index 000000000..c9322f319
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/roles/powerflex_tb/molecule/wrong_tb_credentials/converge.yml
@@ -0,0 +1,20 @@
+---
+- name: Providing incorrect credentials for TB node
+ hosts: tb
+ vars_files:
+ - ../../../../playbooks/roles/vars_files/connection.yml
+ gather_facts: false
+ tasks:
+ - name: "Install and configure powerflex tb with wrong credentials"
+ ansible.builtin.import_role:
+ name: "powerflex_tb"
+ vars:
+ powerflex_tb_state: present
+ ignore_unreachable: true
+ ignore_errors: true
+ register: powerflex_tb_wrong_credentials_output
+
+ - name: "Verifying failure of install package with wrong credentials"
+ ansible.builtin.assert:
+ that:
+ - " 'Communication error' in powerflex_tb_primary_output.msg"
diff --git a/ansible_collections/dellemc/powerflex/roles/powerflex_tb/molecule/wrong_tb_credentials/inventory b/ansible_collections/dellemc/powerflex/roles/powerflex_tb/molecule/wrong_tb_credentials/inventory
new file mode 100644
index 000000000..391105e2b
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/roles/powerflex_tb/molecule/wrong_tb_credentials/inventory
@@ -0,0 +1,4 @@
+node0 ansible_host=10.2.2.2 ansible_port=22 ansible_ssh_pass=wrongpassword ansible_user=root
+
+[tb]
+node0
diff --git a/ansible_collections/dellemc/powerflex/roles/powerflex_tb/molecule/wrong_tb_credentials/molecule.yml b/ansible_collections/dellemc/powerflex/roles/powerflex_tb/molecule/wrong_tb_credentials/molecule.yml
new file mode 100644
index 000000000..805f92879
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/roles/powerflex_tb/molecule/wrong_tb_credentials/molecule.yml
@@ -0,0 +1,11 @@
+---
+provisioner:
+ name: ansible
+ inventory:
+ links:
+ hosts: inventory
+ group_vars: ../../../../playbooks/roles/group_vars/
+ host_vars: ../../../../playbooks/roles/host_vars/
+scenario:
+ test_sequence:
+ - converge
diff --git a/ansible_collections/dellemc/powerflex/roles/powerflex_tb/tasks/install_tb.yml b/ansible_collections/dellemc/powerflex/roles/powerflex_tb/tasks/install_tb.yml
new file mode 100644
index 000000000..504b3e920
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/roles/powerflex_tb/tasks/install_tb.yml
@@ -0,0 +1,8 @@
+---
+- name: Install TB for PowerFlex below 4.x
+ ansible.builtin.include_tasks: install_tb3x.yml
+ when: powerflex_tb_scli_version[0] == '3'
+
+- name: Install TB for PowerFlex 4.x
+ ansible.builtin.include_tasks: install_tb4x.yml
+ when: powerflex_tb_scli_version[0] >= '4'
diff --git a/ansible_collections/dellemc/powerflex/roles/powerflex_tb/tasks/install_tb3x.yml b/ansible_collections/dellemc/powerflex/roles/powerflex_tb/tasks/install_tb3x.yml
new file mode 100644
index 000000000..e602351da
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/roles/powerflex_tb/tasks/install_tb3x.yml
@@ -0,0 +1,81 @@
+---
+- name: Get configured MDM IP addresses
+ dellemc.powerflex.mdm_cluster:
+ hostname: "{{ hostname }}"
+ username: "{{ username }}"
+ password: "{{ password }}"
+ validate_certs: "{{ validate_certs }}"
+ state: "present"
+ register: powerflex_tb_mdm_ip_result
+ delegate_to: "{{ lookup('ansible.builtin.env', 'RUNON', default='localhost') }}"
+
+- name: Set fact - powerflex_mdm_ips
+ ansible.builtin.set_fact:
+ powerflex_tb_mdm_ips: "{{ powerflex_tb_mdm_ip_result.mdm_cluster_details.mdmAddresses | join(',') }}"
+
+- name: Include install_powerflex.yml
+ ansible.builtin.include_tasks: ../../powerflex_common/tasks/install_powerflex.yml
+
+- name: Login with password
+ ansible.builtin.command: scli --login --username {{ username }} --password "{{ password }}"
+ run_once: true
+ register: powerflex_tb_login_output
+ changed_when: powerflex_tb_login_output.rc == 0
+ delegate_to: "{{ powerflex_tb_mdm_primary_hostname }}"
+
+- name: Add primary TB
+ dellemc.powerflex.mdm_cluster:
+ hostname: "{{ hostname }}"
+ username: "{{ username }}"
+ password: "{{ password }}"
+ validate_certs: "{{ validate_certs }}"
+ port: "{{ port }}"
+ mdm_name: "{{ powerflex_tb_primary_name }}"
+ standby_mdm:
+ mdm_ips:
+ - "{{ powerflex_tb_primary_ip }}"
+ role: "TieBreaker"
+ management_ips:
+ - "{{ powerflex_tb_primary_ip }}"
+ state: "present"
+ register: powerflex_tb_primary_output
+ delegate_to: "{{ lookup('ansible.builtin.env', 'RUNON', default='localhost') }}"
+
+- name: Add secondary TB
+ dellemc.powerflex.mdm_cluster:
+ hostname: "{{ hostname }}"
+ username: "{{ username }}"
+ password: "{{ password }}"
+ validate_certs: "{{ validate_certs }}"
+ port: "{{ port }}"
+ mdm_name: "{{ powerflex_tb_secondary_name }}"
+ standby_mdm:
+ mdm_ips:
+ - "{{ powerflex_tb_secondary_ip }}"
+ role: "TieBreaker"
+ management_ips:
+ - "{{ powerflex_tb_secondary_ip }}"
+ state: "present"
+ register: powerflex_tb_secondary_output
+ delegate_to: "{{ lookup('ansible.builtin.env', 'RUNON', default='localhost') }}"
+ when: powerflex_tb_secondary_ip is defined
+
+- name: Switch to cluster three node
+ ansible.builtin.command: |
+ scli --switch_cluster_mode --cluster_mode "3_node" --add_slave_mdm_ip
+ "{{ powerflex_tb_mdm_secondary_ip }}" --add_tb_ip "{{ powerflex_tb_primary_ip }}"
+ run_once: true
+ register: powerflex_tb_cluster_to_three_output
+ changed_when: powerflex_tb_cluster_to_three_output.rc == 0
+ delegate_to: "{{ powerflex_tb_mdm_primary_hostname }}"
+ when: powerflex_tb_primary_output.mdm_cluster_details.clusterMode != "ThreeNodes" and powerflex_tb_cluster_mode == "ThreeNodes"
+
+- name: Switch to cluster five node
+ ansible.builtin.command: |
+ scli --switch_cluster_mode --cluster_mode "5_node" --add_slave_mdm_ip
+ "{{ powerflex_tb_mdm_secondary_ip }}","{{ powerflex_tb_mdm_tertiary_ip }}" --add_tb_ip "{{ powerflex_tb_primary_ip }}","{{ powerflex_tb_secondary_ip }}"
+ run_once: true
+ register: powerflex_tb_cluster_to_five_output
+ changed_when: powerflex_tb_cluster_to_five_output.rc == 0
+ delegate_to: "{{ powerflex_tb_mdm_primary_hostname }}"
+ when: powerflex_tb_primary_output.mdm_cluster_details.clusterMode != "FiveNodes" and powerflex_tb_cluster_mode == "FiveNodes"
diff --git a/ansible_collections/dellemc/powerflex/roles/powerflex_tb/tasks/install_tb4x.yml b/ansible_collections/dellemc/powerflex/roles/powerflex_tb/tasks/install_tb4x.yml
new file mode 100644
index 000000000..d34857ba4
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/roles/powerflex_tb/tasks/install_tb4x.yml
@@ -0,0 +1,69 @@
+---
+- name: Set fact - powerflex_mdm_ips
+ ansible.builtin.set_fact:
+ powerflex_tb_mdm_ips: "{{ powerflex_tb_mdm_primary_ip }},{{ powerflex_tb_mdm_secondary_ip }}"
+ when: powerflex_tb_mdm_count | int == 2
+
+- name: Set fact - powerflex_mdm_ips
+ ansible.builtin.set_fact:
+ powerflex_tb_mdm_ips: "{{ powerflex_tb_mdm_primary_ip }},{{ powerflex_tb_mdm_secondary_ip }},{{ powerflex_tb_mdm_tertiary_ip }}"
+ when: powerflex_tb_mdm_count | int > 2
+
+- name: Include install_powerflex.yml
+ ansible.builtin.include_tasks: ../../powerflex_common/tasks/install_powerflex.yml
+
+- name: Login to primary MDM node
+ ansible.builtin.command: >
+ scli --login --p12_path /opt/emc/scaleio/mdm/cfg/cli_certificate.p12 --p12_password {{ powerflex_tb_cert_password }}
+ run_once: true
+ register: powerflex_tb_login_output
+ changed_when: powerflex_tb_login_output.rc == 0
+ delegate_to: "{{ powerflex_tb_mdm_primary_hostname }}"
+
+- name: Add primary TB
+ ansible.builtin.command: >
+ scli --add_standby_mdm
+ --new_mdm_ip {{ powerflex_tb_primary_ip }}
+ --mdm_role tb
+ --new_mdm_name {{ powerflex_tb_primary_name }}
+ --new_mdm_management_ip {{ powerflex_tb_primary_ip }}
+ run_once: true
+ register: powerflex_tb_primary_output
+ delegate_to: "{{ powerflex_tb_mdm_primary_hostname }}"
+ when: powerflex_tb_primary_ip is defined
+ ignore_errors: true
+ changed_when: powerflex_tb_primary_output.rc == 0
+
+- name: Add secondary TB
+ ansible.builtin.command: >
+ scli --add_standby_mdm
+ --new_mdm_ip {{ powerflex_tb_secondary_ip }}
+ --mdm_role tb
+ --new_mdm_name {{ powerflex_tb_secondary_name }}
+ --new_mdm_management_ip {{ powerflex_tb_secondary_ip }}
+ register: powerflex_tb_secondary_output
+ run_once: true
+ delegate_to: "{{ powerflex_tb_mdm_primary_hostname }}"
+ when: powerflex_tb_secondary_ip is defined
+ ignore_errors: true
+ changed_when: powerflex_tb_secondary_output.rc == 0
+
+- name: Switch to cluster three node
+ ansible.builtin.command: |
+ scli --switch_cluster_mode --cluster_mode "3_node" --add_secondary_mdm_ip
+ "{{ powerflex_tb_mdm_secondary_ip }}" --add_tb_ip "{{ powerflex_tb_primary_ip }}"
+ run_once: true
+ register: powerflex_tb_cluster_to_three_output
+ changed_when: powerflex_tb_cluster_to_three_output.rc == 0
+ delegate_to: "{{ powerflex_tb_mdm_primary_hostname }}"
+ when: powerflex_tb_mdm_cluster_mode[0] != "3_node" and powerflex_tb_cluster_mode == "ThreeNodes"
+
+- name: Switch to cluster five node
+ ansible.builtin.command: |
+ scli --switch_cluster_mode --cluster_mode "5_node" --add_secondary_mdm_ip
+ "{{ powerflex_tb_mdm_secondary_ip }}","{{ powerflex_tb_mdm_tertiary_ip }}" --add_tb_ip "{{ powerflex_tb_primary_ip }}","{{ powerflex_tb_secondary_ip }}"
+ run_once: true
+ register: powerflex_tb_cluster_to_five_output
+ changed_when: powerflex_tb_cluster_to_five_output.rc == 0
+ delegate_to: "{{ powerflex_tb_mdm_primary_hostname }}"
+ when: powerflex_tb_mdm_cluster_mode[0] != "5_node" and powerflex_tb_cluster_mode == "FiveNodes"
diff --git a/ansible_collections/dellemc/powerflex/roles/powerflex_tb/tasks/main.yml b/ansible_collections/dellemc/powerflex/roles/powerflex_tb/tasks/main.yml
new file mode 100644
index 000000000..f98c09ff1
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/roles/powerflex_tb/tasks/main.yml
@@ -0,0 +1,31 @@
+---
+- name: Include set_tb_ips.yml
+ ansible.builtin.include_tasks: set_tb_ips.yml
+
+- name: Get SCli version
+ ansible.builtin.command: >
+ scli --query_cluster
+ register: powerflex_tb_scli_cluster_details
+ tags: register
+ changed_when: powerflex_tb_scli_cluster_details.rc == 0
+ delegate_to: "{{ powerflex_tb_mdm_primary_hostname }}"
+
+- name: Extract the scli version
+ ansible.builtin.set_fact:
+ powerflex_tb_scli_version: "{{ input_query | ansible.builtin.regex_search('Version: (\\d+)\\.(\\d+)', '\\1\\2') }}"
+ vars:
+ input_query: "{{ powerflex_tb_scli_cluster_details.stdout }}"
+
+- name: Extract the cluster mode
+ ansible.builtin.set_fact:
+ powerflex_tb_mdm_cluster_mode: "{{ input_query | ansible.builtin.regex_search('Mode: (\\w+)', '\\1') }}"
+ vars:
+ input_query: "{{ powerflex_tb_scli_cluster_details.stdout }}"
+
+- name: Install TB
+ ansible.builtin.include_tasks: install_tb.yml
+ when: powerflex_tb_state == 'present'
+
+- name: Uninstall TB
+ ansible.builtin.include_tasks: uninstall_tb.yml
+ when: powerflex_tb_state == 'absent'
diff --git a/ansible_collections/dellemc/powerflex/roles/powerflex_tb/tasks/set_tb_ips.yml b/ansible_collections/dellemc/powerflex/roles/powerflex_tb/tasks/set_tb_ips.yml
new file mode 100644
index 000000000..34c0144d5
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/roles/powerflex_tb/tasks/set_tb_ips.yml
@@ -0,0 +1,29 @@
+---
+- name: Set TB count
+ ansible.builtin.set_fact:
+ powerflex_tb_count: "{{ groups['tb'] | length }}"
+ powerflex_tb_mdm_count: "{{ groups['mdm'] | length }}"
+
+- name: Set fact - powerflex_tb_mdm_primary_ip and powerflex_tb_mdm_secondary_ip
+ ansible.builtin.set_fact:
+ powerflex_tb_mdm_primary_ip: "{{ hostvars[groups['mdm'][0]]['ansible_host'] }}"
+ powerflex_tb_mdm_primary_hostname: "{{ hostvars[groups['mdm'][0]]['inventory_hostname'] }}"
+ powerflex_tb_mdm_secondary_ip: "{{ hostvars[groups['mdm'][1]]['ansible_host'] }}"
+ powerflex_tb_mdm_secondary_hostname: "{{ hostvars[groups['mdm'][1]]['inventory_hostname'] }}"
+
+- name: Set fact - powerflex_tb_mdm_tertiary_ip
+ ansible.builtin.set_fact:
+ powerflex_tb_mdm_tertiary_ip: "{{ hostvars[groups['tb'][2]]['ansible_host'] }}"
+ powerflex_tb_mdm_tertiary_hostname: "{{ hostvars[groups['tb'][2]]['inventory_hostname'] }}"
+ when: "powerflex_tb_mdm_count | int > 2"
+
+- name: Set fact - powerflex_tb_primary
+ ansible.builtin.set_fact:
+ powerflex_tb_primary_ip: "{{ hostvars[groups['tb'][0]]['ansible_host'] }}"
+ powerflex_tb_primary_hostname: "{{ hostvars[groups['tb'][0]]['inventory_hostname'] }}"
+
+- name: Set fact - powerflex_tb_primary
+ ansible.builtin.set_fact:
+ powerflex_tb_secondary_ip: "{{ hostvars[groups['tb'][1]]['ansible_host'] }}"
+ powerflex_tb_secondary_hostname: "{{ hostvars[groups['tb'][1]]['inventory_hostname'] }}"
+ when: "powerflex_tb_count | int > 1"
diff --git a/ansible_collections/dellemc/powerflex/roles/powerflex_tb/tasks/uninstall_tb.yml b/ansible_collections/dellemc/powerflex/roles/powerflex_tb/tasks/uninstall_tb.yml
new file mode 100644
index 000000000..b08bffed8
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/roles/powerflex_tb/tasks/uninstall_tb.yml
@@ -0,0 +1,97 @@
+---
+# Switch from three or five to cluster one node for PowerFlex version 3.6
+- name: Login to primary MDM node of PowerFlex version 3.x
+ ansible.builtin.command: scli --login --username {{ username }} --password "{{ password }}"
+ run_once: true
+ register: powerflex_tb_login_output
+ changed_when: powerflex_tb_login_output.rc == 0
+ delegate_to: "{{ powerflex_tb_mdm_primary_hostname }}"
+ when: powerflex_tb_scli_version[0] == '3'
+
+- name: Switch cluster mode from three nodes to one node
+ ansible.builtin.command: |
+ scli --switch_cluster_mode --cluster_mode "1_node" --remove_slave_mdm_ip
+ "{{ powerflex_tb_mdm_secondary_ip }}" --remove_tb_ip "{{ powerflex_tb_primary_ip }}"
+ run_once: true
+ register: powerflex_tb_cluster_to_one_output
+ changed_when: powerflex_tb_cluster_to_one_output.rc == 0
+ delegate_to: "{{ powerflex_tb_mdm_primary_hostname }}"
+ when: powerflex_tb_mdm_cluster_mode[0] == "3_node" and powerflex_tb_scli_version[0] == '3'
+
+- name: Switch cluster mode from five nodes to one node
+ ansible.builtin.command: |
+ scli --switch_cluster_mode --cluster_mode "1_node" --remove_slave_mdm_ip
+ "{{ powerflex_tb_mdm_secondary_ip }}","{{ powerflex_tb_mdm_tertiary_ip }}" --remove_tb_ip "{{ powerflex_tb_primary_ip }}","{{ powerflex_tb_secondary_ip }}"
+ run_once: true
+ register: powerflex_tb_cluster_to_one_output
+ changed_when: powerflex_tb_cluster_to_one_output.rc == 0
+ delegate_to: "{{ powerflex_tb_mdm_primary_hostname }}"
+ when: powerflex_tb_mdm_cluster_mode[0] == "5_node" and powerflex_tb_scli_version[0] == '3'
+
+# Switch from three or five to cluster one node for PowerFlex version 4.5
+- name: Login to primary MDM node of PowerFlex version 4.5
+ ansible.builtin.command: >
+ scli --login --management_system_ip {{ hostname }} --username {{ username }} --password {{ password }}
+ run_once: true
+ register: powerflex_tb_login_output
+ changed_when: powerflex_tb_login_output.rc == 0
+ delegate_to: "{{ powerflex_tb_mdm_primary_hostname }}"
+ when: powerflex_tb_scli_version[0] >= '4'
+
+- name: Switch cluster mode from three nodes to one node
+ ansible.builtin.command: |
+ scli --switch_cluster_mode --cluster_mode "1_node" --remove_secondary_mdm_ip
+ "{{ powerflex_tb_mdm_secondary_ip }}" --remove_tb_ip "{{ powerflex_tb_primary_ip }}"
+ run_once: true
+ register: powerflex_tb_cluster_three_to_one_output
+ changed_when: powerflex_tb_cluster_three_to_one_output.rc == 0
+ delegate_to: "{{ powerflex_tb_mdm_primary_hostname }}"
+ when: powerflex_tb_mdm_cluster_mode[0] == "3_node" and powerflex_tb_scli_version[0] >= '4'
+
+- name: Switch cluster mode from five nodes to one node
+ ansible.builtin.command: |
+ scli --switch_cluster_mode --cluster_mode "1_node" --remove_secondary_mdm_ip
+ "{{ powerflex_tb_mdm_secondary_ip }}","{{ powerflex_tb_mdm_tertiary_ip }}" --remove_tb_ip "{{ powerflex_tb_primary_ip }}","{{ powerflex_tb_secondary_ip }}"
+ run_once: true
+ register: powerflex_tb_cluster_five_to_one_output
+ changed_when: powerflex_tb_cluster_five_to_one_output.rc == 0
+ delegate_to: "{{ powerflex_tb_mdm_primary_hostname }}"
+ when: powerflex_tb_mdm_cluster_mode[0] == "5_node" and powerflex_tb_scli_version[0] >= '4'
+
+# Remove the standby MDMs
+- name: Remove primary tb from standby mdm
+ ansible.builtin.command: scli --remove_standby_mdm --remove_mdm_ip "{{ powerflex_tb_primary_ip }}"
+ run_once: true
+ register: powerflex_tb_remove_primary_tb_output
+ changed_when: powerflex_tb_remove_primary_tb_output.rc == 0
+ ignore_errors: true
+ delegate_to: "{{ powerflex_tb_mdm_primary_hostname }}"
+
+- name: Remove secondary tb from standby mdm
+ ansible.builtin.command: scli --remove_standby_mdm --remove_mdm_ip "{{ powerflex_tb_secondary_ip }}"
+ run_once: true
+ register: powerflex_tb_remove_secondary_tb_output
+ changed_when: powerflex_tb_remove_secondary_tb_output.rc == 0
+ ignore_errors: true
+ delegate_to: "{{ powerflex_tb_mdm_primary_hostname }}"
+ when: powerflex_tb_secondary_ip is defined
+
+- name: Uninstall package
+ register: powerflex_tb_uninstall_output
+ environment:
+ I_AM_SURE: "{{ i_am_sure | int }}"
+ ansible.builtin.package:
+ name: "{{ item }}"
+ state: "absent"
+ with_items:
+ - EMC-ScaleIO-mdm
+ when: ansible_distribution in ("RedHat", "CentOS", "SLES")
+
+- name: Uninstall deb package
+ register: powerflex_tb_uninstall_output
+ ansible.builtin.apt:
+ name: "{{ item }}"
+ state: absent
+ with_items:
+ - EMC-ScaleIO-mdm
+ when: ansible_distribution == "Ubuntu"
diff --git a/ansible_collections/dellemc/powerflex/roles/powerflex_tb/vars/main.yml b/ansible_collections/dellemc/powerflex/roles/powerflex_tb/vars/main.yml
new file mode 100644
index 000000000..aa01f740c
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/roles/powerflex_tb/vars/main.yml
@@ -0,0 +1,6 @@
+---
+# vars file for powerflex_tb
+file_glob_name: mdm
+file_gpg_name: RPM-GPG-KEY-ScaleIO
+powerflex_role_environment:
+ MDM_IP: "{{ powerflex_tb_mdm_ips }}"
diff --git a/ansible_collections/dellemc/powerflex/roles/powerflex_webui/README.md b/ansible_collections/dellemc/powerflex/roles/powerflex_webui/README.md
new file mode 100644
index 000000000..794eb6b08
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/roles/powerflex_webui/README.md
@@ -0,0 +1,165 @@
+# powerflex_webui
+
+Role to manage the installation and uninstallation of Powerflex Web UI.
+
+## Table of contents
+
+* [Requirements](#requirements)
+* [Ansible collections](#ansible-collections)
+* [Role Variables](#role-variables)
+* [Examples](#examples)
+* [Notes](#notes)
+* [Usage instructions](#usage-instructions)
+* [Author Information](#author-information)
+
+## Requirements
+
+```
+ansible
+python
+```
+
+## Ansible collections
+
+Collections required to use the role.
+
+```
+dellemc.powerflex
+```
+
+## Role Variables
+
+<table>
+<thead>
+ <tr>
+ <th>Name</th>
+ <th>Required</th>
+ <th>Description</th>
+ <th>Choices</th>
+ <th>Type</th>
+ <th>Default Value</th>
+ </tr>
+</thead>
+<tbody>
+ <tr>
+ <td>hostname</td>
+ <td>true</td>
+ <td>IP or FQDN of the PowerFlex gateway.</td>
+ <td></td>
+ <td>str</td>
+ <td></td>
+ </tr>
+ <tr>
+ <td>username</td>
+ <td>true</td>
+ <td>The username of the PowerFlex gateway.</td>
+ <td></td>
+ <td>str</td>
+ <td></td>
+ </tr>
+ <tr>
+ <td>password</td>
+ <td>true</td>
+ <td>The password of the PowerFlex gateway.</td>
+ <td></td>
+ <td>str</td>
+ <td></td>
+ </tr>
+ <tr>
+ <td>port</td>
+ <td>false</td>
+ <td>The port of the PowerFlex gateway.</td>
+ <td></td>
+ <td>int</td>
+ <td>443</td>
+ </tr>
+ <tr>
+ <td>validate_certs</td>
+ <td>false</td>
+ <td>If C(false), the SSL certificates will not be validated.<br>Configure C(false) only on personally controlled sites where self-signed certificates are used.</td>
+ <td></td>
+ <td>bool</td>
+ <td>false</td>
+ </tr>
+ <tr>
+ <td>timeout</td>
+ <td>false</td>
+ <td>Time after which connection will get terminated.</td>
+ <td></td>
+ <td>int</td>
+ <td>120</td>
+ </tr>
+ <tr>
+ <td>powerflex_common_file_install_location</td>
+ <td>false</td>
+ <td>Location of installation, compatible installation software package based on the operating system of the node.
+ <br> The files can be downloaded from the Dell Product support page for PowerFlex software.</td>
+ <td></td>
+ <td>path</td>
+ <td>/var/tmp</td>
+ </tr>
+ <tr>
+ <td>powerflex_webui_skip_java</td>
+ <td>false</td>
+ <td>Specifies whether to install java or not.<br></td>
+ <td></td>
+ <td>bool</td>
+ <td>false</td>
+ </tr>
+ <tr>
+ <td>powerflex_webui_state</td>
+ <td>false</td>
+ <td>Specify state of web UI.
+ <br>present will install the web UI and absent will uninstall the web UI.</td>
+ <td>absent, present</td>
+ <td>str</td>
+ <td>present</td>
+ </tr>
+</tbody>
+</table>
+
+## Examples
+----
+```
+ - name: Install and configure powerflex web UI
+ ansible.builtin.import_role:
+ name: "powerflex_webui"
+ vars:
+ hostname: "{{ hostname }}"
+ username: "{{ username }}"
+ password: "{{ password }}"
+ validate_certs: "{{ validate_certs }}"
+ port: "{{ port }}"
+ powerflex_common_file_install_location: "/opt/scaleio/rpm"
+ powerflex_webui_skip_java: true
+ powerflex_webui_state: present
+
+ - name: Uninstall powerflex web UI
+ ansible.builtin.import_role:
+ name: "powerflex_webui"
+ vars:
+ powerflex_webui_state: absent
+
+```
+## Notes
+- Supported only in PowerFlex version 3.6.
+
+## Usage instructions
+----
+### To install all dependency packages, including web UI, on node:
+ ```
+ ansible-playbook -i inventory site.yml
+ ```
+
+### To uninstall web UI:
+ ```
+ ansible-playbook -i inventory uninstall_powerflex.yml
+ ```
+
+Sample playbooks and inventory can be found in the playbooks directory.
+
+## Author Information
+------------------
+
+Dell Technologies <br>
+Trisha Datta (ansible.team@Dell.com) 2023
diff --git a/ansible_collections/dellemc/powerflex/roles/powerflex_webui/defaults/main.yml b/ansible_collections/dellemc/powerflex/roles/powerflex_webui/defaults/main.yml
new file mode 100644
index 000000000..fd2b592a8
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/roles/powerflex_webui/defaults/main.yml
@@ -0,0 +1,6 @@
+---
+powerflex_skip_java: false
+file_glob_name: mgmt-server
+powerflex_role_environment:
+ MDM_IP: "{{ powerflex_webui_mdm_ips }}"
+powerflex_webui_state: "present"
diff --git a/ansible_collections/dellemc/powerflex/roles/powerflex_webui/meta/argument_specs.yml b/ansible_collections/dellemc/powerflex/roles/powerflex_webui/meta/argument_specs.yml
new file mode 100644
index 000000000..50aee5bbe
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/roles/powerflex_webui/meta/argument_specs.yml
@@ -0,0 +1,52 @@
+---
+argument_specs:
+ main:
+ short_description: Role to manage the installation and uninstallation of Powerflex web UI.
+ description:
+ - Role to manage the installation and uninstallation of Powerflex web UI.
+ options:
+ hostname:
+ required: true
+ type: str
+ description: IP or FQDN of the PowerFlex gateway.
+ username:
+ required: true
+ type: str
+ description: The username of the PowerFlex gateway.
+ password:
+ required: true
+ type: str
+ description: The password of the PowerFlex gateway.
+ port:
+ type: int
+ description: Port of the PowerFlex gateway.
+ default: 443
+ validate_certs:
+ description:
+ - If C(false), the SSL certificates will not be validated.
+ - Configure C(false) only on personally controlled sites where self-signed certificates are used.
+ type: bool
+ default: false
+ timeout:
+ description: Time after which connection will get terminated.
+ type: int
+ default: 120
+ powerflex_common_file_install_location:
+ description:
+ - Location of installation, compatible installation software package
+ based on the operating system of the node.
+ - The files can be downloaded from the Dell Product support page for PowerFlex software.
+ type: path
+ default: /var/tmp
+ powerflex_webui_skip_java:
+ type: bool
+ description: Specifies whether to install java or not.
+ default: false
+ powerflex_webui_state:
+ description:
+ - Specifies the state of the web UI.
+ - present will install the web UI if not already installed.
+ - absent will uninstall the web UI if installed.
+ type: str
+ choices: ['absent', 'present']
+ default: present
diff --git a/ansible_collections/dellemc/powerflex/roles/powerflex_webui/meta/main.yml b/ansible_collections/dellemc/powerflex/roles/powerflex_webui/meta/main.yml
new file mode 100644
index 000000000..2872690ea
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/roles/powerflex_webui/meta/main.yml
@@ -0,0 +1,29 @@
+---
+galaxy_info:
+ role_name: powerflex_webui
+ author: Trisha Datta
+ namespace: dellemc
+ description: Role to manage the installation and uninstallation of Powerflex WebUI.
+ company: Dell Technologies
+
+ license: GPL-3.0-only
+
+ min_ansible_version: "2.14.0"
+
+ platforms:
+ - name: EL
+ versions:
+ - "9"
+ - "8"
+ - name: Ubuntu
+ versions:
+ - jammy
+
+ - name: SLES
+ versions:
+ - "15SP3"
+ - "15SP4"
+
+ galaxy_tags: []
+dependencies:
+ - role: powerflex_common
diff --git a/ansible_collections/dellemc/powerflex/roles/powerflex_webui/molecule/webui_installation/converge.yml b/ansible_collections/dellemc/powerflex/roles/powerflex_webui/molecule/webui_installation/converge.yml
new file mode 100644
index 000000000..cd2b9c8b5
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/roles/powerflex_webui/molecule/webui_installation/converge.yml
@@ -0,0 +1,30 @@
+---
+- name: Converge
+ hosts: webui
+ vars_files:
+ - ../../../../playbooks/roles/vars_files/connection.yml
+ gather_facts: true
+ tasks:
+ - name: Install and configure powerflex webui
+ ansible.builtin.import_role:
+ name: "powerflex_webui"
+ vars:
+ powerflex_webui_state: present
+
+ - name: Verifying installation package in check mode
+ ansible.builtin.assert:
+ that:
+ - " 'No changes made, but would have if not in check mode' in powerflex_common_install_package_output.msg"
+ when: ansible_check_mode
+
+ - name: Verifying installation package in converge
+ ansible.builtin.assert:
+ that:
+ - " 'Installed' in powerflex_common_install_package_output.results[0]"
+ when: not ansible_check_mode and powerflex_common_install_package_output.changed
+
+ - name: Verifying installation package in idempotency
+ ansible.builtin.assert:
+ that:
+ - " 'Nothing to do' in powerflex_common_install_package_output.msg"
+ when: not ansible_check_mode and not powerflex_common_install_package_output.changed
diff --git a/ansible_collections/dellemc/powerflex/roles/powerflex_webui/molecule/webui_installation/molecule.yml b/ansible_collections/dellemc/powerflex/roles/powerflex_webui/molecule/webui_installation/molecule.yml
new file mode 100644
index 000000000..ed97d539c
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/roles/powerflex_webui/molecule/webui_installation/molecule.yml
@@ -0,0 +1 @@
+---
diff --git a/ansible_collections/dellemc/powerflex/roles/powerflex_webui/molecule/webui_installation_invalid_path_rpm/converge.yml b/ansible_collections/dellemc/powerflex/roles/powerflex_webui/molecule/webui_installation_invalid_path_rpm/converge.yml
new file mode 100644
index 000000000..f614a1862
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/roles/powerflex_webui/molecule/webui_installation_invalid_path_rpm/converge.yml
@@ -0,0 +1,34 @@
+---
+- name: Converge
+ hosts: webui
+ vars_files:
+ - ../../../../playbooks/roles/vars_files/connection.yml
+ gather_facts: true
+ tasks:
+ - name: Install and configure powerflex webui with no rpm
+ ansible.builtin.import_role:
+ name: "powerflex_webui"
+ vars:
+ powerflex_common_file_install_location: "/opt/empty"
+ powerflex_webui_state: present
+ ignore_errors: true
+ register: powerflex_webui_install_config_no_rpm_result
+
+ - name: Verifying failure of install package with respect to no rpm file
+ ansible.builtin.assert:
+ that:
+ - powerflex_common_package_file.files | length == 0
+
+ - name: Install and configure powerflex webui with wrong file path
+ ansible.builtin.import_role:
+ name: "powerflex_webui"
+ vars:
+ powerflex_common_file_install_location: "/opt/aaab"
+ powerflex_webui_state: present
+ ignore_errors: true
+ register: powerflex_webui_install_config_wrong_path_result
+
+ - name: Verifying failure of install package with wrong file path
+ ansible.builtin.assert:
+ that:
+ - powerflex_common_package_file.files | length == 0
diff --git a/ansible_collections/dellemc/powerflex/roles/powerflex_webui/molecule/webui_installation_invalid_path_rpm/molecule.yml b/ansible_collections/dellemc/powerflex/roles/powerflex_webui/molecule/webui_installation_invalid_path_rpm/molecule.yml
new file mode 100644
index 000000000..93cad84c9
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/roles/powerflex_webui/molecule/webui_installation_invalid_path_rpm/molecule.yml
@@ -0,0 +1,4 @@
+---
+scenario:
+ test_sequence:
+ - converge
diff --git a/ansible_collections/dellemc/powerflex/roles/powerflex_webui/molecule/webui_uninstallation/converge.yml b/ansible_collections/dellemc/powerflex/roles/powerflex_webui/molecule/webui_uninstallation/converge.yml
new file mode 100644
index 000000000..625f18a32
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/roles/powerflex_webui/molecule/webui_uninstallation/converge.yml
@@ -0,0 +1,48 @@
+---
+- name: Converge
+ hosts: webui
+ vars_files:
+ - ../../../../playbooks/roles/vars_files/connection.yml
+ gather_facts: true
+ tasks:
+ - name: Uninstall powerflex webui
+ ansible.builtin.import_role:
+ name: "powerflex_webui"
+ vars:
+ powerflex_webui_state: 'absent'
+
+ - name: Verifying uninstall package in converge
+ ansible.builtin.assert:
+ that:
+ - " 'Removed:' in powerflex_webui_uninstall_output.results[0].results[0]"
+ when: not ansible_check_mode and powerflex_webui_uninstall_output.changed and ansible_distribution in ("RedHat", "CentOS", "SLES")
+
+ - name: Verifying uninstall package in check mode
+ ansible.builtin.assert:
+ that:
+ - powerflex_webui_uninstall_output.results[0].msg == "Check mode: No changes made, but would have if not in check mode"
+ when: ansible_check_mode and ansible_distribution in ("RedHat", "CentOS", "SLES")
+
+ - name: Verifying uninstall package in Idempotency
+ ansible.builtin.assert:
+ that:
+ - powerflex_webui_uninstall_output.results[0].msg == 'Nothing to do'
+ when: not ansible_check_mode and not powerflex_webui_uninstall_output.changed and ansible_distribution in ("RedHat", "CentOS", "SLES")
+
+ - name: Verifying uninstall package in check mode for deb
+ ansible.builtin.assert:
+ that:
+ - powerflex_webui_uninstall_deb_output.results[0].msg == "Check mode: No changes made, but would have if not in check mode"
+ when: ansible_check_mode and ansible_distribution == "Ubuntu"
+
+ - name: Verifying uninstall package in converge for deb
+ ansible.builtin.assert:
+ that:
+ - " 'Removed:' in powerflex_webui_uninstall_deb_output.results[0].results[0]"
+ when: not ansible_check_mode and powerflex_webui_uninstall_deb_output.changed and ansible_distribution == "Ubuntu"
+
+ - name: Verifying uninstall package in Idempotency for deb
+ ansible.builtin.assert:
+ that:
+ - powerflex_webui_uninstall_deb_output.results[0].msg == 'Nothing to do'
+ when: not ansible_check_mode and not powerflex_webui_uninstall_deb_output.changed and ansible_distribution == "Ubuntu"
diff --git a/ansible_collections/dellemc/powerflex/roles/powerflex_webui/molecule/webui_uninstallation/molecule.yml b/ansible_collections/dellemc/powerflex/roles/powerflex_webui/molecule/webui_uninstallation/molecule.yml
new file mode 100644
index 000000000..ed97d539c
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/roles/powerflex_webui/molecule/webui_uninstallation/molecule.yml
@@ -0,0 +1 @@
+---
diff --git a/ansible_collections/dellemc/powerflex/roles/powerflex_webui/tasks/install_webui.yml b/ansible_collections/dellemc/powerflex/roles/powerflex_webui/tasks/install_webui.yml
new file mode 100644
index 000000000..13d58ffac
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/roles/powerflex_webui/tasks/install_webui.yml
@@ -0,0 +1,23 @@
+---
+- name: Check if webui package is available
+ delegate_to: localhost
+ ansible.builtin.find:
+ paths: "{{ powerflex_common_file_install_location }}"
+ patterns: "*{{ file_glob_name }}*"
+
+- name: Get configured MDM IP addresses
+ dellemc.powerflex.mdm_cluster:
+ hostname: "{{ hostname }}"
+ username: "{{ username }}"
+ password: "{{ password }}"
+ validate_certs: "{{ validate_certs }}"
+ state: present
+ register: powerflex_webui_result
+ delegate_to: "{{ lookup('ansible.builtin.env', 'RUNON', default='localhost') }}"
+
+- name: Set fact - powerflex_webui_mdm_ips
+ ansible.builtin.set_fact:
+ powerflex_webui_mdm_ips: "{{ powerflex_webui_result.mdm_cluster_details.mdmAddresses | join(',') }}"
+
+- name: Include install_powerflex.yml
+ ansible.builtin.include_tasks: ../../powerflex_common/tasks/install_powerflex.yml
diff --git a/ansible_collections/dellemc/powerflex/roles/powerflex_webui/tasks/main.yml b/ansible_collections/dellemc/powerflex/roles/powerflex_webui/tasks/main.yml
new file mode 100644
index 000000000..417f5d504
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/roles/powerflex_webui/tasks/main.yml
@@ -0,0 +1,8 @@
+---
+- name: Install and Configure PowerFlex webUI
+ ansible.builtin.include_tasks: install_webui.yml
+ when: powerflex_webui_state == "present"
+
+- name: Uninstall PowerFlex webUI
+ ansible.builtin.include_tasks: uninstall_webui.yml
+ when: powerflex_webui_state == "absent"
diff --git a/ansible_collections/dellemc/powerflex/roles/powerflex_webui/tasks/uninstall_webui.yml b/ansible_collections/dellemc/powerflex/roles/powerflex_webui/tasks/uninstall_webui.yml
new file mode 100644
index 000000000..72d7fd53a
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/roles/powerflex_webui/tasks/uninstall_webui.yml
@@ -0,0 +1,20 @@
+---
+- name: Uninstall web UI package
+ register: powerflex_webui_uninstall_output
+ environment:
+ I_AM_SURE: "{{ i_am_sure | int }}"
+ ansible.builtin.package:
+ name: "{{ item }}"
+ state: "absent"
+ with_items:
+ - EMC-ScaleIO-mgmt-server
+ when: ansible_distribution in ("RedHat", "CentOS", "SLES")
+
+- name: Uninstall deb package
+ register: powerflex_webui_uninstall_deb_output
+ ansible.builtin.apt:
+ name: "{{ item }}"
+ state: absent
+ with_items:
+ - EMC-ScaleIO-mgmt-server
+ when: ansible_distribution == "Ubuntu"
diff --git a/ansible_collections/dellemc/powerflex/roles/powerflex_webui/vars/main.yml b/ansible_collections/dellemc/powerflex/roles/powerflex_webui/vars/main.yml
new file mode 100644
index 000000000..aba9fecd1
--- /dev/null
+++ b/ansible_collections/dellemc/powerflex/roles/powerflex_webui/vars/main.yml
@@ -0,0 +1,5 @@
+---
+file_glob_name: mgmt-server
+powerflex_role_environment:
+ MDM_IP: "{{ powerflex_webui_mdm_ips }}"
+file_gpg_name: RPM-GPG-KEY-ScaleIO
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