summaryrefslogtreecommitdiffstats
path: root/ansible_collections/netapp/storagegrid/tests
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-13 12:04:41 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-13 12:04:41 +0000
commit975f66f2eebe9dadba04f275774d4ab83f74cf25 (patch)
tree89bd26a93aaae6a25749145b7e4bca4a1e75b2be /ansible_collections/netapp/storagegrid/tests
parentInitial commit. (diff)
downloadansible-975f66f2eebe9dadba04f275774d4ab83f74cf25.tar.xz
ansible-975f66f2eebe9dadba04f275774d4ab83f74cf25.zip
Adding upstream version 7.7.0+dfsg.upstream/7.7.0+dfsg
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'ansible_collections/netapp/storagegrid/tests')
-rw-r--r--ansible_collections/netapp/storagegrid/tests/unit/compat/__init__.py0
-rw-r--r--ansible_collections/netapp/storagegrid/tests/unit/compat/builtins.py34
-rw-r--r--ansible_collections/netapp/storagegrid/tests/unit/compat/mock.py125
-rw-r--r--ansible_collections/netapp/storagegrid/tests/unit/compat/unittest.py44
-rw-r--r--ansible_collections/netapp/storagegrid/tests/unit/plugins/modules/test_na_sg_grid_account.py380
-rw-r--r--ansible_collections/netapp/storagegrid/tests/unit/plugins/modules/test_na_sg_grid_certificate.py342
-rw-r--r--ansible_collections/netapp/storagegrid/tests/unit/plugins/modules/test_na_sg_grid_client_certificate.py347
-rw-r--r--ansible_collections/netapp/storagegrid/tests/unit/plugins/modules/test_na_sg_grid_dns.py241
-rw-r--r--ansible_collections/netapp/storagegrid/tests/unit/plugins/modules/test_na_sg_grid_gateway.py693
-rw-r--r--ansible_collections/netapp/storagegrid/tests/unit/plugins/modules/test_na_sg_grid_group.py317
-rw-r--r--ansible_collections/netapp/storagegrid/tests/unit/plugins/modules/test_na_sg_grid_ha_group.py408
-rw-r--r--ansible_collections/netapp/storagegrid/tests/unit/plugins/modules/test_na_sg_grid_identity_federation.py354
-rw-r--r--ansible_collections/netapp/storagegrid/tests/unit/plugins/modules/test_na_sg_grid_info.py362
-rw-r--r--ansible_collections/netapp/storagegrid/tests/unit/plugins/modules/test_na_sg_grid_ntp.py257
-rw-r--r--ansible_collections/netapp/storagegrid/tests/unit/plugins/modules/test_na_sg_grid_regions.py206
-rw-r--r--ansible_collections/netapp/storagegrid/tests/unit/plugins/modules/test_na_sg_grid_traffic_classes.py355
-rw-r--r--ansible_collections/netapp/storagegrid/tests/unit/plugins/modules/test_na_sg_grid_user.py476
-rw-r--r--ansible_collections/netapp/storagegrid/tests/unit/plugins/modules/test_na_sg_org_container.py348
-rw-r--r--ansible_collections/netapp/storagegrid/tests/unit/plugins/modules/test_na_sg_org_group.py403
-rw-r--r--ansible_collections/netapp/storagegrid/tests/unit/plugins/modules/test_na_sg_org_identity_federation.py354
-rw-r--r--ansible_collections/netapp/storagegrid/tests/unit/plugins/modules/test_na_sg_org_info.py263
-rw-r--r--ansible_collections/netapp/storagegrid/tests/unit/plugins/modules/test_na_sg_org_user.py476
-rw-r--r--ansible_collections/netapp/storagegrid/tests/unit/plugins/modules/test_na_sg_org_user_s3_key.py238
-rw-r--r--ansible_collections/netapp/storagegrid/tests/unit/requirements.txt1
24 files changed, 7024 insertions, 0 deletions
diff --git a/ansible_collections/netapp/storagegrid/tests/unit/compat/__init__.py b/ansible_collections/netapp/storagegrid/tests/unit/compat/__init__.py
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/ansible_collections/netapp/storagegrid/tests/unit/compat/__init__.py
diff --git a/ansible_collections/netapp/storagegrid/tests/unit/compat/builtins.py b/ansible_collections/netapp/storagegrid/tests/unit/compat/builtins.py
new file mode 100644
index 000000000..bfc8adfbe
--- /dev/null
+++ b/ansible_collections/netapp/storagegrid/tests/unit/compat/builtins.py
@@ -0,0 +1,34 @@
+# (c) 2014, Toshio Kuratomi <tkuratomi@ansible.com>
+#
+# This file is part of Ansible
+#
+# Ansible is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Ansible is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
+
+# Make coding more python3-ish
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+#
+# Compat for python2.7
+#
+
+# One unittest needs to import builtins via __import__() so we need to have
+# the string that represents it
+try:
+ import __builtin__
+except ImportError:
+ BUILTINS = "builtins"
+else:
+ BUILTINS = "__builtin__"
diff --git a/ansible_collections/netapp/storagegrid/tests/unit/compat/mock.py b/ansible_collections/netapp/storagegrid/tests/unit/compat/mock.py
new file mode 100644
index 000000000..ce13d07cb
--- /dev/null
+++ b/ansible_collections/netapp/storagegrid/tests/unit/compat/mock.py
@@ -0,0 +1,125 @@
+# (c) 2014, Toshio Kuratomi <tkuratomi@ansible.com>
+#
+# This file is part of Ansible
+#
+# Ansible is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Ansible is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
+
+# Make coding more python3-ish
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+"""
+Compat module for Python3.x's unittest.mock module
+"""
+import sys
+
+# Python 2.7
+
+# Note: Could use the pypi mock library on python3.x as well as python2.x. It
+# is the same as the python3 stdlib mock library
+
+try:
+ # Allow wildcard import because we really do want to import all of mock's
+ # symbols into this compat shim
+ # pylint: disable=wildcard-import,unused-wildcard-import
+ from unittest.mock import *
+except ImportError:
+ # Python 2
+ # pylint: disable=wildcard-import,unused-wildcard-import
+ try:
+ from mock import *
+ except ImportError:
+ print("You need the mock library installed on python2.x to run tests")
+
+
+# Prior to 3.4.4, mock_open cannot handle binary read_data
+if sys.version_info >= (3,) and sys.version_info < (3, 4, 4):
+ file_spec = None
+
+ def _iterate_read_data(read_data):
+ # Helper for mock_open:
+ # Retrieve lines from read_data via a generator so that separate calls to
+ # readline, read, and readlines are properly interleaved
+ sep = b"\n" if isinstance(read_data, bytes) else "\n"
+ data_as_list = [l + sep for l in read_data.split(sep)]
+
+ if data_as_list[-1] == sep:
+ # If the last line ended in a newline, the list comprehension will have an
+ # extra entry that's just a newline. Remove this.
+ data_as_list = data_as_list[:-1]
+ else:
+ # If there wasn't an extra newline by itself, then the file being
+ # emulated doesn't have a newline to end the last line remove the
+ # newline that our naive format() added
+ data_as_list[-1] = data_as_list[-1][:-1]
+
+ for line in data_as_list:
+ yield line
+
+ def mock_open(mock=None, read_data=""):
+ """
+ A helper function to create a mock to replace the use of `open`. It works
+ for `open` called directly or used as a context manager.
+ The `mock` argument is the mock object to configure. If `None` (the
+ default) then a `MagicMock` will be created for you, with the API limited
+ to methods or attributes available on standard file handles.
+ `read_data` is a string for the `read` methoddline`, and `readlines` of the
+ file handle to return. This is an empty string by default.
+ """
+
+ def _readlines_side_effect(*args, **kwargs):
+ if handle.readlines.return_value is not None:
+ return handle.readlines.return_value
+ return list(_data)
+
+ def _read_side_effect(*args, **kwargs):
+ if handle.read.return_value is not None:
+ return handle.read.return_value
+ return type(read_data)().join(_data)
+
+ def _readline_side_effect():
+ if handle.readline.return_value is not None:
+ while True:
+ yield handle.readline.return_value
+ for line in _data:
+ yield line
+
+ global file_spec
+ if file_spec is None:
+ import _io
+
+ file_spec = list(
+ set(dir(_io.TextIOWrapper)).union(set(dir(_io.BytesIO)))
+ )
+
+ if mock is None:
+ mock = MagicMock(name="open", spec=open)
+
+ handle = MagicMock(spec=file_spec)
+ handle.__enter__.return_value = handle
+
+ _data = _iterate_read_data(read_data)
+
+ handle.write.return_value = None
+ handle.read.return_value = None
+ handle.readline.return_value = None
+ handle.readlines.return_value = None
+
+ handle.read.side_effect = _read_side_effect
+ handle.readline.side_effect = _readline_side_effect()
+ handle.readlines.side_effect = _readlines_side_effect
+
+ mock.return_value = handle
+ return mock
diff --git a/ansible_collections/netapp/storagegrid/tests/unit/compat/unittest.py b/ansible_collections/netapp/storagegrid/tests/unit/compat/unittest.py
new file mode 100644
index 000000000..73a20cf8c
--- /dev/null
+++ b/ansible_collections/netapp/storagegrid/tests/unit/compat/unittest.py
@@ -0,0 +1,44 @@
+# (c) 2014, Toshio Kuratomi <tkuratomi@ansible.com>
+#
+# This file is part of Ansible
+#
+# Ansible is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Ansible is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
+
+# Make coding more python3-ish
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+
+'''
+Compat module for Python2.7's unittest module
+'''
+
+import sys
+
+import pytest
+
+# Allow wildcard import because we really do want to import all of
+# unittests's symbols into this compat shim
+# pylint: disable=wildcard-import,unused-wildcard-import
+if sys.version_info < (2, 7):
+ try:
+ # Need unittest2 on python2.6
+ from unittest2 import *
+ except ImportError:
+ print('You need unittest2 installed on python2.6.x to run tests')
+
+ class TestCase:
+ """ skip everything """
+ pytestmark = pytest.mark.skip('Skipping Unit Tests on 2.6 as unittest2 may not be available')
+else:
+ from unittest import *
diff --git a/ansible_collections/netapp/storagegrid/tests/unit/plugins/modules/test_na_sg_grid_account.py b/ansible_collections/netapp/storagegrid/tests/unit/plugins/modules/test_na_sg_grid_account.py
new file mode 100644
index 000000000..e96697381
--- /dev/null
+++ b/ansible_collections/netapp/storagegrid/tests/unit/plugins/modules/test_na_sg_grid_account.py
@@ -0,0 +1,380 @@
+# (c) 2020, NetApp, Inc
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+""" unit tests NetApp StorageGRID Tenant Ansible module: na_sg_grid_account"""
+
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+import json
+import pytest
+import sys
+try:
+ from requests import Response
+except ImportError:
+ if sys.version_info < (2, 7):
+ pytestmark = pytest.mark.skip('Skipping Unit Tests on 2.6 as requests is not available')
+ else:
+ raise
+
+from ansible_collections.netapp.storagegrid.tests.unit.compat import unittest
+from ansible_collections.netapp.storagegrid.tests.unit.compat.mock import (
+ patch,
+ Mock,
+)
+from ansible.module_utils import basic
+from ansible.module_utils._text import to_bytes
+from ansible_collections.netapp.storagegrid.plugins.modules.na_sg_grid_account import (
+ SgGridAccount as grid_account_module,
+)
+
+# REST API canned responses when mocking send_request
+SRR = {
+ # common responses
+ "empty_good": ({"data": []}, None),
+ "end_of_sequence": (None, "Unexpected call to send_request"),
+ "generic_error": (None, "Expected error"),
+ "delete_good": ({"code": 204}, None),
+ "pw_change_good": ({"code": 204}, None),
+ "grid_accounts": (
+ {
+ "data": [
+ {
+ "name": "TestTenantAccount",
+ "capabilities": ["management", "s3"],
+ "policy": {
+ "useAccountIdentitySource": True,
+ "allowPlatformServices": False,
+ "quotaObjectBytes": None,
+ },
+ "id": "12345678901234567890",
+ }
+ ]
+ },
+ None,
+ ),
+ "grid_account_record": (
+ {
+ "data": {
+ "name": "TestTenantAccount",
+ "capabilities": ["management", "s3"],
+ "policy": {
+ "useAccountIdentitySource": True,
+ "allowPlatformServices": False,
+ "quotaObjectBytes": None,
+ },
+ "id": "12345678901234567890",
+ }
+ },
+ None,
+ ),
+ "grid_account_record_with_quota": (
+ {
+ "data": {
+ "name": "TestTenantAccount",
+ "capabilities": ["management", "s3"],
+ "policy": {
+ "useAccountIdentitySource": True,
+ "allowPlatformServices": False,
+ "quotaObjectBytes": 10737418240,
+ },
+ "id": "12345678901234567890",
+ }
+ },
+ None,
+ ),
+ "grid_account_record_update_quota": (
+ {
+ "data": {
+ "name": "TestTenantAccount",
+ "capabilities": ["management", "s3"],
+ "policy": {
+ "useAccountIdentitySource": True,
+ "allowPlatformServices": False,
+ "quotaObjectBytes": 21474836480,
+ },
+ "id": "12345678901234567890",
+ }
+ },
+ None,
+ ),
+}
+
+
+def set_module_args(args):
+ """prepare arguments so that they will be picked up during module creation"""
+ args = json.dumps({"ANSIBLE_MODULE_ARGS": args})
+ basic._ANSIBLE_ARGS = to_bytes(args) # pylint: disable=protected-access
+
+
+class AnsibleExitJson(Exception):
+ """Exception class to be raised by module.exit_json and caught by the test case"""
+
+ pass
+
+
+class AnsibleFailJson(Exception):
+ """Exception class to be raised by module.fail_json and caught by the test case"""
+
+ pass
+
+
+def exit_json(*args, **kwargs): # pylint: disable=unused-argument
+ """function to patch over exit_json; package return data into an exception"""
+ if "changed" not in kwargs:
+ kwargs["changed"] = False
+ raise AnsibleExitJson(kwargs)
+
+
+def fail_json(*args, **kwargs): # pylint: disable=unused-argument
+ """function to patch over fail_json; package return data into an exception"""
+ kwargs["failed"] = True
+ raise AnsibleFailJson(kwargs)
+
+
+class TestMyModule(unittest.TestCase):
+ """ a group of related Unit Tests """
+
+ def setUp(self):
+ self.mock_module_helper = patch.multiple(
+ basic.AnsibleModule, exit_json=exit_json, fail_json=fail_json
+ )
+ self.mock_module_helper.start()
+ self.addCleanup(self.mock_module_helper.stop)
+
+ def set_default_args_fail_check(self):
+ return dict(
+ {
+ "name": "TestTenantAccount",
+ "protocol": "s3",
+ "management": True,
+ "use_own_identity_source": True,
+ "allow_platform_services": False,
+ "password": "abc123",
+ "quota_size": 0,
+ "auth_token": "01234567-5678-9abc-78de-9fgabc123def",
+ "validate_certs": False,
+ }
+ )
+
+ def set_default_args_pass_check(self):
+ return dict(
+ {
+ "state": "present",
+ "name": "TestTenantAccount",
+ "protocol": "s3",
+ "management": True,
+ "use_own_identity_source": True,
+ "allow_platform_services": False,
+ "password": "abc123",
+ "quota_size": 0,
+ "api_url": "gmi.example.com",
+ "auth_token": "01234567-5678-9abc-78de-9fgabc123def",
+ "validate_certs": False,
+ }
+ )
+
+ def set_args_create_na_sg_grid_account(self):
+ return dict(
+ {
+ "state": "present",
+ "name": "TestTenantAccount",
+ "protocol": "s3",
+ "management": True,
+ "use_own_identity_source": True,
+ "allow_platform_services": False,
+ "password": "abc123",
+ "quota_size": 0,
+ "api_url": "gmi.example.com",
+ "auth_token": "01234567-5678-9abc-78de-9fgabc123def",
+ "validate_certs": False,
+ }
+ )
+
+ def set_args_delete_na_sg_grid_account(self):
+ return dict(
+ {
+ "state": "absent",
+ "name": "TestTenantAccount",
+ "protocol": "s3",
+ "api_url": "gmi.example.com",
+ "auth_token": "01234567-5678-9abc-78de-9fgabc123def",
+ "validate_certs": False,
+ }
+ )
+
+ def test_module_fail_when_required_args_missing(self):
+ """ required arguments are reported as errors """
+ with pytest.raises(AnsibleFailJson) as exc:
+ set_module_args(self.set_default_args_fail_check())
+ grid_account_module()
+ print(
+ "Info: test_module_fail_when_required_args_missing: %s"
+ % exc.value.args[0]["msg"]
+ )
+
+ def test_module_fail_when_required_args_present(self):
+ """ required arguments are reported as errors """
+ with pytest.raises(AnsibleExitJson) as exc:
+ set_module_args(self.set_default_args_pass_check())
+ grid_account_module()
+ exit_json(changed=True, msg="Induced arguments check")
+ print(
+ "Info: test_module_fail_when_required_args_present: %s"
+ % exc.value.args[0]["msg"]
+ )
+ assert exc.value.args[0]["changed"]
+
+ @patch(
+ "ansible_collections.netapp.storagegrid.plugins.module_utils.netapp.SGRestAPI.send_request"
+ )
+ def test_create_na_sg_grid_account_pass(self, mock_request):
+ set_module_args(self.set_args_create_na_sg_grid_account())
+ my_obj = grid_account_module()
+ mock_request.side_effect = [
+ SRR["empty_good"], # get
+ SRR["grid_accounts"], # post
+ SRR["end_of_sequence"],
+ ]
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ print(
+ "Info: test_create_na_sg_tenant_account_pass: %s"
+ % repr(exc.value.args[0])
+ )
+ assert exc.value.args[0]["changed"]
+
+ @patch(
+ "ansible_collections.netapp.storagegrid.plugins.module_utils.netapp.SGRestAPI.send_request"
+ )
+ def test_idempotent_create_na_sg_grid_account_pass(self, mock_request):
+ set_module_args(self.set_args_create_na_sg_grid_account())
+ my_obj = grid_account_module()
+ mock_request.side_effect = [
+ SRR["grid_accounts"], # get id
+ SRR["grid_account_record"], # get account
+ SRR["end_of_sequence"],
+ ]
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ print(
+ "Info: test_idempotent_create_na_sg_tenant_account_pass: %s"
+ % repr(exc.value.args[0])
+ )
+ assert not exc.value.args[0]["changed"]
+
+ @patch(
+ "ansible_collections.netapp.storagegrid.plugins.module_utils.netapp.SGRestAPI.send_request"
+ )
+ def test_update_na_sg_grid_account_pass(self, mock_request):
+ args = self.set_args_create_na_sg_grid_account()
+ args["quota_size"] = 10
+ set_module_args(args)
+ my_obj = grid_account_module()
+ mock_request.side_effect = [
+ SRR["grid_accounts"], # get
+ SRR["grid_account_record"], # get
+ SRR["grid_account_record_with_quota"], # put
+ SRR["end_of_sequence"],
+ ]
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ print(
+ "Info: test_update_na_sg_tenant_account_pass: %s"
+ % repr(exc.value.args[0])
+ )
+ assert exc.value.args[0]["changed"]
+
+ @patch(
+ "ansible_collections.netapp.storagegrid.plugins.module_utils.netapp.SGRestAPI.send_request"
+ )
+ def test_update_na_sg_grid_account_quota_pass(self, mock_request):
+ args = self.set_args_create_na_sg_grid_account()
+ args["quota_size"] = 20480
+ args["quota_size_unit"] = "mb"
+ set_module_args(args)
+ my_obj = grid_account_module()
+ mock_request.side_effect = [
+ SRR["grid_accounts"], # get
+ SRR["grid_account_record_with_quota"], # get
+ SRR["grid_account_record_update_quota"], # put
+ SRR["end_of_sequence"],
+ ]
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ print(
+ "Info: test_create_na_sg_tenant_account_pass: %s"
+ % repr(exc.value.args[0])
+ )
+ assert exc.value.args[0]["changed"]
+
+ # update Tenant Account and set pass
+ @patch(
+ "ansible_collections.netapp.storagegrid.plugins.module_utils.netapp.SGRestAPI.send_request"
+ )
+ def test_update_na_sg_grid_account_and_set_password_pass(self, mock_request):
+ args = self.set_args_create_na_sg_grid_account()
+ args["quota_size"] = 20480
+ args["quota_size_unit"] = "mb"
+ args["update_password"] = "always"
+
+ set_module_args(args)
+ my_obj = grid_account_module()
+ mock_request.side_effect = [
+ SRR["grid_accounts"], # get
+ SRR["grid_account_record_with_quota"], # get
+ SRR["grid_account_record_update_quota"], # put
+ SRR["pw_change_good"], # post
+ SRR["end_of_sequence"],
+ ]
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ print(
+ "Info: test_update_na_sg_grid_account_and_set_password_pass: %s"
+ % repr(exc.value.args[0])
+ )
+ assert exc.value.args[0]["changed"]
+
+ # set pass only
+
+ @patch(
+ "ansible_collections.netapp.storagegrid.plugins.module_utils.netapp.SGRestAPI.send_request"
+ )
+ def test_set_na_sg_grid_account_root_password_pass(self, mock_request):
+ args = self.set_args_create_na_sg_grid_account()
+ args["update_password"] = "always"
+
+ set_module_args(args)
+ my_obj = grid_account_module()
+ mock_request.side_effect = [
+ SRR["grid_accounts"], # get id
+ SRR["grid_account_record"], # get account
+ SRR["pw_change_good"], # post
+ SRR["end_of_sequence"],
+ ]
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ print(
+ "Info: test_set_na_sg_grid_account_root_password_pass: %s" % repr(exc.value.args[0])
+ )
+ assert exc.value.args[0]["changed"]
+
+ @patch(
+ "ansible_collections.netapp.storagegrid.plugins.module_utils.netapp.SGRestAPI.send_request"
+ )
+ def test_delete_na_sg_grid_account_pass(self, mock_request):
+ set_module_args(self.set_args_delete_na_sg_grid_account())
+ my_obj = grid_account_module()
+ mock_request.side_effect = [
+ SRR["grid_accounts"], # get
+ SRR["grid_account_record"], # get
+ SRR["delete_good"], # delete
+ SRR["end_of_sequence"],
+ ]
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ print(
+ "Info: test_create_na_sg_tenant_account_pass: %s"
+ % repr(exc.value.args[0])
+ )
+ assert exc.value.args[0]["changed"]
diff --git a/ansible_collections/netapp/storagegrid/tests/unit/plugins/modules/test_na_sg_grid_certificate.py b/ansible_collections/netapp/storagegrid/tests/unit/plugins/modules/test_na_sg_grid_certificate.py
new file mode 100644
index 000000000..74974abff
--- /dev/null
+++ b/ansible_collections/netapp/storagegrid/tests/unit/plugins/modules/test_na_sg_grid_certificate.py
@@ -0,0 +1,342 @@
+# (c) 2020, NetApp, Inc
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+""" unit tests NetApp StorageGRID Grid Certificate Ansible module: na_sg_grid_certificate"""
+
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+import json
+import pytest
+import sys
+try:
+ from requests import Response
+except ImportError:
+ if sys.version_info < (2, 7):
+ pytestmark = pytest.mark.skip('Skipping Unit Tests on 2.6 as requests is not available')
+ else:
+ raise
+
+from ansible_collections.netapp.storagegrid.tests.unit.compat import unittest
+from ansible_collections.netapp.storagegrid.tests.unit.compat.mock import (
+ patch,
+ Mock,
+)
+from ansible.module_utils import basic
+from ansible.module_utils._text import to_bytes
+from ansible_collections.netapp.storagegrid.plugins.modules.na_sg_grid_certificate import (
+ SgGridCertificate as grid_certificate_module,
+)
+
+# REST API canned responses when mocking send_request
+SRR = {
+ # common responses
+ "empty_good": ({"data": []}, None),
+ "not_found": ({"status": "error", "code": 404, "data": {}}, {"key": "error.404"},),
+ "end_of_sequence": (None, "Unexpected call to send_request"),
+ "generic_error": (None, "Expected error"),
+ "delete_good": (None, None),
+ "update_good": (None, None),
+ "cert_unset": ({"data": {"serverCertificateEncoded": None, "caBundleEncoded": None}}, None),
+ "storage_api_cert": (
+ {
+ "data": {
+ "serverCertificateEncoded": (
+ "-----BEGIN CERTIFICATE-----\n"
+ "MIICyDCCAbACCQCgFntI3q7iADANBgkqhkiG9w0BAQsFADAmMQswCQYDVQQGEwJV\n"
+ "UzEXMBUGA1UEAwwOczMuZXhhbXBsZS5jb20wHhcNMjEwNDI5MDQ1NTM1WhcNMjIw\n"
+ "NDI5MDQ1NTM1WjAmMQswCQYDVQQGEwJVUzEXMBUGA1UEAwwOczMuZXhhbXBsZS5j\n"
+ "b20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQD0LMcJUdWmTtxi7U7B\n"
+ "yldDRfyCD9W+QJ1Ygm7E9iFwvkThUCV5q+DIcgSfogoSKaQuHaImLXMZn36QC22n\n"
+ "+Ah2EGrQiggyny3wDzuWf5/Qg7ogqQRqiespBFLlV4RGCREHK0y5uq8mzpIaQ8l8\n"
+ "STa7nLS7BIc6rD15BJaNWZpDVHIzhljlnhfnqwio/ZfP++lAjk4/j8pPGPEEI5Fe\n"
+ "WxhOtQjr7xTHeJxKHp2VKiLEvFxniL3qk4uJ3k5fJ7IqALUEPWH92brFp2IkObUA\n"
+ "EGsZYB4KFV7asBVhGuspYNzUQ6NqWbEUmtTjKEXcb1TA8RK+Pc2TotOrQ2E7Z+rU\n"
+ "gl2fAgMBAAEwDQYJKoZIhvcNAQELBQADggEBAD5PW1WI7GCfxLQjaitnXpD1MR2O\n"
+ "6b5csymPYwRejMsSswd8egjs+vO2pbF9TptLjqGliE9XUoI+mWpuMzzd75F0jcjq\n"
+ "1DhlINgAmjUJEAg0RAqce0Kn8xQF+SofMtkOH+nZm3Q9nbTJKr1H5m2TnCq3v5TH\n"
+ "Qo0ASf0LLGgrwUtT0IghdSttYLS89dJprZ6c5wK7qeBzxfdHxxjiaSnvByL2Ryn5\n"
+ "cec9lptYKoRY42hWvkQv9Wkr3DDoyNA3xPdZJr0Hpf8/mSPnt9r/AR8E32xi0SXp\n"
+ "hOMTDgMicbK82ycxz0yW88gm6yhrChlJrWaEsVGod3FU+lbMAnagYZ/Vwp8=\n"
+ "-----END CERTIFICATE-----\n"
+ ),
+ "caBundleEncoded": None,
+ }
+ },
+ None,
+ ),
+ "storage_api_cert_update": (
+ {
+ "data": {
+ "serverCertificateEncoded": (
+ "-----BEGIN CERTIFICATE-----\n"
+ "MIICzjCCAbYCCQDZVi1OT89SAjANBgkqhkiG9w0BAQsFADApMQswCQYDVQQGEwJV\n"
+ "UzEaMBgGA1UEAwwRczMubmV3ZXhhbXBsZS5jb20wHhcNMjEwNDI5MDQ1NzIxWhcN\n"
+ "MjIwNDI5MDQ1NzIxWjApMQswCQYDVQQGEwJVUzEaMBgGA1UEAwwRczMubmV3ZXhh\n"
+ "bXBsZS5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCmg37q2sjZ\n"
+ "k+HsXtai3PSMtGUiqij04JtG9ahMqIejuxy5sDCWnigh//NjdK+wPYc2VfYd6KFA\n"
+ "Uk9rP84M7sqdqGzIzmyEu7INyCnlbxcXlST6UZDsZnVU7Gk2GvUzk2OoO5N+G0oI\n"
+ "Lfc/3eKTx9j9BguOaWUy+ni+Te8j6EwK6HolGRBjLYqf1SYFBzaoVpy7pmzaFZ4R\n"
+ "10jFSxHbotIZ+kR8pPE5jGkP8OjOfrpbhEgmffpeq2MSCMRuhRtRiVp4ULwkMTRN\n"
+ "tFj89mu1gl9T3lYM/LO1SmBv3il0mNmrTL+99UJ4s2eL0zr/uHAVYJcVqFgWP7X8\n"
+ "WnOk+d86b0TXAgMBAAEwDQYJKoZIhvcNAQELBQADggEBAFmGV3IOuNYeM3LQxls+\n"
+ "/CNHznvIqvoiJOWq0S7LFy1eO7PVzCl3l/fDKjGMt2lGXeU89YKdFVPqsainNEFT\n"
+ "cNEWlezVut+/CWQpBXujyBqPLkYbzyGsakMImDb+MrSkBO5MCjlt38vppm5a97fB\n"
+ "9o/wM31e+N6gJLiHWs0XB9TK6bY9CvcutcGUOH/oxH1TEBgrJ3SoS7/HmZJSaCQA\n"
+ "hjZappzuEpGVXT8YDlb67PzUoE2rDWjdSFRXCk/0U6VR0xNgnN1WtfHaypU71DrB\n"
+ "zxbDaOIZoDp5G4OgjkFxoCoSWLant+LsqEwclIbCFgEvJPE8855UThelTHmIfivP\n"
+ "veI=\n-----END CERTIFICATE-----\n"
+ ),
+ "caBundleEncoded": None,
+ }
+ },
+ None,
+ ),
+}
+
+
+def set_module_args(args):
+ """prepare arguments so that they will be picked up during module creation"""
+ args = json.dumps({"ANSIBLE_MODULE_ARGS": args})
+ basic._ANSIBLE_ARGS = to_bytes(args) # pylint: disable=protected-access
+
+
+class AnsibleExitJson(Exception):
+ """Exception class to be raised by module.exit_json and caught by the test case"""
+
+ pass
+
+
+class AnsibleFailJson(Exception):
+ """Exception class to be raised by module.fail_json and caught by the test case"""
+
+ pass
+
+
+def exit_json(*args, **kwargs): # pylint: disable=unused-argument
+ """function to patch over exit_json; package return data into an exception"""
+ if "changed" not in kwargs:
+ kwargs["changed"] = False
+ raise AnsibleExitJson(kwargs)
+
+
+def fail_json(*args, **kwargs): # pylint: disable=unused-argument
+ """function to patch over fail_json; package return data into an exception"""
+ kwargs["failed"] = True
+ raise AnsibleFailJson(kwargs)
+
+
+class TestMyModule(unittest.TestCase):
+ """ a group of related Unit Tests """
+
+ def setUp(self):
+ self.mock_module_helper = patch.multiple(basic.AnsibleModule, exit_json=exit_json, fail_json=fail_json)
+ self.mock_module_helper.start()
+ self.addCleanup(self.mock_module_helper.stop)
+
+ def set_default_args_fail_check(self):
+ return dict(
+ {
+ "server_certificate": (
+ "-----BEGIN CERTIFICATE-----\n"
+ "MIICyDCCAbACCQCgFntI3q7iADANBgkqhkiG9w0BAQsFADAmMQswCQYDVQQGEwJV\n"
+ "UzEXMBUGA1UEAwwOczMuZXhhbXBsZS5jb20wHhcNMjEwNDI5MDQ1NTM1WhcNMjIw\n"
+ "-----END CERTIFICATE-----\n"
+ ),
+ "private_key": (
+ "-----BEGIN PRIVATE KEY-----\n"
+ "MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQD0LMcJUdWmTtxi\n"
+ "7U7ByldDRfyCD9W+QJ1Ygm7E9iFwvkThUCV5q+DIcgSfogoSKaQuHaImLXMZn36Q\n"
+ "C22n+Ah2EGrQiggyny3wDzuWf5/Qg7ogqQRqiespBFLlV4RGCREHK0y5uq8mzpIa\n"
+ "-----END PRIVATE KEY-----\n"
+ ),
+ "auth_token": "01234567-5678-9abc-78de-9fgabc123def",
+ "validate_certs": False,
+ }
+ )
+
+ def set_default_args_pass_check(self):
+ return dict(
+ {
+ "state": "present",
+ "type": "storage-api",
+ "server_certificate": (
+ "-----BEGIN CERTIFICATE-----\n"
+ "MIICyDCCAbACCQCgFntI3q7iADANBgkqhkiG9w0BAQsFADAmMQswCQYDVQQGEwJV\n"
+ "UzEXMBUGA1UEAwwOczMuZXhhbXBsZS5jb20wHhcNMjEwNDI5MDQ1NTM1WhcNMjIw\n"
+ "-----END CERTIFICATE-----\n"
+ ),
+ "private_key": (
+ "-----BEGIN PRIVATE KEY-----\n"
+ "MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQD0LMcJUdWmTtxi\n"
+ "7U7ByldDRfyCD9W+QJ1Ygm7E9iFwvkThUCV5q+DIcgSfogoSKaQuHaImLXMZn36Q\n"
+ "C22n+Ah2EGrQiggyny3wDzuWf5/Qg7ogqQRqiespBFLlV4RGCREHK0y5uq8mzpIa\n"
+ "-----END PRIVATE KEY-----\n"
+ ),
+ "api_url": "gmi.example.com",
+ "auth_token": "01234567-5678-9abc-78de-9fgabc123def",
+ "validate_certs": False,
+ }
+ )
+
+ def set_args_set_na_sg_grid_storage_api_certificate(self):
+ return dict(
+ {
+ "state": "present",
+ "type": "storage-api",
+ "server_certificate": (
+ "-----BEGIN CERTIFICATE-----\n"
+ "MIICyDCCAbACCQCgFntI3q7iADANBgkqhkiG9w0BAQsFADAmMQswCQYDVQQGEwJV\n"
+ "UzEXMBUGA1UEAwwOczMuZXhhbXBsZS5jb20wHhcNMjEwNDI5MDQ1NTM1WhcNMjIw\n"
+ "NDI5MDQ1NTM1WjAmMQswCQYDVQQGEwJVUzEXMBUGA1UEAwwOczMuZXhhbXBsZS5j\n"
+ "b20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQD0LMcJUdWmTtxi7U7B\n"
+ "yldDRfyCD9W+QJ1Ygm7E9iFwvkThUCV5q+DIcgSfogoSKaQuHaImLXMZn36QC22n\n"
+ "+Ah2EGrQiggyny3wDzuWf5/Qg7ogqQRqiespBFLlV4RGCREHK0y5uq8mzpIaQ8l8\n"
+ "STa7nLS7BIc6rD15BJaNWZpDVHIzhljlnhfnqwio/ZfP++lAjk4/j8pPGPEEI5Fe\n"
+ "WxhOtQjr7xTHeJxKHp2VKiLEvFxniL3qk4uJ3k5fJ7IqALUEPWH92brFp2IkObUA\n"
+ "EGsZYB4KFV7asBVhGuspYNzUQ6NqWbEUmtTjKEXcb1TA8RK+Pc2TotOrQ2E7Z+rU\n"
+ "gl2fAgMBAAEwDQYJKoZIhvcNAQELBQADggEBAD5PW1WI7GCfxLQjaitnXpD1MR2O\n"
+ "6b5csymPYwRejMsSswd8egjs+vO2pbF9TptLjqGliE9XUoI+mWpuMzzd75F0jcjq\n"
+ "1DhlINgAmjUJEAg0RAqce0Kn8xQF+SofMtkOH+nZm3Q9nbTJKr1H5m2TnCq3v5TH\n"
+ "Qo0ASf0LLGgrwUtT0IghdSttYLS89dJprZ6c5wK7qeBzxfdHxxjiaSnvByL2Ryn5\n"
+ "cec9lptYKoRY42hWvkQv9Wkr3DDoyNA3xPdZJr0Hpf8/mSPnt9r/AR8E32xi0SXp\n"
+ "hOMTDgMicbK82ycxz0yW88gm6yhrChlJrWaEsVGod3FU+lbMAnagYZ/Vwp8=\n"
+ "-----END CERTIFICATE-----\n"
+ ),
+ "private_key": (
+ "-----BEGIN PRIVATE KEY-----\n"
+ "MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQD0LMcJUdWmTtxi\n"
+ "7U7ByldDRfyCD9W+QJ1Ygm7E9iFwvkThUCV5q+DIcgSfogoSKaQuHaImLXMZn36Q\n"
+ "C22n+Ah2EGrQiggyny3wDzuWf5/Qg7ogqQRqiespBFLlV4RGCREHK0y5uq8mzpIa\n"
+ "Q8l8STa7nLS7BIc6rD15BJaNWZpDVHIzhljlnhfnqwio/ZfP++lAjk4/j8pPGPEE\n"
+ "I5FeWxhOtQjr7xTHeJxKHp2VKiLEvFxniL3qk4uJ3k5fJ7IqALUEPWH92brFp2Ik\n"
+ "ObUAEGsZYB4KFV7asBVhGuspYNzUQ6NqWbEUmtTjKEXcb1TA8RK+Pc2TotOrQ2E7\n"
+ "Z+rUgl2fAgMBAAECggEAAwSSqTDTvSx4WNiqAocnsPMqfckIUUOnLjLef5yzKRuQ\n"
+ "6l/9NpXDP3b5S6fLDBJrrw46tNIW/BgWjl01y7+rCxqE13L9SvLgtHjbua52ITOf\n"
+ "l0u/fDmcKHOfOqpsPhlaloYYeqsuAwLGl4CC+wBEpuj26uDRcw4x7E78NV8IIxDf\n"
+ "8kUNPQXI9ox6P3isXrFkMncDfKLWOYJ5fF5zCoVZai/SS8z3FhGjAXlMkay48RX4\n"
+ "4vuP7TNLZ2O2pAk2aVs54tQyBn9MOxIzOg3/ZFLiKZR4pY6H5sm+bT263TdvN+A4\n"
+ "C8kwML5HnsCjVkTzJ/3dYc9SeUOuqvJI332GCQ9YcQKBgQD8Ev2qhS61kZ3WGO6G\n"
+ "DRkZ6tDyt5vCuzWQ8uAAXcAerFDWN6XtDPfXq2UVcWnoCQOUpnjslCb/NJgCetLh\n"
+ "mOPeJGRWyMly+YuYb4/rnbwSbUs28PO4D9B/f5YQBnBjGDLL/i2+wnXg3WZTVogf\n"
+ "WfdKziOHGSxmWd6JinI+4UkpiwKBgQD3+krkFORTsUAlTgeIy8+QzXSuclwNygcX\n"
+ "HAe0F96hSYHBC7+1n7nzC1lwcbkU3jLIt3A90Uwew4nr5GCu4sSVwDeWrqP2I9WH\n"
+ "4w0zeaFPC1QKfKGBtsIf/89pDz/7iGlcKWlEg+56VVIJn7qC2lO8qbeUCoglsSwC\n"
+ "vr2Qld5WvQKBgQCHM2xpHHv8GPlOTxsIPVg8RW0C8iYSITVO5GXu7FnSWdwVuc0+\n"
+ "QtlgDObvxF/oe4U3Ir7zLVdpRH1Pvy8Cn22AxYYn4hPiniQYg6Xu2zB3tbVE56Hh\n"
+ "FGJhMD59o+Z90AnWziMdENIG5NkwU9Y48pknvz7hBEiDMSqiHObAATerlwKBgQCP\n"
+ "5LhCY3Ees3MCcqXilkmqv93eQFP0WHAG0+gQc+1m7+2QJI4pCTdwtfw/SG5akpkr\n"
+ "aW6DIIkoLNVCgbIsqT/jmbdoA4z3DlIg2PrXDNQytuMcdreNOoyo3trvHr9E6SIi\n"
+ "LZF9BYWDjTDejsY+mgwPJPh2uinInWdpbF85oA11jQKBgQCc6U2fSwpPQowOaat/\n"
+ "pY5bDCKxhfwrKk3Ecye5HfhbBZ0pu6Oneiq6cNhQC0X69iFn6ogTFx5qqyMQrWH0\n"
+ "L+kQRkyYFLnebCzUA8364lieRzc3cN+xQEn+jX8z7eDZ8JsvVnKdc6lTjPTwN1Fj\n"
+ "FZtaH2L1IEiA8ZZapMb/MNNozg==\n"
+ "-----END PRIVATE KEY-----\n"
+ ),
+ "api_url": "gmi.example.com",
+ "auth_token": "01234567-5678-9abc-78de-9fgabc123def",
+ "validate_certs": False,
+ }
+ )
+
+ def set_args_delete_na_sg_grid_storage_api_certificate(self):
+ return dict(
+ {
+ "state": "absent",
+ "type": "storage-api",
+ "server_certificate": (
+ "-----BEGIN CERTIFICATE-----\n"
+ "MIICyDCCAbACCQCgFntI3q7iADANBgkqhkiG9w0BAQsFADAmMQswCQYDVQQGEwJV\n"
+ "UzEXMBUGA1UEAwwOczMuZXhhbXBsZS5jb20wHhcNMjEwNDI5MDQ1NTM1WhcNMjIw\n"
+ "-----END CERTIFICATE-----\n"
+ ),
+ "private_key": (
+ "-----BEGIN PRIVATE KEY-----\n"
+ "MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQD0LMcJUdWmTtxi\n"
+ "7U7ByldDRfyCD9W+QJ1Ygm7E9iFwvkThUCV5q+DIcgSfogoSKaQuHaImLXMZn36Q\n"
+ "C22n+Ah2EGrQiggyny3wDzuWf5/Qg7ogqQRqiespBFLlV4RGCREHK0y5uq8mzpIa\n"
+ "-----END PRIVATE KEY-----\n"
+ ),
+ "api_url": "gmi.example.com",
+ "auth_token": "01234567-5678-9abc-78de-9fgabc123def",
+ "validate_certs": False,
+ }
+ )
+
+ def test_module_fail_when_required_args_missing(self):
+ """ required arguments are reported as errors """
+ with pytest.raises(AnsibleFailJson) as exc:
+ set_module_args(self.set_default_args_fail_check())
+ grid_certificate_module()
+ print("Info: test_module_fail_when_required_args_missing: %s" % exc.value.args[0]["msg"])
+
+ def test_module_pass_when_required_args_present(self):
+ """ required arguments are reported as errors """
+ with pytest.raises(AnsibleExitJson) as exc:
+ set_module_args(self.set_default_args_pass_check())
+ grid_certificate_module()
+ exit_json(changed=True, msg="Induced arguments check")
+ print("Info: test_module_pass_when_required_args_present: %s" % exc.value.args[0]["msg"])
+ assert exc.value.args[0]["changed"]
+
+ @patch("ansible_collections.netapp.storagegrid.plugins.module_utils.netapp.SGRestAPI.send_request")
+ def test_set_na_sg_grid_storage_api_certificate_pass(self, mock_request):
+ set_module_args(self.set_args_set_na_sg_grid_storage_api_certificate())
+ my_obj = grid_certificate_module()
+ mock_request.side_effect = [
+ SRR["cert_unset"], # get
+ SRR["update_good"], # post
+ SRR["storage_api_cert"], # get
+ SRR["end_of_sequence"],
+ ]
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ print("Info: test_set_na_sg_grid_storage_api_certificate_pass: %s" % repr(exc.value.args[0]))
+ assert exc.value.args[0]["changed"]
+
+ @patch("ansible_collections.netapp.storagegrid.plugins.module_utils.netapp.SGRestAPI.send_request")
+ def test_idempotent_set_na_sg_grid_storage_api_certificate_pass(self, mock_request):
+ set_module_args(self.set_args_set_na_sg_grid_storage_api_certificate())
+ my_obj = grid_certificate_module()
+ mock_request.side_effect = [
+ SRR["storage_api_cert"], # get
+ SRR["end_of_sequence"],
+ ]
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ print("Info: test_idempotent_set_na_sg_grid_storage_api_certificate_pass: %s" % repr(exc.value.args[0]))
+ assert not exc.value.args[0]["changed"]
+
+ @patch("ansible_collections.netapp.storagegrid.plugins.module_utils.netapp.SGRestAPI.send_request")
+ def test_update_na_sg_grid_storage_api_certificate_pass(self, mock_request):
+ args = self.set_args_set_na_sg_grid_storage_api_certificate()
+ args["server_certificate"] = ""
+ args["private_key"] = ""
+
+ set_module_args(args)
+ my_obj = grid_certificate_module()
+ mock_request.side_effect = [
+ SRR["storage_api_cert"], # get
+ SRR["update_good"], # put
+ SRR["storage_api_cert_update"], # get
+ SRR["end_of_sequence"],
+ ]
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ print("Info: test_update_na_sg_grid_storage_api_certificate_pass: %s" % repr(exc.value.args[0]))
+ assert exc.value.args[0]["changed"]
+
+ @patch("ansible_collections.netapp.storagegrid.plugins.module_utils.netapp.SGRestAPI.send_request")
+ def test_delete_na_sg_storage_api_certificate_pass(self, mock_request):
+ set_module_args(self.set_args_delete_na_sg_grid_storage_api_certificate())
+ my_obj = grid_certificate_module()
+ mock_request.side_effect = [
+ SRR["storage_api_cert"], # get
+ SRR["delete_good"], # delete
+ SRR["cert_unset"], # get
+ SRR["end_of_sequence"],
+ ]
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ print("Info: test_delete_na_sg_storage_api_certificate_pass: %s" % repr(exc.value.args[0]))
+ assert exc.value.args[0]["changed"]
diff --git a/ansible_collections/netapp/storagegrid/tests/unit/plugins/modules/test_na_sg_grid_client_certificate.py b/ansible_collections/netapp/storagegrid/tests/unit/plugins/modules/test_na_sg_grid_client_certificate.py
new file mode 100644
index 000000000..d21f9da9c
--- /dev/null
+++ b/ansible_collections/netapp/storagegrid/tests/unit/plugins/modules/test_na_sg_grid_client_certificate.py
@@ -0,0 +1,347 @@
+# (c) 2022, NetApp, Inc
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+""" unit tests NetApp StorageGRID Grid HA Group Ansible module: na_sg_grid_client_certificate"""
+
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+import json
+import pytest
+import sys
+
+try:
+ from requests import Response
+except ImportError:
+ if sys.version_info < (2, 7):
+ pytestmark = pytest.mark.skip("Skipping Unit Tests on 2.6 as requests is not available")
+ else:
+ raise
+
+from ansible_collections.netapp.storagegrid.tests.unit.compat import unittest
+from ansible_collections.netapp.storagegrid.tests.unit.compat.mock import (
+ patch,
+ Mock,
+)
+from ansible.module_utils import basic
+from ansible.module_utils._text import to_bytes
+from ansible_collections.netapp.storagegrid.plugins.modules.na_sg_grid_client_certificate import (
+ SgGridClientCertificate as grid_client_certificate_module,
+)
+
+# REST API canned responses when mocking send_request
+SRR = {
+ # common responses
+ "empty_good": ({"data": []}, None),
+ "not_found": (
+ {"status": "error", "code": 404, "data": {}},
+ {"key": "error.404"},
+ ),
+ "end_of_sequence": (None, "Unexpected call to send_request"),
+ "generic_error": (None, "Expected error"),
+ "delete_good": (None, None),
+ "update_good": (None, None),
+ "version_114": ({"data": {"productVersion": "11.4.0-20200721.1338.d3969b3"}}, None),
+ "version_116": ({"data": {"productVersion": "11.6.0-20211120.0301.850531e"}}, None),
+ "client_cert_record": (
+ {
+ "data": {
+ "id": "841ee2c7-3144-4c3c-8709-335462c5b05d",
+ "displayName": "testcert1",
+ "publicKey": (
+ "-----BEGIN CERTIFICATE-----\n"
+ "MIIEOzCCAyOgAwIBAgIIFuVL2ktGT0MwDQYJKoZIhvcNAQELBQAwbzELMAkGA1UE\n"
+ "BhMCVVMxCzAJBgNVBAgMAkNBMRIwEAYDVQQHDAlTdW5ueXZhbGUxFDASBgNVBAoM\n"
+ "-----END CERTIFICATE-----\n"
+ ),
+ "allowPrometheus": True,
+ "expiryDate": "2024-01-01T00:00:00.000Z",
+ }
+ },
+ None,
+ ),
+ "client_cert_record_updated": (
+ {
+ "data": {
+ "id": "841ee2c7-3144-4c3c-8709-335462c5b05d",
+ "displayName": "testcert1",
+ "publicKey": (
+ "-----BEGIN CERTIFICATE-----\n"
+ "MIICrDCCAZSgAwIBAgIUM3IQEKIypqPrXmoA/KmELXfFAz8wDQYJKoZIhvcNAQEL\n"
+ "BQAwADAeFw0yMjA5MDUyMzI3MTVaFw0yNDA5MDQyMzI3MTVaMAAwggEiMA0GCSqG\n"
+ "-----END CERTIFICATE-----\n"
+ ),
+ "allowPrometheus": True,
+ "expiryDate": "2024-01-01T00:00:00.000Z",
+ }
+ },
+ None,
+ ),
+ "client_cert_record_rename": (
+ {
+ "data": {
+ "id": "841ee2c7-3144-4c3c-8709-335462c5b05d",
+ "displayName": "testcert1-rename",
+ "publicKey": (
+ "-----BEGIN CERTIFICATE-----\n"
+ "MIIEOzCCAyOgAwIBAgIIFuVL2ktGT0MwDQYJKoZIhvcNAQELBQAwbzELMAkGA1UE\n"
+ "BhMCVVMxCzAJBgNVBAgMAkNBMRIwEAYDVQQHDAlTdW5ueXZhbGUxFDASBgNVBAoM\n"
+ "-----END CERTIFICATE-----\n"
+ ),
+ "allowPrometheus": True,
+ "expiryDate": "2024-01-01T00:00:00.000Z",
+ }
+ },
+ None,
+ ),
+ "client_certificates": (
+ {
+ "data": [
+ {
+ "id": "841ee2c7-3144-4c3c-8709-335462c5b05d",
+ "displayName": "testcert1",
+ "publicKey": (
+ "-----BEGIN CERTIFICATE-----\n"
+ "MIIEOzCCAyOgAwIBAgIIFuVL2ktGT0MwDQYJKoZIhvcNAQELBQAwbzELMAkGA1UE\n"
+ "BhMCVVMxCzAJBgNVBAgMAkNBMRIwEAYDVQQHDAlTdW5ueXZhbGUxFDASBgNVBAoM\n"
+ "-----END CERTIFICATE-----\n"
+ ),
+ "allowPrometheus": True,
+ "expiryDate": "2024-01-01T00:00:00.000Z",
+ },
+ {
+ "id": "869e1792-5505-42f1-a1fc-57a04e56f644",
+ "displayName": "testcert2",
+ "publicKey": (
+ "-----BEGIN CERTIFICATE-----\n"
+ "MIIC9DCCAdygAwIBAgIUD7y+AyrSqRjQdYVflLJ9aTIJu3wwDQYJKoZIhvcNAQEL\n"
+ "BQAwFTETMBEGA1UEAwwKUHJvbWV0aGV1czAeFw0yMjA4MjQxMjQxNDhaFw0yNDA4\n"
+ "-----END CERTIFICATE-----\n"
+ ),
+ "allowPrometheus": True,
+ "expiryDate": "2024-01-01T00:00:00.000Z",
+ },
+ ]
+ },
+ None,
+ ),
+}
+
+
+def set_module_args(args):
+ """prepare arguments so that they will be picked up during module creation"""
+ args = json.dumps({"ANSIBLE_MODULE_ARGS": args})
+ basic._ANSIBLE_ARGS = to_bytes(args) # pylint: disable=protected-access
+
+
+class AnsibleExitJson(Exception):
+ """Exception class to be raised by module.exit_json and caught by the test case"""
+
+ pass
+
+
+class AnsibleFailJson(Exception):
+ """Exception class to be raised by module.fail_json and caught by the test case"""
+
+ pass
+
+
+def exit_json(*args, **kwargs): # pylint: disable=unused-argument
+ """function to patch over exit_json; package return data into an exception"""
+ if "changed" not in kwargs:
+ kwargs["changed"] = False
+ raise AnsibleExitJson(kwargs)
+
+
+def fail_json(*args, **kwargs): # pylint: disable=unused-argument
+ """function to patch over fail_json; package return data into an exception"""
+ kwargs["failed"] = True
+ raise AnsibleFailJson(kwargs)
+
+
+class TestMyModule(unittest.TestCase):
+ """a group of related Unit Tests"""
+
+ def setUp(self):
+ self.mock_module_helper = patch.multiple(basic.AnsibleModule, exit_json=exit_json, fail_json=fail_json)
+ self.mock_module_helper.start()
+ self.addCleanup(self.mock_module_helper.stop)
+
+ def set_default_args_fail_check(self):
+ return dict(
+ {
+ "allow_prometheus": True,
+ "public_key": (
+ "-----BEGIN CERTIFICATE-----\n"
+ "MIIEOzCCAyOgAwIBAgIIFuVL2ktGT0MwDQYJKoZIhvcNAQELBQAwbzELMAkGA1UE\n"
+ "BhMCVVMxCzAJBgNVBAgMAkNBMRIwEAYDVQQHDAlTdW5ueXZhbGUxFDASBgNVBAoM\n"
+ "-----END CERTIFICATE-----\n"
+ ),
+ "auth_token": "01234567-5678-9abc-78de-9fgabc123def",
+ "validate_certs": False,
+ }
+ )
+
+ def set_default_args_pass_check(self):
+ return dict(
+ {
+ "state": "present",
+ "display_name": "testcert1",
+ "allow_prometheus": True,
+ "public_key": (
+ "-----BEGIN CERTIFICATE-----\n"
+ "MIIEOzCCAyOgAwIBAgIIFuVL2ktGT0MwDQYJKoZIhvcNAQELBQAwbzELMAkGA1UE\n"
+ "BhMCVVMxCzAJBgNVBAgMAkNBMRIwEAYDVQQHDAlTdW5ueXZhbGUxFDASBgNVBAoM\n"
+ "-----END CERTIFICATE-----\n"
+ ),
+ "api_url": "gmi.example.com",
+ "auth_token": "01234567-5678-9abc-78de-9fgabc123def",
+ "validate_certs": False,
+ }
+ )
+
+ def set_args_create_na_sg_grid_client_certificate(self):
+ return dict(
+ {
+ "state": "present",
+ "display_name": "testcert1",
+ "allow_prometheus": True,
+ "public_key": (
+ "-----BEGIN CERTIFICATE-----\n"
+ "MIIEOzCCAyOgAwIBAgIIFuVL2ktGT0MwDQYJKoZIhvcNAQELBQAwbzELMAkGA1UE\n"
+ "BhMCVVMxCzAJBgNVBAgMAkNBMRIwEAYDVQQHDAlTdW5ueXZhbGUxFDASBgNVBAoM\n"
+ "-----END CERTIFICATE-----\n"
+ ),
+ "api_url": "gmi.example.com",
+ "auth_token": "01234567-5678-9abc-78de-9fgabc123def",
+ "validate_certs": False,
+ }
+ )
+
+ def set_args_delete_na_sg_grid_client_certificate(self):
+ return dict(
+ {
+ "state": "absent",
+ "display_name": "testcert1",
+ "api_url": "gmi.example.com",
+ "auth_token": "01234567-5678-9abc-78de-9fgabc123def",
+ "validate_certs": False,
+ }
+ )
+
+ @patch("ansible_collections.netapp.storagegrid.plugins.module_utils.netapp.SGRestAPI.send_request")
+ def test_module_fail_when_required_args_missing(self, mock_request):
+ """required arguments are reported as errors"""
+ with pytest.raises(AnsibleFailJson) as exc:
+ set_module_args(self.set_default_args_fail_check())
+ grid_client_certificate_module()
+ print("Info: test_module_fail_when_required_args_missing: %s" % exc.value.args[0]["msg"])
+
+ @patch("ansible_collections.netapp.storagegrid.plugins.module_utils.netapp.SGRestAPI.send_request")
+ def test_module_pass_when_required_args_present(self, mock_request):
+ """required arguments are reported as errors"""
+ with pytest.raises(AnsibleExitJson) as exc:
+ set_module_args(self.set_default_args_pass_check())
+ grid_client_certificate_module()
+ exit_json(changed=True, msg="Induced arguments check")
+ print("Info: test_module_pass_when_required_args_present: %s" % exc.value.args[0]["msg"])
+ assert exc.value.args[0]["changed"]
+
+ @patch("ansible_collections.netapp.storagegrid.plugins.module_utils.netapp.SGRestAPI.send_request")
+ def test_create_na_sg_grid_client_certificate_pass(self, mock_request):
+ set_module_args(self.set_args_create_na_sg_grid_client_certificate())
+ mock_request.side_effect = [
+ SRR["empty_good"], # get
+ SRR["client_cert_record"], # post
+ SRR["end_of_sequence"],
+ ]
+ my_obj = grid_client_certificate_module()
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ print("Info: test_create_na_sg_grid_client_certificate_pass: %s" % repr(exc.value.args[0]))
+ assert exc.value.args[0]["changed"]
+
+ @patch("ansible_collections.netapp.storagegrid.plugins.module_utils.netapp.SGRestAPI.send_request")
+ def test_idempotent_create_na_sg_grid_client_certificate_pass(self, mock_request):
+ args = self.set_args_create_na_sg_grid_client_certificate()
+ set_module_args(args)
+ mock_request.side_effect = [
+ SRR["client_certificates"], # get
+ SRR["client_cert_record"], # get
+ SRR["end_of_sequence"],
+ ]
+ my_obj = grid_client_certificate_module()
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ print("Info: test_idempotent_create_na_sg_grid_client_certificate_pass: %s" % repr(exc.value.args[0]))
+ assert not exc.value.args[0]["changed"]
+
+ @patch("ansible_collections.netapp.storagegrid.plugins.module_utils.netapp.SGRestAPI.send_request")
+ def test_update_na_sg_grid_client_certificate_pass(self, mock_request):
+ args = self.set_args_create_na_sg_grid_client_certificate()
+ args["public_key"] = (
+ "-----BEGIN CERTIFICATE-----\n"
+ "MIICrDCCAZSgAwIBAgIUM3IQEKIypqPrXmoA/KmELXfFAz8wDQYJKoZIhvcNAQEL\n"
+ "BQAwADAeFw0yMjA5MDUyMzI3MTVaFw0yNDA5MDQyMzI3MTVaMAAwggEiMA0GCSqG\n"
+ "-----END CERTIFICATE-----\n",
+ )
+ set_module_args(args)
+ mock_request.side_effect = [
+ SRR["client_certificates"], # get
+ SRR["client_cert_record"], # get
+ SRR["client_cert_record_updated"], # put
+ SRR["end_of_sequence"],
+ ]
+ my_obj = grid_client_certificate_module()
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ print("Info: test_update_na_sg_grid_client_certificate_pass: %s" % repr(exc.value.args[0]))
+ assert exc.value.args[0]["changed"]
+
+ @patch("ansible_collections.netapp.storagegrid.plugins.module_utils.netapp.SGRestAPI.send_request")
+ def test_rename_na_sg_grid_client_certificate_pass(self, mock_request):
+ args = self.set_args_create_na_sg_grid_client_certificate()
+ args["certificate_id"] = "841ee2c7-3144-4c3c-8709-335462c5b05d"
+ args["display_name"] = "testcert1-rename"
+ set_module_args(args)
+ mock_request.side_effect = [
+ SRR["client_cert_record"], # get
+ SRR["client_cert_record_rename"], # put
+ SRR["end_of_sequence"],
+ ]
+ my_obj = grid_client_certificate_module()
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ print("Info: test_rename_na_sg_grid_client_certificate_pass: %s" % repr(exc.value.args[0]))
+ assert exc.value.args[0]["changed"]
+
+ @patch("ansible_collections.netapp.storagegrid.plugins.module_utils.netapp.SGRestAPI.send_request")
+ def test_delete_na_sg_grid_client_certificate_pass(self, mock_request):
+ args = self.set_args_delete_na_sg_grid_client_certificate()
+ set_module_args(args)
+ mock_request.side_effect = [
+ SRR["client_certificates"], # get
+ SRR["client_cert_record"], # get
+ SRR["delete_good"], # delete
+ SRR["end_of_sequence"],
+ ]
+ my_obj = grid_client_certificate_module()
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ print("Info: test_delete_na_sg_grid_client_certificate_pass: %s" % repr(exc.value.args[0]))
+ assert exc.value.args[0]["changed"]
+
+ @patch("ansible_collections.netapp.storagegrid.plugins.module_utils.netapp.SGRestAPI.send_request")
+ def test_update_na_sg_grid_client_certificate_bad_certificate_id_fail(self, mock_request):
+ args = self.set_args_create_na_sg_grid_client_certificate()
+ args["certificate_id"] = "ffffffff-ffff-aaaa-aaaa-000000000000"
+ args["display_name"] = "Bad ID"
+ set_module_args(args)
+ mock_request.side_effect = [
+ SRR["not_found"], # get
+ ]
+ with pytest.raises(AnsibleFailJson) as exc:
+ my_obj = grid_client_certificate_module()
+ my_obj.apply()
+ print("Info: test_update_na_sg_grid_client_certificate_bad_certificate_id_fail: %s" % repr(exc.value.args[0]))
+ assert exc.value.args[0]["failed"]
diff --git a/ansible_collections/netapp/storagegrid/tests/unit/plugins/modules/test_na_sg_grid_dns.py b/ansible_collections/netapp/storagegrid/tests/unit/plugins/modules/test_na_sg_grid_dns.py
new file mode 100644
index 000000000..42abde9c8
--- /dev/null
+++ b/ansible_collections/netapp/storagegrid/tests/unit/plugins/modules/test_na_sg_grid_dns.py
@@ -0,0 +1,241 @@
+# (c) 2020, NetApp, Inc
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+""" unit tests NetApp StorageGRID DNS Ansible module: na_sg_grid_dns"""
+
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+import json
+import pytest
+import sys
+try:
+ from requests import Response
+except ImportError:
+ if sys.version_info < (2, 7):
+ pytestmark = pytest.mark.skip('Skipping Unit Tests on 2.6 as requests is not available')
+ else:
+ raise
+
+from ansible_collections.netapp.storagegrid.tests.unit.compat import unittest
+from ansible_collections.netapp.storagegrid.tests.unit.compat.mock import (
+ patch,
+ Mock,
+)
+from ansible.module_utils import basic
+from ansible.module_utils._text import to_bytes
+from ansible_collections.netapp.storagegrid.plugins.modules.na_sg_grid_dns import (
+ SgGridDns as grid_dns_module,
+)
+
+# REST API canned responses when mocking send_request
+SRR = {
+ # common responses
+ "empty_good": ({"data": []}, None),
+ "not_found": (
+ {"status": "error", "code": 404, "data": {}},
+ {"key": "error.404"},
+ ),
+ "end_of_sequence": (None, "Unexpected call to send_request"),
+ "generic_error": (None, "Expected error"),
+ "delete_good": ({"code": 204}, None),
+ "no_dns_servers": ({"data": []}, None,),
+ "dns_servers": ({"data": ["10.11.12.5", "10.11.12.6"]}, None,),
+ "add_dns_servers": (
+ {"data": ["10.11.12.5", "10.11.12.6", "10.11.12.7"]},
+ None,
+ ),
+ "remove_dns_servers": ({"data": ["10.11.12.5"]}, None,),
+}
+
+
+def set_module_args(args):
+ """prepare arguments so that they will be picked up during module creation"""
+ args = json.dumps({"ANSIBLE_MODULE_ARGS": args})
+ basic._ANSIBLE_ARGS = to_bytes(args) # pylint: disable=protected-access
+
+
+class AnsibleExitJson(Exception):
+ """Exception class to be raised by module.exit_json and caught by the test case"""
+
+ pass
+
+
+class AnsibleFailJson(Exception):
+ """Exception class to be raised by module.fail_json and caught by the test case"""
+
+ pass
+
+
+def exit_json(*args, **kwargs): # pylint: disable=unused-argument
+ """function to patch over exit_json; package return data into an exception"""
+ if "changed" not in kwargs:
+ kwargs["changed"] = False
+ raise AnsibleExitJson(kwargs)
+
+
+def fail_json(*args, **kwargs): # pylint: disable=unused-argument
+ """function to patch over fail_json; package return data into an exception"""
+ kwargs["failed"] = True
+ raise AnsibleFailJson(kwargs)
+
+
+class TestMyModule(unittest.TestCase):
+ """ a group of related Unit Tests """
+
+ def setUp(self):
+ self.mock_module_helper = patch.multiple(
+ basic.AnsibleModule, exit_json=exit_json, fail_json=fail_json
+ )
+ self.mock_module_helper.start()
+ self.addCleanup(self.mock_module_helper.stop)
+
+ def set_default_args_fail_check(self):
+ return dict(
+ {
+ "dns_servers": "10.11.12.8",
+ "auth_token": "01234567-5678-9abc-78de-9fgabc123def",
+ "validate_certs": False,
+ }
+ )
+
+ def set_default_args_pass_check(self):
+ return dict(
+ {
+ "state": "present",
+ "dns_servers": "10.11.12.8",
+ "api_url": "gmi.example.com",
+ "auth_token": "01234567-5678-9abc-78de-9fgabc123def",
+ "validate_certs": False,
+ }
+ )
+
+ def set_args_set_na_sg_grid_dns_servers(self):
+ return dict(
+ {
+ "state": "present",
+ "dns_servers": "10.11.12.5,10.11.12.6",
+ "api_url": "gmi.example.com",
+ "auth_token": "01234567-5678-9abc-78de-9fgabc123def",
+ "validate_certs": False,
+ }
+ )
+
+ def set_args_add_na_sg_grid_dns_server(self):
+ return dict(
+ {
+ "state": "present",
+ "dns_servers": "10.11.12.5,10.11.12.6,10.11.12.7",
+ "api_url": "gmi.example.com",
+ "auth_token": "01234567-5678-9abc-78de-9fgabc123def",
+ "validate_certs": False,
+ }
+ )
+
+ def set_args_remove_na_sg_grid_dns_server(self):
+ return dict(
+ {
+ "state": "present",
+ "dns_servers": "10.11.12.5",
+ "api_url": "gmi.example.com",
+ "auth_token": "01234567-5678-9abc-78de-9fgabc123def",
+ "validate_certs": False,
+ }
+ )
+
+ def test_module_fail_when_required_args_missing(self):
+ """ required arguments are reported as errors """
+ with pytest.raises(AnsibleFailJson) as exc:
+ set_module_args(self.set_default_args_fail_check())
+ grid_dns_module()
+ print(
+ "Info: test_module_fail_when_required_args_missing: %s"
+ % exc.value.args[0]["msg"]
+ )
+
+ def test_module_fail_when_required_args_present(self):
+ """ required arguments are reported as errors """
+ with pytest.raises(AnsibleExitJson) as exc:
+ set_module_args(self.set_default_args_pass_check())
+ grid_dns_module()
+ exit_json(changed=True, msg="Induced arguments check")
+ print(
+ "Info: test_module_fail_when_required_args_present: %s"
+ % exc.value.args[0]["msg"]
+ )
+ assert exc.value.args[0]["changed"]
+
+ @patch(
+ "ansible_collections.netapp.storagegrid.plugins.module_utils.netapp.SGRestAPI.send_request"
+ )
+ def test_set_na_sg_grid_dns_servers_pass(self, mock_request):
+ set_module_args(self.set_args_set_na_sg_grid_dns_servers())
+ my_obj = grid_dns_module()
+ mock_request.side_effect = [
+ SRR["no_dns_servers"], # get
+ SRR["dns_servers"], # post
+ SRR["end_of_sequence"],
+ ]
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ print(
+ "Info: test_set_na_sg_grid_dns_servers_pass: %s"
+ % repr(exc.value.args[0])
+ )
+ assert exc.value.args[0]["changed"]
+
+ @patch(
+ "ansible_collections.netapp.storagegrid.plugins.module_utils.netapp.SGRestAPI.send_request"
+ )
+ def test_idempotent_set_na_sg_grid_dns_servers_pass(self, mock_request):
+ set_module_args(self.set_args_set_na_sg_grid_dns_servers())
+ my_obj = grid_dns_module()
+ mock_request.side_effect = [
+ SRR["dns_servers"], # get
+ SRR["end_of_sequence"],
+ ]
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ print(
+ "Info: test_idempotent_set_na_sg_grid_dns_servers_pass: %s"
+ % repr(exc.value.args[0])
+ )
+ assert not exc.value.args[0]["changed"]
+
+ @patch(
+ "ansible_collections.netapp.storagegrid.plugins.module_utils.netapp.SGRestAPI.send_request"
+ )
+ def test_add_na_sg_grid_dns_servers_pass(self, mock_request):
+ set_module_args(self.set_args_add_na_sg_grid_dns_server())
+ my_obj = grid_dns_module()
+ mock_request.side_effect = [
+ SRR["dns_servers"], # get
+ SRR["add_dns_servers"], # post
+ SRR["end_of_sequence"],
+ ]
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ print(
+ "Info: test_add_na_sg_grid_dns_servers_pass: %s"
+ % repr(exc.value.args[0])
+ )
+ assert exc.value.args[0]["changed"]
+
+ @patch(
+ "ansible_collections.netapp.storagegrid.plugins.module_utils.netapp.SGRestAPI.send_request"
+ )
+ def test_remove_na_sg_grid_dns_servers_pass(self, mock_request):
+ set_module_args(self.set_args_remove_na_sg_grid_dns_server())
+ my_obj = grid_dns_module()
+ mock_request.side_effect = [
+ SRR["dns_servers"], # get
+ SRR["remove_dns_servers"], # post
+ SRR["end_of_sequence"],
+ ]
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ print(
+ "Info: test_remove_na_sg_grid_dns_servers_pass: %s"
+ % repr(exc.value.args[0])
+ )
+ assert exc.value.args[0]["changed"]
diff --git a/ansible_collections/netapp/storagegrid/tests/unit/plugins/modules/test_na_sg_grid_gateway.py b/ansible_collections/netapp/storagegrid/tests/unit/plugins/modules/test_na_sg_grid_gateway.py
new file mode 100644
index 000000000..0a5a7e386
--- /dev/null
+++ b/ansible_collections/netapp/storagegrid/tests/unit/plugins/modules/test_na_sg_grid_gateway.py
@@ -0,0 +1,693 @@
+# (c) 2020, NetApp, Inc
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+""" unit tests NetApp StorageGRID Grid Load Balancer Endpoint Ansible module: na_sg_grid_gateway"""
+
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+import json
+import pytest
+import sys
+
+# try:
+# from requests import Response
+# except ImportError:
+# if sys.version_info < (2, 7):
+# pytestmark = pytest.mark.skip("Skipping Unit Tests on 2.6 as requests is not available")
+# else:
+# raise
+
+from ansible_collections.netapp.storagegrid.tests.unit.compat import unittest
+from ansible_collections.netapp.storagegrid.tests.unit.compat.mock import (
+ patch,
+ Mock,
+)
+from ansible.module_utils import basic
+from ansible.module_utils._text import to_bytes
+from ansible_collections.netapp.storagegrid.plugins.modules.na_sg_grid_gateway import (
+ SgGridGateway as grid_gateway_module,
+)
+
+# REST API canned responses when mocking send_request
+SRR = {
+ # common responses
+ "empty_good": ({"data": []}, None),
+ "not_found": (
+ {"status": "error", "code": 404, "data": {}},
+ {"key": "error.404"},
+ ),
+ "end_of_sequence": (None, "Unexpected call to send_request"),
+ "generic_error": (None, "Expected error"),
+ "delete_good": (None, None),
+ "update_good": (None, None),
+ "version_114": ({"data": {"productVersion": "11.4.0-20200721.1338.d3969b3"}}, None),
+ "version_116": ({"data": {"productVersion": "11.6.0-20211120.0301.850531e"}}, None),
+ "gateway_record": (
+ {
+ "data": {
+ "id": "e777d415-057f-4d37-9b0c-6d132d872ea0",
+ "displayName": "ansibletest-secure",
+ "enableIPv4": True,
+ "enableIPv6": True,
+ "port": 10443,
+ "secure": True,
+ "accountId": "0",
+ }
+ },
+ None,
+ ),
+ "gateway_record_ha_group_binding": (
+ {
+ "data": {
+ "id": "e777d415-057f-4d37-9b0c-6d132d872ea0",
+ "displayName": "ansibletest-secure",
+ "enableIPv4": True,
+ "enableIPv6": True,
+ "port": 10443,
+ "secure": True,
+ "accountId": "0",
+ "pinTargets": {"haGroups": ["c08e6dca-038d-4a05-9499-6fbd1e6a4c3e"], "nodeInterfaces": []},
+ }
+ },
+ None,
+ ),
+ "gateway_record_node_interface_binding": (
+ {
+ "data": {
+ "id": "e777d415-057f-4d37-9b0c-6d132d872ea0",
+ "displayName": "ansibletest-secure",
+ "enableIPv4": True,
+ "enableIPv6": True,
+ "port": 10443,
+ "secure": True,
+ "accountId": "0",
+ "pinTargets": {
+ "haGroups": [],
+ "nodeInterfaces": [
+ {"interface": "eth2", "nodeId": "0b1866ed-d6e7-41b4-815f-bf867348b76b"},
+ {"interface": "eth2", "nodeId": "970ad050-b68b-4aae-a94d-aef73f3095c4"},
+ ],
+ },
+ }
+ },
+ None,
+ ),
+ "gateway_record_rename": (
+ {
+ "data": {
+ "id": "e777d415-057f-4d37-9b0c-6d132d872ea0",
+ "displayName": "ansibletest-rename",
+ "enableIPv4": True,
+ "enableIPv6": True,
+ "port": 10443,
+ "secure": True,
+ "accountId": "0",
+ "pinTargets": {"haGroups": ["c08e6dca-038d-4a05-9499-6fbd1e6a4c3e"], "nodeInterfaces": []},
+ }
+ },
+ None,
+ ),
+ "ha_groups": (
+ {
+ "data": [
+ {
+ "id": "c08e6dca-038d-4a05-9499-6fbd1e6a4c3e",
+ "name": "site1_primary",
+ "description": "test ha group",
+ "virtualIps": ["10.193.174.117"],
+ "interfaces": [
+ {
+ "nodeId": "0b1866ed-d6e7-41b4-815f-bf867348b76b",
+ "nodeName": "SITE1-ADM1",
+ "interface": "eth2",
+ "preferredMaster": True,
+ },
+ {
+ "nodeId": "970ad050-b68b-4aae-a94d-aef73f3095c4",
+ "nodeName": "SITE2-ADM1",
+ "interface": "eth2",
+ },
+ ],
+ "gatewayCidr": "192.168.14.1/24",
+ },
+ {
+ "id": "da9ac524-9a16-4be0-9d6e-ec9b22218e75",
+ "name": "site1_gw",
+ "description": "another test ha group",
+ "virtualIps": ["10.193.204.200"],
+ "interfaces": [
+ {
+ "nodeId": "7bb5bf05-a04c-4344-8abd-08c5c4048666",
+ "nodeName": "SITE1-GW1",
+ "interface": "eth0",
+ "preferredMaster": True,
+ },
+ ],
+ "gatewayCidr": "192.168.14.1/24",
+ }
+ ]
+ },
+ None,
+ ),
+ "node_health": (
+ {
+ "data": [
+ {
+ "id": "0b1866ed-d6e7-41b4-815f-bf867348b76b",
+ "isPrimaryAdmin": True,
+ "name": "SITE1-ADM1",
+ "siteId": "ae56d06d-bd83-46bd-adce-77146b1d94bd",
+ "siteName": "SITE1",
+ "severity": "normal",
+ "state": "connected",
+ "type": "adminNode",
+ },
+ {
+ "id": "970ad050-b68b-4aae-a94d-aef73f3095c4",
+ "isPrimaryAdmin": False,
+ "name": "SITE2-ADM1",
+ "siteId": "7c24002e-5157-43e9-83e5-02db9b265b02",
+ "siteName": "SITE2",
+ "severity": "normal",
+ "state": "connected",
+ "type": "adminNode",
+ },
+ ]
+ },
+ None,
+ ),
+ "present_gateways": (
+ {
+ "data": [
+ {
+ "id": "e777d415-057f-4d37-9b0c-6d132d872ea0",
+ "displayName": "ansibletest-secure",
+ "enableIPv4": True,
+ "enableIPv6": True,
+ "port": 10443,
+ "secure": True,
+ "accountId": "0",
+ }
+ ]
+ },
+ None,
+ ),
+ "present_gateways_with_binding": (
+ {
+ "data": [
+ {
+ "id": "e777d415-057f-4d37-9b0c-6d132d872ea0",
+ "displayName": "ansibletest-secure",
+ "enableIPv4": True,
+ "enableIPv6": True,
+ "port": 10443,
+ "secure": True,
+ "accountId": "0",
+ "pinTargets": {"haGroups": [], "nodeInterfaces": []},
+ }
+ ]
+ },
+ None,
+ ),
+ "server_config": (
+ {
+ "data": {
+ "defaultServiceType": "s3",
+ "certSource": "plaintext",
+ "plaintextCertData": {
+ "serverCertificateEncoded": (
+ "-----BEGIN CERTIFICATE-----\n"
+ "MIICyDCCAbACCQCgFntI3q7iADANBgkqhkiG9w0BAQsFADAmMQswCQYDVQQGEwJV\n"
+ "UzEXMBUGA1UEAwwOczMuZXhhbXBsZS5jb20wHhcNMjEwNDI5MDQ1NTM1WhcNMjIw\n"
+ "NDI5MDQ1NTM1WjAmMQswCQYDVQQGEwJVUzEXMBUGA1UEAwwOczMuZXhhbXBsZS5j\n"
+ "b20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQD0LMcJUdWmTtxi7U7B\n"
+ "yldDRfyCD9W+QJ1Ygm7E9iFwvkThUCV5q+DIcgSfogoSKaQuHaImLXMZn36QC22n\n"
+ "+Ah2EGrQiggyny3wDzuWf5/Qg7ogqQRqiespBFLlV4RGCREHK0y5uq8mzpIaQ8l8\n"
+ "STa7nLS7BIc6rD15BJaNWZpDVHIzhljlnhfnqwio/ZfP++lAjk4/j8pPGPEEI5Fe\n"
+ "WxhOtQjr7xTHeJxKHp2VKiLEvFxniL3qk4uJ3k5fJ7IqALUEPWH92brFp2IkObUA\n"
+ "EGsZYB4KFV7asBVhGuspYNzUQ6NqWbEUmtTjKEXcb1TA8RK+Pc2TotOrQ2E7Z+rU\n"
+ "gl2fAgMBAAEwDQYJKoZIhvcNAQELBQADggEBAD5PW1WI7GCfxLQjaitnXpD1MR2O\n"
+ "6b5csymPYwRejMsSswd8egjs+vO2pbF9TptLjqGliE9XUoI+mWpuMzzd75F0jcjq\n"
+ "1DhlINgAmjUJEAg0RAqce0Kn8xQF+SofMtkOH+nZm3Q9nbTJKr1H5m2TnCq3v5TH\n"
+ "Qo0ASf0LLGgrwUtT0IghdSttYLS89dJprZ6c5wK7qeBzxfdHxxjiaSnvByL2Ryn5\n"
+ "cec9lptYKoRY42hWvkQv9Wkr3DDoyNA3xPdZJr0Hpf8/mSPnt9r/AR8E32xi0SXp\n"
+ "hOMTDgMicbK82ycxz0yW88gm6yhrChlJrWaEsVGod3FU+lbMAnagYZ/Vwp8=\n"
+ "-----END CERTIFICATE-----\n"
+ ),
+ "caBundleEncoded": None,
+ "metadata": {
+ "serverCertificateDetails": {
+ "subject": "/CN=test",
+ "issuer": "/CN=test",
+ "serialNumber": "32:6F:20:EB:0E:90:60:7E:07:8F:6E:CC:02:2D:7C:37:3D:AB:42:7E",
+ "notBefore": "2021-09-27T12:39:17.000Z",
+ "notAfter": "2023-09-27T12:39:17.000Z",
+ "fingerPrints": {
+ "SHA-1": "A4:F9:74:BE:E8:A2:46:C2:E1:23:DE:8F:A8:1B:F1:C4:91:51:C5:56",
+ "SHA-256": "7B:65:7F:CD:35:8F:33:1C:C8:2D:F0:C1:9F:58:2F:2B:3B:78:44:95:4E:23:8C:1B:2B:91:6C:94:B0:71:64:E8",
+ },
+ "subjectAltNames": ["DNS:*.test.com"],
+ }
+ },
+ },
+ }
+ },
+ None,
+ ),
+ "server_config_cert_update": (
+ {
+ "data": {
+ "defaultServiceType": "s3",
+ "certSource": "plaintext",
+ "plaintextCertData": {
+ "serverCertificateEncoded": (
+ "-----BEGIN CERTIFICATE-----\n"
+ "MIICzjCCAbYCCQDZVi1OT89SAjANBgkqhkiG9w0BAQsFADApMQswCQYDVQQGEwJV\n"
+ "UzEaMBgGA1UEAwwRczMubmV3ZXhhbXBsZS5jb20wHhcNMjEwNDI5MDQ1NzIxWhcN\n"
+ "MjIwNDI5MDQ1NzIxWjApMQswCQYDVQQGEwJVUzEaMBgGA1UEAwwRczMubmV3ZXhh\n"
+ "bXBsZS5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCmg37q2sjZ\n"
+ "k+HsXtai3PSMtGUiqij04JtG9ahMqIejuxy5sDCWnigh//NjdK+wPYc2VfYd6KFA\n"
+ "Uk9rP84M7sqdqGzIzmyEu7INyCnlbxcXlST6UZDsZnVU7Gk2GvUzk2OoO5N+G0oI\n"
+ "Lfc/3eKTx9j9BguOaWUy+ni+Te8j6EwK6HolGRBjLYqf1SYFBzaoVpy7pmzaFZ4R\n"
+ "10jFSxHbotIZ+kR8pPE5jGkP8OjOfrpbhEgmffpeq2MSCMRuhRtRiVp4ULwkMTRN\n"
+ "tFj89mu1gl9T3lYM/LO1SmBv3il0mNmrTL+99UJ4s2eL0zr/uHAVYJcVqFgWP7X8\n"
+ "WnOk+d86b0TXAgMBAAEwDQYJKoZIhvcNAQELBQADggEBAFmGV3IOuNYeM3LQxls+\n"
+ "/CNHznvIqvoiJOWq0S7LFy1eO7PVzCl3l/fDKjGMt2lGXeU89YKdFVPqsainNEFT\n"
+ "cNEWlezVut+/CWQpBXujyBqPLkYbzyGsakMImDb+MrSkBO5MCjlt38vppm5a97fB\n"
+ "9o/wM31e+N6gJLiHWs0XB9TK6bY9CvcutcGUOH/oxH1TEBgrJ3SoS7/HmZJSaCQA\n"
+ "hjZappzuEpGVXT8YDlb67PzUoE2rDWjdSFRXCk/0U6VR0xNgnN1WtfHaypU71DrB\n"
+ "zxbDaOIZoDp5G4OgjkFxoCoSWLant+LsqEwclIbCFgEvJPE8855UThelTHmIfivP\n"
+ "veI=\n-----END CERTIFICATE-----\n"
+ ),
+ "caBundleEncoded": None,
+ "metadata": {
+ "serverCertificateDetails": {
+ "subject": "/CN=test",
+ "issuer": "/CN=test",
+ "serialNumber": "32:6F:20:EB:0E:90:60:7E:07:8F:6E:CC:02:2D:7C:37:3D:AB:42:7E",
+ "notBefore": "2021-09-27T12:39:17.000Z",
+ "notAfter": "2023-09-27T12:39:17.000Z",
+ "fingerPrints": {
+ "SHA-1": "F2:C2:6F:A8:45:DA:86:09:91:F5:04:B0:25:43:B7:FC:FA:C1:43:F8",
+ "SHA-256": "99:3E:21:1A:03:25:69:C8:0A:D5:FE:E3:FB:6E:51:03:BD:A7:0E:88:6B:53:06:04:92:3B:34:17:68:43:F7:2F",
+ },
+ "subjectAltNames": ["DNS:*.test.com"],
+ }
+ },
+ },
+ }
+ },
+ None,
+ ),
+}
+
+
+def set_module_args(args):
+ """prepare arguments so that they will be picked up during module creation"""
+ args = json.dumps({"ANSIBLE_MODULE_ARGS": args})
+ basic._ANSIBLE_ARGS = to_bytes(args) # pylint: disable=protected-access
+
+
+class AnsibleExitJson(Exception):
+ """Exception class to be raised by module.exit_json and caught by the test case"""
+
+ pass
+
+
+class AnsibleFailJson(Exception):
+ """Exception class to be raised by module.fail_json and caught by the test case"""
+
+ pass
+
+
+def exit_json(*args, **kwargs): # pylint: disable=unused-argument
+ """function to patch over exit_json; package return data into an exception"""
+ if "changed" not in kwargs:
+ kwargs["changed"] = False
+ raise AnsibleExitJson(kwargs)
+
+
+def fail_json(*args, **kwargs): # pylint: disable=unused-argument
+ """function to patch over fail_json; package return data into an exception"""
+ kwargs["failed"] = True
+ raise AnsibleFailJson(kwargs)
+
+
+class TestMyModule(unittest.TestCase):
+ """a group of related Unit Tests"""
+
+ def setUp(self):
+ self.mock_module_helper = patch.multiple(basic.AnsibleModule, exit_json=exit_json, fail_json=fail_json)
+ self.mock_module_helper.start()
+ self.addCleanup(self.mock_module_helper.stop)
+
+ def set_default_args_fail_check(self):
+ return dict(
+ {
+ "display_name": "ansibletest-secure",
+ "default_service_type": "s3",
+ "server_certificate": (
+ "-----BEGIN CERTIFICATE-----\n"
+ "MIICyDCCAbACCQCgFntI3q7iADANBgkqhkiG9w0BAQsFADAmMQswCQYDVQQGEwJV\n"
+ "UzEXMBUGA1UEAwwOczMuZXhhbXBsZS5jb20wHhcNMjEwNDI5MDQ1NTM1WhcNMjIw\n"
+ "-----END CERTIFICATE-----\n"
+ ),
+ "private_key": (
+ "-----BEGIN PRIVATE KEY-----\n"
+ "MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQD0LMcJUdWmTtxi\n"
+ "7U7ByldDRfyCD9W+QJ1Ygm7E9iFwvkThUCV5q+DIcgSfogoSKaQuHaImLXMZn36Q\n"
+ "C22n+Ah2EGrQiggyny3wDzuWf5/Qg7ogqQRqiespBFLlV4RGCREHK0y5uq8mzpIa\n"
+ "-----END PRIVATE KEY-----\n"
+ ),
+ "auth_token": "01234567-5678-9abc-78de-9fgabc123def",
+ "validate_certs": False,
+ }
+ )
+
+ def set_default_args_pass_check(self):
+ return dict(
+ {
+ "state": "present",
+ "display_name": "ansibletest-secure",
+ "default_service_type": "s3",
+ "port": 10443,
+ "server_certificate": (
+ "-----BEGIN CERTIFICATE-----\n"
+ "MIICyDCCAbACCQCgFntI3q7iADANBgkqhkiG9w0BAQsFADAmMQswCQYDVQQGEwJV\n"
+ "UzEXMBUGA1UEAwwOczMuZXhhbXBsZS5jb20wHhcNMjEwNDI5MDQ1NTM1WhcNMjIw\n"
+ "-----END CERTIFICATE-----\n"
+ ),
+ "private_key": (
+ "-----BEGIN PRIVATE KEY-----\n"
+ "MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQD0LMcJUdWmTtxi\n"
+ "7U7ByldDRfyCD9W+QJ1Ygm7E9iFwvkThUCV5q+DIcgSfogoSKaQuHaImLXMZn36Q\n"
+ "C22n+Ah2EGrQiggyny3wDzuWf5/Qg7ogqQRqiespBFLlV4RGCREHK0y5uq8mzpIa\n"
+ "-----END PRIVATE KEY-----\n"
+ ),
+ "api_url": "https://gmi.example.com",
+ "auth_token": "01234567-5678-9abc-78de-9fgabc123def",
+ "validate_certs": False,
+ }
+ )
+
+ def set_args_create_na_sg_grid_gateway_port(self):
+ return dict(
+ {
+ "state": "present",
+ "display_name": "ansibletest-secure",
+ "default_service_type": "s3",
+ "port": 10443,
+ "server_certificate": (
+ "-----BEGIN CERTIFICATE-----\n"
+ "MIICyDCCAbACCQCgFntI3q7iADANBgkqhkiG9w0BAQsFADAmMQswCQYDVQQGEwJV\n"
+ "UzEXMBUGA1UEAwwOczMuZXhhbXBsZS5jb20wHhcNMjEwNDI5MDQ1NTM1WhcNMjIw\n"
+ "NDI5MDQ1NTM1WjAmMQswCQYDVQQGEwJVUzEXMBUGA1UEAwwOczMuZXhhbXBsZS5j\n"
+ "b20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQD0LMcJUdWmTtxi7U7B\n"
+ "yldDRfyCD9W+QJ1Ygm7E9iFwvkThUCV5q+DIcgSfogoSKaQuHaImLXMZn36QC22n\n"
+ "+Ah2EGrQiggyny3wDzuWf5/Qg7ogqQRqiespBFLlV4RGCREHK0y5uq8mzpIaQ8l8\n"
+ "STa7nLS7BIc6rD15BJaNWZpDVHIzhljlnhfnqwio/ZfP++lAjk4/j8pPGPEEI5Fe\n"
+ "WxhOtQjr7xTHeJxKHp2VKiLEvFxniL3qk4uJ3k5fJ7IqALUEPWH92brFp2IkObUA\n"
+ "EGsZYB4KFV7asBVhGuspYNzUQ6NqWbEUmtTjKEXcb1TA8RK+Pc2TotOrQ2E7Z+rU\n"
+ "gl2fAgMBAAEwDQYJKoZIhvcNAQELBQADggEBAD5PW1WI7GCfxLQjaitnXpD1MR2O\n"
+ "6b5csymPYwRejMsSswd8egjs+vO2pbF9TptLjqGliE9XUoI+mWpuMzzd75F0jcjq\n"
+ "1DhlINgAmjUJEAg0RAqce0Kn8xQF+SofMtkOH+nZm3Q9nbTJKr1H5m2TnCq3v5TH\n"
+ "Qo0ASf0LLGgrwUtT0IghdSttYLS89dJprZ6c5wK7qeBzxfdHxxjiaSnvByL2Ryn5\n"
+ "cec9lptYKoRY42hWvkQv9Wkr3DDoyNA3xPdZJr0Hpf8/mSPnt9r/AR8E32xi0SXp\n"
+ "hOMTDgMicbK82ycxz0yW88gm6yhrChlJrWaEsVGod3FU+lbMAnagYZ/Vwp8=\n"
+ "-----END CERTIFICATE-----\n"
+ ),
+ "private_key": (
+ "-----BEGIN PRIVATE KEY-----\n"
+ "MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQD0LMcJUdWmTtxi\n"
+ "7U7ByldDRfyCD9W+QJ1Ygm7E9iFwvkThUCV5q+DIcgSfogoSKaQuHaImLXMZn36Q\n"
+ "C22n+Ah2EGrQiggyny3wDzuWf5/Qg7ogqQRqiespBFLlV4RGCREHK0y5uq8mzpIa\n"
+ "Q8l8STa7nLS7BIc6rD15BJaNWZpDVHIzhljlnhfnqwio/ZfP++lAjk4/j8pPGPEE\n"
+ "I5FeWxhOtQjr7xTHeJxKHp2VKiLEvFxniL3qk4uJ3k5fJ7IqALUEPWH92brFp2Ik\n"
+ "ObUAEGsZYB4KFV7asBVhGuspYNzUQ6NqWbEUmtTjKEXcb1TA8RK+Pc2TotOrQ2E7\n"
+ "Z+rUgl2fAgMBAAECggEAAwSSqTDTvSx4WNiqAocnsPMqfckIUUOnLjLef5yzKRuQ\n"
+ "6l/9NpXDP3b5S6fLDBJrrw46tNIW/BgWjl01y7+rCxqE13L9SvLgtHjbua52ITOf\n"
+ "l0u/fDmcKHOfOqpsPhlaloYYeqsuAwLGl4CC+wBEpuj26uDRcw4x7E78NV8IIxDf\n"
+ "8kUNPQXI9ox6P3isXrFkMncDfKLWOYJ5fF5zCoVZai/SS8z3FhGjAXlMkay48RX4\n"
+ "4vuP7TNLZ2O2pAk2aVs54tQyBn9MOxIzOg3/ZFLiKZR4pY6H5sm+bT263TdvN+A4\n"
+ "C8kwML5HnsCjVkTzJ/3dYc9SeUOuqvJI332GCQ9YcQKBgQD8Ev2qhS61kZ3WGO6G\n"
+ "DRkZ6tDyt5vCuzWQ8uAAXcAerFDWN6XtDPfXq2UVcWnoCQOUpnjslCb/NJgCetLh\n"
+ "mOPeJGRWyMly+YuYb4/rnbwSbUs28PO4D9B/f5YQBnBjGDLL/i2+wnXg3WZTVogf\n"
+ "WfdKziOHGSxmWd6JinI+4UkpiwKBgQD3+krkFORTsUAlTgeIy8+QzXSuclwNygcX\n"
+ "HAe0F96hSYHBC7+1n7nzC1lwcbkU3jLIt3A90Uwew4nr5GCu4sSVwDeWrqP2I9WH\n"
+ "4w0zeaFPC1QKfKGBtsIf/89pDz/7iGlcKWlEg+56VVIJn7qC2lO8qbeUCoglsSwC\n"
+ "vr2Qld5WvQKBgQCHM2xpHHv8GPlOTxsIPVg8RW0C8iYSITVO5GXu7FnSWdwVuc0+\n"
+ "QtlgDObvxF/oe4U3Ir7zLVdpRH1Pvy8Cn22AxYYn4hPiniQYg6Xu2zB3tbVE56Hh\n"
+ "FGJhMD59o+Z90AnWziMdENIG5NkwU9Y48pknvz7hBEiDMSqiHObAATerlwKBgQCP\n"
+ "5LhCY3Ees3MCcqXilkmqv93eQFP0WHAG0+gQc+1m7+2QJI4pCTdwtfw/SG5akpkr\n"
+ "aW6DIIkoLNVCgbIsqT/jmbdoA4z3DlIg2PrXDNQytuMcdreNOoyo3trvHr9E6SIi\n"
+ "LZF9BYWDjTDejsY+mgwPJPh2uinInWdpbF85oA11jQKBgQCc6U2fSwpPQowOaat/\n"
+ "pY5bDCKxhfwrKk3Ecye5HfhbBZ0pu6Oneiq6cNhQC0X69iFn6ogTFx5qqyMQrWH0\n"
+ "L+kQRkyYFLnebCzUA8364lieRzc3cN+xQEn+jX8z7eDZ8JsvVnKdc6lTjPTwN1Fj\n"
+ "FZtaH2L1IEiA8ZZapMb/MNNozg==\n"
+ "-----END PRIVATE KEY-----\n"
+ ),
+ "api_url": "https://gmi.example.com",
+ "auth_token": "01234567-5678-9abc-78de-9fgabc123def",
+ "validate_certs": False,
+ }
+ )
+
+ def set_args_delete_na_sg_grid_gateway_port(self):
+ return dict(
+ {
+ "state": "absent",
+ "display_name": "ansibletest-secure",
+ "default_service_type": "s3",
+ "port": 10443,
+ "server_certificate": (
+ "-----BEGIN CERTIFICATE-----\n"
+ "MIICyDCCAbACCQCgFntI3q7iADANBgkqhkiG9w0BAQsFADAmMQswCQYDVQQGEwJV\n"
+ "UzEXMBUGA1UEAwwOczMuZXhhbXBsZS5jb20wHhcNMjEwNDI5MDQ1NTM1WhcNMjIw\n"
+ "-----END CERTIFICATE-----\n"
+ ),
+ "private_key": (
+ "-----BEGIN PRIVATE KEY-----\n"
+ "MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQD0LMcJUdWmTtxi\n"
+ "7U7ByldDRfyCD9W+QJ1Ygm7E9iFwvkThUCV5q+DIcgSfogoSKaQuHaImLXMZn36Q\n"
+ "C22n+Ah2EGrQiggyny3wDzuWf5/Qg7ogqQRqiespBFLlV4RGCREHK0y5uq8mzpIa\n"
+ "-----END PRIVATE KEY-----\n"
+ ),
+ "api_url": "https://gmi.example.com",
+ "auth_token": "01234567-5678-9abc-78de-9fgabc123def",
+ "validate_certs": False,
+ }
+ )
+
+ @patch("ansible_collections.netapp.storagegrid.plugins.module_utils.netapp.SGRestAPI.send_request")
+ def test_module_fail_when_required_args_missing(self, mock_request):
+ """required arguments are reported as errors"""
+ mock_request.side_effect = [
+ SRR["version_114"],
+ ]
+ with pytest.raises(AnsibleFailJson) as exc:
+ set_module_args(self.set_default_args_fail_check())
+ grid_gateway_module()
+ print("Info: test_module_fail_when_required_args_missing: %s" % exc.value.args[0]["msg"])
+
+ @patch("ansible_collections.netapp.storagegrid.plugins.module_utils.netapp.SGRestAPI.send_request")
+ def test_module_pass_when_required_args_present(self, mock_request):
+ """required arguments are reported as errors"""
+ mock_request.side_effect = [
+ SRR["version_114"],
+ ]
+ with pytest.raises(AnsibleExitJson) as exc:
+ set_module_args(self.set_default_args_pass_check())
+ grid_gateway_module()
+ exit_json(changed=True, msg="Induced arguments check")
+ print("Info: test_module_pass_when_required_args_present: %s" % exc.value.args[0]["msg"])
+ assert exc.value.args[0]["changed"]
+
+ @patch("ansible_collections.netapp.storagegrid.plugins.module_utils.netapp.SGRestAPI.send_request")
+ def test_create_na_sg_grid_gateway_port_pass(self, mock_request):
+ set_module_args(self.set_args_create_na_sg_grid_gateway_port())
+ mock_request.side_effect = [
+ SRR["version_114"], # get
+ SRR["empty_good"], # get
+ SRR["gateway_record"], # post
+ SRR["server_config"], # post
+ SRR["end_of_sequence"],
+ ]
+ my_obj = grid_gateway_module()
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ print("Info: test_create_na_sg_grid_gateway_port_pass: %s" % repr(exc.value.args[0]))
+ assert exc.value.args[0]["changed"]
+
+ @patch("ansible_collections.netapp.storagegrid.plugins.module_utils.netapp.SGRestAPI.send_request")
+ def test_idempotent_create_na_sg_grid_gateway_port_pass(self, mock_request):
+ args = self.set_args_create_na_sg_grid_gateway_port()
+ del args["private_key"]
+ set_module_args(args)
+ mock_request.side_effect = [
+ SRR["version_114"], # get
+ SRR["present_gateways"], # get
+ SRR["server_config"], # get
+ SRR["end_of_sequence"],
+ ]
+ my_obj = grid_gateway_module()
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ print("Info: test_idempotent_create_na_sg_grid_gateway_port_pass: %s" % repr(exc.value.args[0]))
+ assert not exc.value.args[0]["changed"]
+
+ @patch("ansible_collections.netapp.storagegrid.plugins.module_utils.netapp.SGRestAPI.send_request")
+ def test_update_na_sg_grid_gateway_certificate_pass(self, mock_request):
+ args = self.set_args_create_na_sg_grid_gateway_port()
+ args["server_certificate"] = "-----BEGIN CERTIFICATE-----\nABCDEFGABCD\n-----END CERTIFICATE-----\n"
+ args["private_key"] = "-----BEGIN PRIVATE KEY-----\nABCDEFGABCD\n-----END PRIVATE KEY-----\n"
+
+ set_module_args(args)
+ mock_request.side_effect = [
+ SRR["version_114"], # get
+ SRR["present_gateways"], # get
+ SRR["server_config"], # get
+ SRR["server_config_cert_update"], # put
+ SRR["end_of_sequence"],
+ ]
+ my_obj = grid_gateway_module()
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ print("Info: test_update_na_sg_grid_gateway_certificate_pass: %s" % repr(exc.value.args[0]))
+ assert exc.value.args[0]["changed"]
+
+ @patch("ansible_collections.netapp.storagegrid.plugins.module_utils.netapp.SGRestAPI.send_request")
+ def test_delete_na_sg_grid_gateway_port_pass(self, mock_request):
+ set_module_args(self.set_args_delete_na_sg_grid_gateway_port())
+ mock_request.side_effect = [
+ SRR["version_114"], # get
+ SRR["present_gateways"], # get
+ SRR["server_config"], # get
+ SRR["delete_good"], # delete
+ SRR["end_of_sequence"],
+ ]
+ my_obj = grid_gateway_module()
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ print("Info: test_delete_na_sg_grid_gateway_port_pass: %s" % repr(exc.value.args[0]))
+ assert exc.value.args[0]["changed"]
+
+ @patch("ansible_collections.netapp.storagegrid.plugins.module_utils.netapp.SGRestAPI.send_request")
+ def test_module_fail_minimum_version_not_met(self, mock_request):
+ args = self.set_args_create_na_sg_grid_gateway_port()
+ args["binding_mode"] = "ha-groups"
+ set_module_args(args)
+ mock_request.side_effect = [
+ SRR["version_114"], # get
+ ]
+ with pytest.raises(AnsibleFailJson) as exc:
+ grid_gateway_module()
+ print("Info: test_module_fail_minimum_version_not_met: %s" % exc.value.args[0]["msg"])
+
+ # test create with ha groups
+ @patch("ansible_collections.netapp.storagegrid.plugins.module_utils.netapp.SGRestAPI.send_request")
+ def test_create_na_sg_grid_gateway_port_with_ha_group_binding_pass(self, mock_request):
+ args = self.set_args_create_na_sg_grid_gateway_port()
+ args["binding_mode"] = "ha-groups"
+ args["ha_groups"] = ["site1_primary", "da9ac524-9a16-4be0-9d6e-ec9b22218e75"]
+ set_module_args(args)
+ mock_request.side_effect = [
+ SRR["version_116"], # get
+ SRR["ha_groups"], # get
+ SRR["empty_good"], # get
+ SRR["gateway_record_ha_group_binding"], # post
+ SRR["server_config"], # post
+ SRR["end_of_sequence"],
+ ]
+ my_obj = grid_gateway_module()
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ print("Info: test_create_na_sg_grid_gateway_port_with_ha_group_binding_pass: %s" % repr(exc.value.args[0]))
+ assert exc.value.args[0]["changed"]
+
+ # test create with bad ha group ID
+ @patch("ansible_collections.netapp.storagegrid.plugins.module_utils.netapp.SGRestAPI.send_request")
+ def test_create_na_sg_grid_gateway_port_with_bad_ha_group_binding_fail(self, mock_request):
+ mock_request.side_effect = [
+ SRR["version_116"], # get
+ SRR["ha_groups"], # get
+ ]
+ with pytest.raises(AnsibleFailJson) as exc:
+ args = self.set_args_create_na_sg_grid_gateway_port()
+ args["binding_mode"] = "ha-groups"
+ args["ha_groups"] = ["fffac524-9a16-4be0-9d6e-ec9b22218e75"]
+ set_module_args(args)
+ grid_gateway_module()
+ print("Info: test_create_na_sg_grid_gateway_port_with_bad_ha_group_binding_fail: %s" % repr(exc.value.args[0]))
+
+ # test create with node interfaces
+ @patch("ansible_collections.netapp.storagegrid.plugins.module_utils.netapp.SGRestAPI.send_request")
+ def test_create_na_sg_grid_gateway_port_with_node_interface_binding_pass(self, mock_request):
+ args = self.set_args_create_na_sg_grid_gateway_port()
+ args["binding_mode"] = "node-interfaces"
+ args["node_interfaces"] = [
+ {"node": "SITE1-ADM1", "interface": "eth2"},
+ {"node": "SITE2-ADM1", "interface": "eth2"},
+ ]
+ set_module_args(args)
+ mock_request.side_effect = [
+ SRR["version_116"], # get
+ SRR["node_health"], # get
+ SRR["empty_good"], # get
+ SRR["gateway_record_node_interface_binding"], # post
+ SRR["server_config"], # post
+ SRR["end_of_sequence"],
+ ]
+ my_obj = grid_gateway_module()
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ print(
+ "Info: test_create_na_sg_grid_gateway_port_with_node_interface_binding_pass: %s" % repr(exc.value.args[0])
+ )
+ assert exc.value.args[0]["changed"]
+
+ # test change from global to ha groups
+ @patch("ansible_collections.netapp.storagegrid.plugins.module_utils.netapp.SGRestAPI.send_request")
+ def test_update_na_sg_grid_gateway_binding_to_ha_groups_pass(self, mock_request):
+ args = self.set_args_create_na_sg_grid_gateway_port()
+ args["binding_mode"] = "ha-groups"
+ args["ha_groups"] = "site1_primary"
+ args["server_certificate"] = "-----BEGIN CERTIFICATE-----\nABCDEFGABCD\n-----END CERTIFICATE-----\n"
+ args["private_key"] = "-----BEGIN PRIVATE KEY-----\nABCDEFGABCD\n-----END PRIVATE KEY-----\n"
+ set_module_args(args)
+ mock_request.side_effect = [
+ SRR["version_116"], # get
+ SRR["ha_groups"], # get
+ SRR["present_gateways_with_binding"], # get
+ SRR["server_config"], # get
+ SRR["gateway_record_ha_group_binding"], # put
+ SRR["server_config_cert_update"], # put
+ SRR["end_of_sequence"],
+ ]
+ my_obj = grid_gateway_module()
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ print("Info: test_update_na_sg_grid_gateway_binding_to_ha_groups_pass: %s" % repr(exc.value.args[0]))
+ assert exc.value.args[0]["changed"]
+
+ # test rename by supplying gateway_id
+ @patch("ansible_collections.netapp.storagegrid.plugins.module_utils.netapp.SGRestAPI.send_request")
+ def test_update_na_sg_grid_gateway_rename_pass(self, mock_request):
+ args = self.set_args_create_na_sg_grid_gateway_port()
+ args["gateway_id"] = "e777d415-057f-4d37-9b0c-6d132d872ea0"
+ args["binding_mode"] = "ha-groups"
+ args["ha_groups"] = "site1_primary"
+ set_module_args(args)
+ mock_request.side_effect = [
+ SRR["version_116"], # get
+ SRR["ha_groups"], # get
+ SRR["gateway_record_ha_group_binding"], # get
+ SRR["server_config"], # get
+ SRR["gateway_record_rename"], # put
+ SRR["end_of_sequence"],
+ ]
+ my_obj = grid_gateway_module()
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ print("Info: test_update_na_sg_grid_gateway_rename_pass: %s" % repr(exc.value.args[0]))
+ assert exc.value.args[0]["changed"]
diff --git a/ansible_collections/netapp/storagegrid/tests/unit/plugins/modules/test_na_sg_grid_group.py b/ansible_collections/netapp/storagegrid/tests/unit/plugins/modules/test_na_sg_grid_group.py
new file mode 100644
index 000000000..fd9fdf15c
--- /dev/null
+++ b/ansible_collections/netapp/storagegrid/tests/unit/plugins/modules/test_na_sg_grid_group.py
@@ -0,0 +1,317 @@
+# (c) 2020, NetApp, Inc
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+""" unit tests NetApp StorageGRID Grid Group Ansible module: na_sg_grid_group"""
+
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+import json
+import pytest
+import sys
+try:
+ from requests import Response
+except ImportError:
+ if sys.version_info < (2, 7):
+ pytestmark = pytest.mark.skip('Skipping Unit Tests on 2.6 as requests is not available')
+ else:
+ raise
+
+from ansible_collections.netapp.storagegrid.tests.unit.compat import unittest
+from ansible_collections.netapp.storagegrid.tests.unit.compat.mock import (
+ patch,
+ Mock,
+)
+from ansible.module_utils import basic
+from ansible.module_utils._text import to_bytes
+from ansible_collections.netapp.storagegrid.plugins.modules.na_sg_grid_group import (
+ SgGridGroup as grid_group_module,
+)
+
+# REST API canned responses when mocking send_request
+SRR = {
+ # common responses
+ "empty_good": ({"data": []}, None),
+ "not_found": (
+ {"status": "error", "code": 404, "data": {}},
+ {"key": "error.404"},
+ ),
+ "end_of_sequence": (None, "Unexpected call to send_request"),
+ "generic_error": (None, "Expected error"),
+ "delete_good": ({"code": 204}, None),
+ "grid_groups": (
+ {
+ "data": [
+ {
+ "displayName": "TestGridGroup",
+ "uniqueName": "group/testgridgroup",
+ "policies": {
+ "management": {
+ "tenantAccounts": True,
+ "metricsQuery": True,
+ "maintenance": True,
+ },
+ },
+ "id": "00000000-0000-0000-0000-000000000000",
+ "federated": False,
+ "groupURN": "urn:sgws:identity::12345678901234567890:group/testgridgroup",
+ }
+ ]
+ },
+ None,
+ ),
+ "grid_group_record": (
+ {
+ "data": {
+ "displayName": "TestGridGroup",
+ "uniqueName": "group/testgridgroup",
+ "policies": {
+ "management": {
+ "tenantAccounts": True,
+ "metricsQuery": True,
+ "maintenance": True,
+ },
+ },
+ "id": "00000000-0000-0000-0000-000000000000",
+ "federated": False,
+ "groupURN": "urn:sgws:identity::12345678901234567890:group/testgridgroup",
+ }
+ },
+ None,
+ ),
+ "grid_group_record_update": (
+ {
+ "data": {
+ "displayName": "TestGridGroup",
+ "uniqueName": "group/testgridgroup",
+ "policies": {
+ "management": {
+ "tenantAccounts": True,
+ "metricsQuery": False,
+ "maintenance": True,
+ "ilm": True,
+ },
+ },
+ "id": "00000000-0000-0000-0000-000000000000",
+ "federated": False,
+ "groupURN": "urn:sgws:identity::12345678901234567890:group/testgridgroup",
+ }
+ },
+ None,
+ ),
+}
+
+
+def set_module_args(args):
+ """prepare arguments so that they will be picked up during module creation"""
+ args = json.dumps({"ANSIBLE_MODULE_ARGS": args})
+ basic._ANSIBLE_ARGS = to_bytes(args) # pylint: disable=protected-access
+
+
+class AnsibleExitJson(Exception):
+ """Exception class to be raised by module.exit_json and caught by the test case"""
+
+ pass
+
+
+class AnsibleFailJson(Exception):
+ """Exception class to be raised by module.fail_json and caught by the test case"""
+
+ pass
+
+
+def exit_json(*args, **kwargs): # pylint: disable=unused-argument
+ """function to patch over exit_json; package return data into an exception"""
+ if "changed" not in kwargs:
+ kwargs["changed"] = False
+ raise AnsibleExitJson(kwargs)
+
+
+def fail_json(*args, **kwargs): # pylint: disable=unused-argument
+ """function to patch over fail_json; package return data into an exception"""
+ kwargs["failed"] = True
+ raise AnsibleFailJson(kwargs)
+
+
+class TestMyModule(unittest.TestCase):
+ """ a group of related Unit Tests """
+
+ def setUp(self):
+ self.mock_module_helper = patch.multiple(
+ basic.AnsibleModule, exit_json=exit_json, fail_json=fail_json
+ )
+ self.mock_module_helper.start()
+ self.addCleanup(self.mock_module_helper.stop)
+
+ def set_default_args_fail_check(self):
+ return dict(
+ {
+ "display_name": "TestGroup",
+ "management_policy": {
+ "maintenance": True,
+ "ilm": True,
+ "root_access": False,
+ },
+ "auth_token": "01234567-5678-9abc-78de-9fgabc123def",
+ "validate_certs": False,
+ }
+ )
+
+ def set_default_args_pass_check(self):
+ return dict(
+ {
+ "state": "present",
+ "display_name": "TestGroup",
+ "unique_name": "group/testgroup",
+ "management_policy": {
+ "maintenance": True,
+ "ilm": True,
+ "root_access": False,
+ },
+ "api_url": "gmi.example.com",
+ "auth_token": "01234567-5678-9abc-78de-9fgabc123def",
+ "validate_certs": False,
+ }
+ )
+
+ def set_args_create_na_sg_grid_group(self):
+ return dict(
+ {
+ "state": "present",
+ "display_name": "TestGridGroup",
+ "unique_name": "group/testgridgroup",
+ "management_policy": {
+ "tenant_accounts": True,
+ "metrics_query": True,
+ "maintenance": True,
+ },
+ "api_url": "gmi.example.com",
+ "auth_token": "01234567-5678-9abc-78de-9fgabc123def",
+ "validate_certs": False,
+ }
+ )
+
+ def set_args_delete_na_sg_grid_group(self):
+ return dict(
+ {
+ "state": "absent",
+ "unique_name": "group/testgridgroup",
+ "api_url": "gmi.example.com",
+ "auth_token": "01234567-5678-9abc-78de-9fgabc123def",
+ "validate_certs": False,
+ }
+ )
+
+ def test_module_fail_when_required_args_missing(self):
+ """ required arguments are reported as errors """
+ with pytest.raises(AnsibleFailJson) as exc:
+ set_module_args(self.set_default_args_fail_check())
+ grid_group_module()
+ print(
+ "Info: test_module_fail_when_required_args_missing: %s"
+ % exc.value.args[0]["msg"]
+ )
+
+ def test_module_fail_when_required_args_present(self):
+ """ required arguments are reported as errors """
+ with pytest.raises(AnsibleExitJson) as exc:
+ set_module_args(self.set_default_args_pass_check())
+ grid_group_module()
+ exit_json(changed=True, msg="Induced arguments check")
+ print(
+ "Info: test_module_fail_when_required_args_present: %s"
+ % exc.value.args[0]["msg"]
+ )
+ assert exc.value.args[0]["changed"]
+
+ def test_module_fail_with_bad_unique_name(self):
+ """ error returned if unique_name doesn't start with group or federated_group """
+ with pytest.raises(AnsibleFailJson) as exc:
+ args = self.set_default_args_pass_check()
+ args["unique_name"] = "noprefixgroup"
+ set_module_args(args)
+ grid_group_module()
+ print(
+ "Info: test_module_fail_with_bad_unique_name: %s"
+ % exc.value.args[0]["msg"]
+ )
+
+ @patch(
+ "ansible_collections.netapp.storagegrid.plugins.module_utils.netapp.SGRestAPI.send_request"
+ )
+ def test_create_na_sg_grid_group_pass(self, mock_request):
+ set_module_args(self.set_args_create_na_sg_grid_group())
+ my_obj = grid_group_module()
+ mock_request.side_effect = [
+ SRR["not_found"], # get
+ SRR["grid_group_record"], # post
+ SRR["end_of_sequence"],
+ ]
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ print(
+ "Info: test_create_na_sg_grid_group_pass: %s"
+ % repr(exc.value.args[0])
+ )
+ assert exc.value.args[0]["changed"]
+
+ @patch(
+ "ansible_collections.netapp.storagegrid.plugins.module_utils.netapp.SGRestAPI.send_request"
+ )
+ def test_idempotent_create_na_sg_grid_group_pass(self, mock_request):
+ set_module_args(self.set_args_create_na_sg_grid_group())
+ my_obj = grid_group_module()
+ mock_request.side_effect = [
+ SRR["grid_group_record"], # get
+ SRR["end_of_sequence"],
+ ]
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ print(
+ "Info: test_idempotent_create_na_sg_grid_group_pass: %s"
+ % repr(exc.value.args[0])
+ )
+ assert not exc.value.args[0]["changed"]
+
+ @patch(
+ "ansible_collections.netapp.storagegrid.plugins.module_utils.netapp.SGRestAPI.send_request"
+ )
+ def test_update_na_sg_grid_group_pass(self, mock_request):
+ args = self.set_args_create_na_sg_grid_group()
+ args["management_policy"]["tenant_accounts"] = True
+ args["management_policy"]["metrics_query"] = False
+ args["management_policy"]["ilm"] = False
+
+ set_module_args(args)
+ my_obj = grid_group_module()
+ mock_request.side_effect = [
+ SRR["grid_group_record"], # get
+ SRR["grid_group_record_update"], # put
+ SRR["end_of_sequence"],
+ ]
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ print(
+ "Info: test_update_na_sg_grid_group_pass: %s"
+ % repr(exc.value.args[0])
+ )
+ assert exc.value.args[0]["changed"]
+
+ @patch(
+ "ansible_collections.netapp.storagegrid.plugins.module_utils.netapp.SGRestAPI.send_request"
+ )
+ def test_delete_na_sg_grid_group_pass(self, mock_request):
+ set_module_args(self.set_args_delete_na_sg_grid_group())
+ my_obj = grid_group_module()
+ mock_request.side_effect = [
+ SRR["grid_group_record"], # get
+ SRR["delete_good"], # delete
+ SRR["end_of_sequence"],
+ ]
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ print(
+ "Info: test_delete_na_sg_grid_group_pass: %s"
+ % repr(exc.value.args[0])
+ )
+ assert exc.value.args[0]["changed"]
diff --git a/ansible_collections/netapp/storagegrid/tests/unit/plugins/modules/test_na_sg_grid_ha_group.py b/ansible_collections/netapp/storagegrid/tests/unit/plugins/modules/test_na_sg_grid_ha_group.py
new file mode 100644
index 000000000..fbc8fd0ce
--- /dev/null
+++ b/ansible_collections/netapp/storagegrid/tests/unit/plugins/modules/test_na_sg_grid_ha_group.py
@@ -0,0 +1,408 @@
+# (c) 2022, NetApp, Inc
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+""" unit tests NetApp StorageGRID Grid HA Group Ansible module: na_sg_grid_ha_group"""
+
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+import json
+import pytest
+import sys
+
+try:
+ from requests import Response
+except ImportError:
+ if sys.version_info < (2, 7):
+ pytestmark = pytest.mark.skip("Skipping Unit Tests on 2.6 as requests is not available")
+ else:
+ raise
+
+from ansible_collections.netapp.storagegrid.tests.unit.compat import unittest
+from ansible_collections.netapp.storagegrid.tests.unit.compat.mock import (
+ patch,
+ Mock,
+)
+from ansible.module_utils import basic
+from ansible.module_utils._text import to_bytes
+from ansible_collections.netapp.storagegrid.plugins.modules.na_sg_grid_ha_group import (
+ SgGridHaGroup as grid_ha_group_module,
+)
+
+# REST API canned responses when mocking send_request
+SRR = {
+ # common responses
+ "empty_good": ({"data": []}, None),
+ "not_found": (
+ {"status": "error", "code": 404, "data": {}},
+ {"key": "error.404"},
+ ),
+ "end_of_sequence": (None, "Unexpected call to send_request"),
+ "generic_error": (None, "Expected error"),
+ "delete_good": (None, None),
+ "update_good": (None, None),
+ "version_114": ({"data": {"productVersion": "11.4.0-20200721.1338.d3969b3"}}, None),
+ "version_116": ({"data": {"productVersion": "11.6.0-20211120.0301.850531e"}}, None),
+ "ha_group_record": (
+ {
+ "data": {
+ "id": "fbe724da-c941-439b-bb61-a536f6211ca9",
+ "name": "ansible-ha-group",
+ "description": None,
+ "virtualIps": ["192.168.50.5"],
+ "interfaces": [
+ {"nodeId": "0b1866ed-d6e7-41b4-815f-bf867348b76b", "interface": "ens256"},
+ {"nodeId": "7bb5bf05-a04c-4344-8abd-08c5c4048666", "interface": "ens256"},
+ ],
+ "gatewayCidr": "192.168.50.1/24",
+ }
+ },
+ None,
+ ),
+ "ha_group_record_twovip": (
+ {
+ "data": {
+ "id": "fbe724da-c941-439b-bb61-a536f6211ca9",
+ "name": "ansible-ha-group",
+ "description": "2 VIP HA Group",
+ "virtualIps": ["192.168.50.5", "192.168.50.6"],
+ "interfaces": [
+ {"nodeId": "0b1866ed-d6e7-41b4-815f-bf867348b76b", "interface": "ens256"},
+ {"nodeId": "7bb5bf05-a04c-4344-8abd-08c5c4048666", "interface": "ens256"},
+ ],
+ "gatewayCidr": "192.168.50.1/24",
+ }
+ },
+ None,
+ ),
+ "ha_group_record_rename": (
+ {
+ "data": {
+ "id": "fbe724da-c941-439b-bb61-a536f6211ca9",
+ "name": "ansible-ha-group-rename",
+ "description": None,
+ "virtualIps": ["192.168.50.5"],
+ "interfaces": [
+ {"nodeId": "0b1866ed-d6e7-41b4-815f-bf867348b76b", "interface": "ens256"},
+ {"nodeId": "7bb5bf05-a04c-4344-8abd-08c5c4048666", "interface": "ens256"},
+ ],
+ "gatewayCidr": "192.168.50.1/24",
+ }
+ },
+ None,
+ ),
+ "ha_groups": (
+ {
+ "data": [
+ {
+ "id": "c08e6dca-038d-4a05-9499-6fbd1e6a4c3e",
+ "name": "site1_primary",
+ "description": "test ha group",
+ "virtualIps": ["10.193.174.117"],
+ "interfaces": [
+ {
+ "nodeId": "0b1866ed-d6e7-41b4-815f-bf867348b76b",
+ "nodeName": "SITE1-ADM1",
+ "interface": "eth2",
+ "preferredMaster": True,
+ },
+ {
+ "nodeId": "970ad050-b68b-4aae-a94d-aef73f3095c4",
+ "nodeName": "SITE2-ADM1",
+ "interface": "eth2",
+ },
+ ],
+ "gatewayCidr": "192.168.14.1/24",
+ },
+ {
+ "id": "fbe724da-c941-439b-bb61-a536f6211ca9",
+ "name": "ansible-ha-group",
+ "description": None,
+ "virtualIps": ["192.168.50.5"],
+ "interfaces": [
+ {"nodeId": "0b1866ed-d6e7-41b4-815f-bf867348b76b", "interface": "ens256"},
+ {"nodeId": "7bb5bf05-a04c-4344-8abd-08c5c4048666", "interface": "ens256"},
+ ],
+ "gatewayCidr": "192.168.50.1/24",
+ },
+ ]
+ },
+ None,
+ ),
+ "node_health": (
+ {
+ "data": [
+ {
+ "id": "0b1866ed-d6e7-41b4-815f-bf867348b76b",
+ "isPrimaryAdmin": True,
+ "name": "SITE1-ADM1",
+ "siteId": "ae56d06d-bd83-46bd-adce-77146b1d94bd",
+ "siteName": "SITE1",
+ "severity": "normal",
+ "state": "connected",
+ "type": "adminNode",
+ },
+ {
+ "id": "7bb5bf05-a04c-4344-8abd-08c5c4048666",
+ "isPrimaryAdmin": None,
+ "name": "SITE1-G1",
+ "siteId": "ae56d06d-bd83-46bd-adce-77146b1d94bd",
+ "siteName": "SITE1",
+ "severity": "normal",
+ "state": "connected",
+ "type": "apiGatewayNode",
+ },
+ {
+ "id": "970ad050-b68b-4aae-a94d-aef73f3095c4",
+ "isPrimaryAdmin": False,
+ "name": "SITE2-ADM1",
+ "siteId": "7c24002e-5157-43e9-83e5-02db9b265b02",
+ "siteName": "SITE2",
+ "severity": "normal",
+ "state": "connected",
+ "type": "adminNode",
+ },
+ ]
+ },
+ None,
+ ),
+}
+
+
+def set_module_args(args):
+ """prepare arguments so that they will be picked up during module creation"""
+ args = json.dumps({"ANSIBLE_MODULE_ARGS": args})
+ basic._ANSIBLE_ARGS = to_bytes(args) # pylint: disable=protected-access
+
+
+class AnsibleExitJson(Exception):
+ """Exception class to be raised by module.exit_json and caught by the test case"""
+
+ pass
+
+
+class AnsibleFailJson(Exception):
+ """Exception class to be raised by module.fail_json and caught by the test case"""
+
+ pass
+
+
+def exit_json(*args, **kwargs): # pylint: disable=unused-argument
+ """function to patch over exit_json; package return data into an exception"""
+ if "changed" not in kwargs:
+ kwargs["changed"] = False
+ raise AnsibleExitJson(kwargs)
+
+
+def fail_json(*args, **kwargs): # pylint: disable=unused-argument
+ """function to patch over fail_json; package return data into an exception"""
+ kwargs["failed"] = True
+ raise AnsibleFailJson(kwargs)
+
+
+class TestMyModule(unittest.TestCase):
+ """a group of related Unit Tests"""
+
+ def setUp(self):
+ self.mock_module_helper = patch.multiple(basic.AnsibleModule, exit_json=exit_json, fail_json=fail_json)
+ self.mock_module_helper.start()
+ self.addCleanup(self.mock_module_helper.stop)
+
+ def set_default_args_fail_check(self):
+ return dict(
+ {
+ "gateway_cidr": "192.168.50.1/24",
+ "virtual_ips": "192.168.50.5",
+ "interfaces": [
+ {"node": "SITE1-ADM1", "interface": "ens256"},
+ {"node": "SITE1-G1", "interface": "ens256"},
+ ],
+ "auth_token": "01234567-5678-9abc-78de-9fgabc123def",
+ "validate_certs": False,
+ }
+ )
+
+ def set_default_args_pass_check(self):
+ return dict(
+ {
+ "state": "present",
+ "name": "ansible-test-ha-group",
+ "gateway_cidr": "192.168.50.1/24",
+ "virtual_ips": "192.168.50.5",
+ "interfaces": [
+ {"node": "SITE1-ADM1", "interface": "ens256"},
+ {"node": "SITE1-G1", "interface": "ens256"},
+ ],
+ "api_url": "https://gmi.example.com",
+ "auth_token": "01234567-5678-9abc-78de-9fgabc123def",
+ "validate_certs": False,
+ }
+ )
+
+ def set_args_create_na_sg_grid_ha_group(self):
+ return dict(
+ {
+ "state": "present",
+ "name": "ansible-ha-group",
+ "gateway_cidr": "192.168.50.1/24",
+ "virtual_ips": "192.168.50.5",
+ "interfaces": [
+ {"node": "SITE1-ADM1", "interface": "ens256"},
+ {"node": "SITE1-G1", "interface": "ens256"},
+ ],
+ "api_url": "https://gmi.example.com",
+ "auth_token": "01234567-5678-9abc-78de-9fgabc123def",
+ "validate_certs": False,
+ }
+ )
+
+ def set_args_delete_na_sg_grid_ha_group(self):
+ return dict(
+ {
+ "state": "absent",
+ "name": "ansible-ha-group",
+ "gateway_cidr": "192.168.50.1/24",
+ "virtual_ips": "192.168.50.5",
+ "interfaces": [
+ {"node": "SITE1-ADM1", "interface": "ens256"},
+ {"node": "SITE1-G1", "interface": "ens256"},
+ ],
+ "api_url": "https://gmi.example.com",
+ "auth_token": "01234567-5678-9abc-78de-9fgabc123def",
+ "validate_certs": False,
+ }
+ )
+
+ @patch("ansible_collections.netapp.storagegrid.plugins.module_utils.netapp.SGRestAPI.send_request")
+ def test_module_fail_when_required_args_missing(self, mock_request):
+ """required arguments are reported as errors"""
+ with pytest.raises(AnsibleFailJson) as exc:
+ set_module_args(self.set_default_args_fail_check())
+ grid_ha_group_module()
+ print("Info: test_module_fail_when_required_args_missing: %s" % exc.value.args[0]["msg"])
+
+ @patch("ansible_collections.netapp.storagegrid.plugins.module_utils.netapp.SGRestAPI.send_request")
+ def test_module_pass_when_required_args_present(self, mock_request):
+ """required arguments are reported as errors"""
+ mock_request.side_effect = [
+ SRR["node_health"], # get
+ ]
+ with pytest.raises(AnsibleExitJson) as exc:
+ set_module_args(self.set_default_args_pass_check())
+ grid_ha_group_module()
+ exit_json(changed=True, msg="Induced arguments check")
+ print("Info: test_module_pass_when_required_args_present: %s" % exc.value.args[0]["msg"])
+ assert exc.value.args[0]["changed"]
+
+ @patch("ansible_collections.netapp.storagegrid.plugins.module_utils.netapp.SGRestAPI.send_request")
+ def test_create_na_sg_grid_ha_group_pass(self, mock_request):
+ set_module_args(self.set_args_create_na_sg_grid_ha_group())
+ mock_request.side_effect = [
+ SRR["node_health"], # get
+ SRR["empty_good"], # get
+ SRR["ha_group_record"], # post
+ SRR["end_of_sequence"],
+ ]
+ my_obj = grid_ha_group_module()
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ print("Info: test_create_na_sg_grid_ha_group_pass: %s" % repr(exc.value.args[0]))
+ assert exc.value.args[0]["changed"]
+
+ @patch("ansible_collections.netapp.storagegrid.plugins.module_utils.netapp.SGRestAPI.send_request")
+ def test_idempotent_create_na_sg_grid_ha_group_pass(self, mock_request):
+ args = self.set_args_create_na_sg_grid_ha_group()
+ set_module_args(args)
+ mock_request.side_effect = [
+ SRR["node_health"], # get
+ SRR["ha_groups"], # get
+ SRR["ha_group_record"], # get
+ SRR["end_of_sequence"],
+ ]
+ my_obj = grid_ha_group_module()
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ print("Info: test_idempotent_create_na_sg_grid_ha_group_pass: %s" % repr(exc.value.args[0]))
+ assert not exc.value.args[0]["changed"]
+
+ @patch("ansible_collections.netapp.storagegrid.plugins.module_utils.netapp.SGRestAPI.send_request")
+ def test_update_na_sg_grid_ha_group_pass(self, mock_request):
+ args = self.set_args_create_na_sg_grid_ha_group()
+ args["description"] = "2 VIP HA Group"
+ args["virtual_ips"] = ["192.168.50.5", "192.168.50.6"]
+ set_module_args(args)
+ mock_request.side_effect = [
+ SRR["node_health"], # get
+ SRR["ha_groups"], # get
+ SRR["ha_group_record"], # get
+ SRR["ha_group_record_twovip"], # post
+ SRR["end_of_sequence"],
+ ]
+ my_obj = grid_ha_group_module()
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ print("Info: test_update_na_sg_grid_ha_group_pass: %s" % repr(exc.value.args[0]))
+ assert exc.value.args[0]["changed"]
+
+ @patch("ansible_collections.netapp.storagegrid.plugins.module_utils.netapp.SGRestAPI.send_request")
+ def test_rename_na_sg_grid_ha_group_pass(self, mock_request):
+ args = self.set_args_create_na_sg_grid_ha_group()
+ args["ha_group_id"] = "fbe724da-c941-439b-bb61-a536f6211ca9"
+ args["name"] = "ansible-ha-group-rename"
+ set_module_args(args)
+ mock_request.side_effect = [
+ SRR["node_health"], # get
+ SRR["ha_group_record"], # get
+ SRR["ha_group_record_rename"], # post
+ SRR["end_of_sequence"],
+ ]
+ my_obj = grid_ha_group_module()
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ print("Info: test_rename_na_sg_grid_ha_group_pass: %s" % repr(exc.value.args[0]))
+ assert exc.value.args[0]["changed"]
+
+ @patch("ansible_collections.netapp.storagegrid.plugins.module_utils.netapp.SGRestAPI.send_request")
+ def test_delete_na_sg_grid_ha_group_pass(self, mock_request):
+ args = self.set_args_delete_na_sg_grid_ha_group()
+ set_module_args(args)
+ mock_request.side_effect = [
+ SRR["ha_groups"], # get
+ SRR["ha_group_record"], # get
+ SRR["delete_good"], # delete
+ SRR["end_of_sequence"],
+ ]
+ my_obj = grid_ha_group_module()
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ print("Info: test_delete_na_sg_grid_ha_group_pass: %s" % repr(exc.value.args[0]))
+ assert exc.value.args[0]["changed"]
+
+ @patch("ansible_collections.netapp.storagegrid.plugins.module_utils.netapp.SGRestAPI.send_request")
+ def test_create_na_sg_grid_ha_group_bad_node_fail(self, mock_request):
+ args = self.set_args_create_na_sg_grid_ha_group()
+ args["interfaces"] = [{"node": "FakeNode", "interface": "eth0"}]
+ set_module_args(args)
+ mock_request.side_effect = [
+ SRR["node_health"], # get
+ ]
+ with pytest.raises(AnsibleFailJson) as exc:
+ grid_ha_group_module()
+ print("Info: test_create_na_sg_grid_ha_group_bad_node_fail: %s" % repr(exc.value.args[0]))
+ assert exc.value.args[0]["failed"]
+
+ @patch("ansible_collections.netapp.storagegrid.plugins.module_utils.netapp.SGRestAPI.send_request")
+ def test_update_na_sg_grid_ha_group_bad_ha_group_id_fail(self, mock_request):
+ args = self.set_args_create_na_sg_grid_ha_group()
+ args["ha_group_id"] = "ffffffff-ffff-aaaa-aaaa-000000000000"
+ args["virtual_ips"] = "192.168.50.10"
+ set_module_args(args)
+ mock_request.side_effect = [
+ SRR["node_health"], # get
+ SRR["not_found"], # get
+ ]
+ with pytest.raises(AnsibleFailJson) as exc:
+ my_obj = grid_ha_group_module()
+ my_obj.apply()
+ print("Info: test_create_na_sg_grid_ha_group_bad_node_fail: %s" % repr(exc.value.args[0]))
+ assert exc.value.args[0]["failed"]
diff --git a/ansible_collections/netapp/storagegrid/tests/unit/plugins/modules/test_na_sg_grid_identity_federation.py b/ansible_collections/netapp/storagegrid/tests/unit/plugins/modules/test_na_sg_grid_identity_federation.py
new file mode 100644
index 000000000..058fc609e
--- /dev/null
+++ b/ansible_collections/netapp/storagegrid/tests/unit/plugins/modules/test_na_sg_grid_identity_federation.py
@@ -0,0 +1,354 @@
+# (c) 2021, NetApp, Inc
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+""" unit tests NetApp StorageGRID Grid Identity Federation Ansible module: na_sg_grid_identity_federation"""
+
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+import json
+import pytest
+import sys
+try:
+ from requests import Response
+except ImportError:
+ if sys.version_info < (2, 7):
+ pytestmark = pytest.mark.skip('Skipping Unit Tests on 2.6 as requests is not available')
+ else:
+ raise
+
+from ansible_collections.netapp.storagegrid.tests.unit.compat import unittest
+from ansible_collections.netapp.storagegrid.tests.unit.compat.mock import (
+ patch,
+ Mock,
+)
+from ansible.module_utils import basic
+from ansible.module_utils._text import to_bytes
+from ansible_collections.netapp.storagegrid.plugins.modules.na_sg_grid_identity_federation import (
+ SgGridIdentityFederation as grid_identity_federation_module,
+)
+
+# REST API canned responses when mocking send_request
+SRR = {
+ # common responses
+ "end_of_sequence": (None, "Unexpected call to send_request"),
+ "generic_error": (None, "Expected error"),
+ "check_mode_good": (None, None),
+ "identity_federation_unset": (
+ {
+ "data": {
+ "id": "09876543-abcd-4321-abcd-0987654321ab",
+ "disable": True,
+ "type": "",
+ "ldapServiceType": "",
+ "hostname": "",
+ "port": 0,
+ "username": "",
+ "password": None,
+ "baseGroupDn": "",
+ "baseUserDn": "",
+ "disableTLS": False,
+ "enableLDAPS": False,
+ "caCert": "",
+ }
+ },
+ None,
+ ),
+ "identity_federation": (
+ {
+ "data": {
+ "id": "09876543-abcd-4321-abcd-0987654321ab",
+ "disable": False,
+ "type": "ldap",
+ "ldapServiceType": "Active Directory",
+ "hostname": "ad.example.com",
+ "port": 389,
+ "username": "binduser",
+ "password": "********",
+ "baseGroupDn": "DC=example,DC=com",
+ "baseUserDn": "DC=example,DC=com",
+ "disableTLS": True,
+ "enableLDAPS": False,
+ "caCert": "",
+ }
+ },
+ None,
+ ),
+ "identity_federation_tls": (
+ {
+ "data": {
+ "id": "09876543-abcd-4321-abcd-0987654321ab",
+ "disable": False,
+ "type": "ldap",
+ "ldapServiceType": "Active Directory",
+ "hostname": "ad.example.com",
+ "port": 636,
+ "username": "binduser",
+ "password": "********",
+ "baseGroupDn": "DC=example,DC=com",
+ "baseUserDn": "DC=example,DC=com",
+ "disableTLS": False,
+ "enableLDAPS": True,
+ "caCert": (
+ "-----BEGIN CERTIFICATE-----\n"
+ "MIIF+DCCBOCgAwIBAgITRwAAAAIg5KzMrJo+kQAAAAAAAjANBgkqhkiG9w0BAQUF\n"
+ "ADBlMRIwEAYKCZImiZPyLGQBGRYCYXUxFjAUBgoJkiaJk/IsZAEZFgZuZXRhcHAx\n"
+ "FjAUBgoJkiaJk/IsZAEZFgZhdXNuZ3MxHzAdBgNVBAMTFmF1c25ncy1NRUxOR1NE\n"
+ "QzAxLUNBLTEwHhcNMjEwMjExMDkzMTIwWhcNMjMwMjExMDk0MTIwWjAAMIIBIjAN\n"
+ "BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAt2xPi4FS4Uc37KrDLEXXUoc4lhhT\n"
+ "uQmMnLc0PYZCIpzYOaosFIeGqco3woiC7wSZJ2whKE4RDcxxgE+azuGiSWVjIxIL\n"
+ "AimmcDhFid/T3KRN5jmkjBzUKuPBYzZBFih8iU9056rqgN7eMKQYjRwPeV0+AeiB\n"
+ "irw46OgkwVQu3shEUtXxZPP2Mb6Md23+4vSmcElUcW28Opt2q/M5fs7DNomG3eaG\n"
+ "-----END CERTIFICATE-----\n"
+ ),
+ }
+ },
+ None,
+ ),
+ "identity_federation_disable": (
+ {
+ "data": {
+ "id": "09876543-abcd-4321-abcd-0987654321ab",
+ "disable": True,
+ "type": "ldap",
+ "ldapServiceType": "Active Directory",
+ "hostname": "ad.example.com",
+ "port": 389,
+ "username": "binduser",
+ "password": "********",
+ "baseGroupDn": "DC=example,DC=com",
+ "baseUserDn": "DC=example,DC=com",
+ "disableTLS": True,
+ "enableLDAPS": False,
+ "caCert": "",
+ }
+ },
+ None,
+ ),
+}
+
+
+def set_module_args(args):
+ """prepare arguments so that they will be picked up during module creation"""
+ args = json.dumps({"ANSIBLE_MODULE_ARGS": args})
+ basic._ANSIBLE_ARGS = to_bytes(args) # pylint: disable=protected-access
+
+
+class AnsibleExitJson(Exception):
+ """Exception class to be raised by module.exit_json and caught by the test case"""
+
+ pass
+
+
+class AnsibleFailJson(Exception):
+ """Exception class to be raised by module.fail_json and caught by the test case"""
+
+ pass
+
+
+def exit_json(*args, **kwargs): # pylint: disable=unused-argument
+ """function to patch over exit_json; package return data into an exception"""
+ if "changed" not in kwargs:
+ kwargs["changed"] = False
+ raise AnsibleExitJson(kwargs)
+
+
+def fail_json(*args, **kwargs): # pylint: disable=unused-argument
+ """function to patch over fail_json; package return data into an exception"""
+ kwargs["failed"] = True
+ raise AnsibleFailJson(kwargs)
+
+
+class TestMyModule(unittest.TestCase):
+ """ a group of related Unit Tests """
+
+ def setUp(self):
+ self.mock_module_helper = patch.multiple(basic.AnsibleModule, exit_json=exit_json, fail_json=fail_json)
+ self.mock_module_helper.start()
+ self.addCleanup(self.mock_module_helper.stop)
+
+ def set_default_args_fail_check(self):
+ return dict(
+ {
+ "ldap_service_type": "Active Directory",
+ "hostname": "ad.example.com",
+ "port": 389,
+ "username": "binduser",
+ "password": "bindpass",
+ "base_group_dn": "DC=example,DC=com",
+ "base_user_dn": "DC=example,DC=com",
+ "tls": "Disabled",
+ "auth_token": "01234567-5678-9abc-78de-9fgabc123def",
+ "validate_certs": False,
+ }
+ )
+
+ def set_default_args_pass_check(self):
+ return dict(
+ {
+ "ldap_service_type": "Active Directory",
+ "hostname": "ad.example.com",
+ "port": 389,
+ "username": "binduser",
+ "password": "bindpass",
+ "base_group_dn": "DC=example,DC=com",
+ "base_user_dn": "DC=example,DC=com",
+ "tls": "Disabled",
+ "state": "present",
+ "api_url": "gmi.example.com",
+ "auth_token": "01234567-5678-9abc-78de-9fgabc123def",
+ "validate_certs": False,
+ }
+ )
+
+ def set_args_set_na_sg_grid_identity_federation(self):
+ return dict(
+ {
+ "ldap_service_type": "Active Directory",
+ "hostname": "ad.example.com",
+ "port": 389,
+ "username": "binduser",
+ "password": "bindpass",
+ "base_group_dn": "DC=example,DC=com",
+ "base_user_dn": "DC=example,DC=com",
+ "tls": "Disabled",
+ "state": "present",
+ "api_url": "gmi.example.com",
+ "auth_token": "01234567-5678-9abc-78de-9fgabc123def",
+ "validate_certs": False,
+ }
+ )
+
+ def set_args_set_na_sg_grid_identity_federation_tls(self):
+ return dict(
+ {
+ "ldap_service_type": "Active Directory",
+ "hostname": "ad.example.com",
+ "port": 636,
+ "username": "binduser",
+ "password": "bindpass",
+ "base_group_dn": "DC=example,DC=com",
+ "base_user_dn": "DC=example,DC=com",
+ "tls": "LDAPS",
+ "ca_cert": (
+ "-----BEGIN CERTIFICATE-----\n"
+ "MIIF+DCCBOCgAwIBAgITRwAAAAIg5KzMrJo+kQAAAAAAAjANBgkqhkiG9w0BAQUF\n"
+ "ADBlMRIwEAYKCZImiZPyLGQBGRYCYXUxFjAUBgoJkiaJk/IsZAEZFgZuZXRhcHAx\n"
+ "FjAUBgoJkiaJk/IsZAEZFgZhdXNuZ3MxHzAdBgNVBAMTFmF1c25ncy1NRUxOR1NE\n"
+ "QzAxLUNBLTEwHhcNMjEwMjExMDkzMTIwWhcNMjMwMjExMDk0MTIwWjAAMIIBIjAN\n"
+ "BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAt2xPi4FS4Uc37KrDLEXXUoc4lhhT\n"
+ "uQmMnLc0PYZCIpzYOaosFIeGqco3woiC7wSZJ2whKE4RDcxxgE+azuGiSWVjIxIL\n"
+ "AimmcDhFid/T3KRN5jmkjBzUKuPBYzZBFih8iU9056rqgN7eMKQYjRwPeV0+AeiB\n"
+ "irw46OgkwVQu3shEUtXxZPP2Mb6Md23+4vSmcElUcW28Opt2q/M5fs7DNomG3eaG\n"
+ "-----END CERTIFICATE-----\n"
+ ),
+ "state": "present",
+ "api_url": "gmi.example.com",
+ "auth_token": "01234567-5678-9abc-78de-9fgabc123def",
+ "validate_certs": False,
+ }
+ )
+
+ def set_args_remove_na_sg_grid_identity_federation(self):
+ return dict(
+ {
+ "ldap_service_type": "Active Directory",
+ "hostname": "ad.example.com",
+ "state": "absent",
+ "api_url": "gmi.example.com",
+ "auth_token": "01234567-5678-9abc-78de-9fgabc123def",
+ "validate_certs": False,
+ }
+ )
+
+ def test_module_fail_when_required_args_missing(self):
+ """ required arguments are reported as errors """
+ with pytest.raises(AnsibleFailJson) as exc:
+ set_module_args(self.set_default_args_fail_check())
+ grid_identity_federation_module()
+ print("Info: test_module_fail_when_required_args_missing: %s" % exc.value.args[0]["msg"])
+
+ def test_module_fail_when_required_args_present(self):
+ """ required arguments are reported as errors """
+ with pytest.raises(AnsibleExitJson) as exc:
+ set_module_args(self.set_default_args_pass_check())
+ grid_identity_federation_module()
+ exit_json(changed=True, msg="Induced arguments check")
+ print("Info: test_module_fail_when_required_args_present: %s" % exc.value.args[0]["msg"])
+ assert exc.value.args[0]["changed"]
+
+ @patch("ansible_collections.netapp.storagegrid.plugins.module_utils.netapp.SGRestAPI.send_request")
+ def test_set_na_sg_grid_identity_federation_pass(self, mock_request):
+ set_module_args(self.set_args_set_na_sg_grid_identity_federation())
+ my_obj = grid_identity_federation_module()
+ mock_request.side_effect = [
+ SRR["identity_federation_unset"], # get
+ SRR["identity_federation"], # post
+ SRR["end_of_sequence"],
+ ]
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ print("Info: test_set_na_sg_grid_identity_federation_pass: %s" % repr(exc.value.args[0]))
+ assert exc.value.args[0]["changed"]
+
+ @patch("ansible_collections.netapp.storagegrid.plugins.module_utils.netapp.SGRestAPI.send_request")
+ def test_idempotent_set_na_sg_grid_identity_federation_pass(self, mock_request):
+ args = self.set_args_set_na_sg_grid_identity_federation()
+ # remove password
+ del args["password"]
+ set_module_args(args)
+ my_obj = grid_identity_federation_module()
+ mock_request.side_effect = [
+ SRR["identity_federation"], # get
+ SRR["end_of_sequence"],
+ ]
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ print("Info: test_idempotent_set_na_sg_grid_identity_federation_pass: %s" % repr(exc.value.args[0]))
+ assert not exc.value.args[0]["changed"]
+
+ @patch("ansible_collections.netapp.storagegrid.plugins.module_utils.netapp.SGRestAPI.send_request")
+ def test_set_na_sg_grid_identity_federation_tls_pass(self, mock_request):
+ set_module_args(self.set_args_set_na_sg_grid_identity_federation_tls())
+ my_obj = grid_identity_federation_module()
+ mock_request.side_effect = [
+ SRR["identity_federation_unset"], # get
+ SRR["identity_federation_tls"], # post
+ SRR["end_of_sequence"],
+ ]
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ print("Info: test_set_na_sg_grid_identity_federation_pass: %s" % repr(exc.value.args[0]))
+ assert exc.value.args[0]["changed"]
+
+ @patch("ansible_collections.netapp.storagegrid.plugins.module_utils.netapp.SGRestAPI.send_request")
+ def test_remove_na_sg_grid_identity_federation_pass(self, mock_request):
+ set_module_args(self.set_args_remove_na_sg_grid_identity_federation())
+ my_obj = grid_identity_federation_module()
+ mock_request.side_effect = [
+ SRR["identity_federation"], # get
+ SRR["identity_federation_disable"], # post
+ SRR["end_of_sequence"],
+ ]
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ print("Info: test_remove_na_sg_grid_identity_federation_pass: %s" % repr(exc.value.args[0]))
+ assert exc.value.args[0]["changed"]
+
+ # test check mode
+
+ @patch("ansible_collections.netapp.storagegrid.plugins.module_utils.netapp.SGRestAPI.send_request")
+ def test_check_mode_na_sg_grid_identity_federation_pass(self, mock_request):
+ set_module_args(self.set_args_set_na_sg_grid_identity_federation())
+ my_obj = grid_identity_federation_module()
+ my_obj.module.check_mode = True
+ mock_request.side_effect = [
+ SRR["identity_federation_unset"], # get
+ SRR["check_mode_good"], # post
+ SRR["end_of_sequence"],
+ ]
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ print("Info: test_check_mode_na_sg_grid_identity_federation_pass: %s" % repr(exc.value.args[0]))
+ assert exc.value.args[0]["changed"]
+ assert exc.value.args[0]["msg"] == "Connection test successful"
diff --git a/ansible_collections/netapp/storagegrid/tests/unit/plugins/modules/test_na_sg_grid_info.py b/ansible_collections/netapp/storagegrid/tests/unit/plugins/modules/test_na_sg_grid_info.py
new file mode 100644
index 000000000..2de26109b
--- /dev/null
+++ b/ansible_collections/netapp/storagegrid/tests/unit/plugins/modules/test_na_sg_grid_info.py
@@ -0,0 +1,362 @@
+# (c) 2020, NetApp, Inc
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+''' Unit Tests NetApp StorageGRID Grid Ansible module: na_sg_grid_info '''
+
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+import json
+import pytest
+
+from ansible.module_utils import basic
+from ansible.module_utils._text import to_bytes
+from ansible_collections.netapp.storagegrid.tests.unit.compat import unittest
+from ansible_collections.netapp.storagegrid.tests.unit.compat.mock import patch
+
+from ansible_collections.netapp.storagegrid.plugins.modules.na_sg_grid_info \
+ import NetAppSgGatherInfo as sg_grid_info_module
+
+# REST API canned responses when mocking send_request
+SRR = {
+ # common responses
+ 'empty_good': ({'data': []}, None),
+ 'end_of_sequence': (None, 'Unexpected call to send_request'),
+ 'generic_error': (None, 'Expected error'),
+ 'grid_accounts': (
+ {
+ 'data': [
+ {
+ 'name': 'TestTenantAccount1',
+ 'capabilities': ['management', 's3'],
+ 'policy': {
+ 'useAccountIdentitySource': True,
+ 'allowPlatformServices': False,
+ 'quotaObjectBytes': None,
+ },
+ 'id': '12345678901234567891',
+ },
+ {
+ 'name': 'TestTenantAccount2',
+ 'capabilities': ['management', 's3'],
+ 'policy': {
+ 'useAccountIdentitySource': True,
+ 'allowPlatformServices': False,
+ 'quotaObjectBytes': None,
+ },
+ 'id': '12345678901234567892',
+ },
+ {
+ 'name': 'TestTenantAccount3',
+ 'capabilities': ['management', 's3'],
+ 'policy': {
+ 'useAccountIdentitySource': True,
+ 'allowPlatformServices': False,
+ 'quotaObjectBytes': None,
+ },
+ 'id': '12345678901234567893',
+ },
+ ]
+ },
+ None,
+ ),
+ 'grid_alarms': ({'data': []}, None),
+ 'grid_audit': ({'data': {}}, None),
+ 'grid_compliance_global': ({'data': {}}, None),
+ 'grid_config': ({'data': {}}, None),
+ 'grid_config_management': ({'data': {}}, None),
+ 'grid_config_product_version': ({'data': {}}, None),
+ 'grid_deactivated_features': ({'data': {}}, None),
+ 'grid_dns_servers': ({'data': []}, None),
+ 'grid_domain_names': ({'data': []}, None),
+ 'grid_ec_profiles': ({'data': []}, None),
+ 'grid_expansion': ({'data': {}}, None),
+ 'grid_expansion_nodes': ({'data': []}, None),
+ 'grid_expansion_sites': ({'data': []}, None),
+ 'grid_grid_networks': ({'data': []}, None),
+ 'grid_groups': ({'data': []}, None),
+ 'grid_health': ({'data': {}}, None),
+ 'grid_health_topology': ({'data': {}}, None),
+ 'grid_identity_source': ({'data': {}}, None),
+ 'grid_ilm_criteria': ({'data': []}, None),
+ 'grid_ilm_policies': ({'data': []}, None),
+ 'grid_ilm_rules': ({'data': []}, None),
+ 'grid_license': ({'data': []}, None),
+ 'grid_management_certificate': ({'data': {}}, None),
+ 'grid_ntp_servers': ({'data': []}, None),
+ 'grid_recovery': ({'data': {}}, None),
+ 'grid_recovery_available_nodes': ({'data': []}, None),
+ 'grid_regions': ({'data': []}, None),
+ 'grid_schemes': ({'data': []}, None),
+ 'grid_snmp': ({'data': {}}, None),
+ 'grid_storage_api_certificate': ({'data': {}}, None),
+ 'grid_untrusted_client_network': ({'data': {}}, None),
+ 'grid_users': (
+ {
+ 'data': [
+ {
+ 'accountId': '0',
+ 'disable': False,
+ 'federated': False,
+ 'fullName': 'Root',
+ 'id': '00000000-0000-0000-0000-000000000000',
+ 'memberOf': None,
+ 'uniqueName': 'root',
+ 'userURN': 'urn:sgws:identity::0:root'
+ },
+ ]
+ },
+ None
+ ),
+ 'grid_users_root': (
+ {
+ 'data': {
+ 'accountId': '0',
+ 'disable': False,
+ 'federated': False,
+ 'fullName': 'Root',
+ 'id': '00000000-0000-0000-0000-000000000000',
+ 'memberOf': None,
+ 'uniqueName': 'root',
+ 'userURN': 'urn:sgws:identity::0:root'
+ },
+ },
+ None
+ ),
+ 'versions': ({'data': [2, 3]}, None),
+}
+
+
+def set_module_args(args):
+ ''' Prepare arguments so that they will be picked up during module creation '''
+ args = json.dumps({'ANSIBLE_MODULE_ARGS': args})
+ basic._ANSIBLE_ARGS = to_bytes(args) # pylint: disable=protected-access
+
+
+class AnsibleExitJson(Exception):
+ ''' Exception class to be raised by module.exit_json and caught by the test case '''
+ pass
+
+
+class AnsibleFailJson(Exception):
+ ''' Exception class to be raised by module.fail_json and caught by the test case '''
+ pass
+
+
+def exit_json(*args, **kwargs): # pylint: disable=unused-argument
+ ''' Function to patch over exit_json; package return data into an exception '''
+ if 'changed' not in kwargs:
+ kwargs['changed'] = False
+ raise AnsibleExitJson(kwargs)
+
+
+def fail_json(*args, **kwargs): # pylint: disable=unused-argument
+ ''' Function to patch over fail_json; package return data into an exception '''
+ kwargs['failed'] = True
+ raise AnsibleFailJson(kwargs)
+
+
+class TestMyModule(unittest.TestCase):
+ ''' A group of related Unit Tests '''
+
+ def setUp(self):
+ self.mock_module_helper = patch.multiple(basic.AnsibleModule,
+ exit_json=exit_json,
+ fail_json=fail_json)
+ self.mock_module_helper.start()
+ self.addCleanup(self.mock_module_helper.stop)
+
+ def set_default_args_fail_check(self):
+ return dict(
+ {
+ 'api_url': 'sgmi.example.com',
+ }
+ )
+
+ def set_default_args_pass_check(self):
+ return dict(
+ {
+ 'api_url': 'sgmi.example.com',
+ 'auth_token': '01234567-5678-9abc-78de-9fgabc123def',
+ }
+ )
+
+ def set_default_optional_args_pass_check(self):
+ return dict(
+ {
+ 'api_url': 'sgmi.example.com',
+ 'auth_token': '01234567-5678-9abc-78de-9fgabc123def',
+ 'validate_certs': False,
+ 'gather_subset': ['all'],
+ 'parameters': {'limit': 5},
+ }
+ )
+
+ def set_args_run_sg_gather_facts_for_all_info(self):
+ return dict({
+ 'api_url': 'sgmi.example.com',
+ 'auth_token': '01234567-5678-9abc-78de-9fgabc123def',
+ 'validate_certs': False,
+ })
+
+ def set_args_run_sg_gather_facts_for_grid_accounts_info(self):
+ return dict({
+ 'api_url': 'sgmi.example.com',
+ 'auth_token': '01234567-5678-9abc-78de-9fgabc123def',
+ 'validate_certs': False,
+ 'gather_subset': ['grid_accounts_info'],
+ })
+
+ def set_args_run_sg_gather_facts_for_grid_accounts_and_grid_users_root_info(self):
+ return dict({
+ 'api_url': 'sgmi.example.com',
+ 'auth_token': '01234567-5678-9abc-78de-9fgabc123def',
+ 'validate_certs': False,
+ 'gather_subset': ['grid_accounts_info', 'grid/users/root'],
+ })
+
+ def test_module_fail_when_required_args_missing(self):
+ ''' required arguments are reported as errors '''
+ with pytest.raises(AnsibleFailJson) as exc:
+ set_module_args(self.set_default_args_fail_check())
+ sg_grid_info_module()
+ print(
+ 'Info: test_module_fail_when_required_args_missing: %s'
+ % exc.value.args[0]['msg']
+ )
+
+ def test_module_pass_when_required_args_present(self):
+ ''' required arguments are reported as errors '''
+ with pytest.raises(AnsibleExitJson) as exc:
+ set_module_args(self.set_default_args_pass_check())
+ sg_grid_info_module()
+ exit_json(changed=True, msg='Induced arguments check')
+ print(
+ 'Info: test_module_pass_when_required_args_present: %s'
+ % exc.value.args[0]['msg']
+ )
+ assert exc.value.args[0]['changed']
+
+ def test_module_pass_when_optional_args_present(self):
+ ''' Optional arguments are reported as pass '''
+ with pytest.raises(AnsibleExitJson) as exc:
+ set_module_args(self.set_default_optional_args_pass_check())
+ sg_grid_info_module()
+ exit_json(changed=True, msg='Induced arguments check')
+ print(
+ 'Info: test_module_pass_when_optional_args_present: %s'
+ % exc.value.args[0]['msg']
+ )
+ assert exc.value.args[0]['changed']
+
+ @patch('ansible_collections.netapp.storagegrid.plugins.module_utils.netapp.SGRestAPI.send_request')
+ def test_run_sg_gather_facts_for_all_info_pass(self, mock_request):
+ set_module_args(self.set_args_run_sg_gather_facts_for_all_info())
+ my_obj = sg_grid_info_module()
+ gather_subset = [
+ 'grid/accounts',
+ 'grid/alarms',
+ 'grid/audit',
+ 'grid/compliance-global',
+ 'grid/config',
+ 'grid/config/management',
+ 'grid/config/product-version',
+ 'grid/deactivated-features',
+ 'grid/dns-servers',
+ 'grid/domain-names',
+ 'grid/ec-profiles',
+ 'grid/expansion',
+ 'grid/expansion/nodes',
+ 'grid/expansion/sites',
+ 'grid/grid-networks',
+ 'grid/groups',
+ 'grid/health',
+ 'grid/health/topology',
+ 'grid/identity-source',
+ 'grid/ilm-criteria',
+ 'grid/ilm-policies',
+ 'grid/ilm-rules',
+ 'grid/license',
+ 'grid/management-certificate',
+ 'grid/ntp-servers',
+ 'grid/recovery/available-nodes',
+ 'grid/recovery',
+ 'grid/regions',
+ 'grid/schemes',
+ 'grid/snmp',
+ 'grid/storage-api-certificate',
+ 'grid/untrusted-client-network',
+ 'grid/users',
+ 'grid/users/root',
+ 'versions',
+ ]
+ mock_request.side_effect = [
+ SRR['grid_accounts'],
+ SRR['grid_alarms'],
+ SRR['grid_audit'],
+ SRR['grid_compliance_global'],
+ SRR['grid_config'],
+ SRR['grid_config_management'],
+ SRR['grid_config_product_version'],
+ SRR['grid_deactivated_features'],
+ SRR['grid_dns_servers'],
+ SRR['grid_domain_names'],
+ SRR['grid_ec_profiles'],
+ SRR['grid_expansion'],
+ SRR['grid_expansion_nodes'],
+ SRR['grid_expansion_sites'],
+ SRR['grid_grid_networks'],
+ SRR['grid_groups'],
+ SRR['grid_health'],
+ SRR['grid_health_topology'],
+ SRR['grid_identity_source'],
+ SRR['grid_ilm_criteria'],
+ SRR['grid_ilm_policies'],
+ SRR['grid_ilm_rules'],
+ SRR['grid_license'],
+ SRR['grid_management_certificate'],
+ SRR['grid_ntp_servers'],
+ SRR['grid_recovery_available_nodes'],
+ SRR['grid_recovery'],
+ SRR['grid_regions'],
+ SRR['grid_schemes'],
+ SRR['grid_snmp'],
+ SRR['grid_storage_api_certificate'],
+ SRR['grid_untrusted_client_network'],
+ SRR['grid_users'],
+ SRR['grid_users_root'],
+ SRR['versions'],
+ SRR['end_of_sequence'],
+ ]
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ print('Info: test_run_sg_gather_facts_for_all_info_pass: %s' % repr(exc.value.args))
+ assert set(exc.value.args[0]['sg_info']) == set(gather_subset)
+
+ @patch('ansible_collections.netapp.storagegrid.plugins.module_utils.netapp.SGRestAPI.send_request')
+ def test_run_sg_gather_facts_for_grid_accounts_info_pass(self, mock_request):
+ set_module_args(self.set_args_run_sg_gather_facts_for_grid_accounts_info())
+ my_obj = sg_grid_info_module()
+ gather_subset = ['grid/accounts']
+ mock_request.side_effect = [
+ SRR['grid_accounts'],
+ SRR['end_of_sequence'],
+ ]
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ print('Info: test_run_sg_gather_facts_for_grid_accounts_info_pass: %s' % repr(exc.value.args))
+ assert set(exc.value.args[0]['sg_info']) == set(gather_subset)
+
+ @patch('ansible_collections.netapp.storagegrid.plugins.module_utils.netapp.SGRestAPI.send_request')
+ def test_run_sg_gather_facts_for_grid_accounts_and_grid_users_root_info_pass(self, mock_request):
+ set_module_args(self.set_args_run_sg_gather_facts_for_grid_accounts_and_grid_users_root_info())
+ my_obj = sg_grid_info_module()
+ gather_subset = ['grid/accounts', 'grid/users/root']
+ mock_request.side_effect = [
+ SRR['grid_accounts'],
+ SRR['grid_users_root'],
+ SRR['end_of_sequence'],
+ ]
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ print('Info: test_run_sg_gather_facts_for_grid_accounts_and_grid_users_root_info_pass: %s' % repr(exc.value.args))
+ assert set(exc.value.args[0]['sg_info']) == set(gather_subset)
diff --git a/ansible_collections/netapp/storagegrid/tests/unit/plugins/modules/test_na_sg_grid_ntp.py b/ansible_collections/netapp/storagegrid/tests/unit/plugins/modules/test_na_sg_grid_ntp.py
new file mode 100644
index 000000000..eed83d49b
--- /dev/null
+++ b/ansible_collections/netapp/storagegrid/tests/unit/plugins/modules/test_na_sg_grid_ntp.py
@@ -0,0 +1,257 @@
+# (c) 2020, NetApp, Inc
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+""" unit tests NetApp StorageGRID NTP Ansible module: na_sg_grid_ntp"""
+
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+import json
+import pytest
+import sys
+try:
+ from requests import Response
+except ImportError:
+ if sys.version_info < (2, 7):
+ pytestmark = pytest.mark.skip('Skipping Unit Tests on 2.6 as requests is not available')
+ else:
+ raise
+
+from ansible_collections.netapp.storagegrid.tests.unit.compat import unittest
+from ansible_collections.netapp.storagegrid.tests.unit.compat.mock import (
+ patch,
+ Mock,
+)
+from ansible.module_utils import basic
+from ansible.module_utils._text import to_bytes
+from ansible_collections.netapp.storagegrid.plugins.modules.na_sg_grid_ntp import (
+ SgGridNtp as grid_ntp_module,
+)
+
+# REST API canned responses when mocking send_request
+SRR = {
+ # common responses
+ "empty_good": ({"data": []}, None),
+ "not_found": (
+ {"status": "error", "code": 404, "data": {}},
+ {"key": "error.404"},
+ ),
+ "end_of_sequence": (None, "Unexpected call to send_request"),
+ "generic_error": (None, "Expected error"),
+ "delete_good": ({"code": 204}, None),
+ "ntp_servers": ({"data": ["123.12.3.123", "123.1.23.123"]}, None,),
+ "update_ntp_servers": ({"data": ["123.12.3.123", "12.3.12.3"]}, None,),
+ "add_ntp_servers": (
+ {"data": ["123.12.3.123", "123.1.23.123", "12.3.12.3"]},
+ None,
+ ),
+ "remove_ntp_servers": ({"data": ["123.12.3.123"]}, None,),
+}
+
+
+def set_module_args(args):
+ """prepare arguments so that they will be picked up during module creation"""
+ args = json.dumps({"ANSIBLE_MODULE_ARGS": args})
+ basic._ANSIBLE_ARGS = to_bytes(args) # pylint: disable=protected-access
+
+
+class AnsibleExitJson(Exception):
+ """Exception class to be raised by module.exit_json and caught by the test case"""
+
+ pass
+
+
+class AnsibleFailJson(Exception):
+ """Exception class to be raised by module.fail_json and caught by the test case"""
+
+ pass
+
+
+def exit_json(*args, **kwargs): # pylint: disable=unused-argument
+ """function to patch over exit_json; package return data into an exception"""
+ if "changed" not in kwargs:
+ kwargs["changed"] = False
+ raise AnsibleExitJson(kwargs)
+
+
+def fail_json(*args, **kwargs): # pylint: disable=unused-argument
+ """function to patch over fail_json; package return data into an exception"""
+ kwargs["failed"] = True
+ raise AnsibleFailJson(kwargs)
+
+
+class TestMyModule(unittest.TestCase):
+ """ a group of related Unit Tests """
+
+ def setUp(self):
+ self.mock_module_helper = patch.multiple(
+ basic.AnsibleModule, exit_json=exit_json, fail_json=fail_json
+ )
+ self.mock_module_helper.start()
+ self.addCleanup(self.mock_module_helper.stop)
+
+ def set_default_args_fail_check(self):
+ return dict(
+ {
+ "ntp_servers": "123.12.3.123,123.1.23.123",
+ "auth_token": "01234567-5678-9abc-78de-9fgabc123def",
+ "validate_certs": False,
+ }
+ )
+
+ def set_default_args_pass_check(self):
+ return dict(
+ {
+ "state": "present",
+ "passphrase": "secretstring",
+ "ntp_servers": "123.12.3.123,123.1.23.123",
+ "api_url": "gmi.example.com",
+ "auth_token": "01234567-5678-9abc-78de-9fgabc123def",
+ "validate_certs": False,
+ }
+ )
+
+ def set_default_args_pass_check(self):
+ return dict(
+ {
+ "state": "present",
+ "passphrase": "secretstring",
+ "ntp_servers": "123.12.3.123,123.1.23.123",
+ "api_url": "gmi.example.com",
+ "auth_token": "01234567-5678-9abc-78de-9fgabc123def",
+ "validate_certs": False,
+ }
+ )
+
+ def set_args_set_na_sg_grid_ntp_servers(self):
+ return dict(
+ {
+ "state": "present",
+ "passphrase": "secretstring",
+ "ntp_servers": "123.12.3.123,12.3.12.3",
+ "api_url": "gmi.example.com",
+ "auth_token": "01234567-5678-9abc-78de-9fgabc123def",
+ "validate_certs": False,
+ }
+ )
+
+ def set_args_add_na_sg_grid_ntp_servers(self):
+ return dict(
+ {
+ "state": "present",
+ "passphrase": "secretstring",
+ "ntp_servers": "123.12.3.123,123.1.23.123,12.3.12.3",
+ "api_url": "gmi.example.com",
+ "auth_token": "01234567-5678-9abc-78de-9fgabc123def",
+ "validate_certs": False,
+ }
+ )
+
+ def set_args_remove_na_sg_grid_ntp_server(self):
+ return dict(
+ {
+ "state": "present",
+ "passphrase": "secretstring",
+ "ntp_servers": "123.12.3.123",
+ "api_url": "gmi.example.com",
+ "auth_token": "01234567-5678-9abc-78de-9fgabc123def",
+ "validate_certs": False,
+ }
+ )
+
+ def test_module_fail_when_required_args_missing(self):
+ """ required arguments are reported as errors """
+ with pytest.raises(AnsibleFailJson) as exc:
+ set_module_args(self.set_default_args_fail_check())
+ grid_ntp_module()
+ print(
+ "Info: test_module_fail_when_required_args_missing: %s"
+ % exc.value.args[0]["msg"]
+ )
+
+ def test_module_fail_when_required_args_present(self):
+ """ required arguments are reported as errors """
+ with pytest.raises(AnsibleExitJson) as exc:
+ set_module_args(self.set_default_args_pass_check())
+ grid_ntp_module()
+ exit_json(changed=True, msg="Induced arguments check")
+ print(
+ "Info: test_module_fail_when_required_args_present: %s"
+ % exc.value.args[0]["msg"]
+ )
+ assert exc.value.args[0]["changed"]
+
+ @patch(
+ "ansible_collections.netapp.storagegrid.plugins.module_utils.netapp.SGRestAPI.send_request"
+ )
+ def test_set_na_sg_grid_ntp_servers_pass(self, mock_request):
+ set_module_args(self.set_args_set_na_sg_grid_ntp_servers())
+ my_obj = grid_ntp_module()
+ mock_request.side_effect = [
+ SRR["ntp_servers"], # get
+ SRR["update_ntp_servers"], # post
+ SRR["end_of_sequence"],
+ ]
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ print(
+ "Info: test_set_na_sg_grid_ntp_servers_pass: %s"
+ % repr(exc.value.args[0])
+ )
+ assert exc.value.args[0]["changed"]
+
+ @patch(
+ "ansible_collections.netapp.storagegrid.plugins.module_utils.netapp.SGRestAPI.send_request"
+ )
+ def test_idempotent_set_na_sg_grid_ntp_servers_pass(self, mock_request):
+ set_module_args(self.set_args_set_na_sg_grid_ntp_servers())
+ my_obj = grid_ntp_module()
+ mock_request.side_effect = [
+ SRR["update_ntp_servers"], # get
+ SRR["end_of_sequence"],
+ ]
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ print(
+ "Info: test_idempotent_set_na_sg_grid_ntp_servers_pass: %s"
+ % repr(exc.value.args[0])
+ )
+ assert not exc.value.args[0]["changed"]
+
+ @patch(
+ "ansible_collections.netapp.storagegrid.plugins.module_utils.netapp.SGRestAPI.send_request"
+ )
+ def test_add_na_sg_grid_ntp_servers_pass(self, mock_request):
+ set_module_args(self.set_args_add_na_sg_grid_ntp_servers())
+ my_obj = grid_ntp_module()
+ mock_request.side_effect = [
+ SRR["ntp_servers"], # get
+ SRR["add_ntp_servers"], # post
+ SRR["end_of_sequence"],
+ ]
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ print(
+ "Info: test_add_na_sg_grid_ntp_servers_pass: %s"
+ % repr(exc.value.args[0])
+ )
+ assert exc.value.args[0]["changed"]
+
+ @patch(
+ "ansible_collections.netapp.storagegrid.plugins.module_utils.netapp.SGRestAPI.send_request"
+ )
+ def test_remove_na_sg_grid_ntp_servers_pass(self, mock_request):
+ set_module_args(self.set_args_remove_na_sg_grid_ntp_server())
+ my_obj = grid_ntp_module()
+ mock_request.side_effect = [
+ SRR["ntp_servers"], # get
+ SRR["remove_ntp_servers"], # post
+ SRR["end_of_sequence"],
+ ]
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ print(
+ "Info: test_remove_na_sg_grid_ntp_servers_pass: %s"
+ % repr(exc.value.args[0])
+ )
+ assert exc.value.args[0]["changed"]
diff --git a/ansible_collections/netapp/storagegrid/tests/unit/plugins/modules/test_na_sg_grid_regions.py b/ansible_collections/netapp/storagegrid/tests/unit/plugins/modules/test_na_sg_grid_regions.py
new file mode 100644
index 000000000..585ba3f45
--- /dev/null
+++ b/ansible_collections/netapp/storagegrid/tests/unit/plugins/modules/test_na_sg_grid_regions.py
@@ -0,0 +1,206 @@
+# (c) 2020, NetApp, Inc
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+""" unit tests NetApp StorageGRID Regions Ansible module: na_sg_grid_regions"""
+
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+import json
+import pytest
+import sys
+try:
+ from requests import Response
+except ImportError:
+ if sys.version_info < (2, 7):
+ pytestmark = pytest.mark.skip('Skipping Unit Tests on 2.6 as requests is not available')
+ else:
+ raise
+
+from ansible_collections.netapp.storagegrid.tests.unit.compat import unittest
+from ansible_collections.netapp.storagegrid.tests.unit.compat.mock import (
+ patch,
+ Mock,
+)
+from ansible.module_utils import basic
+from ansible.module_utils._text import to_bytes
+from ansible_collections.netapp.storagegrid.plugins.modules.na_sg_grid_regions import (
+ SgGridRegions as grid_regions_module,
+)
+
+# REST API canned responses when mocking send_request
+SRR = {
+ # common responses
+ "empty_good": ({"data": []}, None),
+ "not_found": (
+ {"status": "error", "code": 404, "data": {}},
+ {"key": "error.404"},
+ ),
+ "end_of_sequence": (None, "Unexpected call to send_request"),
+ "generic_error": (None, "Expected error"),
+ "delete_good": ({"code": 204}, None),
+ "default_regions": ({"data": ["us-east-1"]}, None,),
+ "regions": ({"data": ["us-east-1", "us-west-1"]}, None,),
+}
+
+
+def set_module_args(args):
+ """prepare arguments so that they will be picked up during module creation"""
+ args = json.dumps({"ANSIBLE_MODULE_ARGS": args})
+ basic._ANSIBLE_ARGS = to_bytes(args) # pylint: disable=protected-access
+
+
+class AnsibleExitJson(Exception):
+ """Exception class to be raised by module.exit_json and caught by the test case"""
+
+ pass
+
+
+class AnsibleFailJson(Exception):
+ """Exception class to be raised by module.fail_json and caught by the test case"""
+
+ pass
+
+
+def exit_json(*args, **kwargs): # pylint: disable=unused-argument
+ """function to patch over exit_json; package return data into an exception"""
+ if "changed" not in kwargs:
+ kwargs["changed"] = False
+ raise AnsibleExitJson(kwargs)
+
+
+def fail_json(*args, **kwargs): # pylint: disable=unused-argument
+ """function to patch over fail_json; package return data into an exception"""
+ kwargs["failed"] = True
+ raise AnsibleFailJson(kwargs)
+
+
+class TestMyModule(unittest.TestCase):
+ """ a group of related Unit Tests """
+
+ def setUp(self):
+ self.mock_module_helper = patch.multiple(
+ basic.AnsibleModule, exit_json=exit_json, fail_json=fail_json
+ )
+ self.mock_module_helper.start()
+ self.addCleanup(self.mock_module_helper.stop)
+
+ def set_default_args_fail_check(self):
+ return dict(
+ {
+ "regions": "us-east-1,us-west-1",
+ "auth_token": "01234567-5678-9abc-78de-9fgabc123def",
+ "validate_certs": False,
+ }
+ )
+
+ def set_default_args_pass_check(self):
+ return dict(
+ {
+ "state": "present",
+ "regions": "us-east-1,us-west-1",
+ "api_url": "gmi.example.com",
+ "auth_token": "01234567-5678-9abc-78de-9fgabc123def",
+ "validate_certs": False,
+ }
+ )
+
+ def set_args_set_na_sg_grid_regions(self):
+ return dict(
+ {
+ "state": "present",
+ "regions": "us-east-1,us-west-1",
+ "api_url": "gmi.example.com",
+ "auth_token": "01234567-5678-9abc-78de-9fgabc123def",
+ "validate_certs": False,
+ }
+ )
+
+ def set_args_remove_na_sg_grid_regions(self):
+ return dict(
+ {
+ "state": "present",
+ "regions": "us-east-1",
+ "api_url": "gmi.example.com",
+ "auth_token": "01234567-5678-9abc-78de-9fgabc123def",
+ "validate_certs": False,
+ }
+ )
+
+ def test_module_fail_when_required_args_missing(self):
+ """ required arguments are reported as errors """
+ with pytest.raises(AnsibleFailJson) as exc:
+ set_module_args(self.set_default_args_fail_check())
+ grid_regions_module()
+ print(
+ "Info: test_module_fail_when_required_args_missing: %s"
+ % exc.value.args[0]["msg"]
+ )
+
+ def test_module_fail_when_required_args_present(self):
+ """ required arguments are reported as errors """
+ with pytest.raises(AnsibleExitJson) as exc:
+ set_module_args(self.set_default_args_pass_check())
+ grid_regions_module()
+ exit_json(changed=True, msg="Induced arguments check")
+ print(
+ "Info: test_module_fail_when_required_args_present: %s"
+ % exc.value.args[0]["msg"]
+ )
+ assert exc.value.args[0]["changed"]
+
+ @patch(
+ "ansible_collections.netapp.storagegrid.plugins.module_utils.netapp.SGRestAPI.send_request"
+ )
+ def test_set_na_sg_grid_regions_pass(self, mock_request):
+ set_module_args(self.set_args_set_na_sg_grid_regions())
+ my_obj = grid_regions_module()
+ mock_request.side_effect = [
+ SRR["default_regions"], # get
+ SRR["regions"], # post
+ SRR["end_of_sequence"],
+ ]
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ print(
+ "Info: test_set_na_sg_grid_regions_pass: %s"
+ % repr(exc.value.args[0])
+ )
+ assert exc.value.args[0]["changed"]
+
+ @patch(
+ "ansible_collections.netapp.storagegrid.plugins.module_utils.netapp.SGRestAPI.send_request"
+ )
+ def test_idempotent_set_na_sg_grid_regions_pass(self, mock_request):
+ set_module_args(self.set_args_set_na_sg_grid_regions())
+ my_obj = grid_regions_module()
+ mock_request.side_effect = [
+ SRR["regions"], # get
+ SRR["end_of_sequence"],
+ ]
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ print(
+ "Info: test_idempotent_set_na_sg_grid_regions_pass: %s"
+ % repr(exc.value.args[0])
+ )
+ assert not exc.value.args[0]["changed"]
+
+ @patch(
+ "ansible_collections.netapp.storagegrid.plugins.module_utils.netapp.SGRestAPI.send_request"
+ )
+ def test_remove_na_sg_grid_regions_pass(self, mock_request):
+ set_module_args(self.set_args_remove_na_sg_grid_regions())
+ my_obj = grid_regions_module()
+ mock_request.side_effect = [
+ SRR["regions"], # get
+ SRR["default_regions"], # post
+ SRR["end_of_sequence"],
+ ]
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ print(
+ "Info: test_remove_na_sg_grid_regions_pass: %s"
+ % repr(exc.value.args[0])
+ )
+ assert exc.value.args[0]["changed"]
diff --git a/ansible_collections/netapp/storagegrid/tests/unit/plugins/modules/test_na_sg_grid_traffic_classes.py b/ansible_collections/netapp/storagegrid/tests/unit/plugins/modules/test_na_sg_grid_traffic_classes.py
new file mode 100644
index 000000000..42fce0e3b
--- /dev/null
+++ b/ansible_collections/netapp/storagegrid/tests/unit/plugins/modules/test_na_sg_grid_traffic_classes.py
@@ -0,0 +1,355 @@
+# (c) 2022, NetApp, Inc
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+""" unit tests NetApp StorageGRID Grid HA Group Ansible module: na_sg_grid_traffic_classes"""
+
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+import json
+import pytest
+import sys
+
+try:
+ from requests import Response
+except ImportError:
+ if sys.version_info < (2, 7):
+ pytestmark = pytest.mark.skip("Skipping Unit Tests on 2.6 as requests is not available")
+ else:
+ raise
+
+from ansible_collections.netapp.storagegrid.tests.unit.compat import unittest
+from ansible_collections.netapp.storagegrid.tests.unit.compat.mock import (
+ patch,
+ Mock,
+)
+from ansible.module_utils import basic
+from ansible.module_utils._text import to_bytes
+from ansible_collections.netapp.storagegrid.plugins.modules.na_sg_grid_traffic_classes import (
+ SgGridTrafficClasses as grid_traffic_classes_module,
+)
+
+# REST API canned responses when mocking send_request
+SRR = {
+ # common responses
+ "empty_good": ({"data": []}, None),
+ "not_found": (
+ {"status": "error", "code": 404, "data": {}},
+ {"key": "error.404"},
+ ),
+ "end_of_sequence": (None, "Unexpected call to send_request"),
+ "generic_error": (None, "Expected error"),
+ "delete_good": (None, None),
+ "update_good": (None, None),
+ "version_114": ({"data": {"productVersion": "11.4.0-20200721.1338.d3969b3"}}, None),
+ "version_116": ({"data": {"productVersion": "11.6.0-20211120.0301.850531e"}}, None),
+ "traffic_class_record": (
+ {
+ "data": {
+ "id": "6b2946e6-7fed-40d0-9262-8e922580aba7",
+ "name": "ansible-test-traffic-class",
+ "description": "Ansible Test",
+ "matchers": [
+ {"type": "cidr", "inverse": False, "members": ["192.168.50.0/24"]},
+ {"type": "bucket", "inverse": False, "members": ["ansible-test1", "ansible-test2"]},
+ ],
+ "limits": [],
+ }
+ },
+ None,
+ ),
+ "traffic_class_record_updated": (
+ {
+ "data": {
+ "id": "6b2946e6-7fed-40d0-9262-8e922580aba7",
+ "name": "ansible-test-traffic-class",
+ "description": "Ansible Test",
+ "matchers": [
+ {"type": "cidr", "inverse": False, "members": ["192.168.50.0/24"]},
+ {"type": "bucket", "inverse": False, "members": ["ansible-test1", "ansible-test2"]},
+ ],
+ "limits": [{"type": "aggregateBandwidthIn", "value": 888888}],
+ }
+ },
+ None,
+ ),
+ "traffic_class_record_rename": (
+ {
+ "data": {
+ "id": "6b2946e6-7fed-40d0-9262-8e922580aba7",
+ "name": "ansible-test-traffic-class-rename",
+ "description": "Ansible Test",
+ "matchers": [
+ {"type": "cidr", "inverse": False, "members": ["192.168.50.0/24"]},
+ {"type": "bucket", "inverse": False, "members": ["ansible-test1", "ansible-test2"]},
+ ],
+ "limits": [],
+ }
+ },
+ None,
+ ),
+ "traffic_classes": (
+ {
+ "data": [
+ {
+ "id": "6b2946e6-7fed-40d0-9262-8e922580aba7",
+ "name": "ansible-test-traffic-class",
+ "description": "Ansible Test",
+ },
+ {
+ "id": "531e6be1-e9b1-4010-bb79-03437c7c13d2",
+ "name": "policy-test1",
+ "description": "First test policy",
+ },
+ ]
+ },
+ None,
+ ),
+ "node_health": (
+ {
+ "data": [
+ {
+ "id": "0b1866ed-d6e7-41b4-815f-bf867348b76b",
+ "isPrimaryAdmin": True,
+ "name": "SITE1-ADM1",
+ "siteId": "ae56d06d-bd83-46bd-adce-77146b1d94bd",
+ "siteName": "SITE1",
+ "severity": "normal",
+ "state": "connected",
+ "type": "adminNode",
+ },
+ {
+ "id": "7bb5bf05-a04c-4344-8abd-08c5c4048666",
+ "isPrimaryAdmin": None,
+ "name": "SITE1-G1",
+ "siteId": "ae56d06d-bd83-46bd-adce-77146b1d94bd",
+ "siteName": "SITE1",
+ "severity": "normal",
+ "state": "connected",
+ "type": "apiGatewayNode",
+ },
+ {
+ "id": "970ad050-b68b-4aae-a94d-aef73f3095c4",
+ "isPrimaryAdmin": False,
+ "name": "SITE2-ADM1",
+ "siteId": "7c24002e-5157-43e9-83e5-02db9b265b02",
+ "siteName": "SITE2",
+ "severity": "normal",
+ "state": "connected",
+ "type": "adminNode",
+ },
+ ]
+ },
+ None,
+ ),
+}
+
+
+def set_module_args(args):
+ """prepare arguments so that they will be picked up during module creation"""
+ args = json.dumps({"ANSIBLE_MODULE_ARGS": args})
+ basic._ANSIBLE_ARGS = to_bytes(args) # pylint: disable=protected-access
+
+
+class AnsibleExitJson(Exception):
+ """Exception class to be raised by module.exit_json and caught by the test case"""
+
+ pass
+
+
+class AnsibleFailJson(Exception):
+ """Exception class to be raised by module.fail_json and caught by the test case"""
+
+ pass
+
+
+def exit_json(*args, **kwargs): # pylint: disable=unused-argument
+ """function to patch over exit_json; package return data into an exception"""
+ if "changed" not in kwargs:
+ kwargs["changed"] = False
+ raise AnsibleExitJson(kwargs)
+
+
+def fail_json(*args, **kwargs): # pylint: disable=unused-argument
+ """function to patch over fail_json; package return data into an exception"""
+ kwargs["failed"] = True
+ raise AnsibleFailJson(kwargs)
+
+
+class TestMyModule(unittest.TestCase):
+ """a group of related Unit Tests"""
+
+ def setUp(self):
+ self.mock_module_helper = patch.multiple(basic.AnsibleModule, exit_json=exit_json, fail_json=fail_json)
+ self.mock_module_helper.start()
+ self.addCleanup(self.mock_module_helper.stop)
+
+ def set_default_args_fail_check(self):
+ return dict(
+ {
+ "matchers": [
+ {"type": "bucket", "members": ["ansible-test1", "ansible-test2"]},
+ {"type": "cidr", "members": ["192.168.50.0/24"]},
+ ],
+ "auth_token": "01234567-5678-9abc-78de-9fgabc123def",
+ "validate_certs": False,
+ }
+ )
+
+ def set_default_args_pass_check(self):
+ return dict(
+ {
+ "state": "present",
+ "name": "ansible-test-traffic-class",
+ "matchers": [
+ {"type": "bucket", "members": ["ansible-test1", "ansible-test2"]},
+ {"type": "cidr", "members": ["192.168.50.0/24"]},
+ ],
+ "api_url": "gmi.example.com",
+ "auth_token": "01234567-5678-9abc-78de-9fgabc123def",
+ "validate_certs": False,
+ }
+ )
+
+ def set_args_create_na_sg_grid_traffic_class(self):
+ return dict(
+ {
+ "state": "present",
+ "name": "ansible-test-traffic-class",
+ "description": "Ansible Test",
+ "matchers": [
+ {"type": "bucket", "members": ["ansible-test1", "ansible-test2"]},
+ {"type": "cidr", "members": ["192.168.50.0/24"]},
+ ],
+ "api_url": "gmi.example.com",
+ "auth_token": "01234567-5678-9abc-78de-9fgabc123def",
+ "validate_certs": False,
+ }
+ )
+
+ def set_args_delete_na_sg_grid_traffic_class(self):
+ return dict(
+ {
+ "state": "absent",
+ "name": "ansible-test-traffic-class",
+ "api_url": "gmi.example.com",
+ "auth_token": "01234567-5678-9abc-78de-9fgabc123def",
+ "validate_certs": False,
+ }
+ )
+
+ @patch("ansible_collections.netapp.storagegrid.plugins.module_utils.netapp.SGRestAPI.send_request")
+ def test_module_fail_when_required_args_missing(self, mock_request):
+ """required arguments are reported as errors"""
+ with pytest.raises(AnsibleFailJson) as exc:
+ set_module_args(self.set_default_args_fail_check())
+ grid_traffic_classes_module()
+ print("Info: test_module_fail_when_required_args_missing: %s" % exc.value.args[0]["msg"])
+
+ @patch("ansible_collections.netapp.storagegrid.plugins.module_utils.netapp.SGRestAPI.send_request")
+ def test_module_pass_when_required_args_present(self, mock_request):
+ """required arguments are reported as errors"""
+ mock_request.side_effect = [
+ SRR["node_health"], # get
+ ]
+ with pytest.raises(AnsibleExitJson) as exc:
+ set_module_args(self.set_default_args_pass_check())
+ grid_traffic_classes_module()
+ exit_json(changed=True, msg="Induced arguments check")
+ print("Info: test_module_pass_when_required_args_present: %s" % exc.value.args[0]["msg"])
+ assert exc.value.args[0]["changed"]
+
+ @patch("ansible_collections.netapp.storagegrid.plugins.module_utils.netapp.SGRestAPI.send_request")
+ def test_create_na_sg_grid_traffic_class_pass(self, mock_request):
+ set_module_args(self.set_args_create_na_sg_grid_traffic_class())
+ mock_request.side_effect = [
+ SRR["empty_good"], # get
+ SRR["traffic_class_record"], # post
+ SRR["end_of_sequence"],
+ ]
+ my_obj = grid_traffic_classes_module()
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ print("Info: test_create_na_sg_grid_traffic_class_pass: %s" % repr(exc.value.args[0]))
+ assert exc.value.args[0]["changed"]
+
+ @patch("ansible_collections.netapp.storagegrid.plugins.module_utils.netapp.SGRestAPI.send_request")
+ def test_idempotent_create_na_sg_grid_traffic_class_pass(self, mock_request):
+ args = self.set_args_create_na_sg_grid_traffic_class()
+ set_module_args(args)
+ mock_request.side_effect = [
+ SRR["traffic_classes"], # get
+ SRR["traffic_class_record"], # get
+ SRR["end_of_sequence"],
+ ]
+ my_obj = grid_traffic_classes_module()
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ print("Info: test_idempotent_create_na_sg_grid_traffic_class_pass: %s" % repr(exc.value.args[0]))
+ assert not exc.value.args[0]["changed"]
+
+ @patch("ansible_collections.netapp.storagegrid.plugins.module_utils.netapp.SGRestAPI.send_request")
+ def test_update_na_sg_grid_traffic_class_pass(self, mock_request):
+ args = self.set_args_create_na_sg_grid_traffic_class()
+ args["description"] = "Ansible Test with Limit"
+ args["limits"] = [{"type": "aggregateBandwidthIn", "value": 888888}]
+ set_module_args(args)
+ mock_request.side_effect = [
+ SRR["traffic_classes"], # get
+ SRR["traffic_class_record"], # get
+ SRR["traffic_class_record_updated"], # put
+ SRR["end_of_sequence"],
+ ]
+ my_obj = grid_traffic_classes_module()
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ print("Info: test_update_na_sg_grid_traffic_class_pass: %s" % repr(exc.value.args[0]))
+ assert exc.value.args[0]["changed"]
+
+ @patch("ansible_collections.netapp.storagegrid.plugins.module_utils.netapp.SGRestAPI.send_request")
+ def test_rename_na_sg_grid_traffic_class_pass(self, mock_request):
+ args = self.set_args_create_na_sg_grid_traffic_class()
+ args["policy_id"] = "6b2946e6-7fed-40d0-9262-8e922580aba7"
+ args["name"] = "ansible-test-traffic-class-rename"
+ set_module_args(args)
+ mock_request.side_effect = [
+ SRR["traffic_class_record"], # get
+ SRR["traffic_class_record_rename"], # put
+ SRR["end_of_sequence"],
+ ]
+ my_obj = grid_traffic_classes_module()
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ print("Info: test_rename_na_sg_grid_traffic_class_pass: %s" % repr(exc.value.args[0]))
+ assert exc.value.args[0]["changed"]
+
+ @patch("ansible_collections.netapp.storagegrid.plugins.module_utils.netapp.SGRestAPI.send_request")
+ def test_delete_na_sg_grid_traffic_class_pass(self, mock_request):
+ args = self.set_args_delete_na_sg_grid_traffic_class()
+ set_module_args(args)
+ mock_request.side_effect = [
+ SRR["traffic_classes"], # get
+ SRR["traffic_class_record"], # get
+ SRR["delete_good"], # delete
+ SRR["end_of_sequence"],
+ ]
+ my_obj = grid_traffic_classes_module()
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ print("Info: test_delete_na_sg_grid_traffic_class_pass: %s" % repr(exc.value.args[0]))
+ assert exc.value.args[0]["changed"]
+
+ @patch("ansible_collections.netapp.storagegrid.plugins.module_utils.netapp.SGRestAPI.send_request")
+ def test_update_na_sg_grid_traffic_class_bad_policy_id_fail(self, mock_request):
+ args = self.set_args_create_na_sg_grid_traffic_class()
+ args["policy_id"] = "ffffffff-ffff-aaaa-aaaa-000000000000"
+ args["description"] = "Bad ID"
+ set_module_args(args)
+ mock_request.side_effect = [
+ SRR["not_found"], # get
+ ]
+ with pytest.raises(AnsibleFailJson) as exc:
+ my_obj = grid_traffic_classes_module()
+ my_obj.apply()
+ print("Info: test_update_na_sg_grid_traffic_class_bad_policy_id_fail: %s" % repr(exc.value.args[0]))
+ assert exc.value.args[0]["failed"]
diff --git a/ansible_collections/netapp/storagegrid/tests/unit/plugins/modules/test_na_sg_grid_user.py b/ansible_collections/netapp/storagegrid/tests/unit/plugins/modules/test_na_sg_grid_user.py
new file mode 100644
index 000000000..c8ec38c09
--- /dev/null
+++ b/ansible_collections/netapp/storagegrid/tests/unit/plugins/modules/test_na_sg_grid_user.py
@@ -0,0 +1,476 @@
+# (c) 2020, NetApp, Inc
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+""" unit tests NetApp StorageGRID Grid User Ansible module: na_sg_grid_user"""
+
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+import json
+import pytest
+import sys
+try:
+ from requests import Response
+except ImportError:
+ if sys.version_info < (2, 7):
+ pytestmark = pytest.mark.skip('Skipping Unit Tests on 2.6 as requests is not available')
+ else:
+ raise
+
+from ansible_collections.netapp.storagegrid.tests.unit.compat import unittest
+from ansible_collections.netapp.storagegrid.tests.unit.compat.mock import (
+ patch,
+ Mock,
+)
+from ansible.module_utils import basic
+from ansible.module_utils._text import to_bytes
+from ansible_collections.netapp.storagegrid.plugins.modules.na_sg_grid_user import (
+ SgGridUser as grid_user_module,
+)
+
+# REST API canned responses when mocking send_request
+SRR = {
+ # common responses
+ "empty_good": ({"data": []}, None),
+ "not_found": ({"status": "error", "code": 404, "data": {}}, {"key": "error.404"},),
+ "end_of_sequence": (None, "Unexpected call to send_request"),
+ "generic_error": (None, "Expected error"),
+ "delete_good": ({"code": 204}, None),
+ "pw_change_good": ({"code": 204}, None),
+ "grid_groups": (
+ {
+ "data": [
+ {
+ "displayName": "TestGridGroup1",
+ "uniqueName": "group/testgridgroup1",
+ "accountId": "12345678901234567890",
+ "id": "12345678-abcd-1234-abcd-1234567890ab",
+ "federated": False,
+ "groupURN": "urn:sgws:identity::12345678901234567890:group/testgridgroup1",
+ },
+ {
+ "displayName": "TestGridGroup2",
+ "uniqueName": "group/testgridgroup2",
+ "accountId": "12345678901234567890",
+ "id": "87654321-abcd-1234-cdef-1234567890ab",
+ "federated": False,
+ "groupURN": "urn:sgws:identity::12345678901234567890:group/testgridgroup2",
+ },
+ ]
+ },
+ None,
+ ),
+ "grid_users": (
+ {
+ "data": [
+ {
+ "id": "09876543-abcd-4321-abcd-0987654321ab",
+ "accountId": "12345678901234567890",
+ "fullName": "testgriduser",
+ "uniqueName": "user/ansible-sg-adm-user1",
+ "userURN": "urn:sgws:identity::12345678901234567890:user/testgriduser",
+ "federated": False,
+ "memberOf": ["12345678-abcd-1234-abcd-1234567890ab"],
+ "disable": False,
+ }
+ ]
+ },
+ None,
+ ),
+ "grid_user_record_no_group": (
+ {
+ "data": {
+ "id": "09876543-abcd-4321-abcd-0987654321ab",
+ "accountId": "12345678901234567890",
+ "fullName": "testgriduser",
+ "uniqueName": "user/ansible-sg-adm-user1",
+ "userURN": "urn:sgws:identity::12345678901234567890:user/testgriduser",
+ "federated": False,
+ "disable": False,
+ }
+ },
+ None,
+ ),
+ "grid_user_record": (
+ {
+ "data": {
+ "id": "09876543-abcd-4321-abcd-0987654321ab",
+ "accountId": "12345678901234567890",
+ "fullName": "testgriduser",
+ "uniqueName": "user/ansible-sg-adm-user1",
+ "userURN": "urn:sgws:identity::12345678901234567890:user/testgriduser",
+ "federated": False,
+ "memberOf": ["12345678-abcd-1234-abcd-1234567890ab"],
+ "disable": False,
+ }
+ },
+ None,
+ ),
+ "grid_user_record_update": (
+ {
+ "data": {
+ "id": "09876543-abcd-4321-abcd-0987654321ab",
+ "accountId": "12345678901234567890",
+ "fullName": "testgriduser",
+ "uniqueName": "user/ansible-sg-adm-user1",
+ "userURN": "urn:sgws:identity::12345678901234567890:user/testgriduser",
+ "federated": False,
+ "memberOf": [
+ "12345678-abcd-1234-abcd-1234567890ab",
+ "87654321-abcd-1234-cdef-1234567890ab",
+ ],
+ "disable": False,
+ }
+ },
+ None,
+ ),
+}
+
+
+def set_module_args(args):
+ """prepare arguments so that they will be picked up during module creation"""
+ args = json.dumps({"ANSIBLE_MODULE_ARGS": args})
+ basic._ANSIBLE_ARGS = to_bytes(args) # pylint: disable=protected-access
+
+
+class AnsibleExitJson(Exception):
+ """Exception class to be raised by module.exit_json and caught by the test case"""
+
+ pass
+
+
+class AnsibleFailJson(Exception):
+ """Exception class to be raised by module.fail_json and caught by the test case"""
+
+ pass
+
+
+def exit_json(*args, **kwargs): # pylint: disable=unused-argument
+ """function to patch over exit_json; package return data into an exception"""
+ if "changed" not in kwargs:
+ kwargs["changed"] = False
+ raise AnsibleExitJson(kwargs)
+
+
+def fail_json(*args, **kwargs): # pylint: disable=unused-argument
+ """function to patch over fail_json; package return data into an exception"""
+ kwargs["failed"] = True
+ raise AnsibleFailJson(kwargs)
+
+
+class TestMyModule(unittest.TestCase):
+ """ a group of related Unit Tests """
+
+ def setUp(self):
+ self.mock_module_helper = patch.multiple(
+ basic.AnsibleModule, exit_json=exit_json, fail_json=fail_json
+ )
+ self.mock_module_helper.start()
+ self.addCleanup(self.mock_module_helper.stop)
+
+ def set_default_args_fail_check(self):
+ return dict(
+ {
+ "full_name": "TestUser",
+ "api_url": "gmi.example.com",
+ "auth_token": "01234567-5678-9abc-78de-9fgabc123def",
+ "validate_certs": False,
+ }
+ )
+
+ def set_default_args_pass_check(self):
+ return dict(
+ {
+ "state": "present",
+ "full_name": "TestUser",
+ "unique_name": "user/testuser",
+ "api_url": "gmi.example.com",
+ "auth_token": "01234567-5678-9abc-78de-9fgabc123def",
+ "validate_certs": False,
+ }
+ )
+
+ def set_args_create_na_sg_grid_user_no_group(self):
+ return dict(
+ {
+ "state": "present",
+ "full_name": "TestUser",
+ "unique_name": "user/testuser",
+ "api_url": "gmi.example.com",
+ "auth_token": "01234567-5678-9abc-78de-9fgabc123def",
+ "validate_certs": False,
+ }
+ )
+
+ def set_args_create_na_sg_grid_user(self):
+ return dict(
+ {
+ "state": "present",
+ "full_name": "TestUser",
+ "unique_name": "user/testuser",
+ "member_of": ["group/testgridgroup1"],
+ "api_url": "gmi.example.com",
+ "auth_token": "01234567-5678-9abc-78de-9fgabc123def",
+ "validate_certs": False,
+ }
+ )
+
+ def set_args_delete_na_sg_grid_user(self):
+ return dict(
+ {
+ "state": "absent",
+ "unique_name": "user/testuser",
+ "api_url": "gmi.example.com",
+ "auth_token": "01234567-5678-9abc-78de-9fgabc123def",
+ "validate_certs": False,
+ }
+ )
+
+ def test_module_fail_when_required_args_missing(self):
+ """ required arguments are reported as errors """
+ with pytest.raises(AnsibleFailJson) as exc:
+ set_module_args(self.set_default_args_fail_check())
+ grid_user_module()
+ print(
+ "Info: test_module_fail_when_required_args_missing: %s"
+ % exc.value.args[0]["msg"]
+ )
+
+ def test_module_fail_when_required_args_present(self):
+ """ required arguments are reported as errors """
+ with pytest.raises(AnsibleExitJson) as exc:
+ set_module_args(self.set_default_args_pass_check())
+ grid_user_module()
+ exit_json(changed=True, msg="Induced arguments check")
+ print(
+ "Info: test_module_fail_when_required_args_present: %s"
+ % exc.value.args[0]["msg"]
+ )
+ assert exc.value.args[0]["changed"]
+
+ def test_module_fail_with_bad_unique_name(self):
+ """ error returned if unique_name doesn't start with user or federated_user """
+ with pytest.raises(AnsibleFailJson) as exc:
+ args = self.set_default_args_pass_check()
+ args["unique_name"] = "noprefixuser"
+ set_module_args(args)
+ grid_user_module()
+ print(
+ "Info: test_module_fail_with_bad_unique_name: %s" % exc.value.args[0]["msg"]
+ )
+
+ def set_args_create_na_sg_grid_user_with_password(self):
+ return dict(
+ {
+ "state": "present",
+ "full_name": "TestUser",
+ "unique_name": "user/testuser",
+ "member_of": ["group/testgridgroup1"],
+ "password": "netapp123",
+ "api_url": "gmi.example.com",
+ "auth_token": "01234567-5678-9abc-78de-9fgabc123def",
+ "validate_certs": False,
+ }
+ )
+
+ @patch(
+ "ansible_collections.netapp.storagegrid.plugins.module_utils.netapp.SGRestAPI.send_request"
+ )
+ def test_create_na_sg_grid_user_no_group_pass(self, mock_request):
+ set_module_args(self.set_args_create_na_sg_grid_user_no_group())
+ my_obj = grid_user_module()
+ mock_request.side_effect = [
+ SRR["not_found"], # get
+ SRR["grid_user_record_no_group"], # post
+ SRR["end_of_sequence"],
+ ]
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ print(
+ "Info: test_create_na_sg_grid_user_no_group_pass: %s"
+ % repr(exc.value.args[0])
+ )
+ assert exc.value.args[0]["changed"]
+
+ @patch(
+ "ansible_collections.netapp.storagegrid.plugins.module_utils.netapp.SGRestAPI.send_request"
+ )
+ def test_create_na_sg_grid_user_pass(self, mock_request):
+ set_module_args(self.set_args_create_na_sg_grid_user())
+ my_obj = grid_user_module()
+ mock_request.side_effect = [
+ SRR["not_found"], # get
+ SRR["grid_groups"], # get
+ SRR["grid_user_record"], # post
+ SRR["end_of_sequence"],
+ ]
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ print("Info: test_create_na_sg_grid_user_pass: %s" % repr(exc.value.args[0]))
+ assert exc.value.args[0]["changed"]
+
+ @patch(
+ "ansible_collections.netapp.storagegrid.plugins.module_utils.netapp.SGRestAPI.send_request"
+ )
+ def test_idempotent_create_na_sg_grid_user_pass(self, mock_request):
+ set_module_args(self.set_args_create_na_sg_grid_user())
+ my_obj = grid_user_module()
+ mock_request.side_effect = [
+ SRR["grid_user_record"], # get
+ SRR["grid_groups"], # get
+ SRR["end_of_sequence"],
+ ]
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ print(
+ "Info: test_idempotent_create_na_sg_grid_user_pass: %s"
+ % repr(exc.value.args[0])
+ )
+ assert not exc.value.args[0]["changed"]
+
+ @patch(
+ "ansible_collections.netapp.storagegrid.plugins.module_utils.netapp.SGRestAPI.send_request"
+ )
+ def test_update_na_sg_grid_user_pass(self, mock_request):
+ args = self.set_args_create_na_sg_grid_user()
+ args["member_of"] = ["group/testgridgroup1", "group/testgridgroup2"]
+
+ set_module_args(args)
+ my_obj = grid_user_module()
+ mock_request.side_effect = [
+ SRR["grid_user_record"], # get
+ SRR["grid_groups"], # get
+ SRR["grid_user_record_update"], # put
+ SRR["end_of_sequence"],
+ ]
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ print("Info: test_update_na_sg_grid_user_pass: %s" % repr(exc.value.args[0]))
+ assert exc.value.args[0]["changed"]
+
+ @patch(
+ "ansible_collections.netapp.storagegrid.plugins.module_utils.netapp.SGRestAPI.send_request"
+ )
+ def test_delete_na_sg_grid_user_pass(self, mock_request):
+ set_module_args(self.set_args_delete_na_sg_grid_user())
+ my_obj = grid_user_module()
+ mock_request.side_effect = [
+ SRR["grid_user_record"], # get
+ SRR["grid_groups"], # get
+ SRR["delete_good"], # delete
+ SRR["end_of_sequence"],
+ ]
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ print("Info: test_delete_na_sg_grid_user_pass: %s" % repr(exc.value.args[0]))
+ assert exc.value.args[0]["changed"]
+
+ # create user and set pass
+ @patch(
+ "ansible_collections.netapp.storagegrid.plugins.module_utils.netapp.SGRestAPI.send_request"
+ )
+ def test_create_na_sg_grid_user_and_set_password_pass(self, mock_request):
+ set_module_args(self.set_args_create_na_sg_grid_user_with_password())
+ my_obj = grid_user_module()
+ mock_request.side_effect = [
+ SRR["not_found"], # get
+ SRR["grid_groups"], # get
+ SRR["grid_user_record"], # post
+ SRR["pw_change_good"], # post
+ SRR["end_of_sequence"],
+ ]
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ print(
+ "Info: test_create_na_sg_grid_user_and_set_password_pass: %s"
+ % repr(exc.value.args[0])
+ )
+ assert exc.value.args[0]["changed"]
+
+ # Idempotent user with password defined
+ @patch(
+ "ansible_collections.netapp.storagegrid.plugins.module_utils.netapp.SGRestAPI.send_request"
+ )
+ def test_idempotent_create_na_sg_grid_user_and_set_password_pass(
+ self, mock_request
+ ):
+ set_module_args(self.set_args_create_na_sg_grid_user_with_password())
+ my_obj = grid_user_module()
+ mock_request.side_effect = [
+ SRR["grid_user_record"], # get
+ SRR["grid_groups"], # get
+ SRR["end_of_sequence"],
+ ]
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ print(
+ "Info: test_idempotent_create_na_sg_grid_user_and_set_password_pass: %s"
+ % repr(exc.value.args[0])
+ )
+ assert not exc.value.args[0]["changed"]
+
+ # update user and set pass
+ @patch(
+ "ansible_collections.netapp.storagegrid.plugins.module_utils.netapp.SGRestAPI.send_request"
+ )
+ def test_update_na_sg_grid_user_and_set_password_pass(self, mock_request):
+ args = self.set_args_create_na_sg_grid_user_with_password()
+ args["member_of"] = ["group/testgridgroup1", "group/testgridgroup2"]
+ args["update_password"] = "always"
+
+ set_module_args(args)
+ my_obj = grid_user_module()
+ mock_request.side_effect = [
+ SRR["grid_user_record"], # get
+ SRR["grid_groups"], # get
+ SRR["grid_user_record_update"], # put
+ SRR["pw_change_good"], # post
+ SRR["end_of_sequence"],
+ ]
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ print(
+ "Info: test_update_na_sg_grid_user_and_set_password_pass: %s"
+ % repr(exc.value.args[0])
+ )
+ assert exc.value.args[0]["changed"]
+
+ # set pass only
+
+ @patch(
+ "ansible_collections.netapp.storagegrid.plugins.module_utils.netapp.SGRestAPI.send_request"
+ )
+ def test_set_na_sg_grid_user_password_pass(self, mock_request):
+ args = self.set_args_create_na_sg_grid_user_with_password()
+ args["update_password"] = "always"
+
+ set_module_args(args)
+ my_obj = grid_user_module()
+ mock_request.side_effect = [
+ SRR["grid_user_record"], # get
+ SRR["grid_groups"], # get
+ SRR["pw_change_good"], # post
+ SRR["end_of_sequence"],
+ ]
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ print(
+ "Info: test_set_na_sg_grid_user_password_pass: %s" % repr(exc.value.args[0])
+ )
+ assert exc.value.args[0]["changed"]
+
+ # attempt to set password on federated user
+
+ @patch(
+ "ansible_collections.netapp.storagegrid.plugins.module_utils.netapp.SGRestAPI.send_request"
+ )
+ def test_fail_set_federated_user_password(self, mock_request):
+ with pytest.raises(AnsibleFailJson) as exc:
+ args = self.set_args_create_na_sg_grid_user_with_password()
+ args["unique_name"] = "federated-user/abc123"
+ args["update_password"] = "always"
+ set_module_args(args)
+ grid_user_module()
+ print(
+ "Info: test_fail_set_federated_user_password: %s" % repr(exc.value.args[0])
+ )
diff --git a/ansible_collections/netapp/storagegrid/tests/unit/plugins/modules/test_na_sg_org_container.py b/ansible_collections/netapp/storagegrid/tests/unit/plugins/modules/test_na_sg_org_container.py
new file mode 100644
index 000000000..21c49a556
--- /dev/null
+++ b/ansible_collections/netapp/storagegrid/tests/unit/plugins/modules/test_na_sg_org_container.py
@@ -0,0 +1,348 @@
+# (c) 2020, NetApp, Inc
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+""" unit tests NetApp StorageGRID Org Container Ansible module: na_sg_org_container"""
+
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+import json
+import pytest
+import sys
+
+try:
+ from requests import Response
+except ImportError:
+ if sys.version_info < (2, 7):
+ pytestmark = pytest.mark.skip("Skipping Unit Tests on 2.6 as requests is not available")
+ else:
+ raise
+
+from ansible_collections.netapp.storagegrid.tests.unit.compat import unittest
+from ansible_collections.netapp.storagegrid.tests.unit.compat.mock import (
+ patch,
+ Mock,
+)
+from ansible.module_utils import basic
+from ansible.module_utils._text import to_bytes
+from ansible_collections.netapp.storagegrid.plugins.modules.na_sg_org_container import (
+ SgOrgContainer as org_container_module,
+)
+
+# REST API canned responses when mocking send_request
+SRR = {
+ # common responses
+ "empty_good": ({"data": []}, None),
+ "not_found": (
+ {"status": "error", "code": 404, "data": {}},
+ {"key": "error.404"},
+ ),
+ "end_of_sequence": (None, "Unexpected call to send_request"),
+ "generic_error": (None, "Expected error"),
+ "delete_good": (None, None),
+ "version_114": ({"data": {"productVersion": "11.4.0-20200721.1338.d3969b3"}}, None),
+ "version_116": ({"data": {"productVersion": "11.6.0-20211120.0301.850531e"}}, None),
+ "global_compliance_disabled": (
+ {
+ "data": {
+ "complianceEnabled": False,
+ }
+ },
+ None,
+ ),
+ "global_compliance_enabled": (
+ {
+ "data": {
+ "complianceEnabled": True,
+ }
+ },
+ None,
+ ),
+ "org_containers": (
+ {"data": [{"name": "testbucket", "creationTime": "2020-02-04T12:43:50.777Z", "region": "us-east-1"}]},
+ None,
+ ),
+ "org_container_record": (
+ {"data": {"name": "testbucket", "creationTime": "2020-02-04T12:43:50.777Z", "region": "us-east-1"}},
+ None,
+ ),
+ "org_container_objectlock_record": (
+ {
+ "data": {
+ "name": "testbucket",
+ "creationTime": "2020-02-04T12:43:50.777Z",
+ "region": "us-east-1",
+ "s3ObjectLock": {"enabled": True},
+ }
+ },
+ None,
+ ),
+ "org_container_record_update": (
+ {
+ "data": {
+ "name": "testbucket",
+ "creationTime": "2020-02-04T12:43:50.777Z",
+ "region": "us-east-1",
+ "compliance": {"autoDelete": False, "legalHold": False},
+ }
+ },
+ None,
+ ),
+ "org_container_versioning_disabled": ({"data": {"versioningEnabled": False, "versioningSuspended": False}}, None),
+ "org_container_versioning_enabled": ({"data": {"versioningEnabled": True, "versioningSuspended": False}}, None),
+ "org_container_versioning_suspended": ({"data": {"versioningEnabled": False, "versioningSuspended": True}}, None),
+}
+
+
+def set_module_args(args):
+ """prepare arguments so that they will be picked up during module creation"""
+ args = json.dumps({"ANSIBLE_MODULE_ARGS": args})
+ basic._ANSIBLE_ARGS = to_bytes(args) # pylint: disable=protected-access
+
+
+class AnsibleExitJson(Exception):
+ """Exception class to be raised by module.exit_json and caught by the test case"""
+
+ pass
+
+
+class AnsibleFailJson(Exception):
+ """Exception class to be raised by module.fail_json and caught by the test case"""
+
+ pass
+
+
+def exit_json(*args, **kwargs): # pylint: disable=unused-argument
+ """function to patch over exit_json; package return data into an exception"""
+ if "changed" not in kwargs:
+ kwargs["changed"] = False
+ raise AnsibleExitJson(kwargs)
+
+
+def fail_json(*args, **kwargs): # pylint: disable=unused-argument
+ """function to patch over fail_json; package return data into an exception"""
+ kwargs["failed"] = True
+ raise AnsibleFailJson(kwargs)
+
+
+class TestMyModule(unittest.TestCase):
+ """a group of related Unit Tests"""
+
+ def setUp(self):
+ self.mock_module_helper = patch.multiple(basic.AnsibleModule, exit_json=exit_json, fail_json=fail_json)
+ self.mock_module_helper.start()
+ self.addCleanup(self.mock_module_helper.stop)
+
+ def set_default_args_fail_check(self):
+ return dict(
+ {"name": "testbucket", "auth_token": "01234567-5678-9abc-78de-9fgabc123def", "validate_certs": False}
+ )
+
+ def set_default_args_pass_check(self):
+ return dict(
+ {
+ "state": "present",
+ "name": "testbucket",
+ "api_url": "gmi.example.com",
+ "auth_token": "01234567-5678-9abc-78de-9fgabc123def",
+ "validate_certs": False,
+ }
+ )
+
+ def set_args_create_na_sg_org_container(self):
+ return dict(
+ {
+ "state": "present",
+ "name": "testbucket",
+ "api_url": "gmi.example.com",
+ "auth_token": "01234567-5678-9abc-78de-9fgabc123def",
+ "validate_certs": False,
+ }
+ )
+
+ def set_args_delete_na_sg_org_container(self):
+ return dict(
+ {
+ "state": "absent",
+ "name": "testbucket",
+ "api_url": "gmi.example.com",
+ "auth_token": "01234567-5678-9abc-78de-9fgabc123def",
+ "validate_certs": False,
+ }
+ )
+
+ @patch("ansible_collections.netapp.storagegrid.plugins.module_utils.netapp.SGRestAPI.send_request")
+ def test_module_fail_when_required_args_missing(self, mock_request):
+ """required arguments are reported as errors"""
+ mock_request.side_effect = [
+ SRR["version_114"],
+ ]
+ with pytest.raises(AnsibleFailJson) as exc:
+ set_module_args(self.set_default_args_fail_check())
+ org_container_module()
+ print("Info: test_module_fail_when_required_args_missing: %s" % exc.value.args[0]["msg"])
+
+ @patch("ansible_collections.netapp.storagegrid.plugins.module_utils.netapp.SGRestAPI.send_request")
+ def test_module_fail_when_required_args_present(self, mock_request):
+ """required arguments are reported as errors"""
+ mock_request.side_effect = [
+ SRR["version_114"],
+ ]
+ with pytest.raises(AnsibleExitJson) as exc:
+ set_module_args(self.set_default_args_pass_check())
+ org_container_module()
+ exit_json(changed=True, msg="Induced arguments check")
+ print("Info: test_module_fail_when_required_args_present: %s" % exc.value.args[0]["msg"])
+ assert exc.value.args[0]["changed"]
+
+ @patch("ansible_collections.netapp.storagegrid.plugins.module_utils.netapp.SGRestAPI.send_request")
+ def test_create_na_sg_org_container_pass(self, mock_request):
+ set_module_args(self.set_args_create_na_sg_org_container())
+ mock_request.side_effect = [
+ SRR["version_114"],
+ SRR["empty_good"], # get
+ SRR["org_container_record"], # post
+ SRR["end_of_sequence"],
+ ]
+ my_obj = org_container_module()
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ print("Info: test_create_na_sg_org_container_pass: %s" % repr(exc.value.args[0]))
+ assert exc.value.args[0]["changed"]
+
+ @patch("ansible_collections.netapp.storagegrid.plugins.module_utils.netapp.SGRestAPI.send_request")
+ def test_idempotent_create_na_sg_org_container_pass(self, mock_request):
+ set_module_args(self.set_args_create_na_sg_org_container())
+ mock_request.side_effect = [
+ SRR["version_114"],
+ SRR["org_containers"], # get
+ SRR["end_of_sequence"],
+ ]
+ my_obj = org_container_module()
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ print("Info: test_idempotent_create_na_sg_org_container_pass: %s" % repr(exc.value.args[0]))
+ assert not exc.value.args[0]["changed"]
+
+ @patch("ansible_collections.netapp.storagegrid.plugins.module_utils.netapp.SGRestAPI.send_request")
+ def test_update_na_sg_org_container_pass(self, mock_request):
+ args = self.set_args_create_na_sg_org_container()
+ args["compliance"] = {"auto_delete": False, "legal_hold": False}
+ set_module_args(args)
+ mock_request.side_effect = [
+ SRR["version_114"],
+ SRR["org_containers"], # get
+ SRR["org_container_record_update"], # put
+ SRR["end_of_sequence"],
+ ]
+ my_obj = org_container_module()
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ print("Info: test_update_na_sg_org_container_pass: %s" % repr(exc.value.args[0]))
+ assert exc.value.args[0]["changed"]
+
+ @patch("ansible_collections.netapp.storagegrid.plugins.module_utils.netapp.SGRestAPI.send_request")
+ def test_delete_na_sg_org_container_pass(self, mock_request):
+ set_module_args(self.set_args_delete_na_sg_org_container())
+ mock_request.side_effect = [
+ SRR["version_114"],
+ SRR["org_containers"], # get
+ SRR["delete_good"], # delete
+ SRR["end_of_sequence"],
+ ]
+ my_obj = org_container_module()
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ print("Info: test_delete_na_sg_org_container_pass: %s" % repr(exc.value.args[0]))
+ assert exc.value.args[0]["changed"]
+
+ @patch("ansible_collections.netapp.storagegrid.plugins.module_utils.netapp.SGRestAPI.send_request")
+ def test_module_fail_minimum_version_not_met_object_lock(self, mock_request):
+ args = self.set_args_create_na_sg_org_container()
+ args["s3_object_lock_enabled"] = True
+ set_module_args(args)
+ mock_request.side_effect = [
+ SRR["version_114"], # get
+ ]
+ with pytest.raises(AnsibleFailJson) as exc:
+ org_container_module()
+ print("Info: test_module_fail_minimum_version_not_met_object_lock: %s" % exc.value.args[0]["msg"])
+
+ @patch("ansible_collections.netapp.storagegrid.plugins.module_utils.netapp.SGRestAPI.send_request")
+ def test_create_na_sg_org_container_objectlock_global_compliance_fail(self, mock_request):
+ args = self.set_args_create_na_sg_org_container()
+ args["s3_object_lock_enabled"] = True
+ set_module_args(args)
+ mock_request.side_effect = [
+ SRR["version_116"],
+ SRR["empty_good"], # get
+ SRR["global_compliance_disabled"], # get
+ ]
+ my_obj = org_container_module()
+ with pytest.raises(AnsibleFailJson) as exc:
+ my_obj.apply()
+ print("Info: test_create_na_sg_org_container_objectlock_global_compliance_fail: %s" % repr(exc.value.args[0]))
+
+ @patch("ansible_collections.netapp.storagegrid.plugins.module_utils.netapp.SGRestAPI.send_request")
+ def test_create_na_sg_org_container_objectlock_pass(self, mock_request):
+ set_module_args(self.set_args_create_na_sg_org_container())
+ mock_request.side_effect = [
+ SRR["version_116"],
+ SRR["empty_good"], # get
+ SRR["global_compliance_enabled"], # get
+ SRR["org_container_objectlock_record"], # post
+ SRR["end_of_sequence"],
+ ]
+ my_obj = org_container_module()
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ print("Info: test_create_na_sg_org_container_pass: %s" % repr(exc.value.args[0]))
+ assert exc.value.args[0]["changed"]
+
+ @patch("ansible_collections.netapp.storagegrid.plugins.module_utils.netapp.SGRestAPI.send_request")
+ def test_module_fail_minimum_version_not_met_versioning(self, mock_request):
+ args = self.set_args_create_na_sg_org_container()
+ args["bucket_versioning_enabled"] = True
+ set_module_args(args)
+ mock_request.side_effect = [
+ SRR["version_114"], # get
+ ]
+ with pytest.raises(AnsibleFailJson) as exc:
+ org_container_module()
+ print("Info: test_module_fail_minimum_version_not_met_versioning: %s" % exc.value.args[0]["msg"])
+
+ @patch("ansible_collections.netapp.storagegrid.plugins.module_utils.netapp.SGRestAPI.send_request")
+ def test_create_na_sg_org_container_with_versioning_pass(self, mock_request):
+ args = self.set_args_create_na_sg_org_container()
+ args["bucket_versioning_enabled"] = True
+ set_module_args(args)
+ mock_request.side_effect = [
+ SRR["version_116"],
+ SRR["empty_good"], # get
+ SRR["org_container_record"], # post
+ SRR["org_container_versioning_enabled"], # post
+ SRR["end_of_sequence"],
+ ]
+ my_obj = org_container_module()
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ print("Info: test_create_na_sg_org_container_with_versioning_pass: %s" % repr(exc.value.args[0]))
+ assert exc.value.args[0]["changed"]
+
+ @patch("ansible_collections.netapp.storagegrid.plugins.module_utils.netapp.SGRestAPI.send_request")
+ def test_update_na_sg_org_container_enable_versioning_pass(self, mock_request):
+ args = self.set_args_create_na_sg_org_container()
+ args["bucket_versioning_enabled"] = True
+ set_module_args(args)
+ mock_request.side_effect = [
+ SRR["version_116"],
+ SRR["org_containers"], # get
+ SRR["org_container_versioning_disabled"], # get
+ SRR["org_container_versioning_enabled"], # put
+ SRR["end_of_sequence"],
+ ]
+ my_obj = org_container_module()
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ print("Info: test_update_na_sg_org_container_enable_versioning_pass: %s" % repr(exc.value.args[0]))
+ assert exc.value.args[0]["changed"]
diff --git a/ansible_collections/netapp/storagegrid/tests/unit/plugins/modules/test_na_sg_org_group.py b/ansible_collections/netapp/storagegrid/tests/unit/plugins/modules/test_na_sg_org_group.py
new file mode 100644
index 000000000..c229130c2
--- /dev/null
+++ b/ansible_collections/netapp/storagegrid/tests/unit/plugins/modules/test_na_sg_org_group.py
@@ -0,0 +1,403 @@
+# (c) 2020, NetApp, Inc
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+""" unit tests NetApp StorageGRID Org Group Ansible module: na_sg_org_group"""
+
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+import json
+import pytest
+import sys
+try:
+ from requests import Response
+except ImportError:
+ if sys.version_info < (2, 7):
+ pytestmark = pytest.mark.skip('Skipping Unit Tests on 2.6 as requests is not available')
+ else:
+ raise
+
+from ansible_collections.netapp.storagegrid.tests.unit.compat import unittest
+from ansible_collections.netapp.storagegrid.tests.unit.compat.mock import (
+ patch,
+ Mock,
+)
+from ansible.module_utils import basic
+from ansible.module_utils._text import to_bytes
+from ansible_collections.netapp.storagegrid.plugins.modules.na_sg_org_group import (
+ SgOrgGroup as org_group_module,
+)
+
+# REST API canned responses when mocking send_request
+SRR = {
+ # common responses
+ "empty_good": ({"data": []}, None),
+ "not_found": (
+ {"status": "error", "code": 404, "data": {}},
+ {"key": "error.404"},
+ ),
+ "end_of_sequence": (None, "Unexpected call to send_request"),
+ "generic_error": (None, "Expected error"),
+ "delete_good": ({"code": 204}, None),
+ "org_groups": (
+ {
+ "data": [
+ {
+ "displayName": "TestOrgGroup",
+ "uniqueName": "group/testorggroup",
+ "policies": {
+ "management": {
+ "manageAllContainers": True,
+ "manageEndpoints": True,
+ "manageOwnS3Credentials": True,
+ },
+ "s3": {
+ "Statement": [
+ {
+ "Effect": "Allow",
+ "Action": "s3:*",
+ "Resource": "arn:aws:s3:::*",
+ }
+ ]
+ },
+ },
+ "accountId": "12345678901234567890",
+ "id": "00000000-0000-0000-0000-000000000000",
+ "federated": False,
+ "groupURN": "urn:sgws:identity::12345678901234567890:group/testorggroup",
+ }
+ ]
+ },
+ None,
+ ),
+ "org_group_record": (
+ {
+ "data": {
+ "displayName": "TestOrgGroup",
+ "uniqueName": "group/testorggroup",
+ "policies": {
+ "management": {
+ "manageAllContainers": True,
+ "manageEndpoints": True,
+ "manageOwnS3Credentials": True,
+ },
+ "s3": {
+ "Statement": [
+ {
+ "Effect": "Allow",
+ "Action": "s3:*",
+ "Resource": "arn:aws:s3:::*",
+ }
+ ]
+ },
+ },
+ "accountId": "12345678901234567890",
+ "id": "00000000-0000-0000-0000-000000000000",
+ "federated": False,
+ "groupURN": "urn:sgws:identity::12345678901234567890:group/testorggroup",
+ }
+ },
+ None,
+ ),
+ "org_group_record_update": (
+ {
+ "data": {
+ "displayName": "TestOrgGroup",
+ "uniqueName": "group/testorggroup",
+ "policies": {
+ "management": {
+ "manageAllContainers": True,
+ "manageEndpoints": True,
+ "manageOwnS3Credentials": True,
+ # "rootAccess": False,
+ },
+ "s3": {
+ "Statement": [
+ {
+ "Effect": "Allow",
+ "Action": "s3:*",
+ "Resource": "arn:aws:s3:::mybucket/*",
+ }
+ ]
+ },
+ },
+ "accountId": "12345678901234567890",
+ "id": "00000000-0000-0000-0000-000000000000",
+ "federated": False,
+ "groupURN": "urn:sgws:identity::12345678901234567890:group/testorggroup",
+ }
+ },
+ None,
+ ),
+}
+
+
+def set_module_args(args):
+ """prepare arguments so that they will be picked up during module creation"""
+ args = json.dumps({"ANSIBLE_MODULE_ARGS": args})
+ basic._ANSIBLE_ARGS = to_bytes(args) # pylint: disable=protected-access
+
+
+class AnsibleExitJson(Exception):
+ """Exception class to be raised by module.exit_json and caught by the test case"""
+
+ pass
+
+
+class AnsibleFailJson(Exception):
+ """Exception class to be raised by module.fail_json and caught by the test case"""
+
+ pass
+
+
+def exit_json(*args, **kwargs): # pylint: disable=unused-argument
+ """function to patch over exit_json; package return data into an exception"""
+ if "changed" not in kwargs:
+ kwargs["changed"] = False
+ raise AnsibleExitJson(kwargs)
+
+
+def fail_json(*args, **kwargs): # pylint: disable=unused-argument
+ """function to patch over fail_json; package return data into an exception"""
+ kwargs["failed"] = True
+ raise AnsibleFailJson(kwargs)
+
+
+class TestMyModule(unittest.TestCase):
+ """ a group of related Unit Tests """
+
+ def setUp(self):
+ self.mock_module_helper = patch.multiple(
+ basic.AnsibleModule, exit_json=exit_json, fail_json=fail_json
+ )
+ self.mock_module_helper.start()
+ self.addCleanup(self.mock_module_helper.stop)
+
+ def set_default_args_fail_check(self):
+ return dict(
+ {
+ "display_name": "TestGroup",
+ "management_policy": {
+ "manage_all_containers": True,
+ "manage_endpoints": True,
+ "manage_own_s3_credentials": True,
+ "root_access": False,
+ },
+ "s3_policy": {
+ "Statement": [
+ {
+ "Effect": "Allow",
+ "Action": "s3:*",
+ "Resource": "arn:aws:s3:::*",
+ }
+ ]
+ },
+ "auth_token": "01234567-5678-9abc-78de-9fgabc123def",
+ "validate_certs": False,
+ }
+ )
+
+ def set_default_args_pass_check(self):
+ return dict(
+ {
+ "state": "present",
+ "display_name": "TestGroup",
+ "unique_name": "group/testgroup",
+ "management_policy": {
+ "manage_all_containers": True,
+ "manage_endpoints": True,
+ "manage_own_s3_credentials": True,
+ "root_access": False,
+ },
+ "s3_policy": {
+ "Statement": [
+ {
+ "Effect": "Allow",
+ "Action": "s3:*",
+ "Resource": "arn:aws:s3:::*",
+ }
+ ]
+ },
+ "api_url": "gmi.example.com",
+ "auth_token": "01234567-5678-9abc-78de-9fgabc123def",
+ "validate_certs": False,
+ }
+ )
+
+ def set_args_create_na_sg_org_group(self):
+ return dict(
+ {
+ "state": "present",
+ "display_name": "TestOrgGroup",
+ "unique_name": "group/testorggroup",
+ "management_policy": {
+ "manage_all_containers": True,
+ "manage_endpoints": True,
+ "manage_own_s3_credentials": True,
+ "root_access": False,
+ },
+ "s3_policy": {
+ "Statement": [
+ {
+ "Effect": "Allow",
+ "Action": "s3:*",
+ "Resource": "arn:aws:s3:::*",
+ }
+ ]
+ },
+ "api_url": "gmi.example.com",
+ "auth_token": "01234567-5678-9abc-78de-9fgabc123def",
+ "validate_certs": False,
+ }
+ )
+
+ def set_args_delete_na_sg_org_group(self):
+ return dict(
+ {
+ "state": "absent",
+ # "display_name": "TestOrgGroup",
+ "unique_name": "group/testorggroup",
+ # "management_policy": {
+ # "manage_all_containers": True,
+ # "manage_endpoints": True,
+ # "manage_own_s3_credentials": True,
+ # "root_access": False,
+ # },
+ # "s3_policy": {
+ # "Statement": [
+ # {
+ # "Effect": "Allow",
+ # "Action": "s3:*",
+ # "Resource": "arn:aws:s3:::*",
+ # }
+ # ]
+ # },
+ "api_url": "gmi.example.com",
+ "auth_token": "01234567-5678-9abc-78de-9fgabc123def",
+ "validate_certs": False,
+ }
+ )
+
+ def test_module_fail_when_required_args_missing(self):
+ """ required arguments are reported as errors """
+ with pytest.raises(AnsibleFailJson) as exc:
+ set_module_args(self.set_default_args_fail_check())
+ org_group_module()
+ print(
+ "Info: test_module_fail_when_required_args_missing: %s"
+ % exc.value.args[0]["msg"]
+ )
+
+ def test_module_fail_when_required_args_present(self):
+ """ required arguments are reported as errors """
+ with pytest.raises(AnsibleExitJson) as exc:
+ set_module_args(self.set_default_args_pass_check())
+ org_group_module()
+ exit_json(changed=True, msg="Induced arguments check")
+ print(
+ "Info: test_module_fail_when_required_args_present: %s"
+ % exc.value.args[0]["msg"]
+ )
+ assert exc.value.args[0]["changed"]
+
+ def test_module_fail_with_bad_unique_name(self):
+ """ error returned if unique_name doesn't start with group or federated_group """
+ with pytest.raises(AnsibleFailJson) as exc:
+ args = self.set_default_args_pass_check()
+ args["unique_name"] = "noprefixgroup"
+ set_module_args(args)
+ org_group_module()
+ print(
+ "Info: test_module_fail_with_bad_unique_name: %s"
+ % exc.value.args[0]["msg"]
+ )
+
+ @patch(
+ "ansible_collections.netapp.storagegrid.plugins.module_utils.netapp.SGRestAPI.send_request"
+ )
+ def test_create_na_sg_org_group_pass(self, mock_request):
+ set_module_args(self.set_args_create_na_sg_org_group())
+ my_obj = org_group_module()
+ mock_request.side_effect = [
+ SRR["not_found"], # get
+ SRR["org_group_record"], # post
+ SRR["end_of_sequence"],
+ ]
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ print(
+ "Info: test_create_na_sg_org_group_pass: %s"
+ % repr(exc.value.args[0])
+ )
+ assert exc.value.args[0]["changed"]
+
+ @patch(
+ "ansible_collections.netapp.storagegrid.plugins.module_utils.netapp.SGRestAPI.send_request"
+ )
+ def test_idempotent_create_na_sg_org_group_pass(self, mock_request):
+ set_module_args(self.set_args_create_na_sg_org_group())
+ my_obj = org_group_module()
+ mock_request.side_effect = [
+ SRR["org_group_record"], # get
+ SRR["end_of_sequence"],
+ ]
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ print(
+ "Info: test_idempotent_create_na_sg_org_group_pass: %s"
+ % repr(exc.value.args[0])
+ )
+ assert not exc.value.args[0]["changed"]
+
+ @patch(
+ "ansible_collections.netapp.storagegrid.plugins.module_utils.netapp.SGRestAPI.send_request"
+ )
+ def test_update_na_sg_org_group_pass(self, mock_request):
+ args = self.set_args_create_na_sg_org_group()
+ args["s3_policy"] = (
+ {
+ "Statement": [
+ {
+ "Effect": "Allow",
+ "Action": "s3:*",
+ "Resource": "arn:aws:s3:::mybucket/*",
+ }
+ ]
+ },
+ )
+
+ args["management_policy"]["manage_endpoints"] = False
+
+ set_module_args(args)
+ my_obj = org_group_module()
+ mock_request.side_effect = [
+ SRR["org_group_record"], # get
+ SRR["org_group_record_update"], # put
+ SRR["end_of_sequence"],
+ ]
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ print(
+ "Info: test_update_na_sg_org_group_pass: %s"
+ % repr(exc.value.args[0])
+ )
+ assert exc.value.args[0]["changed"]
+
+ @patch(
+ "ansible_collections.netapp.storagegrid.plugins.module_utils.netapp.SGRestAPI.send_request"
+ )
+ def test_delete_na_sg_org_group_pass(self, mock_request):
+ set_module_args(self.set_args_delete_na_sg_org_group())
+ my_obj = org_group_module()
+ mock_request.side_effect = [
+ SRR["org_group_record"], # get
+ SRR["delete_good"], # delete
+ SRR["end_of_sequence"],
+ ]
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ print(
+ "Info: test_delete_na_sg_org_group_pass: %s"
+ % repr(exc.value.args[0])
+ )
+ assert exc.value.args[0]["changed"]
diff --git a/ansible_collections/netapp/storagegrid/tests/unit/plugins/modules/test_na_sg_org_identity_federation.py b/ansible_collections/netapp/storagegrid/tests/unit/plugins/modules/test_na_sg_org_identity_federation.py
new file mode 100644
index 000000000..b02259005
--- /dev/null
+++ b/ansible_collections/netapp/storagegrid/tests/unit/plugins/modules/test_na_sg_org_identity_federation.py
@@ -0,0 +1,354 @@
+# (c) 2021, NetApp, Inc
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+""" unit tests NetApp StorageGRID Tenant Identity Federation Ansible module: na_sg_org_identity_federation"""
+
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+import json
+import pytest
+import sys
+try:
+ from requests import Response
+except ImportError:
+ if sys.version_info < (2, 7):
+ pytestmark = pytest.mark.skip('Skipping Unit Tests on 2.6 as requests is not available')
+ else:
+ raise
+
+from ansible_collections.netapp.storagegrid.tests.unit.compat import unittest
+from ansible_collections.netapp.storagegrid.tests.unit.compat.mock import (
+ patch,
+ Mock,
+)
+from ansible.module_utils import basic
+from ansible.module_utils._text import to_bytes
+from ansible_collections.netapp.storagegrid.plugins.modules.na_sg_org_identity_federation import (
+ SgOrgIdentityFederation as org_identity_federation_module,
+)
+
+# REST API canned responses when mocking send_request
+SRR = {
+ # common responses
+ "end_of_sequence": (None, "Unexpected call to send_request"),
+ "generic_error": (None, "Expected error"),
+ "check_mode_good": (None, None),
+ "identity_federation_unset": (
+ {
+ "data": {
+ "id": "09876543-abcd-4321-abcd-0987654321ab",
+ "disable": True,
+ "type": "",
+ "ldapServiceType": "",
+ "hostname": "",
+ "port": 0,
+ "username": "",
+ "password": None,
+ "baseGroupDn": "",
+ "baseUserDn": "",
+ "disableTLS": False,
+ "enableLDAPS": False,
+ "caCert": "",
+ }
+ },
+ None,
+ ),
+ "identity_federation": (
+ {
+ "data": {
+ "id": "09876543-abcd-4321-abcd-0987654321ab",
+ "disable": False,
+ "type": "ldap",
+ "ldapServiceType": "Active Directory",
+ "hostname": "ad.example.com",
+ "port": 389,
+ "username": "binduser",
+ "password": "********",
+ "baseGroupDn": "DC=example,DC=com",
+ "baseUserDn": "DC=example,DC=com",
+ "disableTLS": True,
+ "enableLDAPS": False,
+ "caCert": "",
+ }
+ },
+ None,
+ ),
+ "identity_federation_tls": (
+ {
+ "data": {
+ "id": "09876543-abcd-4321-abcd-0987654321ab",
+ "disable": False,
+ "type": "ldap",
+ "ldapServiceType": "Active Directory",
+ "hostname": "ad.example.com",
+ "port": 636,
+ "username": "binduser",
+ "password": "********",
+ "baseGroupDn": "DC=example,DC=com",
+ "baseUserDn": "DC=example,DC=com",
+ "disableTLS": False,
+ "enableLDAPS": True,
+ "caCert": (
+ "-----BEGIN CERTIFICATE-----\n"
+ "MIIF+DCCBOCgAwIBAgITRwAAAAIg5KzMrJo+kQAAAAAAAjANBgkqhkiG9w0BAQUF\n"
+ "ADBlMRIwEAYKCZImiZPyLGQBGRYCYXUxFjAUBgoJkiaJk/IsZAEZFgZuZXRhcHAx\n"
+ "FjAUBgoJkiaJk/IsZAEZFgZhdXNuZ3MxHzAdBgNVBAMTFmF1c25ncy1NRUxOR1NE\n"
+ "QzAxLUNBLTEwHhcNMjEwMjExMDkzMTIwWhcNMjMwMjExMDk0MTIwWjAAMIIBIjAN\n"
+ "BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAt2xPi4FS4Uc37KrDLEXXUoc4lhhT\n"
+ "uQmMnLc0PYZCIpzYOaosFIeGqco3woiC7wSZJ2whKE4RDcxxgE+azuGiSWVjIxIL\n"
+ "AimmcDhFid/T3KRN5jmkjBzUKuPBYzZBFih8iU9056rqgN7eMKQYjRwPeV0+AeiB\n"
+ "irw46OgkwVQu3shEUtXxZPP2Mb6Md23+4vSmcElUcW28Opt2q/M5fs7DNomG3eaG\n"
+ "-----END CERTIFICATE-----\n"
+ ),
+ }
+ },
+ None,
+ ),
+ "identity_federation_disable": (
+ {
+ "data": {
+ "id": "09876543-abcd-4321-abcd-0987654321ab",
+ "disable": True,
+ "type": "ldap",
+ "ldapServiceType": "Active Directory",
+ "hostname": "ad.example.com",
+ "port": 389,
+ "username": "binduser",
+ "password": "********",
+ "baseGroupDn": "DC=example,DC=com",
+ "baseUserDn": "DC=example,DC=com",
+ "disableTLS": True,
+ "enableLDAPS": False,
+ "caCert": "",
+ }
+ },
+ None,
+ ),
+}
+
+
+def set_module_args(args):
+ """prepare arguments so that they will be picked up during module creation"""
+ args = json.dumps({"ANSIBLE_MODULE_ARGS": args})
+ basic._ANSIBLE_ARGS = to_bytes(args) # pylint: disable=protected-access
+
+
+class AnsibleExitJson(Exception):
+ """Exception class to be raised by module.exit_json and caught by the test case"""
+
+ pass
+
+
+class AnsibleFailJson(Exception):
+ """Exception class to be raised by module.fail_json and caught by the test case"""
+
+ pass
+
+
+def exit_json(*args, **kwargs): # pylint: disable=unused-argument
+ """function to patch over exit_json; package return data into an exception"""
+ if "changed" not in kwargs:
+ kwargs["changed"] = False
+ raise AnsibleExitJson(kwargs)
+
+
+def fail_json(*args, **kwargs): # pylint: disable=unused-argument
+ """function to patch over fail_json; package return data into an exception"""
+ kwargs["failed"] = True
+ raise AnsibleFailJson(kwargs)
+
+
+class TestMyModule(unittest.TestCase):
+ """ a group of related Unit Tests """
+
+ def setUp(self):
+ self.mock_module_helper = patch.multiple(basic.AnsibleModule, exit_json=exit_json, fail_json=fail_json)
+ self.mock_module_helper.start()
+ self.addCleanup(self.mock_module_helper.stop)
+
+ def set_default_args_fail_check(self):
+ return dict(
+ {
+ "ldap_service_type": "Active Directory",
+ "hostname": "ad.example.com",
+ "port": 389,
+ "username": "binduser",
+ "password": "bindpass",
+ "base_group_dn": "DC=example,DC=com",
+ "base_user_dn": "DC=example,DC=com",
+ "tls": "Disabled",
+ "auth_token": "01234567-5678-9abc-78de-9fgabc123def",
+ "validate_certs": False,
+ }
+ )
+
+ def set_default_args_pass_check(self):
+ return dict(
+ {
+ "ldap_service_type": "Active Directory",
+ "hostname": "ad.example.com",
+ "port": 389,
+ "username": "binduser",
+ "password": "bindpass",
+ "base_group_dn": "DC=example,DC=com",
+ "base_user_dn": "DC=example,DC=com",
+ "tls": "Disabled",
+ "state": "present",
+ "api_url": "gmi.example.com",
+ "auth_token": "01234567-5678-9abc-78de-9fgabc123def",
+ "validate_certs": False,
+ }
+ )
+
+ def set_args_set_na_sg_org_identity_federation(self):
+ return dict(
+ {
+ "ldap_service_type": "Active Directory",
+ "hostname": "ad.example.com",
+ "port": 389,
+ "username": "binduser",
+ "password": "bindpass",
+ "base_group_dn": "DC=example,DC=com",
+ "base_user_dn": "DC=example,DC=com",
+ "tls": "Disabled",
+ "state": "present",
+ "api_url": "gmi.example.com",
+ "auth_token": "01234567-5678-9abc-78de-9fgabc123def",
+ "validate_certs": False,
+ }
+ )
+
+ def set_args_set_na_sg_org_identity_federation_tls(self):
+ return dict(
+ {
+ "ldap_service_type": "Active Directory",
+ "hostname": "ad.example.com",
+ "port": 636,
+ "username": "binduser",
+ "password": "bindpass",
+ "base_group_dn": "DC=example,DC=com",
+ "base_user_dn": "DC=example,DC=com",
+ "tls": "LDAPS",
+ "ca_cert": (
+ "-----BEGIN CERTIFICATE-----\n"
+ "MIIF+DCCBOCgAwIBAgITRwAAAAIg5KzMrJo+kQAAAAAAAjANBgkqhkiG9w0BAQUF\n"
+ "ADBlMRIwEAYKCZImiZPyLGQBGRYCYXUxFjAUBgoJkiaJk/IsZAEZFgZuZXRhcHAx\n"
+ "FjAUBgoJkiaJk/IsZAEZFgZhdXNuZ3MxHzAdBgNVBAMTFmF1c25ncy1NRUxOR1NE\n"
+ "QzAxLUNBLTEwHhcNMjEwMjExMDkzMTIwWhcNMjMwMjExMDk0MTIwWjAAMIIBIjAN\n"
+ "BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAt2xPi4FS4Uc37KrDLEXXUoc4lhhT\n"
+ "uQmMnLc0PYZCIpzYOaosFIeGqco3woiC7wSZJ2whKE4RDcxxgE+azuGiSWVjIxIL\n"
+ "AimmcDhFid/T3KRN5jmkjBzUKuPBYzZBFih8iU9056rqgN7eMKQYjRwPeV0+AeiB\n"
+ "irw46OgkwVQu3shEUtXxZPP2Mb6Md23+4vSmcElUcW28Opt2q/M5fs7DNomG3eaG\n"
+ "-----END CERTIFICATE-----\n"
+ ),
+ "state": "present",
+ "api_url": "gmi.example.com",
+ "auth_token": "01234567-5678-9abc-78de-9fgabc123def",
+ "validate_certs": False,
+ }
+ )
+
+ def set_args_remove_na_sg_org_identity_federation(self):
+ return dict(
+ {
+ "ldap_service_type": "Active Directory",
+ "hostname": "ad.example.com",
+ "state": "absent",
+ "api_url": "gmi.example.com",
+ "auth_token": "01234567-5678-9abc-78de-9fgabc123def",
+ "validate_certs": False,
+ }
+ )
+
+ def test_module_fail_when_required_args_missing(self):
+ """ required arguments are reported as errors """
+ with pytest.raises(AnsibleFailJson) as exc:
+ set_module_args(self.set_default_args_fail_check())
+ org_identity_federation_module()
+ print("Info: test_module_fail_when_required_args_missing: %s" % exc.value.args[0]["msg"])
+
+ def test_module_fail_when_required_args_present(self):
+ """ required arguments are reported as errors """
+ with pytest.raises(AnsibleExitJson) as exc:
+ set_module_args(self.set_default_args_pass_check())
+ org_identity_federation_module()
+ exit_json(changed=True, msg="Induced arguments check")
+ print("Info: test_module_fail_when_required_args_present: %s" % exc.value.args[0]["msg"])
+ assert exc.value.args[0]["changed"]
+
+ @patch("ansible_collections.netapp.storagegrid.plugins.module_utils.netapp.SGRestAPI.send_request")
+ def test_set_na_sg_org_identity_federation_pass(self, mock_request):
+ set_module_args(self.set_args_set_na_sg_org_identity_federation())
+ my_obj = org_identity_federation_module()
+ mock_request.side_effect = [
+ SRR["identity_federation_unset"], # get
+ SRR["identity_federation"], # post
+ SRR["end_of_sequence"],
+ ]
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ print("Info: test_set_na_sg_org_identity_federation_pass: %s" % repr(exc.value.args[0]))
+ assert exc.value.args[0]["changed"]
+
+ @patch("ansible_collections.netapp.storagegrid.plugins.module_utils.netapp.SGRestAPI.send_request")
+ def test_idempotent_set_na_sg_org_identity_federation_pass(self, mock_request):
+ args = self.set_args_set_na_sg_org_identity_federation()
+ # remove password
+ del args["password"]
+ set_module_args(args)
+ my_obj = org_identity_federation_module()
+ mock_request.side_effect = [
+ SRR["identity_federation"], # get
+ SRR["end_of_sequence"],
+ ]
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ print("Info: test_idempotent_set_na_sg_org_identity_federation_pass: %s" % repr(exc.value.args[0]))
+ assert not exc.value.args[0]["changed"]
+
+ @patch("ansible_collections.netapp.storagegrid.plugins.module_utils.netapp.SGRestAPI.send_request")
+ def test_set_na_sg_org_identity_federation_tls_pass(self, mock_request):
+ set_module_args(self.set_args_set_na_sg_org_identity_federation_tls())
+ my_obj = org_identity_federation_module()
+ mock_request.side_effect = [
+ SRR["identity_federation_unset"], # get
+ SRR["identity_federation_tls"], # post
+ SRR["end_of_sequence"],
+ ]
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ print("Info: test_set_na_sg_org_identity_federation_pass: %s" % repr(exc.value.args[0]))
+ assert exc.value.args[0]["changed"]
+
+ @patch("ansible_collections.netapp.storagegrid.plugins.module_utils.netapp.SGRestAPI.send_request")
+ def test_remove_na_sg_org_identity_federation_pass(self, mock_request):
+ set_module_args(self.set_args_remove_na_sg_org_identity_federation())
+ my_obj = org_identity_federation_module()
+ mock_request.side_effect = [
+ SRR["identity_federation"], # get
+ SRR["identity_federation_disable"], # post
+ SRR["end_of_sequence"],
+ ]
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ print("Info: test_remove_na_sg_org_identity_federation_pass: %s" % repr(exc.value.args[0]))
+ assert exc.value.args[0]["changed"]
+
+ # test check mode
+
+ @patch("ansible_collections.netapp.storagegrid.plugins.module_utils.netapp.SGRestAPI.send_request")
+ def test_check_mode_na_sg_org_identity_federation_pass(self, mock_request):
+ set_module_args(self.set_args_set_na_sg_org_identity_federation())
+ my_obj = org_identity_federation_module()
+ my_obj.module.check_mode = True
+ mock_request.side_effect = [
+ SRR["identity_federation_unset"], # get
+ SRR["check_mode_good"], # post
+ SRR["end_of_sequence"],
+ ]
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ print("Info: test_check_mode_na_sg_org_identity_federation_pass: %s" % repr(exc.value.args[0]))
+ assert exc.value.args[0]["changed"]
+ assert exc.value.args[0]["msg"] == "Connection test successful"
diff --git a/ansible_collections/netapp/storagegrid/tests/unit/plugins/modules/test_na_sg_org_info.py b/ansible_collections/netapp/storagegrid/tests/unit/plugins/modules/test_na_sg_org_info.py
new file mode 100644
index 000000000..e24c7cd46
--- /dev/null
+++ b/ansible_collections/netapp/storagegrid/tests/unit/plugins/modules/test_na_sg_org_info.py
@@ -0,0 +1,263 @@
+# (c) 2020, NetApp, Inc
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+''' Unit Tests NetApp StorageGRID Org Ansible module: na_sg_org_info '''
+
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+import json
+import pytest
+
+from ansible.module_utils import basic
+from ansible.module_utils._text import to_bytes
+from ansible_collections.netapp.storagegrid.tests.unit.compat import unittest
+from ansible_collections.netapp.storagegrid.tests.unit.compat.mock import patch
+
+from ansible_collections.netapp.storagegrid.plugins.modules.na_sg_org_info \
+ import NetAppSgGatherInfo as sg_org_info_module
+
+# REST API canned responses when mocking send_request
+SRR = {
+ # common responses
+ 'empty_good': ({'data': []}, None),
+ 'end_of_sequence': (None, 'Unexpected call to send_request'),
+ 'generic_error': (None, 'Expected error'),
+ 'org_compliance_global': ({'data': {}}, None),
+ 'org_config': ({'data': {}}, None),
+ 'org_config_product_version': ({'data': {}}, None),
+ 'org_containers': ({'data': {}}, None),
+ 'org_deactivated_features': ({'data': {}}, None),
+ 'org_endpoints': ({'data': []}, None),
+ 'org_groups': ({'data': []}, None),
+ 'org_identity_source': ({'data': {}}, None),
+ 'org_regions': ({'data': []}, None),
+ 'org_users_current_user_s3_access_keys': ({'data': []}, None),
+ 'org_usage': ({'data': {}}, None),
+ 'org_users': (
+ {
+ 'data': [
+ {
+ 'accountId': '99846664116007910793',
+ 'disable': False,
+ 'federated': False,
+ 'fullName': 'Root',
+ 'id': '00000000-0000-0000-0000-000000000000',
+ 'memberOf': None,
+ 'uniqueName': 'root',
+ 'userURN': 'urn:sgws:identity::99846664116007910793:root'
+ },
+ ]
+ },
+ None
+ ),
+ 'org_users_root': (
+ {
+ 'data': {
+ 'accountId': '99846664116007910793',
+ 'disable': False,
+ 'federated': False,
+ 'fullName': 'Root',
+ 'id': '00000000-0000-0000-0000-000000000000',
+ 'memberOf': None,
+ 'uniqueName': 'root',
+ 'userURN': 'urn:sgws:identity::99846664116007910793:root'
+ },
+ },
+ None
+ ),
+ 'versions': ({'data': [2, 3]}, None),
+}
+
+
+def set_module_args(args):
+ ''' Prepare arguments so that they will be picked up during module creation '''
+ args = json.dumps({'ANSIBLE_MODULE_ARGS': args})
+ basic._ANSIBLE_ARGS = to_bytes(args) # pylint: disable=protected-access
+
+
+class AnsibleExitJson(Exception):
+ ''' Exception class to be raised by module.exit_json and caught by the test case '''
+ pass
+
+
+class AnsibleFailJson(Exception):
+ ''' Exception class to be raised by module.fail_json and caught by the test case '''
+ pass
+
+
+def exit_json(*args, **kwargs): # pylint: disable=unused-argument
+ ''' Function to patch over exit_json; package return data into an exception '''
+ if 'changed' not in kwargs:
+ kwargs['changed'] = False
+ raise AnsibleExitJson(kwargs)
+
+
+def fail_json(*args, **kwargs): # pylint: disable=unused-argument
+ ''' Function to patch over fail_json; package return data into an exception '''
+ kwargs['failed'] = True
+ raise AnsibleFailJson(kwargs)
+
+
+class TestMyModule(unittest.TestCase):
+ ''' A group of related Unit Tests '''
+
+ def setUp(self):
+ self.mock_module_helper = patch.multiple(basic.AnsibleModule,
+ exit_json=exit_json,
+ fail_json=fail_json)
+ self.mock_module_helper.start()
+ self.addCleanup(self.mock_module_helper.stop)
+
+ def set_default_args_fail_check(self):
+ return dict(
+ {
+ 'api_url': 'sgmi.example.com',
+ }
+ )
+
+ def set_default_args_pass_check(self):
+ return dict(
+ {
+ 'api_url': 'sgmi.example.com',
+ 'auth_token': '01234567-5678-9abc-78de-9fgabc123def',
+ }
+ )
+
+ def set_default_optional_args_pass_check(self):
+ return dict(
+ {
+ 'api_url': 'sgmi.example.com',
+ 'auth_token': '01234567-5678-9abc-78de-9fgabc123def',
+ 'validate_certs': False,
+ 'gather_subset': ['all'],
+ 'parameters': {'limit': 5},
+ }
+ )
+
+ def set_args_run_sg_gather_facts_for_all_info(self):
+ return dict({
+ 'api_url': 'sgmi.example.com',
+ 'auth_token': '01234567-5678-9abc-78de-9fgabc123def',
+ 'validate_certs': False,
+ })
+
+ def set_args_run_sg_gather_facts_for_org_users_info(self):
+ return dict({
+ 'api_url': 'sgmi.example.com',
+ 'auth_token': '01234567-5678-9abc-78de-9fgabc123def',
+ 'validate_certs': False,
+ 'gather_subset': ['org_users_info'],
+ })
+
+ def set_args_run_sg_gather_facts_for_org_users_and_org_users_root_info(self):
+ return dict({
+ 'api_url': 'sgmi.example.com',
+ 'auth_token': '01234567-5678-9abc-78de-9fgabc123def',
+ 'validate_certs': False,
+ 'gather_subset': ['org_users_info', 'org/users/root'],
+ })
+
+ def test_module_fail_when_required_args_missing(self):
+ ''' required arguments are reported as errors '''
+ with pytest.raises(AnsibleFailJson) as exc:
+ set_module_args(self.set_default_args_fail_check())
+ sg_org_info_module()
+ print(
+ 'Info: test_module_fail_when_required_args_missing: %s'
+ % exc.value.args[0]['msg']
+ )
+
+ def test_module_pass_when_required_args_present(self):
+ ''' required arguments are reported as errors '''
+ with pytest.raises(AnsibleExitJson) as exc:
+ set_module_args(self.set_default_args_pass_check())
+ sg_org_info_module()
+ exit_json(changed=True, msg='Induced arguments check')
+ print(
+ 'Info: test_module_pass_when_required_args_present: %s'
+ % exc.value.args[0]['msg']
+ )
+ assert exc.value.args[0]['changed']
+
+ def test_module_pass_when_optional_args_present(self):
+ ''' Optional arguments are reported as pass '''
+ with pytest.raises(AnsibleExitJson) as exc:
+ set_module_args(self.set_default_optional_args_pass_check())
+ sg_org_info_module()
+ exit_json(changed=True, msg='Induced arguments check')
+ print(
+ 'Info: test_module_pass_when_optional_args_present: %s'
+ % exc.value.args[0]['msg']
+ )
+ assert exc.value.args[0]['changed']
+
+ @patch('ansible_collections.netapp.storagegrid.plugins.module_utils.netapp.SGRestAPI.send_request')
+ def test_run_sg_gather_facts_for_all_info_pass(self, mock_request):
+ set_module_args(self.set_args_run_sg_gather_facts_for_all_info())
+ my_obj = sg_org_info_module()
+ gather_subset = [
+ 'org/compliance-global',
+ 'org/config',
+ 'org/config/product-version',
+ 'org/containers',
+ 'org/deactivated-features',
+ 'org/endpoints',
+ 'org/groups',
+ 'org/identity-source',
+ 'org/regions',
+ 'org/users/current-user/s3-access-keys',
+ 'org/usage',
+ 'org/users',
+ 'org/users/root',
+ 'versions',
+ ]
+ mock_request.side_effect = [
+ SRR['org_compliance_global'],
+ SRR['org_config'],
+ SRR['org_config_product_version'],
+ SRR['org_containers'],
+ SRR['org_deactivated_features'],
+ SRR['org_endpoints'],
+ SRR['org_groups'],
+ SRR['org_identity_source'],
+ SRR['org_regions'],
+ SRR['org_users_current_user_s3_access_keys'],
+ SRR['org_usage'],
+ SRR['org_users'],
+ SRR['org_users_root'],
+ SRR['versions'],
+ SRR['end_of_sequence'],
+ ]
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ print('Info: test_run_sg_gather_facts_for_all_info_pass: %s' % repr(exc.value.args))
+ assert set(exc.value.args[0]['sg_info']) == set(gather_subset)
+
+ @patch('ansible_collections.netapp.storagegrid.plugins.module_utils.netapp.SGRestAPI.send_request')
+ def test_run_sg_gather_facts_for_org_users_info_pass(self, mock_request):
+ set_module_args(self.set_args_run_sg_gather_facts_for_org_users_info())
+ my_obj = sg_org_info_module()
+ gather_subset = ['org/users']
+ mock_request.side_effect = [
+ SRR['org_users'],
+ SRR['end_of_sequence'],
+ ]
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ print('Info: test_run_sg_gather_facts_for_org_users_info_pass: %s' % repr(exc.value.args))
+ assert set(exc.value.args[0]['sg_info']) == set(gather_subset)
+
+ @patch('ansible_collections.netapp.storagegrid.plugins.module_utils.netapp.SGRestAPI.send_request')
+ def test_run_sg_gather_facts_for_org_users_and_org_users_root_info_pass(self, mock_request):
+ set_module_args(self.set_args_run_sg_gather_facts_for_org_users_and_org_users_root_info())
+ my_obj = sg_org_info_module()
+ gather_subset = ['org/users', 'org/users/root']
+ mock_request.side_effect = [
+ SRR['org_users'],
+ SRR['org_users_root'],
+ SRR['end_of_sequence'],
+ ]
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ print('Info: test_run_sg_gather_facts_for_org_users_and_org_users_root_info_pass: %s' % repr(exc.value.args))
+ assert set(exc.value.args[0]['sg_info']) == set(gather_subset)
diff --git a/ansible_collections/netapp/storagegrid/tests/unit/plugins/modules/test_na_sg_org_user.py b/ansible_collections/netapp/storagegrid/tests/unit/plugins/modules/test_na_sg_org_user.py
new file mode 100644
index 000000000..8fcec6734
--- /dev/null
+++ b/ansible_collections/netapp/storagegrid/tests/unit/plugins/modules/test_na_sg_org_user.py
@@ -0,0 +1,476 @@
+# (c) 2020, NetApp, Inc
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+""" unit tests NetApp StorageGRID Org Group Ansible module: na_sg_org_user"""
+
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+import json
+import pytest
+import sys
+try:
+ from requests import Response
+except ImportError:
+ if sys.version_info < (2, 7):
+ pytestmark = pytest.mark.skip('Skipping Unit Tests on 2.6 as requests is not available')
+ else:
+ raise
+
+from ansible_collections.netapp.storagegrid.tests.unit.compat import unittest
+from ansible_collections.netapp.storagegrid.tests.unit.compat.mock import (
+ patch,
+ Mock,
+)
+from ansible.module_utils import basic
+from ansible.module_utils._text import to_bytes
+from ansible_collections.netapp.storagegrid.plugins.modules.na_sg_org_user import (
+ SgOrgUser as org_user_module,
+)
+
+# REST API canned responses when mocking send_request
+SRR = {
+ # common responses
+ "empty_good": ({"data": []}, None),
+ "not_found": ({"status": "error", "code": 404, "data": {}}, {"key": "error.404"},),
+ "end_of_sequence": (None, "Unexpected call to send_request"),
+ "generic_error": (None, "Expected error"),
+ "delete_good": ({"code": 204}, None),
+ "pw_change_good": ({"code": 204}, None),
+ "org_groups": (
+ {
+ "data": [
+ {
+ "displayName": "TestOrgGroup1",
+ "uniqueName": "group/testorggroup1",
+ "accountId": "12345678901234567890",
+ "id": "12345678-abcd-1234-abcd-1234567890ab",
+ "federated": False,
+ "groupURN": "urn:sgws:identity::12345678901234567890:group/testorggroup1",
+ },
+ {
+ "displayName": "TestOrgGroup2",
+ "uniqueName": "group/testorggroup2",
+ "accountId": "12345678901234567890",
+ "id": "87654321-abcd-1234-cdef-1234567890ab",
+ "federated": False,
+ "groupURN": "urn:sgws:identity::12345678901234567890:group/testorggroup2",
+ },
+ ]
+ },
+ None,
+ ),
+ "org_users": (
+ {
+ "data": [
+ {
+ "id": "09876543-abcd-4321-abcd-0987654321ab",
+ "accountId": "12345678901234567890",
+ "fullName": "testorguser",
+ "uniqueName": "user/ansible-sg-demo-user1",
+ "userURN": "urn:sgws:identity::12345678901234567890:user/testorguser",
+ "federated": False,
+ "memberOf": ["12345678-abcd-1234-abcd-1234567890ab"],
+ "disable": False,
+ }
+ ]
+ },
+ None,
+ ),
+ "org_user_record_no_group": (
+ {
+ "data": {
+ "id": "09876543-abcd-4321-abcd-0987654321ab",
+ "accountId": "12345678901234567890",
+ "fullName": "testorguser",
+ "uniqueName": "user/ansible-sg-demo-user1",
+ "userURN": "urn:sgws:identity::12345678901234567890:user/testorguser",
+ "federated": False,
+ "disable": False,
+ }
+ },
+ None,
+ ),
+ "org_user_record": (
+ {
+ "data": {
+ "id": "09876543-abcd-4321-abcd-0987654321ab",
+ "accountId": "12345678901234567890",
+ "fullName": "testorguser",
+ "uniqueName": "user/ansible-sg-demo-user1",
+ "userURN": "urn:sgws:identity::12345678901234567890:user/testorguser",
+ "federated": False,
+ "memberOf": ["12345678-abcd-1234-abcd-1234567890ab"],
+ "disable": False,
+ }
+ },
+ None,
+ ),
+ "org_user_record_update": (
+ {
+ "data": {
+ "id": "09876543-abcd-4321-abcd-0987654321ab",
+ "accountId": "12345678901234567890",
+ "fullName": "testorguser",
+ "uniqueName": "user/ansible-sg-demo-user1",
+ "userURN": "urn:sgws:identity::12345678901234567890:user/testorguser",
+ "federated": False,
+ "memberOf": [
+ "12345678-abcd-1234-abcd-1234567890ab",
+ "87654321-abcd-1234-cdef-1234567890ab",
+ ],
+ "disable": False,
+ }
+ },
+ None,
+ ),
+}
+
+
+def set_module_args(args):
+ """prepare arguments so that they will be picked up during module creation"""
+ args = json.dumps({"ANSIBLE_MODULE_ARGS": args})
+ basic._ANSIBLE_ARGS = to_bytes(args) # pylint: disable=protected-access
+
+
+class AnsibleExitJson(Exception):
+ """Exception class to be raised by module.exit_json and caught by the test case"""
+
+ pass
+
+
+class AnsibleFailJson(Exception):
+ """Exception class to be raised by module.fail_json and caught by the test case"""
+
+ pass
+
+
+def exit_json(*args, **kwargs): # pylint: disable=unused-argument
+ """function to patch over exit_json; package return data into an exception"""
+ if "changed" not in kwargs:
+ kwargs["changed"] = False
+ raise AnsibleExitJson(kwargs)
+
+
+def fail_json(*args, **kwargs): # pylint: disable=unused-argument
+ """function to patch over fail_json; package return data into an exception"""
+ kwargs["failed"] = True
+ raise AnsibleFailJson(kwargs)
+
+
+class TestMyModule(unittest.TestCase):
+ """ a group of related Unit Tests """
+
+ def setUp(self):
+ self.mock_module_helper = patch.multiple(
+ basic.AnsibleModule, exit_json=exit_json, fail_json=fail_json
+ )
+ self.mock_module_helper.start()
+ self.addCleanup(self.mock_module_helper.stop)
+
+ def set_default_args_fail_check(self):
+ return dict(
+ {
+ "full_name": "TestUser",
+ "api_url": "gmi.example.com",
+ "auth_token": "01234567-5678-9abc-78de-9fgabc123def",
+ "validate_certs": False,
+ }
+ )
+
+ def set_default_args_pass_check(self):
+ return dict(
+ {
+ "state": "present",
+ "full_name": "TestUser",
+ "unique_name": "user/testuser",
+ "api_url": "gmi.example.com",
+ "auth_token": "01234567-5678-9abc-78de-9fgabc123def",
+ "validate_certs": False,
+ }
+ )
+
+ def set_args_create_na_sg_org_user_no_group(self):
+ return dict(
+ {
+ "state": "present",
+ "full_name": "TestUser",
+ "unique_name": "user/testuser",
+ "api_url": "gmi.example.com",
+ "auth_token": "01234567-5678-9abc-78de-9fgabc123def",
+ "validate_certs": False,
+ }
+ )
+
+ def set_args_create_na_sg_org_user(self):
+ return dict(
+ {
+ "state": "present",
+ "full_name": "TestUser",
+ "unique_name": "user/testuser",
+ "member_of": ["group/testorggroup1"],
+ "api_url": "gmi.example.com",
+ "auth_token": "01234567-5678-9abc-78de-9fgabc123def",
+ "validate_certs": False,
+ }
+ )
+
+ def set_args_delete_na_sg_org_user(self):
+ return dict(
+ {
+ "state": "absent",
+ # "full_name": "TestUser",
+ "unique_name": "user/testuser",
+ # "member_of": ["group/testorggroup1"],
+ "api_url": "gmi.example.com",
+ "auth_token": "01234567-5678-9abc-78de-9fgabc123def",
+ "validate_certs": False,
+ }
+ )
+
+ def test_module_fail_when_required_args_missing(self):
+ """ required arguments are reported as errors """
+ with pytest.raises(AnsibleFailJson) as exc:
+ set_module_args(self.set_default_args_fail_check())
+ org_user_module()
+ print(
+ "Info: test_module_fail_when_required_args_missing: %s"
+ % exc.value.args[0]["msg"]
+ )
+
+ def test_module_fail_when_required_args_present(self):
+ """ required arguments are reported as errors """
+ with pytest.raises(AnsibleExitJson) as exc:
+ set_module_args(self.set_default_args_pass_check())
+ org_user_module()
+ exit_json(changed=True, msg="Induced arguments check")
+ print(
+ "Info: test_module_fail_when_required_args_present: %s"
+ % exc.value.args[0]["msg"]
+ )
+ assert exc.value.args[0]["changed"]
+
+ def test_module_fail_with_bad_unique_name(self):
+ """ error returned if unique_name doesn't start with user or federated_user """
+ with pytest.raises(AnsibleFailJson) as exc:
+ args = self.set_default_args_pass_check()
+ args["unique_name"] = "noprefixuser"
+ set_module_args(args)
+ org_user_module()
+ print(
+ "Info: test_module_fail_with_bad_unique_name: %s" % exc.value.args[0]["msg"]
+ )
+
+ def set_args_create_na_sg_org_user_with_password(self):
+ return dict(
+ {
+ "state": "present",
+ "full_name": "TestUser",
+ "unique_name": "user/testuser",
+ "member_of": ["group/testorggroup1"],
+ "password": "netapp123",
+ "api_url": "gmi.example.com",
+ "auth_token": "01234567-5678-9abc-78de-9fgabc123def",
+ "validate_certs": False,
+ }
+ )
+
+ @patch(
+ "ansible_collections.netapp.storagegrid.plugins.module_utils.netapp.SGRestAPI.send_request"
+ )
+ def test_create_na_sg_org_user_no_group_pass(self, mock_request):
+ set_module_args(self.set_args_create_na_sg_org_user_no_group())
+ my_obj = org_user_module()
+ mock_request.side_effect = [
+ SRR["not_found"], # get
+ SRR["org_user_record_no_group"], # post
+ SRR["end_of_sequence"],
+ ]
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ print(
+ "Info: test_create_na_sg_org_user_no_group_pass: %s"
+ % repr(exc.value.args[0])
+ )
+ assert exc.value.args[0]["changed"]
+
+ @patch(
+ "ansible_collections.netapp.storagegrid.plugins.module_utils.netapp.SGRestAPI.send_request"
+ )
+ def test_create_na_sg_org_user_pass(self, mock_request):
+ set_module_args(self.set_args_create_na_sg_org_user())
+ my_obj = org_user_module()
+ mock_request.side_effect = [
+ SRR["not_found"], # get
+ SRR["org_groups"], # get
+ SRR["org_user_record"], # post
+ SRR["end_of_sequence"],
+ ]
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ print("Info: test_create_na_sg_org_user_pass: %s" % repr(exc.value.args[0]))
+ assert exc.value.args[0]["changed"]
+
+ @patch(
+ "ansible_collections.netapp.storagegrid.plugins.module_utils.netapp.SGRestAPI.send_request"
+ )
+ def test_idempotent_create_na_sg_org_user_pass(self, mock_request):
+ set_module_args(self.set_args_create_na_sg_org_user())
+ my_obj = org_user_module()
+ mock_request.side_effect = [
+ SRR["org_user_record"], # get
+ SRR["org_groups"], # get
+ SRR["end_of_sequence"],
+ ]
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ print(
+ "Info: test_idempotent_create_na_sg_org_user_pass: %s"
+ % repr(exc.value.args[0])
+ )
+ assert not exc.value.args[0]["changed"]
+
+ @patch(
+ "ansible_collections.netapp.storagegrid.plugins.module_utils.netapp.SGRestAPI.send_request"
+ )
+ def test_update_na_sg_org_user_pass(self, mock_request):
+ args = self.set_args_create_na_sg_org_user()
+ args["member_of"] = ["group/testorggroup1", "group/testorggroup2"]
+
+ set_module_args(args)
+ my_obj = org_user_module()
+ mock_request.side_effect = [
+ SRR["org_user_record"], # get
+ SRR["org_groups"], # get
+ SRR["org_user_record_update"], # put
+ SRR["end_of_sequence"],
+ ]
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ print("Info: test_update_na_sg_org_user_pass: %s" % repr(exc.value.args[0]))
+ assert exc.value.args[0]["changed"]
+
+ @patch(
+ "ansible_collections.netapp.storagegrid.plugins.module_utils.netapp.SGRestAPI.send_request"
+ )
+ def test_delete_na_sg_org_user_pass(self, mock_request):
+ set_module_args(self.set_args_delete_na_sg_org_user())
+ my_obj = org_user_module()
+ mock_request.side_effect = [
+ SRR["org_user_record"], # get
+ SRR["org_groups"], # get
+ SRR["delete_good"], # delete
+ SRR["end_of_sequence"],
+ ]
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ print("Info: test_delete_na_sg_org_user_pass: %s" % repr(exc.value.args[0]))
+ assert exc.value.args[0]["changed"]
+
+ # create user and set pass
+ @patch(
+ "ansible_collections.netapp.storagegrid.plugins.module_utils.netapp.SGRestAPI.send_request"
+ )
+ def test_create_na_sg_org_user_and_set_password_pass(self, mock_request):
+ set_module_args(self.set_args_create_na_sg_org_user_with_password())
+ my_obj = org_user_module()
+ mock_request.side_effect = [
+ SRR["not_found"], # get
+ SRR["org_groups"], # get
+ SRR["org_user_record"], # post
+ SRR["pw_change_good"], # post
+ SRR["end_of_sequence"],
+ ]
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ print(
+ "Info: test_create_na_sg_org_user_and_set_password_pass: %s"
+ % repr(exc.value.args[0])
+ )
+ assert exc.value.args[0]["changed"]
+
+ # Idempotent user with password defined
+ @patch(
+ "ansible_collections.netapp.storagegrid.plugins.module_utils.netapp.SGRestAPI.send_request"
+ )
+ def test_idempotent_create_na_sg_org_user_and_set_password_pass(self, mock_request):
+ set_module_args(self.set_args_create_na_sg_org_user_with_password())
+ my_obj = org_user_module()
+ mock_request.side_effect = [
+ SRR["org_user_record"], # get
+ SRR["org_groups"], # get
+ SRR["end_of_sequence"],
+ ]
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ print(
+ "Info: test_idempotent_create_na_sg_org_user_and_set_password_pass: %s"
+ % repr(exc.value.args[0])
+ )
+ assert not exc.value.args[0]["changed"]
+
+ # update user and set pass
+ @patch(
+ "ansible_collections.netapp.storagegrid.plugins.module_utils.netapp.SGRestAPI.send_request"
+ )
+ def test_update_na_sg_org_user_and_set_password_pass(self, mock_request):
+ args = self.set_args_create_na_sg_org_user_with_password()
+ args["member_of"] = ["group/testorggroup1", "group/testorggroup2"]
+ args["update_password"] = "always"
+
+ set_module_args(args)
+ my_obj = org_user_module()
+ mock_request.side_effect = [
+ SRR["org_user_record"], # get
+ SRR["org_groups"], # get
+ SRR["org_user_record_update"], # put
+ SRR["pw_change_good"], # post
+ SRR["end_of_sequence"],
+ ]
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ print(
+ "Info: test_update_na_sg_org_user_and_set_password_pass: %s"
+ % repr(exc.value.args[0])
+ )
+ assert exc.value.args[0]["changed"]
+
+ # set pass only
+
+ @patch(
+ "ansible_collections.netapp.storagegrid.plugins.module_utils.netapp.SGRestAPI.send_request"
+ )
+ def test_set_na_sg_org_user_password_pass(self, mock_request):
+ args = self.set_args_create_na_sg_org_user_with_password()
+ args["update_password"] = "always"
+
+ set_module_args(args)
+ my_obj = org_user_module()
+ mock_request.side_effect = [
+ SRR["org_user_record"], # get
+ SRR["org_groups"], # get
+ SRR["pw_change_good"], # post
+ SRR["end_of_sequence"],
+ ]
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ print(
+ "Info: test_set_na_sg_org_user_password_pass: %s" % repr(exc.value.args[0])
+ )
+ assert exc.value.args[0]["changed"]
+
+ # attempt to set password on federated user
+
+ @patch(
+ "ansible_collections.netapp.storagegrid.plugins.module_utils.netapp.SGRestAPI.send_request"
+ )
+ def test_fail_set_federated_user_password(self, mock_request):
+ with pytest.raises(AnsibleFailJson) as exc:
+ args = self.set_args_create_na_sg_org_user_with_password()
+ args["unique_name"] = "federated-user/abc123"
+ args["update_password"] = "always"
+ set_module_args(args)
+ org_user_module()
+ print(
+ "Info: test_fail_set_federated_user_password: %s" % repr(exc.value.args[0])
+ )
diff --git a/ansible_collections/netapp/storagegrid/tests/unit/plugins/modules/test_na_sg_org_user_s3_key.py b/ansible_collections/netapp/storagegrid/tests/unit/plugins/modules/test_na_sg_org_user_s3_key.py
new file mode 100644
index 000000000..53696bdbf
--- /dev/null
+++ b/ansible_collections/netapp/storagegrid/tests/unit/plugins/modules/test_na_sg_org_user_s3_key.py
@@ -0,0 +1,238 @@
+# (c) 2020, NetApp, Inc
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+""" unit tests NetApp StorageGRID Org Group Ansible module: na_sg_org_user_s3_key"""
+
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+import json
+import pytest
+import sys
+try:
+ from requests import Response
+except ImportError:
+ if sys.version_info < (2, 7):
+ pytestmark = pytest.mark.skip('Skipping Unit Tests on 2.6 as requests is not be available')
+ else:
+ raise
+
+from ansible_collections.netapp.storagegrid.tests.unit.compat import unittest
+from ansible_collections.netapp.storagegrid.tests.unit.compat.mock import (
+ patch,
+ Mock,
+)
+from ansible.module_utils import basic
+from ansible.module_utils._text import to_bytes
+from ansible_collections.netapp.storagegrid.plugins.modules.na_sg_org_user_s3_key import (
+ SgOrgUserS3Key as org_s3_key_module,
+)
+
+# REST API canned responses when mocking send_request
+SRR = {
+ # common responses
+ "empty_good": ({"data": []}, None),
+ "not_found": (
+ {"status": "error", "code": 404, "data": {}},
+ {"key": "error.404"},
+ ),
+ "end_of_sequence": (None, "Unexpected call to send_request"),
+ "generic_error": (None, "Expected error"),
+ "delete_good": ({"code": 204}, None),
+ "org_user_record": (
+ {
+ "data": {
+ "id": "09876543-abcd-4321-abcd-0987654321ab",
+ "accountId": "12345678901234567890",
+ "fullName": "testorguser",
+ "uniqueName": "user/testorguser",
+ "userURN": "urn:sgws:identity::12345678901234567890:user/testorguser",
+ "federated": False,
+ "memberOf": ["12345678-abcd-1234-abcd-1234567890ab"],
+ "disable": False,
+ }
+ },
+ None,
+ ),
+ "org_s3_key": (
+ {
+ "data": {
+ "id": "abcABC_01234-0123456789abcABCabc0123456789==",
+ "accountId": 12345678901234567000,
+ "displayName": "****************AB12",
+ "userURN": "urn:sgws:identity::12345678901234567890:root",
+ "userUUID": "09876543-abcd-4321-abcd-0987654321ab",
+ "expires": "2020-09-04T00:00:00.000Z",
+ "accessKey": "ABCDEFabcd1234567890",
+ "secretAccessKey": "abcABC+123456789012345678901234567890123",
+ }
+ },
+ None,
+ ),
+}
+
+
+def set_module_args(args):
+ """prepare arguments so that they will be picked up during module creation"""
+ args = json.dumps({"ANSIBLE_MODULE_ARGS": args})
+ basic._ANSIBLE_ARGS = to_bytes(args) # pylint: disable=protected-access
+
+
+class AnsibleExitJson(Exception):
+ """Exception class to be raised by module.exit_json and caught by the test case"""
+
+ pass
+
+
+class AnsibleFailJson(Exception):
+ """Exception class to be raised by module.fail_json and caught by the test case"""
+
+ pass
+
+
+def exit_json(*args, **kwargs): # pylint: disable=unused-argument
+ """function to patch over exit_json; package return data into an exception"""
+ if "changed" not in kwargs:
+ kwargs["changed"] = False
+ raise AnsibleExitJson(kwargs)
+
+
+def fail_json(*args, **kwargs): # pylint: disable=unused-argument
+ """function to patch over fail_json; package return data into an exception"""
+ kwargs["failed"] = True
+ raise AnsibleFailJson(kwargs)
+
+
+class TestMyModule(unittest.TestCase):
+ """ a group of related Unit Tests """
+
+ def setUp(self):
+ self.mock_module_helper = patch.multiple(
+ basic.AnsibleModule, exit_json=exit_json, fail_json=fail_json
+ )
+ self.mock_module_helper.start()
+ self.addCleanup(self.mock_module_helper.stop)
+
+ def set_default_args_fail_check(self):
+ return dict(
+ {
+ "unique_user_name": "user/testorguser",
+ "auth_token": "01234567-5678-9abc-78de-9fgabc123def",
+ "validate_certs": False,
+ }
+ )
+
+ def set_default_args_pass_check(self):
+ return dict(
+ {
+ "state": "present",
+ "unique_user_name": "user/testorguser",
+ "api_url": "gmi.example.com",
+ "auth_token": "01234567-5678-9abc-78de-9fgabc123def",
+ "validate_certs": False,
+ }
+ )
+
+ def set_args_create_na_sg_org_user_s3_keys(self):
+ return dict(
+ {
+ "state": "present",
+ "unique_user_name": "user/testorguser",
+ "api_url": "gmi.example.com",
+ "auth_token": "01234567-5678-9abc-78de-9fgabc123def",
+ "validate_certs": False,
+ }
+ )
+
+ def set_args_delete_na_sg_org_user_s3_keys(self):
+ return dict(
+ {
+ "state": "absent",
+ "unique_user_name": "user/testorguser",
+ "access_key": "ABCDEFabcd1234567890",
+ "api_url": "gmi.example.com",
+ "auth_token": "01234567-5678-9abc-78de-9fgabc123def",
+ "validate_certs": False,
+ }
+ )
+
+ def test_module_fail_when_required_args_missing(self):
+ """ required arguments are reported as errors """
+ with pytest.raises(AnsibleFailJson) as exc:
+ set_module_args(self.set_default_args_fail_check())
+ org_s3_key_module()
+ print(
+ "Info: test_module_fail_when_required_args_missing: %s"
+ % exc.value.args[0]["msg"]
+ )
+
+ def test_module_fail_when_required_args_present(self):
+ """ required arguments are reported as errors """
+ with pytest.raises(AnsibleExitJson) as exc:
+ set_module_args(self.set_default_args_pass_check())
+ org_s3_key_module()
+ exit_json(changed=True, msg="Induced arguments check")
+ print(
+ "Info: test_module_fail_when_required_args_present: %s"
+ % exc.value.args[0]["msg"]
+ )
+ assert exc.value.args[0]["changed"]
+
+ @patch(
+ "ansible_collections.netapp.storagegrid.plugins.module_utils.netapp.SGRestAPI.send_request"
+ )
+ def test_create_na_sg_org_user_s3_key_pass(self, mock_request):
+ set_module_args(self.set_args_create_na_sg_org_user_s3_keys())
+ my_obj = org_s3_key_module()
+ mock_request.side_effect = [
+ SRR["org_user_record"], # get
+ SRR["org_s3_key"], # post
+ SRR["end_of_sequence"],
+ ]
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ print(
+ "Info: test_create_na_sg_org_user_s3_key_pass: %s"
+ % repr(exc.value.args[0])
+ )
+ assert exc.value.args[0]["changed"]
+
+ @patch(
+ "ansible_collections.netapp.storagegrid.plugins.module_utils.netapp.SGRestAPI.send_request"
+ )
+ def test_idempotent_create_na_sg_org_user_s3_key_pass(self, mock_request):
+ args = self.set_args_create_na_sg_org_user_s3_keys()
+ args["access_key"] = "ABCDEFabcd1234567890"
+ set_module_args(args)
+ my_obj = org_s3_key_module()
+ mock_request.side_effect = [
+ SRR["org_user_record"], # get
+ SRR["org_s3_key"], # get
+ SRR["end_of_sequence"],
+ ]
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ print(
+ "Info: test_idempotent_create_na_sg_org_user_s3_key_pass: %s"
+ % repr(exc.value.args[0])
+ )
+ assert not exc.value.args[0]["changed"]
+
+ @patch(
+ "ansible_collections.netapp.storagegrid.plugins.module_utils.netapp.SGRestAPI.send_request"
+ )
+ def test_delete_na_sg_org_user_s3_keys_pass(self, mock_request):
+ set_module_args(self.set_args_delete_na_sg_org_user_s3_keys())
+ my_obj = org_s3_key_module()
+ mock_request.side_effect = [
+ SRR["org_s3_key"], # get
+ SRR["delete_good"], # delete
+ SRR["end_of_sequence"],
+ ]
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ print(
+ "Info: test_delete_na_sg_org_user_s3_keys_pass: %s"
+ % repr(exc.value.args[0])
+ )
+ assert exc.value.args[0]["changed"]
diff --git a/ansible_collections/netapp/storagegrid/tests/unit/requirements.txt b/ansible_collections/netapp/storagegrid/tests/unit/requirements.txt
new file mode 100644
index 000000000..b754473a9
--- /dev/null
+++ b/ansible_collections/netapp/storagegrid/tests/unit/requirements.txt
@@ -0,0 +1 @@
+requests ; python_version >= '2.7'