From 38b7c80217c4e72b1d8988eb1e60bb6e77334114 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Thu, 18 Apr 2024 07:52:22 +0200 Subject: Adding upstream version 9.4.0+dfsg. Signed-off-by: Daniel Baumann --- .../modules/test_na_ontap_cg_snapshot_rest.py | 331 +++++++++++++++++++++ .../plugins/modules/test_na_ontap_cifs_server.py | 81 ++++- ...test_na_ontap_cifs_unix_symlink_mapping_rest.py | 252 ++++++++++++++++ .../modules/test_na_ontap_cli_timeout_rest.py | 104 +++++++ .../unit/plugins/modules/test_na_ontap_cluster.py | 24 ++ .../plugins/modules/test_na_ontap_cluster_peer.py | 48 +++ .../unit/plugins/modules/test_na_ontap_dns.py | 1 + .../modules/test_na_ontap_ems_config_rest.py | 107 +++++++ .../modules/test_na_ontap_ems_destination.py | 237 ++++++++++++++- .../plugins/modules/test_na_ontap_ems_filter.py | 139 +++++++-- .../unit/plugins/modules/test_na_ontap_info.py | 4 + .../modules/test_na_ontap_kerberos_realm.py | 36 ++- .../unit/plugins/modules/test_na_ontap_lun_rest.py | 14 +- .../plugins/modules/test_na_ontap_net_ifgrp.py | 3 +- .../modules/test_na_ontap_qos_policy_group.py | 22 +- .../plugins/modules/test_na_ontap_rest_info.py | 42 ++- .../unit/plugins/modules/test_na_ontap_restit.py | 2 +- .../plugins/modules/test_na_ontap_s3_services.py | 39 ++- .../modules/test_na_ontap_security_certificates.py | 24 +- .../plugins/modules/test_na_ontap_snapmirror.py | 41 ++- .../modules/test_na_ontap_snapshot_policy_rest.py | 8 +- .../unit/plugins/modules/test_na_ontap_snmp.py | 128 +++++++- .../modules/test_na_ontap_snmp_config_rest.py | 123 ++++++++ .../unit/plugins/modules/test_na_ontap_svm.py | 2 +- .../unit/plugins/modules/test_na_ontap_volume.py | 1 + .../plugins/modules/test_na_ontap_volume_rest.py | 111 ++++++- .../test_na_ontap_vscan_scanner_pool_rest.py | 256 ++++++++++++++++ 27 files changed, 2113 insertions(+), 67 deletions(-) create mode 100644 ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_cg_snapshot_rest.py create mode 100644 ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_cifs_unix_symlink_mapping_rest.py create mode 100644 ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_cli_timeout_rest.py create mode 100644 ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_ems_config_rest.py create mode 100644 ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_snmp_config_rest.py create mode 100644 ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_vscan_scanner_pool_rest.py (limited to 'ansible_collections/netapp/ontap/tests') diff --git a/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_cg_snapshot_rest.py b/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_cg_snapshot_rest.py new file mode 100644 index 000000000..52264b43e --- /dev/null +++ b/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_cg_snapshot_rest.py @@ -0,0 +1,331 @@ +# (c) 2023, NetApp, Inc +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +""" unit tests for Ansible module: na_ontap_cg_snapshot while using REST """ + +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type +import pytest +import sys + +import ansible_collections.netapp.ontap.plugins.module_utils.netapp as netapp_utils +# pylint: disable=unused-import +from ansible_collections.netapp.ontap.tests.unit.plugins.module_utils.ansible_mocks import patch_ansible, \ + create_and_apply, create_module +from ansible_collections.netapp.ontap.tests.unit.framework.mock_rest_and_zapi_requests import get_mock_record, \ + patch_request_and_invoke, register_responses +from ansible_collections.netapp.ontap.tests.unit.framework.rest_factory import rest_responses + +from ansible_collections.netapp.ontap.plugins.modules.na_ontap_cg_snapshot \ + import NetAppONTAPCGSnapshot as my_module # module under test + +if not netapp_utils.HAS_REQUESTS and sys.version_info < (2, 7): + pytestmark = pytest.mark.skip('Skipping Unit Tests on 2.6 as requests is not available') + + +DEFAULT_ARGS = { + 'hostname': 'hostname', + 'username': 'username', + 'password': 'password', + 'use_rest': 'always', + 'vserver': 'ansibleSVM' +} + + +# REST API canned responses when mocking send_request. +# The rest_factory provides default responses shared across testcases. +SRR = rest_responses({ + # module specific responses + 'cg_info_by_cg_name': (200, {"records": [ + { + "uuid": "af37131d-5dd2-11ee-b8da-005056b37403", + "name": "cg1", + "svm": { + "uuid": "39c2a5a0-35e2-11ee-b8da-005056b37403", + "name": "ansibleSVM" + } + } + ], + "num_records": 1 + }, None), + 'cg_info_by_volumes': (200, {"records": [ + { + "uuid": "af37131d-5dd2-11ee-b8da-005056b37403", + "name": "cg1", + "svm": { + "uuid": "39c2a5a0-35e2-11ee-b8da-005056b37403", + "name": "ansibleSVM" + }, + "volumes": [ + { + "name": "vol1" + }, + { + "name": "vol2" + } + ] + } + ], + "num_records": 1 + }, None), + 'cg_snapshot_info': (200, {"records": [ + { + "consistency_group": { + "name": "cg1" + }, + "uuid": "695a3306-6361-11ee-b8da-005056b37403", + "name": "snapshot1", + "comment": "dummy comment", + "snapmirror_label": "sm_label1" + } + ], + "num_records": 1 + }, None), +}) + + +cg_uuid = SRR['cg_info_by_cg_name'][1]['records'][0]['uuid'] +snapshot_uuid = SRR['cg_snapshot_info'][1]['records'][0]['uuid'] + + +def test_rest_successful_create_snapshot_given_consistency_group(): + '''Test successful rest create snapshot given consistency_group''' + register_responses([ + ('GET', 'cluster', SRR['is_rest_9_10_1']), + ('GET', '/application/consistency-groups', SRR['cg_info_by_cg_name']), # retrieve CG, given consistency_group + ('GET', '/application/consistency-groups/%s/snapshots' % (cg_uuid), SRR['empty_records']), # retrieve snapshots for the CG + ('POST', '/application/consistency-groups/%s/snapshots' % (cg_uuid), SRR['success']), # create CG snapshot + ]) + module_args = { + 'state': 'present', + 'consistency_group': 'cg1', + 'snapshot': 'snap1', + 'snapmirror_label': 'sm_label1', + 'comment': 'dummy comment' + } + assert create_and_apply(my_module, DEFAULT_ARGS, module_args)['changed'] + + +def test_rest_successful_create_snapshot_idempotency(): + '''Test successful rest create snapshot idempotency''' + register_responses([ + ('GET', 'cluster', SRR['is_rest_9_10_1']), + ('GET', '/application/consistency-groups', SRR['cg_info_by_cg_name']), # retrieve CG, given consistency_group + ('GET', '/application/consistency-groups/%s/snapshots' % (cg_uuid), SRR['cg_snapshot_info']), # retrieve snapshots for the CG + ]) + module_args = { + 'state': 'present', + 'consistency_group': 'cg1', + 'snapshot': 'snap1', + 'snapmirror_label': 'sm_label1', + 'comment': 'dummy comment' + } + assert create_and_apply(my_module, DEFAULT_ARGS, module_args)['changed'] is False + + +def test_rest_successful_create_snapshot_given_volumes(): + '''Test successful rest create snapshot given volumes''' + register_responses([ + ('GET', 'cluster', SRR['is_rest_9_10_1']), + ('GET', '/application/consistency-groups', SRR['cg_info_by_volumes']), # retrieve CG, given volumes + ('GET', '/application/consistency-groups/%s/snapshots' % (cg_uuid), SRR['empty_records']), # retrieve snapshots for the CG + ('POST', '/application/consistency-groups/%s/snapshots' % (cg_uuid), SRR['success']), # create CG snapshot + ]) + module_args = { + 'state': 'present', + 'volumes': ['vol1', 'vol2'], + 'snapshot': 'snap1', + 'snapmirror_label': 'sm_label1', + 'comment': 'dummy comment' + } + assert create_and_apply(my_module, DEFAULT_ARGS, module_args)['changed'] + + +def test_rest_error_create_snapshot(): + '''Test error rest create snapshot''' + register_responses([ + ('GET', 'cluster', SRR['is_rest_9_10_1']), + ('GET', '/application/consistency-groups', SRR['cg_info_by_cg_name']), # retrieve CG, given consistency_group + ('GET', '/application/consistency-groups/%s/snapshots' % (cg_uuid), SRR['empty_records']), # retrieve snapshots for the CG + ('POST', '/application/consistency-groups/%s/snapshots' % (cg_uuid), SRR['generic_error']), + ]) + module_args = { + 'state': 'present', + 'consistency_group': 'cg1', + 'snapshot': 'snap1', + 'snapmirror_label': 'sm_label1', + 'comment': 'dummy comment' + } + error = create_and_apply(my_module, DEFAULT_ARGS, module_args, fail=True)['msg'] + assert 'Error creating consistency group snapshot' in error + + +def test_rest_successful_delete_snapshot_given_consistency_group(): + '''Test successful rest delete snapshot given consistency_group''' + register_responses([ + ('GET', 'cluster', SRR['is_rest_9_10_1']), + ('GET', '/application/consistency-groups', SRR['cg_info_by_cg_name']), # retrieve CG, given consistency_group + ('GET', '/application/consistency-groups/%s/snapshots' % (cg_uuid), SRR['cg_snapshot_info']), # retrieve snapshots for the CG + ('DELETE', '/application/consistency-groups/%s/snapshots/%s' % (cg_uuid, snapshot_uuid), SRR['success']), # delete CG snapshot + ]) + module_args = { + 'state': 'absent', + 'consistency_group': 'cg1', + 'snapshot': 'snap1', + 'snapmirror_label': 'sm_label1', + 'comment': 'dummy comment' + } + assert create_and_apply(my_module, DEFAULT_ARGS, module_args)['changed'] + + +def test_rest_successful_delete_snapshot_idempotency(): + '''Test successful rest delete snapshot idempotency''' + register_responses([ + ('GET', 'cluster', SRR['is_rest_9_10_1']), + ('GET', '/application/consistency-groups', SRR['cg_info_by_cg_name']), # retrieve CG, given consistency_group + ('GET', '/application/consistency-groups/%s/snapshots' % (cg_uuid), SRR['empty_records']), # retrieve snapshots for the CG + ]) + module_args = { + 'state': 'absent', + 'consistency_group': 'cg1', + 'snapshot': 'snap1', + 'snapmirror_label': 'sm_label1', + 'comment': 'dummy comment' + } + assert create_and_apply(my_module, DEFAULT_ARGS, module_args)['changed'] is False + + +def test_rest_successful_delete_snapshot_given_volumes(): + '''Test successful rest delete snapshot given volumes''' + register_responses([ + ('GET', 'cluster', SRR['is_rest_9_10_1']), + ('GET', '/application/consistency-groups', SRR['cg_info_by_volumes']), # retrieve CG, given volumes + ('GET', '/application/consistency-groups/%s/snapshots' % (cg_uuid), SRR['cg_snapshot_info']), # retrieve snapshots for the CG + ('DELETE', '/application/consistency-groups/%s/snapshots/%s' % (cg_uuid, snapshot_uuid), SRR['success']), # delete CG snapshot + ]) + module_args = { + 'state': 'absent', + 'volumes': ['vol1', 'vol2'], + 'snapshot': 'snap1', + 'snapmirror_label': 'sm_label1', + 'comment': 'dummy comment' + } + assert create_and_apply(my_module, DEFAULT_ARGS, module_args)['changed'] + + +def test_rest_error_delete_snapshot(): + '''Test error rest delete snapshot''' + register_responses([ + ('GET', 'cluster', SRR['is_rest_9_10_1']), + ('GET', '/application/consistency-groups', SRR['cg_info_by_cg_name']), # retrieve CG, given consistency_group + ('GET', '/application/consistency-groups/%s/snapshots' % (cg_uuid), SRR['cg_snapshot_info']), # retrieve snapshots for the CG + ('DELETE', '/application/consistency-groups/%s/snapshots/%s' % (cg_uuid, snapshot_uuid), SRR['generic_error']), + ]) + module_args = { + 'state': 'absent', + 'consistency_group': 'cg1', + 'snapshot': 'snap1', + 'snapmirror_label': 'sm_label1', + 'comment': 'dummy comment' + } + error = create_and_apply(my_module, DEFAULT_ARGS, module_args, fail=True)['msg'] + assert 'Error deleting consistency group snapshot' in error + + +def test_error_ontap_version(): + ''' Test module supported from 9.10 ''' + register_responses([ + ('GET', 'cluster', SRR['is_rest']) + ]) + module_args = { + 'state': 'present', + 'consistency_group': 'cg1', + 'snapshot': 'snap1' + } + error = create_module(my_module, DEFAULT_ARGS, module_args, fail=True)['msg'] + assert 'requires ONTAP 9.10.1 or later' in error + + +def test_rest_error_mutually_exclusive_params(): + '''Test error rest mutually exclusive parameters''' + register_responses([ + ]) + module_args = { + 'state': 'present', + 'consistency_group': 'cg1', + 'volumes': ['vol1', 'vol2'], + 'snapshot': 'snap1', + 'snapmirror_label': 'sm_label1', + 'comment': 'dummy comment' + } + error = create_module(my_module, DEFAULT_ARGS, module_args, fail=True)['msg'] + assert "parameters are mutually exclusive: consistency_group|volumes" in error + + +def test_rest_error_cg_not_found_given_consistency_group(): + '''Test error rest consistency group not found, given consistency_group''' + register_responses([ + ('GET', 'cluster', SRR['is_rest_9_10_1']), + ('GET', '/application/consistency-groups', SRR['empty_records']), # retrieve CG, given consistency_group + ]) + module_args = { + 'state': 'present', + 'consistency_group': 'cg1', + 'snapshot': 'snap1', + 'snapmirror_label': 'sm_label1', + 'comment': 'dummy comment' + } + error = create_and_apply(my_module, DEFAULT_ARGS, module_args, fail=True)['msg'] + assert "Consistency group named 'cg1' not found" in error + + +def test_rest_error_cg_not_found_given_volumes(): + '''Test error rest consistency group not found, given volumes''' + register_responses([ + ('GET', 'cluster', SRR['is_rest_9_10_1']), + ('GET', '/application/consistency-groups', SRR['empty_records']), # retrieve CG, given volumes + ]) + module_args = { + 'state': 'present', + 'volumes': ['vol3'], + 'snapshot': 'snap1', + 'snapmirror_label': 'sm_label1', + 'comment': 'dummy comment' + } + error = create_and_apply(my_module, DEFAULT_ARGS, module_args, fail=True)['msg'] + assert "Consistency group having volumes '['vol3']' not found" in error + + +def test_rest_error_retrieve_cg(): + '''Test error rest retrieve consistency group''' + register_responses([ + ('GET', 'cluster', SRR['is_rest_9_10_1']), + ('GET', '/application/consistency-groups', SRR['generic_error']), # retrieve CG, given consistency_group + ]) + module_args = { + 'state': 'absent', + 'consistency_group': 'cg1', + 'snapshot': 'snap1', + 'snapmirror_label': 'sm_label1', + 'comment': 'dummy comment' + } + error = create_and_apply(my_module, DEFAULT_ARGS, module_args, fail=True)['msg'] + assert 'Error searching for consistency group' in error + + +def test_rest_error_retrieve_cg_snapshot(): + '''Test error rest retrieve consistency group snapshot''' + register_responses([ + ('GET', 'cluster', SRR['is_rest_9_10_1']), + ('GET', '/application/consistency-groups', SRR['cg_info_by_cg_name']), # retrieve CG, given consistency_group + ('GET', '/application/consistency-groups/%s/snapshots' % (cg_uuid), SRR['generic_error']), # retrieve snapshots for the CG + ]) + module_args = { + 'state': 'absent', + 'consistency_group': 'cg1', + 'snapshot': 'snap1', + 'snapmirror_label': 'sm_label1', + 'comment': 'dummy comment' + } + error = create_and_apply(my_module, DEFAULT_ARGS, module_args, fail=True)['msg'] + assert 'Error searching for consistency group snapshot' in error diff --git a/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_cifs_server.py b/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_cifs_server.py index 820c33d17..1ec919657 100644 --- a/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_cifs_server.py +++ b/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_cifs_server.py @@ -1,4 +1,4 @@ -# (c) 2018-2022, NetApp, Inc +# (c) 2018-2023, NetApp, Inc # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) ''' unit tests ONTAP Ansible module: na_ontap_cifs_server ''' @@ -36,6 +36,7 @@ SRR = rest_responses({ "enabled": True, "security": { "encrypt_dc_connection": False, + "lm_compatibility_level": "lm_ntlm_ntlmv2_krb", "smb_encryption": False, "kdc_encryption": False, "smb_signing": False, @@ -47,6 +48,9 @@ SRR = rest_responses({ "use_ldaps": False, "use_start_tls": False }, + "options": { + "multichannel": True + }, "target": { "name": "20:05:00:50:56:b3:0c:fa" }, @@ -68,6 +72,7 @@ SRR = rest_responses({ "enabled": False, "security": { "encrypt_dc_connection": False, + "lm_compatibility_level": "lm_ntlm_ntlmv2_krb", "smb_encryption": False, "kdc_encryption": False, "smb_signing": False, @@ -79,8 +84,11 @@ SRR = rest_responses({ "use_ldaps": False, "use_start_tls": False }, + "options": { + "multichannel": True + }, "target": { - "nam,e": "20:05:00:50:56:b3:0c:fa" + "name": "20:05:00:50:56:b3:0c:fa" }, "name": "cifs_server_name" } @@ -100,6 +108,7 @@ SRR = rest_responses({ "enabled": True, "security": { "encrypt_dc_connection": False, + "lm_compatibility_level": "lm_ntlm_ntlmv2_krb", "smb_encryption": False, "kdc_encryption": False, "smb_signing": False, @@ -457,6 +466,19 @@ def test_rest_successful_create_with_domain(): assert create_and_apply(my_module, ARGS_REST, module_args)['changed'] +def test_rest_successful_create_with_default_site(): + '''Test successful rest create''' + register_responses([ + ('GET', 'cluster', SRR['is_rest_9_13_1']), + ('GET', 'protocols/cifs/services', SRR['empty_records']), + ('POST', 'protocols/cifs/services', SRR['empty_good']), + ]) + module_args = { + 'default_site': 'default_site' + } + assert create_and_apply(my_module, ARGS_REST, module_args)['changed'] + + def test_rest_successful_create_with_security(): '''Test successful rest create''' register_responses([ @@ -469,11 +491,48 @@ def test_rest_successful_create_with_security(): 'smb_signing': True, 'kdc_encryption': True, 'encrypt_dc_connection': True, - 'restrict_anonymous': 'no_enumeration' + 'restrict_anonymous': 'no_enumeration', + 'lm_compatibility_level': 'lm_ntlm_ntlmv2_krb' } assert create_and_apply(my_module, ARGS_REST, module_args)['changed'] +def test_rest_version_error_with_security_options_9_8(): + register_responses([ + ('GET', 'cluster', SRR['is_rest']) + ]) + module_args = { + 'use_rest': 'always', + 'lm_compatibility_level': 'ntlm_ntlmv2_krb', + } + error = create_module(my_module, ARGS_REST, module_args, fail=True)['msg'] + assert 'Minimum version of ONTAP for lm_compatibility_level is (9, 8)' in error + + +def test_rest_version_error_with_service_options_9_10(): + register_responses([ + ('GET', 'cluster', SRR['is_rest']) + ]) + module_args = { + 'use_rest': 'always', + 'is_multichannel_enabled': False + } + error = create_module(my_module, ARGS_REST, module_args, fail=True)['msg'] + assert 'Minimum version of ONTAP for is_multichannel_enabled is (9, 10, 1)' in error + + +def test_rest_version_error_with_default_site(): + register_responses([ + ('GET', 'cluster', SRR['is_rest_9_12_1']) + ]) + module_args = { + 'use_rest': 'always', + 'default_site': 'default_site', + } + error = create_module(my_module, ARGS_REST, module_args, fail=True)['msg'] + assert 'Minimum version of ONTAP for default_site is (9, 13, 1)' in error + + def test_rest_version_error_with_security_encryption(): register_responses([ ('GET', 'cluster', SRR['is_rest_96']) @@ -625,7 +684,7 @@ def test_rest_negative_security_options_modify(): def test_rest_successful_security_options_modify(): - '''Test successful rest enable''' + '''Test successful rest security options modify''' register_responses([ ('GET', 'cluster', SRR['is_rest_9_10_1']), ('GET', 'protocols/cifs/services', SRR['cifs_record_disabled']), @@ -635,12 +694,26 @@ def test_rest_successful_security_options_modify(): "aes_netlogon_enabled": True, "ldap_referral_enabled": True, "session_security": "seal", + "lm_compatibility_level": "ntlm_ntlmv2_krb", "try_ldap_channel_binding": False, "use_ldaps": True } assert create_and_apply(my_module, ARGS_REST, module_args)['changed'] +def test_rest_successful_service_options_modify(): + '''Test successful rest service options modify''' + register_responses([ + ('GET', 'cluster', SRR['is_rest_9_10_1']), + ('GET', 'protocols/cifs/services', SRR['cifs_record_disabled']), + ('PATCH', 'protocols/cifs/services/671aa46e-11ad-11ec-a267-005056b30cfa', SRR['empty_good']), + ]) + module_args = { + "is_multichannel_enabled": False + } + assert create_and_apply(my_module, ARGS_REST, module_args)['changed'] + + def test_rest_successful_rename_cifs(): '''Test successful rest rename''' register_responses([ diff --git a/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_cifs_unix_symlink_mapping_rest.py b/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_cifs_unix_symlink_mapping_rest.py new file mode 100644 index 000000000..28e62f6f0 --- /dev/null +++ b/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_cifs_unix_symlink_mapping_rest.py @@ -0,0 +1,252 @@ +# Copyright: NetApp, Inc +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +""" unit tests for Ansible module: na_ontap_cifs_unix_symlink_mapping """ + +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type +import pytest +import sys + +import ansible_collections.netapp.ontap.plugins.module_utils.netapp as netapp_utils +# pylint: disable=unused-import +from ansible_collections.netapp.ontap.tests.unit.plugins.module_utils.ansible_mocks import patch_ansible, \ + create_and_apply, create_module, call_main, expect_and_capture_ansible_exception +from ansible_collections.netapp.ontap.tests.unit.framework.mock_rest_and_zapi_requests import get_mock_record, \ + patch_request_and_invoke, register_responses +from ansible_collections.netapp.ontap.tests.unit.framework.rest_factory import rest_responses + +from ansible_collections.netapp.ontap.plugins.modules.na_ontap_cifs_unix_symlink_mapping \ + import NetAppOntapCifsUnixSymlink as my_module, main as my_main # module under test + +if not netapp_utils.HAS_REQUESTS and sys.version_info < (2, 7): + pytestmark = pytest.mark.skip( + 'Skipping Unit Tests on 2.6 as requests is not available') + + +DEFAULT_ARGS = { + 'hostname': 'hostname', + 'username': 'username', + 'password': 'password', + 'use_rest': 'always' +} + + +# REST API canned responses when mocking send_request. +# The rest_factory provides default responses shared across testcases. +SRR = rest_responses({ + # module specific responses + 'symlink_mapping': (200, {"records": [ + { + "svm": { + "uuid": "a7d278fb-2d2d-11ee-b8da-005056b37403", + "name": "ansibleSVM" + }, + "unix_path": "/example1/", + "target": { + "share": "share1", + "path": "/path1/test_dir/", + "server": "CIFS", + "locality": "local", + "home_directory": False + } + } + ] + }, None), +}) + + +svm_uuid = 'a7d278fb-2d2d-11ee-b8da-005056b37403' +unix_path = '/example1/' +unix_path_encoded = unix_path.replace('/', '%2F') + + +def test_successful_create(): + ''' Test successful rest create ''' + register_responses([ + ('GET', 'cluster', SRR['is_rest_96']), + ('GET', 'protocols/cifs/unix-symlink-mapping', SRR['empty_records']), + ('POST', 'protocols/cifs/unix-symlink-mapping', SRR['empty_good']), + ]) + args = { + 'state': 'present', + 'vserver': 'ansibleSVM', + 'unix_path': '/example1/', + 'share_name': 'share1', + 'cifs_path': '/path1/test_dir/' + } + assert create_and_apply(my_module, DEFAULT_ARGS, args)['changed'] + + +def test_successful_create_idempotency(): + ''' Test successful rest create idempotency ''' + register_responses([ + ('GET', 'cluster', SRR['is_rest_96']), + ('GET', 'protocols/cifs/unix-symlink-mapping', SRR['symlink_mapping']), + ]) + args = { + 'state': 'present', + 'vserver': 'ansibleSVM', + 'unix_path': '/example1/', + 'share_name': 'share1', + 'cifs_path': '/path1/test_dir/' + } + assert create_and_apply(my_module, DEFAULT_ARGS, args)['changed'] is False + + +def test_successful_delete(): + ''' Test successful rest delete ''' + unix_path_encoded = '%2Fexample1%2F' + register_responses([ + ('GET', 'cluster', SRR['is_rest_96']), + ('GET', 'protocols/cifs/unix-symlink-mapping', SRR['symlink_mapping']), + ('DELETE', 'protocols/cifs/unix-symlink-mapping/%s/%s' % (svm_uuid, unix_path_encoded), SRR['success']), + ]) + args = { + 'state': 'absent', + 'vserver': 'ansibleSVM', + 'unix_path': '/example1/' + } + assert create_and_apply(my_module, DEFAULT_ARGS, args)['changed'] + + +def test_successful_delete_idempotency(): + ''' Test successful rest delete idempotency ''' + register_responses([ + ('GET', 'cluster', SRR['is_rest_96']), + ('GET', 'protocols/cifs/unix-symlink-mapping', SRR['empty_records']), + ]) + args = { + 'state': 'absent', + 'vserver': 'ansibleSVM', + 'unix_path': '/example1/' + } + assert create_and_apply(my_module, DEFAULT_ARGS, args)['changed'] is False + + +def test_successful_modify(): + ''' Test successful rest modify ''' + register_responses([ + ('GET', 'cluster', SRR['is_rest_96']), + ('GET', 'protocols/cifs/unix-symlink-mapping', SRR['symlink_mapping']), + ('PATCH', 'protocols/cifs/unix-symlink-mapping/%s/%s' % (svm_uuid, unix_path_encoded), SRR['success']), + ]) + args = { + 'state': 'present', + 'vserver': 'ansibleSVM', + 'unix_path': '/example1/', + 'share_name': 'share2', + 'cifs_path': '/path2/test_dir/' + } + assert create_and_apply(my_module, DEFAULT_ARGS, args)['changed'] + + +def test_error_get(): + ''' Test error rest get ''' + register_responses([ + ('GET', 'cluster', SRR['is_rest_96']), + ('GET', 'protocols/cifs/unix-symlink-mapping', SRR['generic_error']), + ]), + args = { + 'state': 'present', + 'vserver': 'ansibleSVM', + 'unix_path': '/example1/', + 'share_name': 'share1', + 'cifs_path': '/path1/test_dir/' + } + error = create_and_apply(my_module, DEFAULT_ARGS, args, fail=True)['msg'] + assert 'Error while fetching cifs unix symlink mapping' in error + + +def test_error_create(): + ''' Test error rest create ''' + register_responses([ + ('GET', 'cluster', SRR['is_rest_96']), + ('GET', 'protocols/cifs/unix-symlink-mapping', SRR['empty_records']), + ('POST', 'protocols/cifs/unix-symlink-mapping', SRR['generic_error']), + ]), + args = { + 'state': 'present', + 'vserver': 'ansibleSVM', + 'unix_path': '/example1/', + 'share_name': 'share1', + 'cifs_path': '/path1/test_dir/' + } + error = create_and_apply(my_module, DEFAULT_ARGS, args, fail=True)['msg'] + assert 'Error while creating cifs unix symlink mapping' in error + + +def test_error_modify(): + ''' Test error rest modify ''' + register_responses([ + ('GET', 'cluster', SRR['is_rest_96']), + ('GET', 'protocols/cifs/unix-symlink-mapping', SRR['symlink_mapping']), + ('PATCH', 'protocols/cifs/unix-symlink-mapping/%s/%s' % (svm_uuid, unix_path_encoded), SRR['generic_error']), + ]) + args = { + 'state': 'present', + 'vserver': 'ansibleSVM', + 'unix_path': '/example1/', + 'share_name': 'share2', + 'cifs_path': '/path2/test_dir/' + } + error = create_and_apply(my_module, DEFAULT_ARGS, args, fail=True)['msg'] + assert 'Error while modifying cifs unix symlink mapping' in error + + +def test_error_delete(): + ''' Test error rest delete ''' + register_responses([ + ('GET', 'cluster', SRR['is_rest_96']), + ('GET', 'protocols/cifs/unix-symlink-mapping', SRR['symlink_mapping']), + ('DELETE', 'protocols/cifs/unix-symlink-mapping/%s/%s' % (svm_uuid, unix_path_encoded), SRR['generic_error']), + ]) + args = { + 'state': 'absent', + 'vserver': 'ansibleSVM', + 'unix_path': '/example1/' + } + error = create_and_apply(my_module, DEFAULT_ARGS, args, fail=True)['msg'] + assert 'Error while deleting cifs unix symlink mapping' in error + + +def test_error_ontap96(): + ''' Test error module supported from 9.6 ''' + register_responses([ + ('GET', 'cluster', SRR['is_rest']) + ]) + args = { + 'state': 'present', + 'vserver': 'ansibleSVM', + 'unix_path': 'example1', + 'share_name': 'share1', + 'cifs_path': '/path1/test_dir/' + } + assert 'requires ONTAP 9.6.0 or later' in call_main(my_main, DEFAULT_ARGS, args, fail=True)['msg'] + + +def test_missing_options_state_present(): + ''' Test error missing options with state=present ''' + register_responses([]) + args = { + 'state': 'present', + 'vserver': 'ansibleSVM', + 'unix_path': '/example1/' + } + error = create_module(my_module, DEFAULT_ARGS, args, fail=True)['msg'] + assert 'state is present but all of the following are missing: share_name, cifs_path' in error + + +def test_missing_options_locality_widelink(): + ''' Test error missing cifs_server with locality=widelink ''' + register_responses([]) + args = { + 'state': 'present', + 'vserver': 'ansibleSVM', + 'unix_path': '/example1/', + 'share_name': 'share1', + 'cifs_path': '/path1/test_dir/', + 'locality': 'widelink' + } + error = create_module(my_module, DEFAULT_ARGS, args, fail=True)['msg'] + assert 'locality is widelink but all of the following are missing: cifs_server' in error diff --git a/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_cli_timeout_rest.py b/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_cli_timeout_rest.py new file mode 100644 index 000000000..86ec7640e --- /dev/null +++ b/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_cli_timeout_rest.py @@ -0,0 +1,104 @@ +# Copyright: NetApp, Inc +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +""" unit tests for Ansible module: na_ontap_cli_timeout """ + +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type +import pytest +import sys + +import ansible_collections.netapp.ontap.plugins.module_utils.netapp as netapp_utils +# pylint: disable=unused-import +from ansible_collections.netapp.ontap.tests.unit.plugins.module_utils.ansible_mocks import patch_ansible, \ + create_and_apply, create_module, call_main, expect_and_capture_ansible_exception +from ansible_collections.netapp.ontap.tests.unit.framework.mock_rest_and_zapi_requests import get_mock_record, \ + patch_request_and_invoke, register_responses +from ansible_collections.netapp.ontap.tests.unit.framework.rest_factory import rest_responses + +from ansible_collections.netapp.ontap.plugins.modules.na_ontap_cli_timeout \ + import NetAppOntapCliTimeout as my_module, main as my_main # module under test + +if not netapp_utils.HAS_REQUESTS and sys.version_info < (2, 7): + pytestmark = pytest.mark.skip( + 'Skipping Unit Tests on 2.6 as requests is not available') + + +DEFAULT_ARGS = { + 'hostname': 'hostname', + 'username': 'username', + 'password': 'password', + 'use_rest': 'always', + 'state': 'present' +} + + +# REST API canned responses when mocking send_request. +# The rest_factory provides default responses shared across testcases. +SRR = rest_responses({ + # module specific responses + 'cli_timeout': (200, { + 'timeout': 30 + }, None), +}) + + +def test_successful_modify(): + ''' Test successful modify timeout value ''' + register_responses([ + ('GET', 'cluster', SRR['is_rest_96']), + ('GET', 'private/cli/system/timeout', SRR['cli_timeout']), # get timeout value + ('PATCH', 'private/cli/system/timeout', SRR['success']), # modify timeout value + ]) + args = { + 'timeout': 0 + } + assert create_and_apply(my_module, DEFAULT_ARGS, args)['changed'] + + +def test_successful_modify_idempotency(): + ''' Test successful modify timeout value idempotency ''' + register_responses([ + ('GET', 'cluster', SRR['is_rest_96']), + ('GET', 'private/cli/system/timeout', SRR['cli_timeout']), # get timeout value + ]) + args = { + 'timeout': 30 + } + assert not create_and_apply(my_module, DEFAULT_ARGS, args)['changed'] + + +def test_all_methods_catch_exception(): + ''' Test exception in get/modify timeout value ''' + register_responses([ + ('GET', 'cluster', SRR['is_rest_96']), + # GET/PATCH error + ('GET', 'private/cli/system/timeout', SRR['generic_error']), + ('PATCH', 'private/cli/system/timeout', SRR['generic_error']) + ]) + args = { + 'timeout': 15 + } + cli_timeout = create_module(my_module, DEFAULT_ARGS, args) + error = 'Error fetching CLI sessions timeout value' + assert error in expect_and_capture_ansible_exception(cli_timeout.get_timeout_value_rest, 'fail')['msg'] + error = 'Error modifying CLI sessions timeout value' + assert error in expect_and_capture_ansible_exception(cli_timeout.modify_timeout_value_rest, 'fail', args)['msg'] + + +def test_missing_options(): + ''' Test error missing required option timeout ''' + register_responses([]) + error = create_module(my_module, DEFAULT_ARGS, fail=True)['msg'] + assert 'missing required arguments: timeout' in error + + +def test_error_ontap96(): + ''' Test error module supported from 9.6 ''' + register_responses([ + ('GET', 'cluster', SRR['is_rest']) + ]) + args = { + 'timeout': 15 + } + assert 'requires ONTAP 9.6.0 or later' in call_main(my_main, DEFAULT_ARGS, args, fail=True)['msg'] diff --git a/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_cluster.py b/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_cluster.py index 89fe069a3..bfd5ca4ef 100644 --- a/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_cluster.py +++ b/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_cluster.py @@ -404,11 +404,13 @@ SRR = { 'is_rest_95': (200, dict(version=dict(generation=9, major=5, minor=0, full='dummy_9_5_0')), None), 'is_rest_96': (200, dict(version=dict(generation=9, major=6, minor=0, full='dummy_9_6_0')), None), 'is_rest_97': (200, dict(version=dict(generation=9, major=7, minor=0, full='dummy_9_7_0')), None), + 'is_rest_910': (200, dict(version=dict(generation=9, major=10, minor=1, full='dummy_9_10_1')), None), 'is_zapi': (400, {}, "Unreachable"), 'empty_good': ({}, None, None), 'zero_record': (200, {'records': []}, None), 'precluster': (500, None, {'message': 'are available in precluster.'}), 'cluster_identity': (200, {'location': 'Oz', 'name': 'abc'}, None), + 'cluster_web_service': (200, {'certificate': {'uuid': 'abcd12'}}, None), 'nodes': (200, {'records': [ {'name': 'node2', 'uuid': 'uuid2', 'cluster_interfaces': [{'ip': {'address': '10.10.10.2'}}]} ]}, None), @@ -470,6 +472,28 @@ def test_rest_create_timezone(mock_request, patch_ansible): assert len(mock_request.mock_calls) == 3 +@patch('ansible_collections.netapp.ontap.plugins.module_utils.netapp.OntapRestAPI.send_request') +def test_rest_modify_certificate(mock_request, patch_ansible): + ''' modify cluster certificate ''' + args = dict(set_default_args()) + args['certificate'] = {'uuid': 'abcd123'} + set_module_args(args) + mock_request.side_effect = [ + SRR['is_rest_910'], + SRR['cluster_identity'], # get /cluster + SRR['cluster_web_service'], # get /cluster/web + SRR['empty_good'], # patch /cluster + SRR['empty_good'], # patch /cluster/web + SRR['end_of_sequence'] + ] + my_obj = my_module() + with pytest.raises(AnsibleExitJson) as exc: + my_obj.apply() + print(mock_request.mock_calls) + assert exc.value.args[0]['changed'] is True + assert len(mock_request.mock_calls) == 5 + + @patch('ansible_collections.netapp.ontap.plugins.module_utils.netapp.OntapRestAPI.send_request') def test_rest_create_single(mock_request, patch_ansible): ''' create cluster ''' diff --git a/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_cluster_peer.py b/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_cluster_peer.py index 7551a619e..190169cee 100644 --- a/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_cluster_peer.py +++ b/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_cluster_peer.py @@ -267,6 +267,38 @@ def test_delete_idempotency_rest(): assert create_and_apply(my_module, DEFAULT_ARGS, module_args) +def test_successful_modify_rest(): + ''' Test successful modify ''' + module_args = DEFAULT_ARGS + module_args['dest_intercluster_lifs'] = ['10.193.179.58'] + module_args['source_intercluster_lifs'] = ['10.193.179.181'] + register_responses([ + ('GET', 'cluster', SRR['is_rest']), + ('GET', 'cluster', SRR['is_rest']), + ('GET', 'cluster/peers', SRR['cluster_peer_src']), + ('GET', 'cluster/peers', SRR['cluster_peer_dst']), + ('PATCH', 'cluster/peers/1fg98aba-2aa6-11ec-b7be-005fgvb366e1', SRR['empty_good']), + ('PATCH', 'cluster/peers/1e698aba-2aa6-11ec-b7be-005056b366e1', SRR['empty_good']) + ]) + assert create_and_apply(my_module, module_args) + + +def test_modify_idempotency_rest(): + ''' Test successful modify idempotency ''' + module_args = DEFAULT_ARGS + module_args['dest_intercluster_lifs'] = ['10.193.179.58'] + module_args['source_intercluster_lifs'] = ['10.193.179.181'] + SRR['cluster_peer_src'][1]['records'][0]['remote']['ip_addresses'] = ['10.193.179.58'] + SRR['cluster_peer_dst'][1]['records'][0]['remote']['ip_addresses'] = ['10.193.179.181'] + register_responses([ + ('GET', 'cluster', SRR['is_rest']), + ('GET', 'cluster', SRR['is_rest']), + ('GET', 'cluster/peers', SRR['cluster_peer_src']), + ('GET', 'cluster/peers', SRR['cluster_peer_dst']) + ]) + assert create_and_apply(my_module, module_args) + + def test_error_get_cluster_peer_rest(): ''' Test get error ''' register_responses([ @@ -303,3 +335,19 @@ def test_error_create_cluster_peer_rest(): ]) error = create_and_apply(my_module, DEFAULT_ARGS, fail=True)['msg'] assert 'calling: cluster/peers: got Expected error.' == error + + +def test_error_modify_cluster_peer_rest(): + ''' Test modify error ''' + module_args = DEFAULT_ARGS + module_args['dest_intercluster_lifs'] = ['10.193.179.59'] + module_args['source_intercluster_lifs'] = ['10.193.179.180'] + register_responses([ + ('GET', 'cluster', SRR['is_rest']), + ('GET', 'cluster', SRR['is_rest']), + ('GET', 'cluster/peers', SRR['cluster_peer_src']), + ('GET', 'cluster/peers', SRR['cluster_peer_dst']), + ('PATCH', 'cluster/peers/1fg98aba-2aa6-11ec-b7be-005fgvb366e1', SRR['generic_error']), + ]) + error = create_and_apply(my_module, DEFAULT_ARGS, module_args, fail=True)['msg'] + assert 'calling: cluster/peers/1fg98aba-2aa6-11ec-b7be-005fgvb366e1: got Expected error.' == error diff --git a/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_dns.py b/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_dns.py index c592f5c88..bb88ee5fc 100644 --- a/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_dns.py +++ b/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_dns.py @@ -33,6 +33,7 @@ SRR = rest_responses({ 'end_of_sequence': (500, None, "Unexpected call to send_request"), 'generic_error': (400, None, "Expected error"), 'dns_record': (200, {"records": [{"domains": ['test.com'], + "uuid": "02c9e252-41be-11e9-81d5-00a0986138f7", "servers": ['0.0.0.0'], "svm": {"name": "svm1", "uuid": "02c9e252-41be-11e9-81d5-00a0986138f7"}}]}, None), 'cluster_data': (200, {"dns_domains": ['test.com'], diff --git a/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_ems_config_rest.py b/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_ems_config_rest.py new file mode 100644 index 000000000..71efac389 --- /dev/null +++ b/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_ems_config_rest.py @@ -0,0 +1,107 @@ +# Copyright: NetApp, Inc +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +""" unit tests for Ansible module: na_ontap_ems_config """ + +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type +import pytest +import sys + +import ansible_collections.netapp.ontap.plugins.module_utils.netapp as netapp_utils +# pylint: disable=unused-import +from ansible_collections.netapp.ontap.tests.unit.plugins.module_utils.ansible_mocks import patch_ansible, \ + create_and_apply, create_module, call_main, expect_and_capture_ansible_exception +from ansible_collections.netapp.ontap.tests.unit.framework.mock_rest_and_zapi_requests import get_mock_record, \ + patch_request_and_invoke, register_responses +from ansible_collections.netapp.ontap.tests.unit.framework.rest_factory import rest_responses + +from ansible_collections.netapp.ontap.plugins.modules.na_ontap_ems_config \ + import NetAppOntapEmsConfig as my_module, main as my_main # module under test + +if not netapp_utils.HAS_REQUESTS and sys.version_info < (2, 7): + pytestmark = pytest.mark.skip( + 'Skipping Unit Tests on 2.6 as requests is not available') + + +DEFAULT_ARGS = { + 'hostname': 'hostname', + 'username': 'username', + 'password': 'password', + 'use_rest': 'always', + 'state': 'present' +} + + +# REST API canned responses when mocking send_request. +# The rest_factory provides default responses shared across testcases. +SRR = rest_responses({ + # module specific responses + 'ems_config': (200, { + 'mail_from': 'administrator@mycompany.com', + 'mail_server': 'mail.mycompany.com', + 'pubsub_enabled': True + }, None), + 'ems_config_modified': (200, { + 'mail_from': 'admin@mycompany.com', + 'mail_server': 'mail.mycompany.com', + 'proxy_url': 'http://proxyserver.mycompany.com', + 'proxy_user': 'proxy_user', + 'pubsub_enabled': False + }, None), +}) + + +def test_successful_modify(): + ''' Test successful rest modify ems config with idempotency check''' + register_responses([ + ('GET', 'cluster', SRR['is_rest_9_10_1']), + ('GET', 'support/ems', SRR['ems_config']), # get ems config + ('PATCH', 'support/ems', SRR['success']), # modify ems config + + ('GET', 'cluster', SRR['is_rest_9_10_1']), # get ems config + ('GET', 'support/ems', SRR['ems_config_modified']), # modify ems config + ]) + args = { + 'mail_from': 'admin@mycompany.com', + 'mail_server': 'mail.mycompany.com', + 'pubsub_enabled': 'false' + } + assert create_and_apply(my_module, DEFAULT_ARGS, args)['changed'] + assert not create_and_apply(my_module, DEFAULT_ARGS, args)['changed'] + + +def test_all_methods_catch_exception(): + ''' Test exception in get/modify ems config ''' + register_responses([ + ('GET', 'cluster', SRR['is_rest_9_10_1']), + # GET/PATCH error + ('GET', 'support/ems', SRR['generic_error']), + ('PATCH', 'support/ems', SRR['generic_error']) + ]) + modify_args = { + 'pubsub_enabled': 'false' + } + ems_config = create_module(my_module, DEFAULT_ARGS) + assert 'Error fetching EMS config' in expect_and_capture_ansible_exception(ems_config.get_ems_config_rest, 'fail')['msg'] + assert 'Error modifying EMS config' in expect_and_capture_ansible_exception(ems_config.modify_ems_config_rest, 'fail', modify_args)['msg'] + + +def test_error_ontap96(): + ''' Test module supported from 9.6 ''' + register_responses([ + ('GET', 'cluster', SRR['is_rest']) + ]) + assert 'requires ONTAP 9.6.0 or later' in call_main(my_main, DEFAULT_ARGS, fail=True)['msg'] + + +def test_version_error_with_pubsub_enabled(): + ''' Test version error for pubsub_enabled ''' + register_responses([ + ('GET', 'cluster', SRR['is_rest_96']) + ]) + args = { + 'pubsub_enabled': 'false' + } + error = create_module(my_module, DEFAULT_ARGS, args, fail=True)['msg'] + assert 'Minimum version of ONTAP for pubsub_enabled is (9, 10, 1)' in error diff --git a/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_ems_destination.py b/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_ems_destination.py index ca951ba58..dacc2cb97 100644 --- a/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_ems_destination.py +++ b/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_ems_destination.py @@ -48,7 +48,50 @@ SRR = rest_responses({ "destination": "https://test.destination" }], "num_records": 1 - }, None) + }, None), + 'certificate_record_1': (200, + {'records': [{"name": "cert_1", + "uuid": "cert_uuid_1", + "serial_number": "cert_serial"}]}, None), + 'ems_destination_with_cert': (200, { + "records": [ + { + "name": "test", + "type": "rest-api", + "destination": "https://test.destination", + "certificate": { + "ca": "cert_ca", + "name": "cert1" + }, + "filters": [ + { + "name": "test-filter" + } + ] + }], + "num_records": 1 + }, None), + 'ems_destination_type_syslog': (200, { + "records": [ + { + "name": "test", + "type": "syslog", + "destination": "https://test.destination", + "filters": [ + { + "name": "test-filter" + } + ], + "syslog": { + "port": 514, + "transport": "udp_unencrypted", + "message_format": "legacy_netapp", + "timestamp_format_override": "no_override", + "hostname_format_override": "no_override" + } + }], + "num_records": 1 + }, None), }) DEFAULT_ARGS = { @@ -90,6 +133,40 @@ def test_create_ems_destination(): assert create_and_apply(my_module, DEFAULT_ARGS, module_args)['changed'] +def test_create_ems_destination_with_cert(): + register_responses([ + ('GET', 'cluster', SRR['is_rest_9_11_1']), + ('GET', 'support/ems/destinations', SRR['empty_records']), + ('GET', 'security/certificates', SRR['certificate_record_1']), + ('POST', 'support/ems/destinations', SRR['empty_good']) + ]) + module_args = { + 'name': 'test', + 'type': 'rest_api', + 'destination': 'https://test.destination', + 'filters': ['test-filter'], + 'certificate': 'cert1', + 'ca': 'cert_ca', + } + assert create_and_apply(my_module, DEFAULT_ARGS, module_args)['changed'] + + +def test_error_create_ems_destination_with_cert_unsupported_rest(): + register_responses([ + ('GET', 'cluster', SRR['is_rest_9_10_1']), + ]) + module_args = { + 'name': 'test', + 'type': 'rest_api', + 'destination': 'https://test.destination', + 'filters': ['test-filter'], + 'certificate': 'cert1', + 'ca': 'cert_ca', + } + error = call_main(my_main, DEFAULT_ARGS, module_args, fail=True)['msg'] + assert 'na_ontap_ems_destination is only supported with REST API' == error + + def test_create_ems_destination_error(): register_responses([ ('GET', 'cluster', SRR['is_rest_9_10_1']), @@ -224,3 +301,161 @@ def test_empty_modify_skips_patch(): module_args = {'name': 'test', 'type': 'rest_api', 'destination': 'https://test.destination', 'filters': ['test-filter']} my_obj = create_module(my_module, DEFAULT_ARGS, module_args) my_obj.modify_ems_destination('test', {}) + + +def test_module_error_missing_required_together_param(): + module_args = { + 'name': 'test', + 'type': 'rest_api', + 'destination': 'https://test.destination', + 'filters': ['test-filter'], + 'certificate': 'cert1', + } + error = call_main(my_main, DEFAULT_ARGS, module_args, fail=True)['msg'] + assert 'parameters are required together: certificate, ca' == error + + +def test_module_error_cert_not_found(): + register_responses([ + ('GET', 'cluster', SRR['is_rest_9_11_1']), + ('GET', 'support/ems/destinations', SRR['ems_destination']), + ('GET', 'security/certificates', SRR['empty_records']), + ]) + module_args = { + 'name': 'test', + 'type': 'rest_api', + 'destination': 'https://test.destination', + 'filters': ['test-filter'], + 'certificate': 'cert1', + 'ca': 'my_cert_ca', + } + error = call_main(my_main, DEFAULT_ARGS, module_args, fail=True)['msg'] + assert 'Error certificate not found: cert1.' == error + + +def test_module_error_rest_get_cert(): + register_responses([ + ('GET', 'cluster', SRR['is_rest_9_11_1']), + ('GET', 'support/ems/destinations', SRR['ems_destination']), + ('GET', 'security/certificates', SRR['generic_error']), + ]) + module_args = { + 'name': 'test', + 'type': 'rest_api', + 'destination': 'https://test.destination', + 'filters': ['test-filter'], + 'certificate': 'cert1', + 'ca': 'my_cert_ca', + } + error = call_main(my_main, DEFAULT_ARGS, module_args, fail=True)['msg'] + assert 'Error retrieving certificates: calling: security/certificates: got Expected error.' == error + + +def test_modify_ems_cert(): + register_responses([ + ('GET', 'cluster', SRR['is_rest_9_11_1']), + ('GET', 'support/ems/destinations', SRR['ems_destination']), + ('GET', 'security/certificates', SRR['certificate_record_1']), + ('PATCH', 'support/ems/destinations/test', SRR['empty_good']) + ]) + module_args = { + 'name': 'test', + 'type': 'rest_api', + 'destination': 'https://test.destination', + 'filters': ['test-filter'], + 'certificate': 'cert1', + 'ca': 'cert_ca', + } + assert create_and_apply(my_module, DEFAULT_ARGS, module_args)['changed'] + + +def test_create_ems_destination_with_type_syslog(): + register_responses([ + ('GET', 'cluster', SRR['is_rest_9_12_1']), + ('GET', 'support/ems/destinations', SRR['empty_records']), + ('POST', 'support/ems/destinations', SRR['empty_good']) + ]) + module_args = { + 'name': 'test', + 'type': 'syslog', + 'destination': 'https://test.destination', + 'filters': ['test-filter'], + 'syslog': { + 'port': 514, + 'transport': 'udp_unencrypted', + 'message_format': 'legacy_netapp', + 'timestamp_format_override': 'no_override', + 'hostname_format_override': 'no_override' + } + } + assert create_and_apply(my_module, DEFAULT_ARGS, module_args)['changed'] + + +def test_error_ontap_9_12_1(): + ''' syslog option supported from 9.12.1 ''' + register_responses([ + ('GET', 'cluster', SRR['is_rest_9_10_1']) + ]) + module_args = { + 'name': 'test', + 'type': 'syslog', + 'use_rest': 'always', + 'destination': 'https://test.destination', + 'filters': ['test-filter'], + 'syslog': { + 'port': 514, + 'transport': 'udp_unencrypted', + 'message_format': 'legacy_netapp', + 'timestamp_format_override': 'no_override', + 'hostname_format_override': 'no_override' + } + } + assert 'Error: Minimum version of ONTAP for syslog is (9, 12, 1). Current version: (9, 10, 1).' in call_main(my_main, DEFAULT_ARGS, + module_args, fail=True)['msg'] + + +def test_create_ems_destination_with_type_syslog_and_add_certs(): + register_responses([ + ('GET', 'cluster', SRR['is_rest_9_12_1']), + ('GET', 'support/ems/destinations', SRR['empty_records']), + ('GET', 'security/certificates', SRR['certificate_record_1']), + ('POST', 'support/ems/destinations', SRR['empty_good']) + ]) + module_args = { + 'name': 'test', + 'type': 'syslog', + 'destination': 'https://test.destination', + 'filters': ['test-filter'], + 'certificate': 'cert1', + 'ca': 'cert_ca', + 'syslog': { + 'port': 514, + 'transport': 'udp_unencrypted', + 'message_format': 'legacy_netapp', + 'timestamp_format_override': 'no_override', + 'hostname_format_override': 'no_override' + } + } + assert create_and_apply(my_module, DEFAULT_ARGS, module_args)['changed'] + + +def test_modify_ems_destination_with_type_syslog(): + register_responses([ + ('GET', 'cluster', SRR['is_rest_9_12_1']), + ('GET', 'support/ems/destinations', SRR['ems_destination_type_syslog']), + ('PATCH', 'support/ems/destinations/test', SRR['empty_good']) + ]) + module_args = { + 'name': 'test', + 'type': 'syslog', + 'destination': 'https://test.destination', + 'filters': ['test-filter'], + 'syslog': { + 'port': 614, + 'transport': 'tcp_unencrypted', + 'message_format': 'rfc_5424', + 'timestamp_format_override': 'no_override', + 'hostname_format_override': 'fqdn' + } + } + assert create_and_apply(my_module, DEFAULT_ARGS, module_args)['changed'] diff --git a/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_ems_filter.py b/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_ems_filter.py index f7f0a1feb..8223944c9 100644 --- a/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_ems_filter.py +++ b/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_ems_filter.py @@ -25,6 +25,18 @@ if not netapp_utils.HAS_REQUESTS and sys.version_info < (2, 7): pytestmark = pytest.mark.skip('Skipping Unit Tests on 2.6 as requests is not available') SRR = rest_responses({ + 'default_ems_filter': (200, { + "name": "snmp-traphost", + "rules": [{ + "index": "1", + "type": "exclude", + "message_criteria": { + "severities": "*", + "name_pattern": "*", + "snmp_trap_types": "*", + } + }] + }, None), 'ems_filter': (200, { "name": "snmp-traphost", "rules": [{ @@ -44,7 +56,8 @@ SRR = rest_responses({ } }] }, None), - 'ems_filter_2_riles': (200, { + 'post_empty_good': (201, {}, None), + 'ems_filter_2_rules': (200, { "name": "snmp-traphost", "rules": [{ "index": "1", @@ -70,6 +83,39 @@ SRR = rest_responses({ } }] }, None), + 'ems_filter_3_rules': (200, { + "name": "snmp-traphost", + "rules": [{ + "index": "1", + "type": "include", + "message_criteria": { + "severities": "error", + "name_pattern": "*", + } + }, { + "index": "2", + "type": "include", + "message_criteria": { + "severities": "alert", + "name_pattern": "callhome.*", + } + }, { + "index": "3", + "type": "include", + "message_criteria": { + "severities": "emergency", + "name_pattern": "callhome.*", + } + }, { + "index": "4", + "type": "exclude", + "message_criteria": { + "severities": "*", + "name_pattern": "*", + "snmp_trap_types": "*", + } + }] + }, None), 'ems_filter_no_rules': (200, { "name": "snmp-traphost", }, None) @@ -94,17 +140,19 @@ DEFAULT_RULE = [{ DEFAULT_RULE_2_RULES = [{ "index": "1", - "type": "include", + "type": "exclude", "message_criteria": { "severities": "error,informational", "name_pattern": "callhome.*", - }}, { + } +}, { "index": "2", - "type": "include", + "type": "exclude", "message_criteria": { - "severities": "alert", - "name_pattern": "callhome.*", - }}] + "severities": "*", + "name_pattern": "*", + } +}] DEFAULT_RULE_MODIFY_TYPE_2_RULES = [{ "index": "1", @@ -126,7 +174,7 @@ DEFAULT_RULE_MODIFY_SEVERITIES_2_RULES = [{ "index": "1", "type": "include", "message_criteria": { - "severities": "informational", + "severities": "notice", "name_pattern": "callhome.*", } }, { @@ -150,6 +198,29 @@ DEFAULT_RULE_MODIFY_NAME_PATTERN_2_RULES = [{ "type": "include", "message_criteria": { "severities": "alert", + "name_pattern": "*", + } +}] + +DEFAULT_RULE_MODIFY_SEVERITIES_3_RULES = [{ + "index": "1", + "type": "include", + "message_criteria": { + "severities": "error, informational", + "name_pattern": "*", + } +}, { + "index": "2", + "type": "include", + "message_criteria": { + "severities": "alert", + "name_pattern": "callhome.*", + } +}, { + "index": "3", + "type": "include", + "message_criteria": { + "severities": "emergency", "name_pattern": "callhome.*", } }] @@ -241,28 +312,32 @@ def test_delete_ems_filter_error(): def test_modify_ems_filter_add_rule(): register_responses([ ('GET', 'cluster', SRR['is_rest_9_10_1']), - ('GET', 'support/ems/filters', SRR['ems_filter']), - ('PATCH', 'support/ems/filters/snmp-traphost', SRR['empty_good']) + ('GET', 'support/ems/filters', SRR['default_ems_filter']), + ('POST', 'support/ems/filters/snmp-traphost/rules', SRR['post_empty_good']), ]) - module_args = {'rules': DEFAULT_RULE_2_RULES} + module_args = {'rules': DEFAULT_RULE} assert create_and_apply(my_module, DEFAULT_ARGS, module_args)['changed'] def test_modify_ems_filter_change_type(): register_responses([ ('GET', 'cluster', SRR['is_rest_9_10_1']), - ('GET', 'support/ems/filters', SRR['ems_filter_2_riles']), - ('PATCH', 'support/ems/filters/snmp-traphost', SRR['empty_good']) + ('GET', 'support/ems/filters', SRR['ems_filter']), + ('PATCH', 'support/ems/filters/snmp-traphost', SRR['empty_good']), + ('POST', 'support/ems/filters/snmp-traphost/rules', SRR['post_empty_good']), + ('POST', 'support/ems/filters/snmp-traphost/rules', SRR['post_empty_good']) ]) - module_args = {'rules': DEFAULT_RULE_MODIFY_TYPE_2_RULES} + module_args = {'rules': DEFAULT_RULE_2_RULES} assert create_and_apply(my_module, DEFAULT_ARGS, module_args)['changed'] def test_modify_ems_filter_change_severities(): register_responses([ ('GET', 'cluster', SRR['is_rest_9_10_1']), - ('GET', 'support/ems/filters', SRR['ems_filter_2_riles']), - ('PATCH', 'support/ems/filters/snmp-traphost', SRR['empty_good']) + ('GET', 'support/ems/filters', SRR['ems_filter_2_rules']), + ('PATCH', 'support/ems/filters/snmp-traphost', SRR['empty_good']), + ('POST', 'support/ems/filters/snmp-traphost/rules', SRR['post_empty_good']), + ('POST', 'support/ems/filters/snmp-traphost/rules', SRR['post_empty_good']) ]) module_args = {'rules': DEFAULT_RULE_MODIFY_SEVERITIES_2_RULES} assert create_and_apply(my_module, DEFAULT_ARGS, module_args)['changed'] @@ -271,21 +346,38 @@ def test_modify_ems_filter_change_severities(): def test_modify_ems_filter_change_name_pattern(): register_responses([ ('GET', 'cluster', SRR['is_rest_9_10_1']), - ('GET', 'support/ems/filters', SRR['ems_filter_2_riles']), - ('PATCH', 'support/ems/filters/snmp-traphost', SRR['empty_good']) + ('GET', 'support/ems/filters', SRR['ems_filter_2_rules']), + ('PATCH', 'support/ems/filters/snmp-traphost', SRR['empty_good']), + ('POST', 'support/ems/filters/snmp-traphost/rules', SRR['post_empty_good']), + ('POST', 'support/ems/filters/snmp-traphost/rules', SRR['post_empty_good']) ]) module_args = {'rules': DEFAULT_RULE_MODIFY_NAME_PATTERN_2_RULES} assert create_and_apply(my_module, DEFAULT_ARGS, module_args)['changed'] +def test_modify_ems_filter_add_rule_and_change_severities(): + register_responses([ + ('GET', 'cluster', SRR['is_rest_9_10_1']), + ('GET', 'support/ems/filters', SRR['ems_filter_2_rules']), + ('PATCH', 'support/ems/filters/snmp-traphost', SRR['empty_good']), + ('POST', 'support/ems/filters/snmp-traphost/rules', SRR['post_empty_good']), + ('POST', 'support/ems/filters/snmp-traphost/rules', SRR['post_empty_good']), + ('POST', 'support/ems/filters/snmp-traphost/rules', SRR['post_empty_good']) + ]) + module_args = {'rules': DEFAULT_RULE_MODIFY_SEVERITIES_3_RULES} + assert create_and_apply(my_module, DEFAULT_ARGS, module_args)['changed'] + + def test_modify_ems_filter_error(): register_responses([ ('GET', 'cluster', SRR['is_rest_9_10_1']), - ('PATCH', 'support/ems/filters/snmp-traphost', SRR['generic_error']) + ('PATCH', 'support/ems/filters/snmp-traphost', SRR['generic_error']), ]) my_obj = create_module(my_module, DEFAULT_ARGS) - my_obj.parameters['rules'] = DEFAULT_RULE_2_RULES - error = expect_and_capture_ansible_exception(my_obj.modify_ems_filter, 'fail')['msg'] + patch_rules = [{'index': 1, 'type': 'include', 'message_criteria': {'severities': 'error', 'name_pattern': '*'}}] + post_rules = [{'index': 2, 'type': 'include', 'message_criteria': {'severities': 'notice', 'name_pattern': '*'}}] + desired_rules = {'patch_rules': patch_rules, 'post_rules': post_rules} + error = expect_and_capture_ansible_exception(my_obj.modify_ems_filter, 'fail', desired_rules)['msg'] print('Info: %s' % error) assert 'Error modifying EMS filter snmp-traphost: calling: support/ems/filters/snmp-traphost: got Expected error.' == error @@ -293,7 +385,7 @@ def test_modify_ems_filter_error(): def test_modify_ems_filter_no_rules(): register_responses([ ('GET', 'cluster', SRR['is_rest_9_10_1']), - ('GET', 'support/ems/filters', SRR['ems_filter_no_rules']), + ('GET', 'support/ems/filters', SRR['default_ems_filter']), ]) assert not create_and_apply(my_module, DEFAULT_ARGS, {})['changed'] @@ -302,7 +394,8 @@ def test_modify_star_test(): register_responses([ ('GET', 'cluster', SRR['is_rest_9_10_1']), ('GET', 'support/ems/filters', SRR['ems_filter']), - ('PATCH', 'support/ems/filters/snmp-traphost', SRR['empty_good']) + ('PATCH', 'support/ems/filters/snmp-traphost', SRR['empty_good']), + ('POST', 'support/ems/filters/snmp-traphost/rules', SRR['post_empty_good']) ]) module_args = {'rules': DEFAULT_RULE_STARS} assert create_and_apply(my_module, DEFAULT_ARGS, module_args)['changed'] diff --git a/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_info.py b/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_info.py index 18c35c910..c3c845b16 100644 --- a/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_info.py +++ b/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_info.py @@ -7,6 +7,7 @@ from __future__ import (absolute_import, division, print_function) __metaclass__ = type import pytest +import sys from ansible_collections.netapp.ontap.tests.unit.compat.mock import patch import ansible_collections.netapp.ontap.plugins.module_utils.netapp as netapp_utils @@ -21,6 +22,9 @@ from ansible_collections.netapp.ontap.plugins.modules.na_ontap_info import conve if not netapp_utils.has_netapp_lib(): pytestmark = pytest.mark.skip('skipping as missing required netapp_lib') +if sys.version_info < (2, 8): + pytestmark = pytest.mark.skip('Skipping Unit Tests on python 2') + DEFAULT_ARGS = { 'hostname': 'hostname', diff --git a/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_kerberos_realm.py b/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_kerberos_realm.py index 30f577d4c..c1db9d26a 100644 --- a/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_kerberos_realm.py +++ b/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_kerberos_realm.py @@ -1,4 +1,4 @@ -# (c) 2018-2022, NetApp, Inc +# (c) 2018-2023, NetApp, Inc # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) ''' unit test for ONTAP Kerberos Realm module ''' @@ -36,7 +36,8 @@ DEFAULT_ARGS = { 'realm': 'NETAPP.COM', 'vserver': 'vserver1', 'kdc_ip': '192.168.0.1', - 'kdc_vendor': 'other' + 'kdc_vendor': 'other', + } kerberos_info = { @@ -78,6 +79,15 @@ SRR = rest_responses({ "ip": "10.193.115.116", "port": 88 }, + "admin_server": { + "address": "10.193.115.116", + "port": 126 + }, + "password_server": { + "address": "1.2.3.4", + "port": 0 + }, + "clock_skew": 10, "comment": "mohan", "ad_server": { "name": "netapp", @@ -157,7 +167,7 @@ def test_if_all_methods_catch_exception(): ('kerberos-realm-create', ZRR['error']), ('kerberos-realm-modify', ZRR['error']), ('kerberos-realm-delete', ZRR['error']), - ('GET', 'cluster', SRR['is_rest_9_9_0']), + ('GET', 'cluster', SRR['is_rest_9_13_1']), ('GET', 'protocols/nfs/kerberos/realms', SRR['generic_error']), ('POST', 'protocols/nfs/kerberos/realms', SRR['generic_error']), ('PATCH', 'protocols/nfs/kerberos/realms/89368b07/NETAPP.COM', SRR['generic_error']), @@ -180,7 +190,7 @@ def test_if_all_methods_catch_exception(): def test_successfully_create_realm_rest(): ''' Test successfully create realm ''' register_responses([ - ('GET', 'cluster', SRR['is_rest_9_9_0']), + ('GET', 'cluster', SRR['is_rest_9_13_1']), ('GET', 'protocols/nfs/kerberos/realms', SRR['empty_records']), ('POST', 'protocols/nfs/kerberos/realms', SRR['success']), ]) @@ -191,11 +201,11 @@ def test_successfully_modify_realm_rest(): ''' Test modify realm successful for modifying kdc_ip. ''' register_responses([ # modify ip. - ('GET', 'cluster', SRR['is_rest_9_9_0']), + ('GET', 'cluster', SRR['is_rest_9_13_1']), ('GET', 'protocols/nfs/kerberos/realms', SRR['kerberos_info']), ('PATCH', 'protocols/nfs/kerberos/realms/89368b07/NETAPP.COM', SRR['success']), # modify port. - ('GET', 'cluster', SRR['is_rest_9_9_0']), + ('GET', 'cluster', SRR['is_rest_9_13_1']), ('GET', 'protocols/nfs/kerberos/realms', SRR['kerberos_info']), ('PATCH', 'protocols/nfs/kerberos/realms/89368b07/NETAPP.COM', SRR['success']), ]) @@ -206,8 +216,20 @@ def test_successfully_modify_realm_rest(): def test_successfully_delete_realm_rest(): ''' Test successfully delete realm ''' register_responses([ - ('GET', 'cluster', SRR['is_rest_9_9_0']), + ('GET', 'cluster', SRR['is_rest_9_13_1']), ('GET', 'protocols/nfs/kerberos/realms', SRR['kerberos_info']), ('DELETE', 'protocols/nfs/kerberos/realms/89368b07/NETAPP.COM', SRR['success']) ]) assert create_and_apply(my_module, DEFAULT_ARGS, {'use_rest': 'always', 'state': 'absent'}) + + +def test_error_with_params_supported_before_9_13_1_rest(): + register_responses([ + ('GET', 'cluster', SRR['is_rest_9_9_0']), + ]) + args = {'use_rest': 'always', 'admin_server_ip': '10.193.115.116', 'admin_server_port': 126, 'clock_skew': 10, 'pw_server_ip': '1.2.3.4', + 'pw_server_port': 0} + error = create_module(my_module, DEFAULT_ARGS, args, fail=True)['msg'] + unsupported_param_before_9_13_1 = ['admin_server_ip', 'admin_server_port', 'clock_skew', 'pw_server_ip', 'pw_server_port'] + msg = 'Error: Minimum version of ONTAP for ' + ' is (9, 13, 1).\nMinimum version of ONTAP for '.join(unsupported_param_before_9_13_1) + ' is (9, 13, 1).' + assert msg in error diff --git a/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_lun_rest.py b/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_lun_rest.py index fd65062d0..36d07815f 100644 --- a/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_lun_rest.py +++ b/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_lun_rest.py @@ -1,4 +1,4 @@ -# (c) 2022, NetApp, Inc +# (c) 2022-2023, NetApp, Inc # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) from __future__ import (absolute_import, division, print_function) @@ -36,6 +36,10 @@ SRR = rest_responses({ "name": "volume1", "uuid": "028baa66-41bd-11e9-81d5-00a0986138f7" }, + "qtree": { + "name": "qtree1", + "id": 1, + }, }, "name": "/vol/volume1/qtree1/lun1", "space": { @@ -77,6 +81,10 @@ SRR = rest_responses({ "name": "volume1", "uuid": "028baa66-41bd-11e9-81d5-00a0986138f7" }, + "qtree": { + "name": "qtree1", + "id": 1, + }, }, "name": "/vol/volume1/qtree1/lun1", "space": { @@ -114,6 +122,10 @@ SRR = rest_responses({ "name": "volume2", "uuid": "028baa66-41bd-11e9-81d5-00a0986138f3" }, + "qtree": { + "name": "qtree1", + "id": 1, + }, }, "name": "/vol/volume1/qtree1/lun2", "space": { diff --git a/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_net_ifgrp.py b/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_net_ifgrp.py index 7e3e58783..011d53083 100644 --- a/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_net_ifgrp.py +++ b/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_net_ifgrp.py @@ -1,4 +1,4 @@ -# (c) 2018, NetApp, Inc +# (c) 2018-2023, NetApp, Inc # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) ''' unit test template for ONTAP Ansible module ''' @@ -629,6 +629,7 @@ def test_create_ifgrp_port(mock_request, patch_ansible): SRR['is_rest_9_8'], # get version SRR['ifgrp_record_create'], # get SRR['empty_good'], # create + SRR['ifgrp_record_create'], # get details of created lag SRR['end_of_sequence'] ] my_obj = ifgrp_module() diff --git a/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_qos_policy_group.py b/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_qos_policy_group.py index c14b13151..44388fd54 100644 --- a/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_qos_policy_group.py +++ b/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_qos_policy_group.py @@ -1,7 +1,7 @@ # (c) 2018-2023, NetApp, Inc # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) -''' unit test template for ONTAP Ansible module ''' +''' unit test cases for ONTAP Ansible module: na_ontap_qos_policy_group ''' from __future__ import (absolute_import, division, print_function) __metaclass__ = type @@ -349,6 +349,10 @@ def test_successful_create_adaptive_rest(): ('GET', 'cluster', SRR['is_rest_9_10_1']), ('GET', 'storage/qos/policies', SRR['empty_records']), ('POST', 'storage/qos/policies', SRR['success']), + # with expected and peak IOPS per TB + ('GET', 'cluster', SRR['is_rest_9_10_1']), + ('GET', 'storage/qos/policies', SRR['empty_records']), + ('POST', 'storage/qos/policies', SRR['success']), ]) DEFAULT_ARGS_COPY = DEFAULT_ARGS_REST.copy() del DEFAULT_ARGS_COPY['fixed_qos_options'] @@ -358,9 +362,14 @@ def test_successful_create_adaptive_rest(): "peak_iops": 500 } assert create_and_apply(qos_policy_group_module, DEFAULT_ARGS_COPY)['changed'] + DEFAULT_ARGS_COPY['adaptive_qos_options']['block_size'] = '4k' assert create_and_apply(qos_policy_group_module, DEFAULT_ARGS_COPY)['changed'] + DEFAULT_ARGS_COPY['adaptive_qos_options']['expected_iops_allocation'] = 'used_space' + DEFAULT_ARGS_COPY['adaptive_qos_options']['peak_iops_allocation'] = 'allocated_space' + assert create_and_apply(qos_policy_group_module, DEFAULT_ARGS_COPY)['changed'] + def test_partially_supported_option_rest(): ''' Test delete error ''' @@ -370,16 +379,19 @@ def test_partially_supported_option_rest(): ]) error = create_module(qos_policy_group_module, DEFAULT_ARGS_REST, fail=True)['msg'] assert "Minimum version of ONTAP for 'fixed_qos_options.min_throughput_mbps' is (9, 8, 0)" in error + DEFAULT_ARGS_COPY = DEFAULT_ARGS_REST.copy() del DEFAULT_ARGS_COPY['fixed_qos_options'] DEFAULT_ARGS_COPY['adaptive_qos_options'] = { "absolute_min_iops": 100, "expected_iops": 200, "peak_iops": 500, - "block_size": "4k" + "block_size": "4k", + "expected_iops_allocation": "used_space", + "peak_iops_allocation": "allocated_space" } error = create_module(qos_policy_group_module, DEFAULT_ARGS_COPY, fail=True)['msg'] - assert "Minimum version of ONTAP for 'adaptive_qos_options.block_size' is (9, 10, 1)" in error + assert "using any of ['block_size', 'expected_iops_allocation', 'peak_iops_allocation'] requires ONTAP 9.10.1 or later and REST must be enabled" in error def test_error_create_adaptive_rest(): @@ -486,7 +498,9 @@ def test_successful_modify_adaptive_qos_options_rest(): 'expected_iops': 300, 'peak_iops': 600, 'absolute_min_iops': 200, - 'block_size': '4k' + 'block_size': '4k', + 'expected_iops_allocation': 'used_space', + 'peak_iops_allocation': 'allocated_space' } } assert create_and_apply(qos_policy_group_module, DEFAULT_ARGS_REST_COPY, args)['changed'] diff --git a/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_rest_info.py b/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_rest_info.py index bf678e3ac..08ea9f2bc 100644 --- a/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_rest_info.py +++ b/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_rest_info.py @@ -1,4 +1,4 @@ -# (c) 2020-2022, NetApp, Inc +# (c) 2020-2023, NetApp, Inc # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) ''' Unit Tests NetApp ONTAP REST APIs Ansible module: na_ontap_rest_info ''' @@ -56,6 +56,12 @@ SRR = rest_responses({ 'records': [{'name': 'dummy_vol1'}, {'name': 'dummy_vol2'}], 'version': 'ontap_version'}, None), + 'get_subset_info_without_hal_links': (200, + {'num_records': 3, + 'records': [{'name': 'dummy_vol1'}, + {'name': 'dummy_vol2'}, + {'name': 'dummy_vol3'}], + 'version': 'ontap_version'}, None), 'metrocluster_post': (200, {'job': { 'uuid': 'fde79888-692a-11ea-80c2-005056b39fe7', @@ -617,6 +623,17 @@ def set_default_args(): }) +def set_args_run_ontap_gather_facts_disable_hal_links(): + return dict({ + 'hostname': 'hostname', + 'username': 'username', + 'password': 'password', + 'https': True, + 'validate_certs': False, + 'hal_linking': False + }) + + def set_args_run_ontap_version_check(): return dict({ 'hostname': 'hostname', @@ -763,6 +780,19 @@ def test_version_warning_message(): 'your version of ONTAP cluster/metrocluster/diagnostics requires (9, 8), ') +def test_owning_resource_warning_message(): + gather_subset = ['cluster/nodes'] + register_responses([ + ('GET', 'cluster', SRR['is_rest_96']), + ('GET', 'storage/volumes', SRR['get_subset_info']), + ]) + extra_args = { + 'owning_resource': {'svm_name': 'testSVM'} + } + create_and_apply(ontap_rest_info_module, set_args_run_ontap_version_check(), extra_args) + assert_warning_was_raised("Kindly refer to Ansible documentation to check the subsets that support option 'owning_resource'.") + + # metrocluster/diagnostics doesn't call get_subset_info and has 3 api calls instead of 1 def test_run_metrocluster_pass(): gather_subset = ['cluster/metrocluster/diagnostics'] @@ -981,6 +1011,16 @@ def test_demo_subset(): assert 'cluster/nodes' in call_main(my_main, set_default_args(), {'gather_subset': 'demo'})['ontap_info'] +def test_demo_subset_without_hal_links(): + register_responses([ + ('GET', 'cluster', SRR['validate_ontap_version_pass']), + ('GET', 'cluster/software', SRR['get_subset_info_without_hal_links']), + ('GET', 'svm/svms', SRR['get_subset_info_without_hal_links']), + ('GET', 'cluster/nodes', SRR['get_subset_info_without_hal_links']), + ]) + assert 'cluster/nodes' in call_main(my_main, set_args_run_ontap_gather_facts_disable_hal_links(), {'gather_subset': 'demo'})['ontap_info'] + + def test_subset_with_default_fields(): register_responses([ ('GET', 'cluster', SRR['validate_ontap_version_pass']), diff --git a/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_restit.py b/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_restit.py index 89289386a..6b15d9087 100644 --- a/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_restit.py +++ b/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_restit.py @@ -97,7 +97,7 @@ def test_rest_run_default_get(mock_request, patch_ansible): my_obj = my_module() with pytest.raises(AnsibleExitJson) as exc: my_obj.apply() - assert exc.value.args[0]['changed'] is True + assert exc.value.args[0]['changed'] is False print(mock_request.mock_calls) assert len(mock_request.mock_calls) == 1 diff --git a/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_s3_services.py b/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_s3_services.py index fce59093a..4bbd43f82 100644 --- a/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_s3_services.py +++ b/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_s3_services.py @@ -1,4 +1,4 @@ -# (c) 2022, NetApp, Inc +# (c) 2022-2023, NetApp, Inc # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) from __future__ import (absolute_import, division, print_function) @@ -97,7 +97,8 @@ def test_create_s3_service(): ('GET', 'cluster', SRR['is_rest_9_10_1']), ('GET', 'cluster', SRR['is_rest_9_10_1']), ('GET', 'protocols/s3/services', SRR['empty_records']), - ('POST', 'protocols/s3/services', SRR['empty_good']) + ('POST', 'protocols/s3/services', SRR['empty_good']), + ('GET', 'protocols/s3/services', SRR['s3_service']), ]) module_args = { 'enabled': True, @@ -107,6 +108,22 @@ def test_create_s3_service(): assert create_and_apply(my_module, DEFAULT_ARGS, module_args)['changed'] +def test_create_s3_service_response(): + register_responses([ + ('GET', 'cluster', SRR['is_rest_9_10_1']), + ('GET', 'cluster', SRR['is_rest_9_10_1']), + ('GET', 'protocols/s3/services', SRR['empty_records']), + ('POST', 'protocols/s3/services', SRR['empty_good']), + ('GET', 'protocols/s3/services', SRR['s3_service']), + ]) + module_args = { + 'enabled': True, + 'comment': 'this is a s3 service', + 'certificate_name': 'cert1', + } + assert create_and_apply(my_module, DEFAULT_ARGS, module_args)['s3_service_info'] is not None + + def test_create_s3_service_error(): register_responses([ ('GET', 'cluster', SRR['is_rest_9_8_0']), @@ -152,7 +169,8 @@ def test_modify_s3_service(): ('GET', 'cluster', SRR['is_rest_9_10_1']), ('GET', 'cluster', SRR['is_rest_9_10_1']), ('GET', 'protocols/s3/services', SRR['s3_service']), - ('PATCH', 'protocols/s3/services/08c8a385-b1ac-11ec-bd2e-005056b3b297', SRR['empty_good']) + ('PATCH', 'protocols/s3/services/08c8a385-b1ac-11ec-bd2e-005056b3b297', SRR['empty_good']), + ('GET', 'protocols/s3/services', SRR['s3_service']), ]) module_args = {'comment': 'this is a modified s3 service', 'enabled': False, @@ -161,6 +179,21 @@ def test_modify_s3_service(): assert create_and_apply(my_module, DEFAULT_ARGS, module_args)['changed'] +def test_modify_s3_service_response(): + register_responses([ + ('GET', 'cluster', SRR['is_rest_9_10_1']), + ('GET', 'cluster', SRR['is_rest_9_10_1']), + ('GET', 'protocols/s3/services', SRR['s3_service']), + ('PATCH', 'protocols/s3/services/08c8a385-b1ac-11ec-bd2e-005056b3b297', SRR['empty_good']), + ('GET', 'protocols/s3/services', SRR['s3_service']), + ]) + module_args = {'comment': 'this is a modified s3 service', + 'enabled': False, + 'certificate_name': 'cert2', + } + assert create_and_apply(my_module, DEFAULT_ARGS, module_args)['s3_service_info'] is not None + + def test_modify_s3_service_error(): register_responses([ ('GET', 'cluster', SRR['is_rest_9_8_0']), diff --git a/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_security_certificates.py b/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_security_certificates.py index 866dd3a58..636e826ab 100644 --- a/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_security_certificates.py +++ b/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_security_certificates.py @@ -113,7 +113,7 @@ def test_rest_create_failed(mock_request): def test_rest_successful_create(mock_request): mock_request.side_effect = [ SRR['is_rest'], - SRR['get_uuid'], # validate data vserver exist. + SRR['get_uuid'], # validate data vserver exists. SRR['empty_records'], # get certificate -> not found SRR['empty_good'], SRR['end_of_sequence'] @@ -131,6 +131,28 @@ def test_rest_successful_create(mock_request): assert exc.value.args[0]['changed'] +@patch('ansible_collections.netapp.ontap.plugins.module_utils.netapp.OntapRestAPI.send_request') +def test_rest_check_module_output(mock_request): + mock_request.side_effect = [ + SRR['is_rest'], + SRR['get_uuid'], # validate data vserver exists. + SRR['empty_records'], # get certificate -> not found + SRR['empty_good'], + SRR['end_of_sequence'] + ] + data = { + 'type': 'server', + 'vserver': 'abc', + 'common_name': 'cname' + } + data.update(set_default_args()) + set_module_args(data) + my_obj = my_module() + with pytest.raises(AnsibleExitJson) as exc: + my_obj.apply() + assert exc.value.args[0]['ontap_info'] is not None + + @patch('ansible_collections.netapp.ontap.plugins.module_utils.netapp.OntapRestAPI.send_request') def test_rest_idempotent_create(mock_request): mock_request.side_effect = [ diff --git a/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_snapmirror.py b/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_snapmirror.py index 9ba179279..0227c4e44 100644 --- a/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_snapmirror.py +++ b/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_snapmirror.py @@ -45,7 +45,7 @@ DEFAULT_ARGS = { } -def sm_rest_info(state, healthy, transfer_state=None, destination_path=DEFAULT_ARGS['destination_path']): +def sm_rest_info(state, healthy, transfer_state=None, policy_type=None, destination_path=DEFAULT_ARGS['destination_path']): record = { 'uuid': 'b5ee4571-5429-11ec-9779-005056b39a06', 'destination': { @@ -63,6 +63,8 @@ def sm_rest_info(state, healthy, transfer_state=None, destination_path=DEFAULT_A record['transfer']['uuid'] = 'xfer_uuid' if healthy is False: record['unhealthy_reason'] = 'this is why the relationship is not healthy.' + if policy_type: + record['policy']['type'] = policy_type record['transfer_schedule'] = {'name': 'abc'} return { @@ -110,9 +112,12 @@ SRR = rest_responses({ 'sm_get_uninitialized': (200, sm_rest_info('uninitialized', True), None), 'sm_get_uninitialized_xfering': (200, sm_rest_info('uninitialized', True, 'transferring'), None), 'sm_get_mirrored': (200, sm_rest_info('snapmirrored', True, 'success'), None), + 'sm_sync_get_mirrored': (200, sm_rest_info('in_sync', True, 'success', 'sync'), None), 'sm_get_restore': (200, sm_rest_info('snapmirrored', True, 'success', destination_path=DEFAULT_ARGS['source_path']), None), 'sm_get_paused': (200, sm_rest_info('paused', True, 'success'), None), + 'sm_sync_get_paused': (200, sm_rest_info('paused', True, 'success', 'sync'), None), 'sm_get_broken': (200, sm_rest_info('broken_off', True, 'success'), None), + 'sm_sync_get_broken': (200, sm_rest_info('broken_off', True, 'success', 'sync'), None), 'sm_get_data_transferring': (200, sm_rest_info('transferring', True, 'transferring'), None), 'sm_get_abort': (200, sm_rest_info('sm_get_abort', False, 'failed'), None), 'sm_get_resync': (200, { @@ -1181,12 +1186,28 @@ def test_rest_resync_when_state_is_broken(dont_sleep): assert call_main(my_main, DEFAULT_ARGS, module_args)['changed'] +@patch('time.sleep') +def test_rest_synchronous_sm_resync_when_state_is_broken(dont_sleep): + ''' resync when snapmirror state is broken and relationship_state active ''' + register_responses([ + ('GET', 'cluster', SRR['is_rest_9_8_0']), + ('GET', 'snapmirror/relationships', SRR['sm_sync_get_broken']), # apply first sm_get with state broken_off + ('PATCH', 'snapmirror/relationships/b5ee4571-5429-11ec-9779-005056b39a06', SRR['success']), # sm resync response + ('GET', 'snapmirror/relationships', SRR['sm_sync_get_mirrored']), # check for idle + ('GET', 'snapmirror/relationships', SRR['sm_sync_get_mirrored']), # check_health calls sm_get + ]) + module_args = { + "use_rest": "always", + } + assert call_main(my_main, DEFAULT_ARGS, module_args)['changed'] + + def test_rest_resume_when_state_quiesced(): ''' resync when snapmirror state is broken and relationship_state active ''' register_responses([ ('GET', 'cluster', SRR['is_rest_9_8_0']), ('GET', 'snapmirror/relationships', SRR['sm_get_paused']), # apply first sm_get with state quiesced - ('PATCH', 'snapmirror/relationships/b5ee4571-5429-11ec-9779-005056b39a06', SRR['success']), # sm resync response + ('PATCH', 'snapmirror/relationships/b5ee4571-5429-11ec-9779-005056b39a06', SRR['success']), # sm resume response ('GET', 'snapmirror/relationships', SRR['sm_get_mirrored']), # sm update calls sm_get ('POST', 'snapmirror/relationships/b5ee4571-5429-11ec-9779-005056b39a06/transfers', SRR['success']), # sm update response ('GET', 'snapmirror/relationships', SRR['sm_get_mirrored']), # check_health calls sm_get @@ -1197,6 +1218,22 @@ def test_rest_resume_when_state_quiesced(): assert call_main(my_main, DEFAULT_ARGS, module_args)['changed'] +def test_rest_synchronous_sm_resume_when_state_quiesced(): + ''' resync when snapmirror state is broken and relationship_state active ''' + register_responses([ + ('GET', 'cluster', SRR['is_rest_9_8_0']), + ('GET', 'snapmirror/relationships', SRR['sm_sync_get_paused']), # apply first sm_get with state quiesced + ('PATCH', 'snapmirror/relationships/b5ee4571-5429-11ec-9779-005056b39a06', SRR['success']), # sm resume response + ('GET', 'snapmirror/relationships', SRR['sm_sync_get_mirrored']), # sm update calls sm_get + # ('POST', 'snapmirror/relationships/b5ee4571-5429-11ec-9779-005056b39a06/transfers', SRR['success']), # sm update response + ('GET', 'snapmirror/relationships', SRR['sm_sync_get_mirrored']), # check_health calls sm_get + ]) + module_args = { + "use_rest": "always", + } + assert call_main(my_main, DEFAULT_ARGS, module_args)['changed'] + + @patch('time.sleep') def test_rest_snapmirror_delete(dont_sleep): ''' snapmirror delete ''' diff --git a/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_snapshot_policy_rest.py b/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_snapshot_policy_rest.py index b79507759..5533451af 100644 --- a/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_snapshot_policy_rest.py +++ b/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_snapshot_policy_rest.py @@ -156,7 +156,7 @@ def test_module_error_ontap_version(): assert 'Error: REST requires ONTAP 9.8 or later for snapshot schedules.' == msg -def test_create_snapshot_polciy_rest(): +def test_create_snapshot_policy_rest(): ''' Test create with rest API''' register_responses([ ('GET', 'cluster', SRR['is_rest_9_9_0']), @@ -166,7 +166,7 @@ def test_create_snapshot_polciy_rest(): assert create_and_apply(my_module, ARGS_REST) -def test_create_snapshot_polciy_with_snapmirror_label_rest(): +def test_create_snapshot_policy_with_snapmirror_label_rest(): ''' Test create with rest API''' register_responses([ ('GET', 'cluster', SRR['is_rest_9_9_0']), @@ -179,7 +179,7 @@ def test_create_snapshot_polciy_with_snapmirror_label_rest(): assert create_and_apply(my_module, ARGS_REST, module_args) -def test_create_snapshot_polciy_with_prefix_rest(): +def test_create_snapshot_policy_with_prefix_rest(): ''' Test create with rest API''' register_responses([ ('GET', 'cluster', SRR['is_rest_9_9_0']), @@ -192,7 +192,7 @@ def test_create_snapshot_polciy_with_prefix_rest(): assert create_and_apply(my_module, ARGS_REST, module_args) -def test_error_create_snapshot_polciy_rest(): +def test_error_create_snapshot_policy_rest(): ''' Test error create with rest API''' register_responses([ ('GET', 'cluster', SRR['is_rest_9_9_0']), diff --git a/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_snmp.py b/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_snmp.py index 24d8c5da4..48cbc1107 100644 --- a/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_snmp.py +++ b/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_snmp.py @@ -47,6 +47,7 @@ SRR = { 'zero_record': (200, dict(records=[], num_records=0), None), 'one_record_uuid': (200, dict(records=[dict(uuid='a1b2c3')], num_records=1), None), 'end_of_sequence': (500, None, "Unexpected call to send_request"), + 'server_error': (500, None, "Internal Server error"), 'generic_error': (400, None, "Expected error"), 'community_user_record': (200, { 'records': [{ @@ -56,11 +57,17 @@ SRR = { }], 'num_records': 1 }, None), - 'snmp_user_record': (200, { + 'snmp_usm_user_record': (200, { 'records': [{ "name": "snmpv3user3", "authentication_method": "usm", - 'engine_id': "80000315058e02057c0fb8e911bc9f005056bb942e" + 'engine_id': "80000315058e02057c0fb8e911bc9f005056bb942e", + 'snmpv3': { + 'privacy_protocol': 'aes128', + 'authentication_password': 'humTdumt*@t0nAwa11', + 'authentication_protocol': 'sha', + 'privacy_password': 'p@**GOandCLCt*200' + } }], 'num_records': 1 }, None), @@ -73,15 +80,15 @@ def test_module_fail_when_required_args_missing(patch_ansible): set_module_args(dict(hostname='')) my_module() print('Info: %s' % exc.value.args[0]['msg']) - msg = 'missing required arguments: community_name' + msg = 'missing required arguments: snmp_username' assert msg == exc.value.args[0]['msg'] @patch('ansible_collections.netapp.ontap.plugins.module_utils.netapp.OntapRestAPI.send_request') -def test_ensure_get_community_called(mock_request, patch_ansible): +def test_ensure_get_snmp_community_called(mock_request, patch_ansible): ''' test get''' args = dict(default_args()) - args['community_name'] = 'snmpv3user2' + args['snmp_username'] = 'snmpv3user2' set_module_args(args) mock_request.side_effect = [ SRR['is_rest_9_8'], # get version @@ -97,10 +104,29 @@ def test_ensure_get_community_called(mock_request, patch_ansible): @patch('ansible_collections.netapp.ontap.plugins.module_utils.netapp.OntapRestAPI.send_request') -def test_ensure_create_community_called(mock_request, patch_ansible): +def test_ensure_get_snmp_usm_called(mock_request, patch_ansible): + ''' test get''' + args = dict(default_args()) + args['snmp_username'] = 'snmpv3user3' + set_module_args(args) + mock_request.side_effect = [ + SRR['is_rest_9_8'], # get version + SRR['snmp_usm_user_record'], # get + SRR['end_of_sequence'] + ] + my_obj = my_module() + with pytest.raises(AnsibleExitJson) as exc: + my_obj.apply() + print('Info: %s' % exc.value.args[0]) + assert exc.value.args[0]['changed'] is False + assert_no_warnings() + + +@patch('ansible_collections.netapp.ontap.plugins.module_utils.netapp.OntapRestAPI.send_request') +def test_ensure_create_snmp_community_called(mock_request, patch_ansible): ''' test get''' args = dict(default_args()) - args['community_name'] = 'snmpv3user2' + args['snmp_username'] = 'snmpv3user2' set_module_args(args) mock_request.side_effect = [ SRR['is_rest_9_8'], # get version @@ -117,10 +143,48 @@ def test_ensure_create_community_called(mock_request, patch_ansible): @patch('ansible_collections.netapp.ontap.plugins.module_utils.netapp.OntapRestAPI.send_request') -def test_ensure_delete_community_called(mock_request, patch_ansible): +def test_ensure_create_snmp_usm_called(mock_request, patch_ansible): + ''' test get''' + args = dict(default_args()) + args['snmp_username'] = 'snmpv3user3' + set_module_args(args) + mock_request.side_effect = [ + SRR['is_rest_9_8'], # get version + SRR['zero_record'], # get + SRR['empty_good'], # create + SRR['end_of_sequence'] + ] + my_obj = my_module() + with pytest.raises(AnsibleExitJson) as exc: + my_obj.apply() + print('Info: %s' % exc.value.args[0]) + assert exc.value.args[0]['changed'] is True + assert_no_warnings() + + +def test_fail_to_create_snmp_usm_without_snmpv3_passwords(patch_ansible): + ''' required arguments are reported as errors ''' + usm_record = { + 'snmp_username': 'usm19', + 'authentication_method': 'usm', + 'snmpv3': { + 'privacy_protocol': 'aes128', + 'authentication_protocol': 'sha' + } + } + with pytest.raises(AnsibleFailJson) as exc: + set_module_args(usm_record) + my_module() + print('Info: %s' % exc.value.args[0]['msg']) + msg = 'missing required arguments:' + assert msg in exc.value.args[0]['msg'] + + +@patch('ansible_collections.netapp.ontap.plugins.module_utils.netapp.OntapRestAPI.send_request') +def test_ensure_delete_snmp_community_called(mock_request, patch_ansible): ''' test get''' args = dict(default_args()) - args['community_name'] = 'snmpv3user2' + args['snmp_username'] = 'snmpv3user2' args['state'] = 'absent' set_module_args(args) mock_request.side_effect = [ @@ -139,10 +203,52 @@ def test_ensure_delete_community_called(mock_request, patch_ansible): @patch('ansible_collections.netapp.ontap.plugins.module_utils.netapp.OntapRestAPI.send_request') -def test_ensure_delete_community_idempotent(mock_request, patch_ansible): +def test_ensure_delete_snmp_usm_called(mock_request, patch_ansible): + ''' test get''' + args = dict(default_args()) + args['snmp_username'] = 'snmpv3user3' + args['state'] = 'absent' + set_module_args(args) + mock_request.side_effect = [ + SRR['is_rest_9_8'], # get version + SRR['snmp_usm_user_record'], # get + SRR['snmp_usm_user_record'], + SRR['empty_good'], # delete + SRR['end_of_sequence'] + ] + my_obj = my_module() + with pytest.raises(AnsibleExitJson) as exc: + my_obj.apply() + print('Info: %s' % exc.value.args[0]) + assert exc.value.args[0]['changed'] is True + assert_no_warnings() + + +@patch('ansible_collections.netapp.ontap.plugins.module_utils.netapp.OntapRestAPI.send_request') +def test_ensure_delete_snmp_community_idempotent(mock_request, patch_ansible): + ''' test get''' + args = dict(default_args()) + args['snmp_username'] = 'snmpv3user2' + args['state'] = 'absent' + set_module_args(args) + mock_request.side_effect = [ + SRR['is_rest_9_8'], # get version + SRR['zero_record'], # get + SRR['end_of_sequence'] + ] + my_obj = my_module() + with pytest.raises(AnsibleExitJson) as exc: + my_obj.apply() + print('Info: %s' % exc.value.args[0]) + assert exc.value.args[0]['changed'] is False + assert_no_warnings() + + +@patch('ansible_collections.netapp.ontap.plugins.module_utils.netapp.OntapRestAPI.send_request') +def test_ensure_delete_snmp_usm_idempotent(mock_request, patch_ansible): ''' test get''' args = dict(default_args()) - args['community_name'] = 'snmpv3user2' + args['snmp_username'] = 'snmpv3user3' args['state'] = 'absent' set_module_args(args) mock_request.side_effect = [ diff --git a/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_snmp_config_rest.py b/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_snmp_config_rest.py new file mode 100644 index 000000000..bf6236825 --- /dev/null +++ b/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_snmp_config_rest.py @@ -0,0 +1,123 @@ +# Copyright: NetApp, Inc +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +""" unit tests for Ansible module: na_ontap_snmp_config """ + +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type +import pytest +import sys + +import ansible_collections.netapp.ontap.plugins.module_utils.netapp as netapp_utils +# pylint: disable=unused-import +from ansible_collections.netapp.ontap.tests.unit.plugins.module_utils.ansible_mocks import patch_ansible, \ + create_and_apply, create_module, call_main, expect_and_capture_ansible_exception +from ansible_collections.netapp.ontap.tests.unit.framework.mock_rest_and_zapi_requests import get_mock_record, \ + patch_request_and_invoke, register_responses +from ansible_collections.netapp.ontap.tests.unit.framework.rest_factory import rest_responses + +from ansible_collections.netapp.ontap.plugins.modules.na_ontap_snmp_config \ + import NetAppOntapSNMPConfig as my_module, main as my_main # module under test + +if not netapp_utils.HAS_REQUESTS and sys.version_info < (2, 7): + pytestmark = pytest.mark.skip( + 'Skipping Unit Tests on 2.6 as requests is not available') + + +DEFAULT_ARGS = { + 'hostname': 'hostname', + 'username': 'username', + 'password': 'password', + 'use_rest': 'always', + 'state': 'present' +} + + +# REST API canned responses when mocking send_request. +# The rest_factory provides default responses shared across testcases. +SRR = rest_responses({ + # module specific responses + 'snmp_config': (200, { + 'auth_traps_enabled': False, + 'enabled': True, + 'traps_enabled': False, + }, None), + 'snmp_config_disabled': (200, { + 'enabled': False + }, None), + 'snmp_config_modified': (200, { + 'auth_traps_enabled': True, + 'traps_enabled': True + }, None), +}) + + +def test_successful_disable_snmp(): + ''' Test successful rest modify SNMP config with idempotency check''' + register_responses([ + ('GET', 'cluster', SRR['is_rest_97']), + ('GET', 'support/snmp', SRR['snmp_config']), # get SNMP config + ('PATCH', 'support/snmp', SRR['success']), # update SNMP config + + ('GET', 'cluster', SRR['is_rest_97']), + ('GET', 'support/snmp', SRR['snmp_config_disabled']), # get SNMP config + ]) + args = { + 'enabled': 'false' + } + assert create_and_apply(my_module, DEFAULT_ARGS, args)['changed'] + assert not create_and_apply(my_module, DEFAULT_ARGS, args)['changed'] + + +def test_successful_modify_snmp_config(): + ''' Test successful rest modify SNMP config with idempotency check''' + register_responses([ + ('GET', 'cluster', SRR['is_rest_9_10_1']), + ('GET', 'support/snmp', SRR['snmp_config']), # get SNMP config + ('PATCH', 'support/snmp', SRR['success']), # update SNMP config + + ('GET', 'cluster', SRR['is_rest_9_10_1']), + ('GET', 'support/snmp', SRR['snmp_config_modified']), # get SNMP config + ]) + args = { + 'auth_traps_enabled': True, + 'traps_enabled': True + } + assert create_and_apply(my_module, DEFAULT_ARGS, args)['changed'] + assert not create_and_apply(my_module, DEFAULT_ARGS, args)['changed'] + + +def test_all_methods_catch_exception(): + ''' Test exception in get/modify SNMP config ''' + register_responses([ + ('GET', 'cluster', SRR['is_rest_9_10_1']), + # GET/PATCH error + ('GET', 'support/snmp', SRR['generic_error']), + ('PATCH', 'support/snmp', SRR['generic_error']) + ]) + modify_args = { + 'enabled': 'false' + } + snmp_config = create_module(my_module, DEFAULT_ARGS) + assert 'Error fetching SNMP config' in expect_and_capture_ansible_exception(snmp_config.get_snmp_config_rest, 'fail')['msg'] + assert 'Error modifying SNMP config' in expect_and_capture_ansible_exception(snmp_config.modify_snmp_config_rest, 'fail', modify_args)['msg'] + + +def test_error_ontap97(): + ''' Test module supported from 9.7 ''' + register_responses([ + ('GET', 'cluster', SRR['is_rest']) + ]) + assert 'requires ONTAP 9.7.0 or later' in call_main(my_main, DEFAULT_ARGS, fail=True)['msg'] + + +def test_partially_supported_options_rest(): + ''' Test REST version error for parameters ''' + register_responses([ + ('GET', 'cluster', SRR['is_rest_97']), + ]) + args = { + 'traps_enabled': 'true' + } + error = create_module(my_module, DEFAULT_ARGS, args, fail=True)['msg'] + assert 'Minimum version of ONTAP for traps_enabled is (9, 10, 1)' in error diff --git a/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_svm.py b/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_svm.py index d18d32a57..757eca2e9 100644 --- a/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_svm.py +++ b/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_svm.py @@ -282,7 +282,7 @@ def test_init_error(): 'services': {'ndmp': {'allowed': True}}, } error = create_module(svm_module, DEFAULT_ARGS, module_args, fail=True)['msg'] - assert error == 'using ndmp requires ONTAP 9.7 or later and REST must be enabled - ONTAP version: 9.6.0 - using REST.' + assert error == 'using ndmp requires ONTAP 9.10.1 or later and REST must be enabled - ONTAP version: 9.6.0 - using REST.' def test_successful_rename(): diff --git a/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_volume.py b/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_volume.py index 3161ead04..aae0f49a3 100644 --- a/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_volume.py +++ b/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_volume.py @@ -1544,6 +1544,7 @@ def test_successful_modify_snapshot_auto_delete(get_volume): ''' Test successful modify unix permissions flexGroup ''' register_responses([ # One ZAPI call for each option! + ('ZAPI', 'volume-modify-iter', ZRR['success']), ('ZAPI', 'snapshot-autodelete-set-option', ZRR['success']), ('ZAPI', 'snapshot-autodelete-set-option', ZRR['success']), ('ZAPI', 'snapshot-autodelete-set-option', ZRR['success']), diff --git a/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_volume_rest.py b/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_volume_rest.py index 47525beec..20e3ba0f7 100644 --- a/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_volume_rest.py +++ b/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_volume_rest.py @@ -78,7 +78,16 @@ volume_info = { }, "size": 10737418240, "snapshot": { - "reserve_percent": 5 + "reserve_percent": 5, + "autodelete": { + "enabled": False, + "trigger": "volume", + "delete_order": "oldest_first", + "defer_delete": "user_created", + "commitment": "try", + "target_free_space": 20, + "prefix": "(not specified)" + } } }, "guarantee": { @@ -89,7 +98,9 @@ volume_info = { }, "analytics": { "state": "on" - } + }, + "access_time_enabled": True, + "snapshot_directory_access_enabled": True } volume_info_mount = copy.deepcopy(volume_info) @@ -114,6 +125,8 @@ SRR = rest_responses({ # common responses 'is_rest': (200, dict(version=dict(generation=9, major=8, minor=0, full='dummy')), None), 'is_rest_96': (200, dict(version=dict(generation=9, major=6, minor=0, full='dummy_9_6_0')), None), + 'is_rest_9_8_0': (200, dict(version=dict(generation=9, major=8, minor=0, full='dummy_9_8_0')), None), + 'is_rest_9_13_1': (200, dict(version=dict(generation=9, major=13, minor=1, full='dummy_9_13_1')), None), 'is_zapi': (400, {}, "Unreachable"), 'empty_good': (200, {}, None), 'no_record': (200, {'num_records': 0, 'records': []}, None), @@ -507,6 +520,100 @@ def test_rest_error_modify_attributes(): assert create_and_apply(volume_module, DEFAULT_VOLUME_ARGS, module_args, fail=True)['msg'] == msg +def test_rest_version_error_with_atime_update(): + register_responses([ + ('GET', 'cluster', SRR['is_rest_96']) + ]) + module_args = { + 'atime_update': False + } + error = create_module(volume_module, DEFAULT_VOLUME_ARGS, module_args, fail=True)['msg'] + print('error', error) + assert 'Minimum version of ONTAP for atime_update is (9, 8)' in error + + +def test_rest_version_error_with_snapdir_access(): + register_responses([ + ('GET', 'cluster', SRR['is_rest_9_12_1']) + ]) + module_args = { + 'snapdir_access': False + } + error = create_module(volume_module, DEFAULT_VOLUME_ARGS, module_args, fail=True)['msg'] + print('error', error) + assert 'Minimum version of ONTAP for snapdir_access is (9, 13, 1)' in error + + +def test_rest_version_error_with_snapshot_auto_delete(): + register_responses([ + ('GET', 'cluster', SRR['is_rest_9_12_1']) + ]) + module_args = { + 'snapshot_auto_delete': {'state': 'on'} + } + error = create_module(volume_module, DEFAULT_VOLUME_ARGS, module_args, fail=True)['msg'] + print('error', error) + assert 'Minimum version of ONTAP for snapshot_auto_delete is (9, 13, 1)' in error + + +def test_rest_version_error_with_vol_nearly_full_threshold_percent(): + register_responses([ + ('GET', 'cluster', SRR['is_rest_9_8_0']) + ]) + module_args = { + 'vol_nearly_full_threshold_percent': 96 + } + error = create_module(volume_module, DEFAULT_VOLUME_ARGS, module_args, fail=True)['msg'] + print('error', error) + assert 'Minimum version of ONTAP for vol_nearly_full_threshold_percent is (9, 9)' in error + + +def test_rest_successfully_modify_attributes_atime_update(): + register_responses([ + ('GET', 'cluster', SRR['is_rest_9_8_0']), + ('GET', 'storage/volumes', SRR['get_volume']), # Get Volume + ('PATCH', 'storage/volumes/7882901a-1aef-11ec-a267-005056b30cfa', SRR['no_record']), # Modify + ]) + module_args = { + 'atime_update': False, + } + assert create_and_apply(volume_module, DEFAULT_VOLUME_ARGS, module_args)['changed'] + + +def test_rest_successfully_modify_attributes_snapdir_access_and_snapshot_auto_delete(): + register_responses([ + ('GET', 'cluster', SRR['is_rest_9_13_1']), + ('GET', 'storage/volumes', SRR['get_volume']), # Get Volume + ('PATCH', 'storage/volumes/7882901a-1aef-11ec-a267-005056b30cfa', SRR['no_record']), # Modify + ]) + module_args = { + 'snapdir_access': False, + 'snapshot_auto_delete': { + 'state': 'on', + 'trigger': 'volume', + 'delete_order': 'oldest_first', + 'defer_delete': 'user_created', + 'commitment': 'try', + 'target_free_space': 25, + 'prefix': 'prefix1' + } + } + assert create_and_apply(volume_module, DEFAULT_VOLUME_ARGS, module_args)['changed'] + + +def test_rest_successfully_modify_vol_threshold_percent_params(): + register_responses([ + ('GET', 'cluster', SRR['is_rest_9_9_0']), + ('GET', 'storage/volumes', SRR['get_volume']), # Get Volume + ('PATCH', 'storage/volumes/7882901a-1aef-11ec-a267-005056b30cfa', SRR['no_record']), # Modify + ]) + module_args = { + 'vol_nearly_full_threshold_percent': 98, + 'vol_full_threshold_percent': 99 + } + assert create_and_apply(volume_module, DEFAULT_VOLUME_ARGS, module_args)['changed'] + + def test_rest_successfully_create_volume(): register_responses([ ('GET', 'cluster', SRR['is_rest']), diff --git a/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_vscan_scanner_pool_rest.py b/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_vscan_scanner_pool_rest.py new file mode 100644 index 000000000..2cdc97a59 --- /dev/null +++ b/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_vscan_scanner_pool_rest.py @@ -0,0 +1,256 @@ +# Copyright: NetApp, Inc +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +""" unit tests for Ansible module: na_ontap_vscan_scanner_pool """ + +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type +import pytest +import sys + +import ansible_collections.netapp.ontap.plugins.module_utils.netapp as netapp_utils +# pylint: disable=unused-import +from ansible_collections.netapp.ontap.tests.unit.plugins.module_utils.ansible_mocks import patch_ansible, \ + create_and_apply, create_module, call_main, expect_and_capture_ansible_exception +from ansible_collections.netapp.ontap.tests.unit.framework.mock_rest_and_zapi_requests import get_mock_record, \ + patch_request_and_invoke, register_responses +from ansible_collections.netapp.ontap.tests.unit.framework.rest_factory import rest_responses + +from ansible_collections.netapp.ontap.plugins.modules.na_ontap_vscan_scanner_pool \ + import NetAppOntapVscanScannerPool as my_module, main as my_main # module under test + +if not netapp_utils.HAS_REQUESTS and sys.version_info < (2, 7): + pytestmark = pytest.mark.skip( + 'Skipping Unit Tests on 2.6 as requests is not available') + + +DEFAULT_ARGS = { + 'hostname': 'hostname', + 'username': 'username', + 'password': 'password', + 'use_rest': 'always' +} + + +# REST API canned responses when mocking send_request. +# The rest_factory provides default responses shared across testcases. +SRR = rest_responses({ + # module specific responses + 'scanner_pool_info': (200, {"records": [ + { + "name": "Scanner1", + "servers": [ + "10.193.78.219", + "10.193.78.221" + ], + "privileged_users": [ + "cifs\\user1", + "cifs\\user2" + ], + "role": "primary" + } + ]}, None), + 'svm_uuid': (200, {"records": [ + { + 'uuid': '5ec77839-b9b9-11ee-8084-005056b3d69a' + } + ], "num_records": 1}, None) +}) + + +svm_uuid = '5ec77839-b9b9-11ee-8084-005056b3d69a' +scanner_pool_name = 'Scanner1' + + +def test_successful_create(): + ''' Test successful rest create ''' + register_responses([ + ('GET', 'cluster', SRR['is_rest_96']), + ('GET', 'svm/svms', SRR['svm_uuid']), + ('GET', 'protocols/vscan/%s/scanner-pools' % (svm_uuid), SRR['empty_records']), + ('POST', 'protocols/vscan/%s/scanner-pools' % (svm_uuid), SRR['empty_good']), + ]) + args = { + 'state': 'present', + 'vserver': 'ansibleSVM', + 'hostnames': ['10.193.78.219', '10.193.78.221'], + 'scanner_policy': 'primary', + 'privileged_users': ['cifs\\user1', 'cifs\\user2'], + 'scanner_pool': 'Scanner1' + } + assert create_and_apply(my_module, DEFAULT_ARGS, args)['changed'] + + +def test_successful_create_idempotency(): + ''' Test successful rest create idempotency ''' + register_responses([ + ('GET', 'cluster', SRR['is_rest_96']), + ('GET', 'svm/svms', SRR['svm_uuid']), + ('GET', 'protocols/vscan/%s/scanner-pools' % (svm_uuid), SRR['scanner_pool_info']), + ]) + args = { + 'state': 'present', + 'vserver': 'ansibleSVM', + 'hostnames': ['10.193.78.219', '10.193.78.221'], + 'scanner_policy': 'primary', + 'privileged_users': ['cifs\\user1', 'cifs\\user2'], + 'scanner_pool': 'Scanner1' + } + assert create_and_apply(my_module, DEFAULT_ARGS, args)['changed'] is False + + +def test_successful_delete(): + ''' Test successful rest delete ''' + register_responses([ + ('GET', 'cluster', SRR['is_rest_96']), + ('GET', 'svm/svms', SRR['svm_uuid']), + ('GET', 'protocols/vscan/%s/scanner-pools' % (svm_uuid), SRR['scanner_pool_info']), + ('DELETE', 'protocols/vscan/%s/scanner-pools/%s' % (svm_uuid, scanner_pool_name), SRR['success']), + ]) + args = { + 'state': 'absent', + 'vserver': 'ansibleSVM', + 'scanner_pool': 'Scanner1' + } + assert create_and_apply(my_module, DEFAULT_ARGS, args)['changed'] + + +def test_successful_delete_idempotency(): + ''' Test successful rest delete idempotency ''' + register_responses([ + ('GET', 'cluster', SRR['is_rest_96']), + ('GET', 'svm/svms', SRR['svm_uuid']), + ('GET', 'protocols/vscan/%s/scanner-pools' % (svm_uuid), SRR['empty_records']), + ]) + args = { + 'state': 'absent', + 'vserver': 'ansibleSVM', + 'scanner_pool': 'Scanner1' + } + assert create_and_apply(my_module, DEFAULT_ARGS, args)['changed'] is False + + +def test_successful_modify(): + ''' Test successful rest modify ''' + register_responses([ + ('GET', 'cluster', SRR['is_rest_96']), + ('GET', 'svm/svms', SRR['svm_uuid']), + ('GET', 'protocols/vscan/%s/scanner-pools' % (svm_uuid), SRR['scanner_pool_info']), + ('PATCH', 'protocols/vscan/%s/scanner-pools/%s' % (svm_uuid, scanner_pool_name), SRR['success']), + ]) + args = { + 'state': 'present', + 'vserver': 'ansibleSVM', + 'hostnames': ['10.193.78.219'], + 'scanner_policy': 'idle', + 'privileged_users': ['cifs\\user1'], + 'scanner_pool': 'Scanner1' + } + assert create_and_apply(my_module, DEFAULT_ARGS, args)['changed'] + + +def test_error_get(): + ''' Test error rest get ''' + register_responses([ + ('GET', 'cluster', SRR['is_rest_96']), + ('GET', 'svm/svms', SRR['svm_uuid']), + ('GET', 'protocols/vscan/%s/scanner-pools' % (svm_uuid), SRR['generic_error']), + ]), + args = { + 'state': 'present', + 'vserver': 'ansibleSVM', + 'hostnames': ['10.193.78.219', '10.193.78.221'], + 'scanner_policy': 'primary', + 'privileged_users': ['cifs\\user1', 'cifs\\user2'], + 'scanner_pool': 'Scanner1' + } + error = create_and_apply(my_module, DEFAULT_ARGS, args, fail=True)['msg'] + assert 'Error searching for Vscan Scanner Pool' in error + + +def test_error_create(): + ''' Test error rest create ''' + register_responses([ + ('GET', 'cluster', SRR['is_rest_96']), + ('GET', 'svm/svms', SRR['svm_uuid']), + ('GET', 'protocols/vscan/%s/scanner-pools' % (svm_uuid), SRR['empty_records']), + ('POST', 'protocols/vscan/%s/scanner-pools' % (svm_uuid), SRR['generic_error']), + ]), + args = { + 'state': 'present', + 'vserver': 'ansibleSVM', + 'hostnames': ['10.193.78.219', '10.193.78.221'], + 'scanner_policy': 'primary', + 'privileged_users': ['cifs\\user1', 'cifs\\user2'], + 'scanner_pool': 'Scanner1' + } + error = create_and_apply(my_module, DEFAULT_ARGS, args, fail=True)['msg'] + assert 'Error creating Vscan Scanner Pool' in error + + +def test_error_modify(): + ''' Test error rest modify ''' + register_responses([ + ('GET', 'cluster', SRR['is_rest_96']), + ('GET', 'svm/svms', SRR['svm_uuid']), + ('GET', 'protocols/vscan/%s/scanner-pools' % (svm_uuid), SRR['scanner_pool_info']), + ('PATCH', 'protocols/vscan/%s/scanner-pools/%s' % (svm_uuid, scanner_pool_name), SRR['generic_error']), + ]) + args = { + 'state': 'present', + 'vserver': 'ansibleSVM', + 'hostnames': ['10.193.78.219'], + 'scanner_policy': 'idle', + 'privileged_users': ['cifs\\user1'], + 'scanner_pool': 'Scanner1' + } + error = create_and_apply(my_module, DEFAULT_ARGS, args, fail=True)['msg'] + assert 'Error modifying Vscan Scanner Pool' in error + + +def test_error_delete(): + ''' Test error rest delete ''' + register_responses([ + ('GET', 'cluster', SRR['is_rest_96']), + ('GET', 'svm/svms', SRR['svm_uuid']), + ('GET', 'protocols/vscan/%s/scanner-pools' % (svm_uuid), SRR['scanner_pool_info']), + ('DELETE', 'protocols/vscan/%s/scanner-pools/%s' % (svm_uuid, scanner_pool_name), SRR['generic_error']), + ]) + args = { + 'state': 'absent', + 'vserver': 'ansibleSVM', + 'scanner_pool': 'Scanner1' + } + error = create_and_apply(my_module, DEFAULT_ARGS, args, fail=True)['msg'] + assert 'Error deleting Vscan Scanner Pool' in error + + +def test_error_ontap96(): + ''' Test error module supported from 9.6 ''' + register_responses([ + ('GET', 'cluster', SRR['is_rest']) + ]) + args = { + 'state': 'present', + 'vserver': 'ansibleSVM', + 'hostnames': ['10.193.78.219', '10.193.78.221'], + 'scanner_policy': 'primary', + 'privileged_users': ['cifs\\user1', 'cifs\\user2'], + 'scanner_pool': 'Scanner1' + } + msg = 'REST requires ONTAP 9.6 or later for /protocols/vscan/{{svm.uuid}}/scanner-pools APIs' + assert msg in call_main(my_main, DEFAULT_ARGS, args, fail=True)['msg'] + + +def test_missing_options_scanner_pool(): + ''' Test error missing option scanner_pool ''' + register_responses([]) + args = { + 'state': 'present', + 'vserver': 'ansibleSVM', + 'hostnames': ['10.193.78.219', '10.193.78.221'], + 'scanner_policy': 'primary', + 'privileged_users': ['cifs\\user1', 'cifs\\user2'], + } + error = create_module(my_module, DEFAULT_ARGS, args, fail=True)['msg'] + assert 'missing required arguments: scanner_pool' in error -- cgit v1.2.3