summaryrefslogtreecommitdiffstats
path: root/collections-debian-merged/ansible_collections/netapp/ontap/tests
diff options
context:
space:
mode:
Diffstat (limited to 'collections-debian-merged/ansible_collections/netapp/ontap/tests')
-rw-r--r--collections-debian-merged/ansible_collections/netapp/ontap/tests/sanity/ignore-2.10.txt6
-rw-r--r--collections-debian-merged/ansible_collections/netapp/ontap/tests/sanity/ignore-2.9.txt1
-rw-r--r--collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/compat/__init__.py0
-rw-r--r--collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/compat/builtins.py33
-rw-r--r--collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/compat/mock.py122
-rw-r--r--collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/compat/unittest.py38
-rw-r--r--collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/module_utils/test_netapp.py468
-rw-r--r--collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/module_utils/test_netapp_module.py400
-rw-r--r--collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_aggregate.py419
-rw-r--r--collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_autosupport.py245
-rw-r--r--collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_autosupport_invoke.py135
-rw-r--r--collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_broadcast_domain.py309
-rw-r--r--collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_cg_snapshot.py116
-rw-r--r--collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_cifs.py228
-rw-r--r--collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_cifs_server.py222
-rw-r--r--collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_cluster.py429
-rw-r--r--collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_cluster_peer.py212
-rw-r--r--collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_command.py205
-rw-r--r--collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_dns.py321
-rw-r--r--collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_efficiency_policy.py232
-rw-r--r--collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_export_policy.py289
-rw-r--r--collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_export_policy_rule.py269
-rw-r--r--collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_file_directory_policy.py173
-rw-r--r--collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_firewall_policy.py296
-rw-r--r--collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_firmware_upgrade.py436
-rw-r--r--collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_flexcache.py531
-rw-r--r--collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_igroup.py260
-rw-r--r--collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_igroup_initiator.py218
-rw-r--r--collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_info.py557
-rw-r--r--collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_interface.py312
-rw-r--r--collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_ipspace.py269
-rw-r--r--collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_iscsi_security.py256
-rw-r--r--collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_job_schedule.py369
-rw-r--r--collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_kerberos_realm.py269
-rw-r--r--collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_ldap_client.py185
-rw-r--r--collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_login_messages.py287
-rw-r--r--collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_lun.py177
-rw-r--r--collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_lun_copy.py155
-rw-r--r--collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_lun_map.py192
-rw-r--r--collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_lun_rest.py277
-rw-r--r--collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_mcc_mediator.py156
-rw-r--r--collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_metrocluster.py149
-rw-r--r--collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_metrocluster_dr_group.py196
-rw-r--r--collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_motd.py182
-rw-r--r--collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_name_service_switch.py180
-rw-r--r--collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_ndmp.py227
-rw-r--r--collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_net_ifgrp.py299
-rw-r--r--collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_net_port.py180
-rw-r--r--collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_net_routes.py461
-rw-r--r--collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_net_subnet.py265
-rw-r--r--collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_nfs.py309
-rw-r--r--collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_ntfs_dacl.py268
-rw-r--r--collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_ntfs_sd.py225
-rw-r--r--collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_nvme.py217
-rw-r--r--collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_nvme_namespace.py201
-rw-r--r--collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_nvme_subsystem.py242
-rw-r--r--collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_object_store.py300
-rw-r--r--collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_ports.py173
-rw-r--r--collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_portset.py190
-rw-r--r--collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_qos_adaptive_policy_group.py347
-rw-r--r--collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_qos_policy_group.py340
-rw-r--r--collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_qtree.py464
-rw-r--r--collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_quota_policy.py207
-rw-r--r--collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_quotas.py243
-rw-r--r--collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_rest_cli.py137
-rw-r--r--collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_rest_info.py543
-rw-r--r--collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_security_certificates.py435
-rw-r--r--collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_security_key_manager.py174
-rw-r--r--collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_service_processor_network.py234
-rw-r--r--collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_snapmirror.py630
-rw-r--r--collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_snapmirror_policy.py717
-rw-r--r--collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_snapshot.py227
-rw-r--r--collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_snapshot_policy.py691
-rw-r--r--collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_snmp_traphosts.py153
-rw-r--r--collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_software_update.py190
-rw-r--r--collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_svm.py430
-rw-r--r--collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_template.py121
-rw-r--r--collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_ucadapter.py176
-rw-r--r--collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_unix_group.py289
-rw-r--r--collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_unix_user.py283
-rw-r--r--collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_user.py505
-rw-r--r--collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_user_role.py239
-rw-r--r--collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_volume.py1183
-rw-r--r--collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_volume_autosize.py243
-rw-r--r--collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_volume_clone.py257
-rw-r--r--collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_volume_rest.py333
-rw-r--r--collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_volume_snaplock.py166
-rw-r--r--collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_vscan.py234
-rw-r--r--collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_vscan_on_access_policy.py159
-rw-r--r--collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_vscan_on_demand_task.py168
-rw-r--r--collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_vscan_scanner_pool.py188
-rw-r--r--collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_vserver_cifs_security.py166
-rw-r--r--collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_vserver_peer.py250
-rw-r--r--collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_wwpn_alias.py224
-rw-r--r--collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/requirements.txt3
95 files changed, 25687 insertions, 0 deletions
diff --git a/collections-debian-merged/ansible_collections/netapp/ontap/tests/sanity/ignore-2.10.txt b/collections-debian-merged/ansible_collections/netapp/ontap/tests/sanity/ignore-2.10.txt
new file mode 100644
index 00000000..e686ab44
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/netapp/ontap/tests/sanity/ignore-2.10.txt
@@ -0,0 +1,6 @@
+plugins/modules/na_ontap_autosupport_invoke.py validate-modules:invalid-argument-name
+plugins/modules/na_ontap_info.py validate-modules:parameter-state-invalid-choice
+plugins/modules/na_ontap_login_messages.py validate-modules:invalid-argument-name
+plugins/modules/na_ontap_motd.py validate-modules:invalid-argument-name
+plugins/modules/na_ontap_nfs.py validate-modules:parameter-invalid
+plugins/modules/na_ontap_rest_info.py validate-modules:parameter-state-invalid-choice
diff --git a/collections-debian-merged/ansible_collections/netapp/ontap/tests/sanity/ignore-2.9.txt b/collections-debian-merged/ansible_collections/netapp/ontap/tests/sanity/ignore-2.9.txt
new file mode 100644
index 00000000..5c626a03
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/netapp/ontap/tests/sanity/ignore-2.9.txt
@@ -0,0 +1 @@
+plugins/modules/na_ontap_nfs.py validate-modules:parameter-invalid \ No newline at end of file
diff --git a/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/compat/__init__.py b/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/compat/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/compat/__init__.py
diff --git a/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/compat/builtins.py b/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/compat/builtins.py
new file mode 100644
index 00000000..f60ee678
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/compat/builtins.py
@@ -0,0 +1,33 @@
+# (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/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/compat/mock.py b/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/compat/mock.py
new file mode 100644
index 00000000..0972cd2e
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/compat/mock.py
@@ -0,0 +1,122 @@
+# (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/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/compat/unittest.py b/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/compat/unittest.py
new file mode 100644
index 00000000..98f08ad6
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/compat/unittest.py
@@ -0,0 +1,38 @@
+# (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
+
+# 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')
+else:
+ from unittest import *
diff --git a/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/module_utils/test_netapp.py b/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/module_utils/test_netapp.py
new file mode 100644
index 00000000..be76d435
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/module_utils/test_netapp.py
@@ -0,0 +1,468 @@
+# Copyright (c) 2018 NetApp
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+''' unit tests for module_utils netapp.py '''
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+
+import json
+import os.path
+import tempfile
+
+import pytest
+
+from ansible.module_utils.ansible_release import __version__ as ansible_version
+from ansible.module_utils import basic
+from ansible.module_utils._text import to_bytes
+from ansible_collections.netapp.ontap.plugins.module_utils.netapp import COLLECTION_VERSION
+from ansible_collections.netapp.ontap.tests.unit.compat.mock import patch
+
+import ansible_collections.netapp.ontap.plugins.module_utils.netapp as netapp_utils
+
+if not netapp_utils.has_netapp_lib():
+ pytestmark = pytest.mark.skip("skipping as missing required netapp_lib")
+
+
+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 AnsibleFailJson(Exception):
+ """Exception class to be raised by module.fail_json and caught by the test case"""
+
+
+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)
+
+
+SRR = {
+ # common responses
+ 'is_rest': (200, {}, None),
+ 'is_zapi': (400, {}, "Unreachable"),
+ 'empty_good': ({}, None),
+ 'end_of_sequence': (None, "Unexpected call to send_request"),
+ 'generic_error': (None, "Expected error"),
+}
+
+
+class MockONTAPConnection(object):
+ ''' mock a server connection to ONTAP host '''
+
+ def __init__(self, kind=None, parm1=None):
+ ''' save arguments '''
+ self.type = kind
+ self.parm1 = parm1
+ self.xml_in = None
+ self.xml_out = None
+
+ def invoke_successfully(self, xml, enable_tunneling): # pylint: disable=unused-argument
+ ''' mock invoke_successfully returning xml data '''
+ self.xml_in = xml
+ if self.type == 'vserver':
+ xml = self.build_vserver_info(self.parm1)
+ self.xml_out = xml
+ return xml
+
+ @staticmethod
+ def build_vserver_info(vserver):
+ ''' build xml data for vserser-info '''
+ xml = netapp_utils.zapi.NaElement('xml')
+ attributes = netapp_utils.zapi.NaElement('attributes-list')
+ attributes.add_node_with_children('vserver-info',
+ **{'vserver-name': vserver})
+ xml.add_child_elem(attributes)
+ return xml
+
+
+def test_ems_log_event_version():
+ ''' validate Ansible version is correctly read '''
+ source = 'unittest'
+ server = MockONTAPConnection()
+ netapp_utils.ems_log_event(source, server)
+ xml = server.xml_in
+ version = xml.get_child_content('app-version')
+ if version == ansible_version:
+ assert version == ansible_version
+ else:
+ assert version == COLLECTION_VERSION
+ print("Ansible version: %s" % ansible_version)
+
+
+def test_get_cserver():
+ ''' validate cluster vserser name is correctly retrieved '''
+ svm_name = 'svm1'
+ server = MockONTAPConnection('vserver', svm_name)
+ cserver = netapp_utils.get_cserver(server)
+ assert cserver == svm_name
+
+
+def mock_args(feature_flags=None):
+ args = {
+ 'hostname': 'test',
+ 'username': 'test_user',
+ 'password': 'test_pass!'
+ }
+ if feature_flags is not None:
+ args.update({'feature_flags': feature_flags})
+ return args
+
+
+def cert_args(feature_flags=None):
+ args = {
+ 'hostname': 'test',
+ 'cert_filepath': 'test_pem.pem',
+ 'key_filepath': 'test_key.key'
+ }
+ if feature_flags is not None:
+ args.update({'feature_flags': feature_flags})
+ return args
+
+
+def create_module(args):
+ argument_spec = netapp_utils.na_ontap_host_argument_spec()
+ set_module_args(args)
+ module = basic.AnsibleModule(argument_spec)
+ return module
+
+
+def create_restapi_object(args):
+ module = create_module(args)
+ module.fail_json = fail_json
+ rest_api = netapp_utils.OntapRestAPI(module)
+ return rest_api
+
+
+def create_ontapzapicx_object(args, feature_flags=None):
+ module_args = dict(args)
+ if feature_flags is not None:
+ module_args['feature_flags'] = feature_flags
+ module = create_module(module_args)
+ module.fail_json = fail_json
+ my_args = dict(args)
+ my_args.update(dict(module=module))
+ zapi_cx = netapp_utils.OntapZAPICx(**my_args)
+ return zapi_cx
+
+
+def test_write_to_file():
+ ''' check error and debug logs can be written to disk '''
+ rest_api = create_restapi_object(mock_args())
+ # logging an error also add a debug record
+ rest_api.log_error(404, '404 error')
+ print(rest_api.errors)
+ print(rest_api.debug_logs)
+ # logging a debug record only
+ rest_api.log_debug(501, '501 error')
+ print(rest_api.errors)
+ print(rest_api.debug_logs)
+
+ try:
+ tempdir = tempfile.TemporaryDirectory()
+ filepath = os.path.join(tempdir.name, 'log.txt')
+ except AttributeError:
+ # python 2.7 does not support tempfile.TemporaryDirectory
+ # we're taking a small chance that there is a race condition
+ filepath = '/tmp/deleteme354.txt'
+ rest_api.write_debug_log_to_file(filepath=filepath, append=False)
+ with open(filepath, 'r') as log:
+ lines = log.readlines()
+ assert len(lines) == 4
+ assert lines[0].strip() == 'Debug: 404'
+ assert lines[2].strip() == 'Debug: 501'
+
+ # Idempotent, as append is False
+ rest_api.write_debug_log_to_file(filepath=filepath, append=False)
+ with open(filepath, 'r') as log:
+ lines = log.readlines()
+ assert len(lines) == 4
+ assert lines[0].strip() == 'Debug: 404'
+ assert lines[2].strip() == 'Debug: 501'
+
+ # Duplication, as append is True
+ rest_api.write_debug_log_to_file(filepath=filepath, append=True)
+ with open(filepath, 'r') as log:
+ lines = log.readlines()
+ assert len(lines) == 8
+ assert lines[0].strip() == 'Debug: 404'
+ assert lines[2].strip() == 'Debug: 501'
+ assert lines[4].strip() == 'Debug: 404'
+ assert lines[6].strip() == 'Debug: 501'
+
+ rest_api.write_errors_to_file(filepath=filepath, append=False)
+ with open(filepath, 'r') as log:
+ lines = log.readlines()
+ assert len(lines) == 1
+ assert lines[0].strip() == 'Error: 404 error'
+
+ # Idempotent, as append is False
+ rest_api.write_errors_to_file(filepath=filepath, append=False)
+ with open(filepath, 'r') as log:
+ lines = log.readlines()
+ assert len(lines) == 1
+ assert lines[0].strip() == 'Error: 404 error'
+
+ # Duplication, as append is True
+ rest_api.write_errors_to_file(filepath=filepath, append=True)
+ with open(filepath, 'r') as log:
+ lines = log.readlines()
+ assert len(lines) == 2
+ assert lines[0].strip() == 'Error: 404 error'
+ assert lines[1].strip() == 'Error: 404 error'
+
+
+@patch('ansible_collections.netapp.ontap.plugins.module_utils.netapp.OntapRestAPI.send_request')
+def test_is_rest_true(mock_request):
+ ''' is_rest is expected to return True '''
+ mock_request.side_effect = [
+ SRR['is_rest'],
+ ]
+ rest_api = create_restapi_object(mock_args())
+ is_rest = rest_api.is_rest()
+ print(rest_api.errors)
+ print(rest_api.debug_logs)
+ assert is_rest
+
+
+@patch('ansible_collections.netapp.ontap.plugins.module_utils.netapp.OntapRestAPI.send_request')
+def test_is_rest_false(mock_request):
+ ''' is_rest is expected to return False '''
+ mock_request.side_effect = [
+ SRR['is_zapi'],
+ ]
+ rest_api = create_restapi_object(mock_args())
+ is_rest = rest_api.is_rest()
+ print(rest_api.errors)
+ print(rest_api.debug_logs)
+ assert not is_rest
+ assert rest_api.errors[0] == SRR['is_zapi'][2]
+ assert rest_api.debug_logs[0][0] == SRR['is_zapi'][0] # status_code
+ assert rest_api.debug_logs[0][1] == SRR['is_zapi'][2] # error
+
+
+def test_has_feature_success_default():
+ ''' existing feature_flag with default '''
+ flag = 'deprecation_warning'
+ module = create_module(mock_args())
+ value = netapp_utils.has_feature(module, flag)
+ assert value
+
+
+def test_has_feature_success_user_true():
+ ''' existing feature_flag with value set to True '''
+ flag = 'user_deprecation_warning'
+ args = dict(mock_args({flag: True}))
+ module = create_module(args)
+ value = netapp_utils.has_feature(module, flag)
+ assert value
+
+
+def test_has_feature_success_user_false():
+ ''' existing feature_flag with value set to False '''
+ flag = 'user_deprecation_warning'
+ args = dict(mock_args({flag: False}))
+ print(args)
+ module = create_module(args)
+ value = netapp_utils.has_feature(module, flag)
+ assert not value
+
+
+def test_has_feature_invalid_key():
+ ''' existing feature_flag with unknown key '''
+ flag = 'deprecation_warning_bad_key'
+ module = create_module(mock_args())
+ # replace ANsible fail method with ours
+ module.fail_json = fail_json
+ with pytest.raises(AnsibleFailJson) as exc:
+ netapp_utils.has_feature(module, flag)
+ msg = 'Internal error: unexpected feature flag: %s' % flag
+ assert exc.value.args[0]['msg'] == msg
+
+
+def test_fail_has_username_password_and_cert():
+ ''' failure case in auth_method '''
+ args = mock_args()
+ args.update(dict(cert_filepath='dummy'))
+ with pytest.raises(AnsibleFailJson) as exc:
+ create_restapi_object(args)
+ msg = 'Error: cannot have both basic authentication (username/password) and certificate authentication (cert/key files)'
+ assert exc.value.args[0]['msg'] == msg
+
+
+def test_fail_has_username_password_and_key():
+ ''' failure case in auth_method '''
+ args = mock_args()
+ args.update(dict(key_filepath='dummy'))
+ with pytest.raises(AnsibleFailJson) as exc:
+ create_restapi_object(args)
+ msg = 'Error: cannot have both basic authentication (username/password) and certificate authentication (cert/key files)'
+ assert exc.value.args[0]['msg'] == msg
+
+
+def test_fail_has_username_and_cert():
+ ''' failure case in auth_method '''
+ args = mock_args()
+ args.update(dict(cert_filepath='dummy'))
+ del args['password']
+ with pytest.raises(AnsibleFailJson) as exc:
+ create_restapi_object(args)
+ msg = 'Error: username and password have to be provided together and cannot be used with cert or key files'
+ assert exc.value.args[0]['msg'] == msg
+
+
+def test_fail_has_password_and_cert():
+ ''' failure case in auth_method '''
+ args = mock_args()
+ args.update(dict(cert_filepath='dummy'))
+ del args['username']
+ with pytest.raises(AnsibleFailJson) as exc:
+ create_restapi_object(args)
+ msg = 'Error: username and password have to be provided together and cannot be used with cert or key files'
+ assert exc.value.args[0]['msg'] == msg
+
+
+def test_has_username_password():
+ ''' auth_method reports expected value '''
+ args = mock_args()
+ rest_api = create_restapi_object(args)
+ assert rest_api.auth_method == 'speedy_basic_auth'
+
+
+def test_has_cert_no_key():
+ ''' auth_method reports expected value '''
+ args = cert_args()
+ del args['key_filepath']
+ rest_api = create_restapi_object(args)
+ assert rest_api.auth_method == 'single_cert'
+
+
+def test_has_cert_and_key():
+ ''' auth_method reports expected value '''
+ args = cert_args()
+ rest_api = create_restapi_object(args)
+ assert rest_api.auth_method == 'cert_key'
+
+
+def test_certificate_method_zapi():
+ ''' should fail when trying to read the certificate file '''
+ args = cert_args()
+ zapi_cx = create_ontapzapicx_object(args)
+ with pytest.raises(AnsibleFailJson) as exc:
+ zapi_cx._create_certificate_auth_handler()
+ msg1 = 'Cannot load SSL certificate, check files exist.'
+ # for python 2,6 :(
+ msg2 = 'SSL certificate authentication requires python 2.7 or later.'
+ assert exc.value.args[0]['msg'].startswith((msg1, msg2))
+
+
+def test_classify_zapi_exception_cluster_only():
+ ''' verify output matches expectations '''
+ code = 13005
+ message = 'Unable to find API: diagnosis-alert-get-iter on data vserver trident_svm'
+ zapi_exception = netapp_utils.zapi.NaApiError(code, message)
+ kind, new_message = netapp_utils.classify_zapi_exception(zapi_exception)
+ assert kind == 'missing_vserver_api_error'
+ assert new_message.endswith("%d:%s" % (code, message))
+
+
+def test_classify_zapi_exception_rpc_error():
+ ''' verify output matches expectations '''
+ code = 13001
+ message = "RPC: Couldn't make connection [from mgwd on node \"laurentn-vsim1\" (VSID: -1) to mgwd at 172.32.78.223]"
+ error_message = 'NetApp API failed. Reason - %d:%s' % (code, message)
+ zapi_exception = netapp_utils.zapi.NaApiError(code, message)
+ kind, new_message = netapp_utils.classify_zapi_exception(zapi_exception)
+ assert kind == 'rpc_error'
+ assert new_message == error_message
+
+
+def test_classify_zapi_exception_other_error():
+ ''' verify output matches expectations '''
+ code = 13008
+ message = 'whatever'
+ error_message = 'NetApp API failed. Reason - %d:%s' % (code, message)
+ zapi_exception = netapp_utils.zapi.NaApiError(code, message)
+ kind, new_message = netapp_utils.classify_zapi_exception(zapi_exception)
+ assert kind == 'other_error'
+ assert new_message == error_message
+
+
+def test_zapi_parse_response_sanitized():
+ ''' should not fail when trying to read invalid XML characters (\x08) '''
+ args = mock_args()
+ zapi_cx = create_ontapzapicx_object(args)
+ response = b"<?xml version='1.0' encoding='UTF-8' ?>\n<!DOCTYPE netapp SYSTEM 'file:/etc/netapp_gx.dtd'>\n"
+ response += b"<netapp version='1.180' xmlns='http://www.netapp.com/filer/admin'>\n<results status=\"passed\">"
+ response += b"<cli-output> (cluster log-forwarding create)\n\n"
+ response += b"Testing network connectivity to the destination host 10.10.10.10. \x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\n\n"
+ response += b"Error: command failed: Cannot contact destination host (10.10.10.10) from node\n"
+ response += b" &quot;laurentn-vsim1&quot;. Verify connectivity to desired host or skip the\n"
+ response += b" connectivity check with the &quot;-force&quot; parameter.</cli-output>"
+ response += b"<cli-result-value>0</cli-result-value></results></netapp>\n"
+ # Manually extract cli-output contents
+ cli_output = response.split(b'<cli-output>')[1]
+ cli_output = cli_output.split(b'</cli-output>')[0]
+ cli_output = cli_output.replace(b'&quot;', b'"')
+ # the XML parser would chole on \x08, zapi_cx._parse_response replaces them with '.'
+ cli_output = cli_output.replace(b'\x08', b'.')
+ # Use xml parser to extract cli-output contents
+ xml = zapi_cx._parse_response(response)
+ results = xml.get_child_by_name('results')
+ new_cli_output = results.get_child_content('cli-output')
+ assert cli_output.decode() == new_cli_output
+
+
+def test_zapi_parse_response_unsanitized():
+ ''' should fail when trying to read invalid XML characters (\x08) '''
+ args = mock_args()
+ # use feature_flags to disable sanitization
+ zapi_cx = create_ontapzapicx_object(args, dict(sanitize_xml=False))
+ response = b"<?xml version='1.0' encoding='UTF-8' ?>\n<!DOCTYPE netapp SYSTEM 'file:/etc/netapp_gx.dtd'>\n"
+ response += b"<netapp version='1.180' xmlns='http://www.netapp.com/filer/admin'>\n<results status=\"passed\">"
+ response += b"<cli-output> (cluster log-forwarding create)\n\n"
+ response += b"Testing network connectivity to the destination host 10.10.10.10. \x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\n\n"
+ response += b"Error: command failed: Cannot contact destination host (10.10.10.10) from node\n"
+ response += b" &quot;laurentn-vsim1&quot;. Verify connectivity to desired host or skip the\n"
+ response += b" connectivity check with the &quot;-force&quot; parameter.</cli-output>"
+ response += b"<cli-result-value>0</cli-result-value></results></netapp>\n"
+ with pytest.raises(netapp_utils.zapi.etree.XMLSyntaxError) as exc:
+ zapi_cx._parse_response(response)
+ msg = 'PCDATA invalid Char value 8'
+ assert exc.value.msg.startswith(msg)
+
+
+def test_zapi_cx_add_auth_header():
+ ''' should add header '''
+ args = mock_args()
+ module = create_module(args)
+ zapi_cx = netapp_utils.setup_na_ontap_zapi(module)
+ assert isinstance(zapi_cx, netapp_utils.OntapZAPICx)
+ assert zapi_cx.base64_creds is not None
+ request, dummy = zapi_cx._create_request(netapp_utils.zapi.NaElement('dummy_tag'))
+ assert "Authorization" in [x[0] for x in request.header_items()]
+
+
+def test_zapi_cx_add_auth_header_explicit():
+ ''' should add header '''
+ args = mock_args()
+ args['feature_flags'] = dict(classic_basic_authorization=False)
+ module = create_module(args)
+ zapi_cx = netapp_utils.setup_na_ontap_zapi(module)
+ assert isinstance(zapi_cx, netapp_utils.OntapZAPICx)
+ assert zapi_cx.base64_creds is not None
+ request, dummy = zapi_cx._create_request(netapp_utils.zapi.NaElement('dummy_tag'))
+ assert "Authorization" in [x[0] for x in request.header_items()]
+
+
+def test_zapi_cx_no_auth_header():
+ ''' should add header '''
+ args = mock_args()
+ args['feature_flags'] = dict(classic_basic_authorization=True)
+ module = create_module(args)
+ zapi_cx = netapp_utils.setup_na_ontap_zapi(module)
+ assert not isinstance(zapi_cx, netapp_utils.OntapZAPICx)
+ request, dummy = zapi_cx._create_request(netapp_utils.zapi.NaElement('dummy_tag'))
+ assert "Authorization" not in [x[0] for x in request.header_items()]
diff --git a/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/module_utils/test_netapp_module.py b/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/module_utils/test_netapp_module.py
new file mode 100644
index 00000000..8b31ed7f
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/module_utils/test_netapp_module.py
@@ -0,0 +1,400 @@
+# Copyright (c) 2018 NetApp
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+''' unit tests for module_utils netapp_module.py '''
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+
+import sys
+
+import pytest
+
+from ansible_collections.netapp.ontap.tests.unit.compat import unittest
+from ansible_collections.netapp.ontap.plugins.module_utils.netapp_module import NetAppModule as na_helper
+
+
+class AnsibleFailJson(Exception):
+ """Exception class to be raised by module.fail_json and caught by the test case"""
+
+
+class MockModule(object):
+ ''' rough mock for an Ansible module class '''
+ def __init__(self, required_param=None, not_required_param=None, unqualified_param=None):
+ self.argument_spec = dict(
+ required_param=dict(required=True),
+ not_required_param=dict(required=False),
+ unqualified_param=dict(),
+ feature_flags=dict(type='dict')
+ )
+ self.params = dict(
+ required_param=required_param,
+ not_required_param=not_required_param,
+ unqualified_param=unqualified_param,
+ feature_flags=dict(type='dict')
+ )
+
+ def fail_json(self, *args, **kwargs): # pylint: disable=unused-argument
+ """function to simulate 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 test_get_cd_action_create(self):
+ ''' validate cd_action for create '''
+ current = None
+ desired = {'state': 'present'}
+ my_obj = na_helper()
+ result = my_obj.get_cd_action(current, desired)
+ assert result == 'create'
+
+ def test_get_cd_action_delete(self):
+ ''' validate cd_action for delete '''
+ current = {'state': 'absent'}
+ desired = {'state': 'absent'}
+ my_obj = na_helper()
+ result = my_obj.get_cd_action(current, desired)
+ assert result == 'delete'
+
+ def test_get_cd_action(self):
+ ''' validate cd_action for returning None '''
+ current = None
+ desired = {'state': 'absent'}
+ my_obj = na_helper()
+ result = my_obj.get_cd_action(current, desired)
+ assert result is None
+
+ def test_get_modified_attributes_for_no_data(self):
+ ''' validate modified attributes when current is None '''
+ current = None
+ desired = {'name': 'test'}
+ my_obj = na_helper()
+ result = my_obj.get_modified_attributes(current, desired)
+ assert result == {}
+
+ def test_get_modified_attributes(self):
+ ''' validate modified attributes '''
+ current = {'name': ['test', 'abcd', 'xyz', 'pqr'], 'state': 'present'}
+ desired = {'name': ['abcd', 'abc', 'xyz', 'pqr'], 'state': 'absent'}
+ my_obj = na_helper()
+ result = my_obj.get_modified_attributes(current, desired)
+ assert result == desired
+
+ def test_get_modified_attributes_for_intersecting_mixed_list(self):
+ ''' validate modified attributes for list diff '''
+ current = {'name': [2, 'four', 'six', 8]}
+ desired = {'name': ['a', 8, 'ab', 'four', 'abcd']}
+ my_obj = na_helper()
+ result = my_obj.get_modified_attributes(current, desired, True)
+ assert result == {'name': ['a', 'ab', 'abcd']}
+
+ def test_get_modified_attributes_for_intersecting_list(self):
+ ''' validate modified attributes for list diff '''
+ current = {'name': ['two', 'four', 'six', 'eight']}
+ desired = {'name': ['a', 'six', 'ab', 'four', 'abc']}
+ my_obj = na_helper()
+ result = my_obj.get_modified_attributes(current, desired, True)
+ assert result == {'name': ['a', 'ab', 'abc']}
+
+ def test_get_modified_attributes_for_nonintersecting_list(self):
+ ''' validate modified attributes for list diff '''
+ current = {'name': ['two', 'four', 'six', 'eight']}
+ desired = {'name': ['a', 'ab', 'abd']}
+ my_obj = na_helper()
+ result = my_obj.get_modified_attributes(current, desired, True)
+ assert result == {'name': ['a', 'ab', 'abd']}
+
+ def test_get_modified_attributes_for_list_of_dicts_no_data(self):
+ ''' validate modified attributes for list diff '''
+ current = None
+ desired = {'address_blocks': [{'start': '10.20.10.40', 'size': 5}]}
+ my_obj = na_helper()
+ result = my_obj.get_modified_attributes(current, desired, True)
+ assert result == {}
+
+ def test_get_modified_attributes_for_intersecting_list_of_dicts(self):
+ ''' validate modified attributes for list diff '''
+ current = {'address_blocks': [{'start': '10.10.10.23', 'size': 5}, {'start': '10.10.10.30', 'size': 5}]}
+ desired = {'address_blocks': [{'start': '10.10.10.23', 'size': 5}, {'start': '10.10.10.30', 'size': 5}, {'start': '10.20.10.40', 'size': 5}]}
+ my_obj = na_helper()
+ result = my_obj.get_modified_attributes(current, desired, True)
+ assert result == {'address_blocks': [{'start': '10.20.10.40', 'size': 5}]}
+
+ def test_get_modified_attributes_for_nonintersecting_list_of_dicts(self):
+ ''' validate modified attributes for list diff '''
+ current = {'address_blocks': [{'start': '10.10.10.23', 'size': 5}, {'start': '10.10.10.30', 'size': 5}]}
+ desired = {'address_blocks': [{'start': '10.20.10.23', 'size': 5}, {'start': '10.20.10.30', 'size': 5}, {'start': '10.20.10.40', 'size': 5}]}
+ my_obj = na_helper()
+ result = my_obj.get_modified_attributes(current, desired, True)
+ assert result == {'address_blocks': [{'start': '10.20.10.23', 'size': 5}, {'start': '10.20.10.30', 'size': 5}, {'start': '10.20.10.40', 'size': 5}]}
+
+ def test_get_modified_attributes_for_list_diff(self):
+ ''' validate modified attributes for list diff '''
+ current = {'name': ['test', 'abcd'], 'state': 'present'}
+ desired = {'name': ['abcd', 'abc'], 'state': 'present'}
+ my_obj = na_helper()
+ result = my_obj.get_modified_attributes(current, desired, True)
+ assert result == {'name': ['abc']}
+
+ def test_get_modified_attributes_for_no_change(self):
+ ''' validate modified attributes for same data in current and desired '''
+ current = {'name': 'test'}
+ desired = {'name': 'test'}
+ my_obj = na_helper()
+ result = my_obj.get_modified_attributes(current, desired)
+ assert result == {}
+
+ def test_get_modified_attributes_for_an_empty_desired_list(self):
+ ''' validate modified attributes for an empty desired list '''
+ current = {'snapmirror_label': ['daily', 'weekly', 'monthly'], 'state': 'present'}
+ desired = {'snapmirror_label': [], 'state': 'present'}
+ my_obj = na_helper()
+ result = my_obj.get_modified_attributes(current, desired)
+ assert result == {'snapmirror_label': []}
+
+ def test_get_modified_attributes_for_an_empty_desired_list_diff(self):
+ ''' validate modified attributes for an empty desired list with diff'''
+ current = {'snapmirror_label': ['daily', 'weekly', 'monthly'], 'state': 'present'}
+ desired = {'snapmirror_label': [], 'state': 'present'}
+ my_obj = na_helper()
+ result = my_obj.get_modified_attributes(current, desired, True)
+ assert result == {'snapmirror_label': []}
+
+ def test_get_modified_attributes_for_an_empty_current_list(self):
+ ''' validate modified attributes for an empty current list '''
+ current = {'snapmirror_label': [], 'state': 'present'}
+ desired = {'snapmirror_label': ['daily', 'weekly', 'monthly'], 'state': 'present'}
+ my_obj = na_helper()
+ result = my_obj.get_modified_attributes(current, desired)
+ assert result == {'snapmirror_label': ['daily', 'weekly', 'monthly']}
+
+ def test_get_modified_attributes_for_an_empty_current_list_diff(self):
+ ''' validate modified attributes for an empty current list with diff'''
+ current = {'snapmirror_label': [], 'state': 'present'}
+ desired = {'snapmirror_label': ['daily', 'weekly', 'monthly'], 'state': 'present'}
+ my_obj = na_helper()
+ result = my_obj.get_modified_attributes(current, desired, True)
+ assert result == {'snapmirror_label': ['daily', 'weekly', 'monthly']}
+
+ def test_get_modified_attributes_for_empty_lists(self):
+ ''' validate modified attributes for empty lists '''
+ current = {'snapmirror_label': [], 'state': 'present'}
+ desired = {'snapmirror_label': [], 'state': 'present'}
+ my_obj = na_helper()
+ result = my_obj.get_modified_attributes(current, desired)
+ assert result == {}
+
+ def test_get_modified_attributes_for_empty_lists_diff(self):
+ ''' validate modified attributes for empty lists with diff '''
+ current = {'snapmirror_label': [], 'state': 'present'}
+ desired = {'snapmirror_label': [], 'state': 'present'}
+ my_obj = na_helper()
+ result = my_obj.get_modified_attributes(current, desired, True)
+ assert result == {}
+
+ def test_get_modified_attributes_equal_lists_with_duplicates(self):
+ ''' validate modified attributes for equal lists with duplicates '''
+ current = {'schedule': ['hourly', 'daily', 'daily', 'weekly', 'monthly', 'daily'], 'state': 'present'}
+ desired = {'schedule': ['hourly', 'daily', 'daily', 'weekly', 'monthly', 'daily'], 'state': 'present'}
+ my_obj = na_helper()
+ result = my_obj.get_modified_attributes(current, desired, False)
+ assert result == {}
+
+ def test_get_modified_attributes_equal_lists_with_duplicates_diff(self):
+ ''' validate modified attributes for equal lists with duplicates with diff '''
+ current = {'schedule': ['hourly', 'daily', 'daily', 'weekly', 'monthly', 'daily'], 'state': 'present'}
+ desired = {'schedule': ['hourly', 'daily', 'daily', 'weekly', 'monthly', 'daily'], 'state': 'present'}
+ my_obj = na_helper()
+ result = my_obj.get_modified_attributes(current, desired, True)
+ assert result == {}
+
+ def test_get_modified_attributes_for_current_list_with_duplicates(self):
+ ''' validate modified attributes for current list with duplicates '''
+ current = {'schedule': ['hourly', 'daily', 'daily', 'weekly', 'monthly', 'daily'], 'state': 'present'}
+ desired = {'schedule': ['daily', 'daily', 'weekly', 'monthly'], 'state': 'present'}
+ my_obj = na_helper()
+ result = my_obj.get_modified_attributes(current, desired, False)
+ assert result == {'schedule': ['daily', 'daily', 'weekly', 'monthly']}
+
+ def test_get_modified_attributes_for_current_list_with_duplicates_diff(self):
+ ''' validate modified attributes for current list with duplicates with diff '''
+ current = {'schedule': ['hourly', 'daily', 'daily', 'weekly', 'monthly', 'daily'], 'state': 'present'}
+ desired = {'schedule': ['daily', 'daily', 'weekly', 'monthly'], 'state': 'present'}
+ my_obj = na_helper()
+ result = my_obj.get_modified_attributes(current, desired, True)
+ assert result == {'schedule': []}
+
+ def test_get_modified_attributes_for_desired_list_with_duplicates(self):
+ ''' validate modified attributes for desired list with duplicates '''
+ current = {'schedule': ['daily', 'weekly', 'monthly'], 'state': 'present'}
+ desired = {'schedule': ['hourly', 'daily', 'daily', 'weekly', 'monthly', 'daily'], 'state': 'present'}
+ my_obj = na_helper()
+ result = my_obj.get_modified_attributes(current, desired, False)
+ assert result == {'schedule': ['hourly', 'daily', 'daily', 'weekly', 'monthly', 'daily']}
+
+ def test_get_modified_attributes_for_desired_list_with_duplicates_diff(self):
+ ''' validate modified attributes for desired list with duplicates with diff '''
+ current = {'schedule': ['daily', 'weekly', 'monthly'], 'state': 'present'}
+ desired = {'schedule': ['hourly', 'daily', 'daily', 'weekly', 'monthly', 'daily'], 'state': 'present'}
+ my_obj = na_helper()
+ result = my_obj.get_modified_attributes(current, desired, True)
+ assert result == {'schedule': ['hourly', 'daily', 'daily']}
+
+ def test_is_rename_action_for_empty_input(self):
+ ''' validate rename action for input None '''
+ source = None
+ target = None
+ my_obj = na_helper()
+ result = my_obj.is_rename_action(source, target)
+ assert result == source
+
+ def test_is_rename_action_for_no_source(self):
+ ''' validate rename action when source is None '''
+ source = None
+ target = 'test2'
+ my_obj = na_helper()
+ result = my_obj.is_rename_action(source, target)
+ assert result is False
+
+ def test_is_rename_action_for_no_target(self):
+ ''' validate rename action when target is None '''
+ source = 'test2'
+ target = None
+ my_obj = na_helper()
+ result = my_obj.is_rename_action(source, target)
+ assert result is True
+
+ def test_is_rename_action(self):
+ ''' validate rename action '''
+ source = 'test'
+ target = 'test2'
+ my_obj = na_helper()
+ result = my_obj.is_rename_action(source, target)
+ assert result is False
+
+ def test_required_is_not_set_to_none(self):
+ ''' if a key is present, without a value, Ansible sets it to None '''
+ my_obj = na_helper()
+ my_module = MockModule()
+ print(my_module.argument_spec)
+ with pytest.raises(AnsibleFailJson) as exc:
+ my_obj.check_and_set_parameters(my_module)
+ msg = 'required_param requires a value, got: None'
+ assert exc.value.args[0]['msg'] == msg
+
+ # force a value different than None
+ my_module.params['required_param'] = 1
+ my_params = my_obj.check_and_set_parameters(my_module)
+ assert set(my_params.keys()) == set(['required_param', 'feature_flags'])
+
+ def test_sanitize_wwn_no_action(self):
+ ''' no change '''
+ initiator = 'tEsT'
+ expected = initiator
+ my_obj = na_helper()
+ result = my_obj.sanitize_wwn(initiator)
+ assert result == expected
+
+ def test_sanitize_wwn_no_action_valid_iscsi(self):
+ ''' no change '''
+ initiator = 'iqn.1995-08.com.eXaMpLe:StRiNg'
+ expected = initiator
+ my_obj = na_helper()
+ result = my_obj.sanitize_wwn(initiator)
+ assert result == expected
+
+ def test_sanitize_wwn_no_action_valid_wwn(self):
+ ''' no change '''
+ initiator = '01:02:03:04:0A:0b:0C:0d'
+ expected = initiator.lower()
+ my_obj = na_helper()
+ result = my_obj.sanitize_wwn(initiator)
+ assert result == expected
+
+ def test_filter_empty_dict(self):
+ ''' empty dict return empty dict '''
+ my_obj = na_helper()
+ arg = dict()
+ result = my_obj.filter_out_none_entries(arg)
+ assert arg == result
+
+ def test_filter_empty_list(self):
+ ''' empty list return empty list '''
+ my_obj = na_helper()
+ arg = list()
+ result = my_obj.filter_out_none_entries(arg)
+ assert arg == result
+
+ def test_filter_typeerror_on_none(self):
+ ''' empty list return empty list '''
+ my_obj = na_helper()
+ arg = None
+ with pytest.raises(TypeError) as exc:
+ my_obj.filter_out_none_entries(arg)
+ msg = "unexpected type <class 'NoneType'>"
+ if sys.version_info < (3, 0):
+ # the assert fails on 2.x
+ return
+ assert exc.value.args[0] == msg
+
+ def test_filter_typeerror_on_str(self):
+ ''' empty list return empty list '''
+ my_obj = na_helper()
+ arg = ""
+ with pytest.raises(TypeError) as exc:
+ my_obj.filter_out_none_entries(arg)
+ msg = "unexpected type <class 'str'>"
+ if sys.version_info < (3, 0):
+ # the assert fails on 2.x
+ return
+ assert exc.value.args[0] == msg
+
+ def test_filter_simple_dict(self):
+ ''' simple dict return simple dict '''
+ my_obj = na_helper()
+ arg = dict(a=None, b=1, c=None, d=2, e=3)
+ expected = dict(b=1, d=2, e=3)
+ result = my_obj.filter_out_none_entries(arg)
+ assert expected == result
+
+ def test_filter_simple_list(self):
+ ''' simple list return simple list '''
+ my_obj = na_helper()
+ arg = [None, 2, 3, None, 5]
+ expected = [2, 3, 5]
+ result = my_obj.filter_out_none_entries(arg)
+ assert expected == result
+
+ def test_filter_dict_dict(self):
+ ''' simple dict return simple dict '''
+ my_obj = na_helper()
+ arg = dict(a=None, b=dict(u=1, v=None, w=2), c=dict(), d=2, e=3)
+ expected = dict(b=dict(u=1, w=2), d=2, e=3)
+ result = my_obj.filter_out_none_entries(arg)
+ assert expected == result
+
+ def test_filter_list_list(self):
+ ''' simple list return simple list '''
+ my_obj = na_helper()
+ arg = [None, [1, None, 3], 3, None, 5]
+ expected = [[1, 3], 3, 5]
+ result = my_obj.filter_out_none_entries(arg)
+ assert expected == result
+
+ def test_filter_dict_list_dict(self):
+ ''' simple dict return simple dict '''
+ my_obj = na_helper()
+ arg = dict(a=None, b=[dict(u=1, v=None, w=2), 5, None, dict(x=6, y=None)], c=dict(), d=2, e=3)
+ expected = dict(b=[dict(u=1, w=2), 5, dict(x=6)], d=2, e=3)
+ result = my_obj.filter_out_none_entries(arg)
+ assert expected == result
+
+ def test_filter_list_dict_list(self):
+ ''' simple list return simple list '''
+ my_obj = na_helper()
+ arg = [None, [1, None, 3], dict(a=None, b=[7, None, 9], c=None, d=dict(u=None, v=10)), None, 5]
+ expected = [[1, 3], dict(b=[7, 9], d=dict(v=10)), 5]
+ result = my_obj.filter_out_none_entries(arg)
+ assert expected == result
diff --git a/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_aggregate.py b/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_aggregate.py
new file mode 100644
index 00000000..58d4eac1
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_aggregate.py
@@ -0,0 +1,419 @@
+# (c) 2018, NetApp, Inc
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+""" unit tests for Ansible module: na_ontap_aggregate """
+
+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.ontap.tests.unit.compat import unittest
+from ansible_collections.netapp.ontap.tests.unit.compat.mock import patch, Mock
+import ansible_collections.netapp.ontap.plugins.module_utils.netapp as netapp_utils
+
+from ansible_collections.netapp.ontap.plugins.modules.na_ontap_aggregate \
+ import NetAppOntapAggregate as my_module # module under test
+
+if not netapp_utils.has_netapp_lib():
+ pytestmark = pytest.mark.skip('skipping as missing required netapp_lib')
+
+
+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"""
+
+
+class AnsibleFailJson(Exception):
+ """Exception class to be raised by module.fail_json and caught by the test case"""
+
+
+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)
+
+
+AGGR_NAME = 'aggr_name'
+OS_NAME = 'abc'
+
+
+class MockONTAPConnection(object):
+ ''' mock server connection to ONTAP host '''
+
+ def __init__(self, kind=None, parm1=None, parm2=None):
+ ''' save arguments '''
+ self.type = kind
+ self.parm1 = parm1
+ self.parm2 = parm2
+ self.xml_in = None
+ self.xml_out = None
+ self.zapis = list()
+
+ def invoke_successfully(self, xml, enable_tunneling): # pylint: disable=unused-argument
+ ''' mock invoke_successfully returning xml data '''
+ self.xml_in = xml
+ print('request:', xml.to_string())
+ zapi = xml.get_name()
+ self.zapis.append(zapi)
+ if zapi == 'aggr-object-store-get-iter':
+ if self.type in ('aggregate_no_object_store',):
+ xml = None
+ else:
+ xml = self.build_object_store_info()
+ elif self.type in ('aggregate', 'aggr_disks', 'aggr_mirrors', 'aggregate_no_object_store'):
+ with_os = self.type != 'aggregate_no_object_store'
+ xml = self.build_aggregate_info(self.parm1, self.parm2, with_object_store=with_os)
+ if self.type in ('aggr_disks', 'aggr_mirrors'):
+ self.type = 'disks'
+ elif self.type == 'no_aggregate':
+ xml = None
+ elif self.type == 'no_aggregate_then_aggregate':
+ xml = None
+ self.type = 'aggregate'
+ elif self.type == 'disks':
+ xml = self.build_disk_info()
+ elif self.type == 'aggregate_fail':
+ raise netapp_utils.zapi.NaApiError(code='TEST', message="This exception is from the unit test")
+ self.xml_out = xml
+ return xml
+
+ @staticmethod
+ def build_aggregate_info(vserver, aggregate, with_object_store):
+ ''' build xml data for aggregate and vserser-info '''
+ xml = netapp_utils.zapi.NaElement('xml')
+ data = {'num-records': 3,
+ 'attributes-list':
+ {'aggr-attributes':
+ {'aggregate-name': aggregate,
+ 'aggr-raid-attributes': {'state': 'offline'}
+ },
+ 'object-store-information': {'object-store-name': 'abc'}
+ },
+ 'vserver-info':
+ {'vserver-name': vserver
+ }
+ }
+ if not with_object_store:
+ del data['attributes-list']['object-store-information']
+ xml.translate_struct(data)
+ print(xml.to_string())
+ return xml
+
+ @staticmethod
+ def build_object_store_info():
+ ''' build xml data for object_store '''
+ xml = netapp_utils.zapi.NaElement('xml')
+ data = {'num-records': 3,
+ 'attributes-list':
+ {'object-store-information': {'object-store-name': 'abc'}
+ }
+ }
+ xml.translate_struct(data)
+ print(xml.to_string())
+ return xml
+
+ @staticmethod
+ def build_disk_info():
+ ''' build xml data for disk '''
+ xml = netapp_utils.zapi.NaElement('xml')
+ data = {'num-records': 1,
+ 'attributes-list': [
+ {'disk-info':
+ {'disk-name': '1',
+ 'disk-raid-info':
+ {'disk-aggregate-info':
+ {'plex-name': 'plex0'}
+ }}},
+ {'disk-info':
+ {'disk-name': '2',
+ 'disk-raid-info':
+ {'disk-aggregate-info':
+ {'plex-name': 'plex0'}
+ }}},
+ {'disk-info':
+ {'disk-name': '3',
+ 'disk-raid-info':
+ {'disk-aggregate-info':
+ {'plex-name': 'plexM'}
+ }}},
+ {'disk-info':
+ {'disk-name': '4',
+ 'disk-raid-info':
+ {'disk-aggregate-info':
+ {'plex-name': 'plexM'}
+ }}},
+ ]}
+ xml.translate_struct(data)
+ print(xml.to_string())
+ return xml
+
+
+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)
+ self.server = MockONTAPConnection('aggregate', '12', 'name')
+ # whether to use a mock or a simulator
+ self.onbox = False
+ self.zapis = list()
+
+ def set_default_args(self):
+ if self.onbox:
+ hostname = '10.193.74.78'
+ username = 'admin'
+ password = 'netapp1!'
+ name = 'name'
+ else:
+ hostname = 'hostname'
+ username = 'username'
+ password = 'password'
+ name = AGGR_NAME
+ return dict({
+ 'hostname': hostname,
+ 'username': username,
+ 'password': password,
+ 'name': name
+ })
+
+ def call_command(self, module_args, what=None):
+ ''' utility function to call apply '''
+ args = dict(self.set_default_args())
+ args.update(module_args)
+ set_module_args(args)
+ my_obj = my_module()
+ my_obj.asup_log_for_cserver = Mock(return_value=None)
+ aggregate = 'aggregate'
+ if what == 'disks':
+ aggregate = 'aggr_disks'
+ elif what == 'mirrors':
+ aggregate = 'aggr_mirrors'
+ elif what is not None:
+ aggregate = what
+
+ if not self.onbox:
+ # mock the connection
+ my_obj.server = MockONTAPConnection(aggregate, '12', AGGR_NAME)
+ self.zapis = my_obj.server.zapis
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ return exc.value.args[0]['changed']
+
+ def test_module_fail_when_required_args_missing(self):
+ ''' required arguments are reported as errors '''
+ with pytest.raises(AnsibleFailJson) as exc:
+ set_module_args({})
+ my_module()
+ print('Info: %s' % exc.value.args[0]['msg'])
+
+ def test_create(self):
+ module_args = {
+ 'disk_count': '2',
+ 'is_mirrored': 'true',
+ }
+ changed = self.call_command(module_args, what='no_aggregate')
+ assert changed
+ assert 'aggr-object-store-attach' not in self.zapis
+
+ def test_create_with_object_store(self):
+ module_args = {
+ 'disk_count': '2',
+ 'is_mirrored': 'true',
+ 'object_store_name': 'abc'
+ }
+ changed = self.call_command(module_args, what='no_aggregate')
+ assert changed
+ assert 'aggr-object-store-attach' in self.zapis
+
+ def test_is_mirrored(self):
+ module_args = {
+ 'disk_count': '2',
+ 'is_mirrored': 'true',
+ }
+ changed = self.call_command(module_args)
+ assert not changed
+
+ def test_disks_list(self):
+ module_args = {
+ 'disks': ['1', '2'],
+ }
+ changed = self.call_command(module_args, 'disks')
+ assert not changed
+
+ def test_mirror_disks(self):
+ module_args = {
+ 'disks': ['1', '2'],
+ 'mirror_disks': ['3', '4']
+ }
+ changed = self.call_command(module_args, 'mirrors')
+ assert not changed
+
+ def test_spare_pool(self):
+ module_args = {
+ 'disk_count': '2',
+ 'spare_pool': 'Pool1'
+ }
+ changed = self.call_command(module_args)
+ assert not changed
+
+ def test_rename(self):
+ module_args = {
+ 'from_name': 'test_name2'
+ }
+ changed = self.call_command(module_args, 'no_aggregate_then_aggregate')
+ assert changed
+ assert 'aggr-rename' in self.zapis
+
+ def test_rename_error_no_from(self):
+ module_args = {
+ 'from_name': 'test_name2'
+ }
+ with pytest.raises(AnsibleFailJson) as exc:
+ self.call_command(module_args, 'no_aggregate')
+ msg = 'Error renaming: aggregate %s does not exist' % module_args['from_name']
+ assert msg in exc.value.args[0]['msg']
+
+ def test_rename_with_add_object_store(self):
+ module_args = {
+ 'from_name': 'test_name2'
+ }
+ changed = self.call_command(module_args, 'aggregate_no_object_store')
+ assert not changed
+
+ def test_object_store_present(self):
+ module_args = {
+ 'object_store_name': 'abc'
+ }
+ changed = self.call_command(module_args)
+ assert not changed
+
+ def test_object_store_create(self):
+ module_args = {
+ 'object_store_name': 'abc'
+ }
+ changed = self.call_command(module_args, 'aggregate_no_object_store')
+ assert changed
+
+ def test_object_store_modify(self):
+ ''' not supported '''
+ module_args = {
+ 'object_store_name': 'def'
+ }
+ with pytest.raises(AnsibleFailJson) as exc:
+ self.call_command(module_args)
+ msg = 'Error: object store %s is already associated with aggregate %s.' % (OS_NAME, AGGR_NAME)
+ assert msg in exc.value.args[0]['msg']
+
+ def test_if_all_methods_catch_exception(self):
+ module_args = {}
+ module_args.update(self.set_default_args())
+ module_args.update({'service_state': 'online'})
+ module_args.update({'unmount_volumes': 'True'})
+ module_args.update({'from_name': 'test_name2'})
+ set_module_args(module_args)
+ my_obj = my_module()
+ if not self.onbox:
+ my_obj.server = MockONTAPConnection('aggregate_fail')
+ with pytest.raises(AnsibleFailJson) as exc:
+ my_obj.aggr_get_iter(module_args.get('name'))
+ assert '' in exc.value.args[0]['msg']
+ with pytest.raises(AnsibleFailJson) as exc:
+ my_obj.aggregate_online()
+ assert 'Error changing the state of aggregate' in exc.value.args[0]['msg']
+ with pytest.raises(AnsibleFailJson) as exc:
+ my_obj.aggregate_offline()
+ assert 'Error changing the state of aggregate' in exc.value.args[0]['msg']
+ with pytest.raises(AnsibleFailJson) as exc:
+ my_obj.create_aggr()
+ assert 'Error provisioning aggregate' in exc.value.args[0]['msg']
+ with pytest.raises(AnsibleFailJson) as exc:
+ my_obj.delete_aggr()
+ assert 'Error removing aggregate' in exc.value.args[0]['msg']
+ with pytest.raises(AnsibleFailJson) as exc:
+ my_obj.rename_aggregate()
+ assert 'Error renaming aggregate' in exc.value.args[0]['msg']
+ with pytest.raises(AnsibleFailJson) as exc:
+ my_obj.asup_log_for_cserver = Mock(return_value=None)
+ my_obj.apply()
+ assert 'TEST:This exception is from the unit test' in exc.value.args[0]['msg']
+
+ def test_disks_bad_mapping(self):
+ module_args = {
+ 'disks': ['0'],
+ }
+ with pytest.raises(AnsibleFailJson) as exc:
+ self.call_command(module_args, 'mirrors')
+ msg = "Error mapping disks for aggregate %s: cannot not match disks with current aggregate disks." % AGGR_NAME
+ assert exc.value.args[0]['msg'].startswith(msg)
+
+ def test_disks_overlapping_mirror(self):
+ module_args = {
+ 'disks': ['1', '2', '3'],
+ }
+ with pytest.raises(AnsibleFailJson) as exc:
+ self.call_command(module_args, 'mirrors')
+ msg = "Error mapping disks for aggregate %s: found overlapping plexes:" % AGGR_NAME
+ assert exc.value.args[0]['msg'].startswith(msg)
+
+ def test_disks_removing_disk(self):
+ module_args = {
+ 'disks': ['1'],
+ }
+ with pytest.raises(AnsibleFailJson) as exc:
+ self.call_command(module_args, 'mirrors')
+ msg = "Error removing disks is not supported. Aggregate %s: these disks cannot be removed: ['2']." % AGGR_NAME
+ assert exc.value.args[0]['msg'].startswith(msg)
+
+ def test_disks_removing_mirror_disk(self):
+ module_args = {
+ 'disks': ['1', '2'],
+ 'mirror_disks': ['4', '6']
+ }
+ with pytest.raises(AnsibleFailJson) as exc:
+ self.call_command(module_args, 'mirrors')
+ msg = "Error removing disks is not supported. Aggregate %s: these disks cannot be removed: ['3']." % AGGR_NAME
+ assert exc.value.args[0]['msg'].startswith(msg)
+
+ def test_disks_add(self):
+ module_args = {
+ 'disks': ['1', '2', '5'],
+ }
+ changed = self.call_command(module_args, 'disks')
+ assert changed
+
+ def test_mirror_disks_add(self):
+ module_args = {
+ 'disks': ['1', '2', '5'],
+ 'mirror_disks': ['3', '4', '6']
+ }
+ changed = self.call_command(module_args, 'mirrors')
+ assert changed
+
+ def test_mirror_disks_add_unbalanced(self):
+ module_args = {
+ 'disks': ['1', '2'],
+ 'mirror_disks': ['3', '4', '6']
+ }
+ with pytest.raises(AnsibleFailJson) as exc:
+ self.call_command(module_args, 'mirrors')
+ msg = "Error cannot add mirror disks ['6'] without adding disks for aggregate %s." % AGGR_NAME
+ assert exc.value.args[0]['msg'].startswith(msg)
diff --git a/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_autosupport.py b/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_autosupport.py
new file mode 100644
index 00000000..c5c591f7
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_autosupport.py
@@ -0,0 +1,245 @@
+# (c) 2018, NetApp, Inc
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+''' unit test template for ONTAP Ansible module '''
+
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+import json
+import pytest
+
+from ansible_collections.netapp.ontap.tests.unit.compat import unittest
+from ansible_collections.netapp.ontap.tests.unit.compat.mock import patch, Mock
+from ansible.module_utils import basic
+from ansible.module_utils._text import to_bytes
+import ansible_collections.netapp.ontap.plugins.module_utils.netapp as netapp_utils
+
+from ansible_collections.netapp.ontap.plugins.modules.na_ontap_autosupport \
+ import NetAppONTAPasup as asup_module # module under test
+
+if not netapp_utils.has_netapp_lib():
+ pytestmark = pytest.mark.skip('skipping as missing required netapp_lib')
+
+
+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 MockONTAPConnection(object):
+ ''' mock server connection to ONTAP host '''
+
+ def __init__(self, kind=None, data=None):
+ ''' save arguments '''
+ self.kind = kind
+ self.params = data
+ self.xml_in = None
+ self.xml_out = None
+
+ def invoke_successfully(self, xml, enable_tunneling): # pylint: disable=unused-argument
+ ''' mock invoke_successfully returning xml data '''
+ self.xml_in = xml
+ if self.kind == 'asup':
+ xml = self.build_asup_config_info(self.params)
+ self.xml_out = xml
+ return xml
+
+ @staticmethod
+ def build_asup_config_info(asup_data):
+ ''' build xml data for asup-config '''
+ xml = netapp_utils.zapi.NaElement('xml')
+ attributes = {'attributes': {'autosupport-config-info': {
+ 'node-name': asup_data['node_name'],
+ 'is-enabled': asup_data['is_enabled'],
+ 'is-support-enabled': asup_data['support'],
+ 'proxy-url': asup_data['proxy_url'],
+ 'post-url': asup_data['post_url'],
+ 'transport': asup_data['transport'],
+ 'is-node-in-subject': 'false',
+ 'from': 'test',
+ 'mail-hosts': [{'string': '1.2.3.4'}, {'string': '4.5.6.8'}],
+ 'noteto': [{'mail-address': 'abc@test.com'},
+ {'mail-address': 'def@test.com'}],
+ }}}
+ xml.translate_struct(attributes)
+ return xml
+
+
+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)
+ self.server = MockONTAPConnection()
+ self.mock_asup = {
+ 'node_name': 'test-vsim1',
+ 'transport': 'https',
+ 'support': 'false',
+ 'post_url': 'testbed.netapp.com/asupprod/post/1.0/postAsup',
+ 'proxy_url': 'something.com',
+ }
+
+ def mock_args(self):
+ return {
+ 'node_name': self.mock_asup['node_name'],
+ 'transport': self.mock_asup['transport'],
+ 'support': self.mock_asup['support'],
+ 'post_url': self.mock_asup['post_url'],
+ 'proxy_url': self.mock_asup['proxy_url'],
+ 'hostname': 'host',
+ 'username': 'admin',
+ 'password': 'password',
+ }
+
+ def get_asup_mock_object(self, kind=None, enabled='false'):
+ """
+ Helper method to return an na_ontap_volume object
+ :param kind: passes this param to MockONTAPConnection()
+ :return: na_ontap_volume object
+ """
+ asup_obj = asup_module()
+ asup_obj.autosupport_log = Mock(return_value=None)
+ if kind is None:
+ asup_obj.server = MockONTAPConnection()
+ else:
+ data = self.mock_asup
+ data['is_enabled'] = enabled
+ asup_obj.server = MockONTAPConnection(kind='asup', data=data)
+ return asup_obj
+
+ def test_module_fail_when_required_args_missing(self):
+ ''' required arguments are reported as errors '''
+ with pytest.raises(AnsibleFailJson) as exc:
+ set_module_args({})
+ asup_module()
+ print('Info: %s' % exc.value.args[0]['msg'])
+
+ def test_enable_asup(self):
+ ''' a more interesting test '''
+ data = self.mock_args()
+ set_module_args(data)
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_asup_mock_object('asup').apply()
+ assert exc.value.args[0]['changed']
+
+ def test_disable_asup(self):
+ ''' a more interesting test '''
+ # enable asup
+ data = self.mock_args()
+ data['state'] = 'absent'
+ set_module_args(data)
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_asup_mock_object(kind='asup', enabled='true').apply()
+ assert exc.value.args[0]['changed']
+
+ def test_result_from_get(self):
+ ''' Check boolean and service_state conversion from get '''
+ data = self.mock_args()
+ set_module_args(data)
+ obj = self.get_asup_mock_object(kind='asup', enabled='true')
+ # constructed based on valued passed in self.mock_asup and build_asup_config_info()
+ expected_dict = {
+ 'node_name': 'test-vsim1',
+ 'service_state': 'started',
+ 'support': False,
+ 'hostname_in_subject': False,
+ 'transport': self.mock_asup['transport'],
+ 'post_url': self.mock_asup['post_url'],
+ 'proxy_url': self.mock_asup['proxy_url'],
+ 'from_address': 'test',
+ 'mail_hosts': ['1.2.3.4', '4.5.6.8'],
+ 'partner_addresses': [],
+ 'to_addresses': [],
+ 'noteto': ['abc@test.com', 'def@test.com']
+ }
+ result = obj.get_autosupport_config()
+ assert result == expected_dict
+
+ def test_modify_config(self):
+ ''' Check boolean and service_state conversion from get '''
+ data = self.mock_args()
+ data['transport'] = 'http'
+ data['post_url'] = 'somethingelse.com'
+ data['proxy_url'] = 'somethingelse.com'
+ set_module_args(data)
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_asup_mock_object('asup').apply()
+ assert exc.value.args[0]['changed']
+
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_autosupport.NetAppONTAPasup.get_autosupport_config')
+ def test_get_called(self, get_asup):
+ data = self.mock_args()
+ set_module_args(data)
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_asup_mock_object('asup').apply()
+ get_asup.assert_called_with()
+
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_autosupport.NetAppONTAPasup.modify_autosupport_config')
+ def test_modify_called(self, modify_asup):
+ data = self.mock_args()
+ data['transport'] = 'http'
+ set_module_args(data)
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_asup_mock_object('asup').apply()
+ modify_asup.assert_called_with({'transport': 'http', 'service_state': 'started'})
+
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_autosupport.NetAppONTAPasup.modify_autosupport_config')
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_autosupport.NetAppONTAPasup.get_autosupport_config')
+ def test_modify_not_called(self, get_asup, modify_asup):
+ data = self.mock_args()
+ set_module_args(data)
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_asup_mock_object('asup').apply()
+ get_asup.assert_called_with()
+ modify_asup.assert_not_called()
+
+ def test_modify_packet(self):
+ '''check XML construction for nested attributes like mail-hosts, noteto, partner-address, and to'''
+ data = self.mock_args()
+ set_module_args(data)
+ obj = self.get_asup_mock_object(kind='asup', enabled='true')
+ modify_dict = {
+ 'noteto': ['one@test.com'],
+ 'partner_addresses': ['firstpartner@test.com'],
+ 'mail_hosts': ['1.1.1.1'],
+ 'to_addresses': ['first@test.com']
+ }
+ obj.modify_autosupport_config(modify_dict)
+ xml = obj.server.xml_in
+ for key in ['noteto', 'to', 'partner-address']:
+ assert xml[key] is not None
+ assert xml[key]['mail-address'] is not None
+ assert xml['noteto']['mail-address'] == modify_dict['noteto'][0]
+ assert xml['to']['mail-address'] == modify_dict['to_addresses'][0]
+ assert xml['partner-address']['mail-address'] == modify_dict['partner_addresses'][0]
+ assert xml['mail-hosts'] is not None
+ assert xml['mail-hosts']['string'] is not None
+ assert xml['mail-hosts']['string'] == modify_dict['mail_hosts'][0]
diff --git a/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_autosupport_invoke.py b/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_autosupport_invoke.py
new file mode 100644
index 00000000..b250bdef
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_autosupport_invoke.py
@@ -0,0 +1,135 @@
+# (c) 2020, NetApp, Inc
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+''' unit tests for Ansible module: na_ontap_autosupport_invoke '''
+
+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.ontap.tests.unit.compat import unittest
+from ansible_collections.netapp.ontap.tests.unit.compat.mock import patch, Mock
+import ansible_collections.netapp.ontap.plugins.module_utils.netapp as netapp_utils
+
+from ansible_collections.netapp.ontap.plugins.modules.na_ontap_autosupport_invoke \
+ import NetAppONTAPasupInvoke as invoke_module # module under test
+
+# REST API canned responses when mocking send_request
+SRR = {
+ # common responses
+ 'is_rest': (200, {}, None),
+ 'is_zapi': (400, {}, "Unreachable"),
+ 'empty_good': (200, {}, None),
+ 'end_of_sequence': (500, None, "Unexpected call to send_request"),
+ 'generic_error': (400, None, "Expected error")
+}
+
+
+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"""
+
+
+class AnsibleFailJson(Exception):
+ """Exception class to be raised by module.fail_json and caught by the test case"""
+
+
+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 MockONTAPConnection(object):
+ ''' mock server connection to ONTAP host '''
+
+ def invoke_successfully(self, xml, enable_tunneling):
+ raise netapp_utils.zapi.NaApiError('test', 'Expected error')
+
+
+class TestMyModule(unittest.TestCase):
+ ''' Unit tests for na_ontap_wwpn_alias '''
+
+ 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)
+ self.mock_invoke = {
+ 'name': 'test_node',
+ 'message': 'test_message',
+ 'type': 'all'
+ }
+
+ def mock_args(self):
+ return {
+ 'message': self.mock_invoke['message'],
+ 'name': self.mock_invoke['name'],
+ 'type': self.mock_invoke['type'],
+ 'hostname': 'test_host',
+ 'username': 'test_user',
+ 'password': 'test_pass!'
+ }
+
+ def get_invoke_mock_object(self, use_rest=True):
+ invoke_obj = invoke_module()
+ if not use_rest:
+ invoke_obj.ems_log_event = Mock()
+ invoke_obj.server = MockONTAPConnection()
+ return invoke_obj
+
+ @patch('ansible_collections.netapp.ontap.plugins.module_utils.netapp.OntapRestAPI.send_request')
+ def test_rest_successful_send(self, mock_request):
+ '''Test successful send message'''
+ data = self.mock_args()
+ set_module_args(data)
+ mock_request.side_effect = [
+ SRR['is_rest'],
+ SRR['empty_good'],
+ SRR['end_of_sequence']
+ ]
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_invoke_mock_object().apply()
+ assert exc.value.args[0]['changed']
+
+ @patch('ansible_collections.netapp.ontap.plugins.module_utils.netapp.OntapRestAPI.send_request')
+ def test_rest_send_error(self, mock_request):
+ '''Test rest send error'''
+ data = self.mock_args()
+ set_module_args(data)
+ mock_request.side_effect = [
+ SRR['is_rest'],
+ SRR['generic_error'],
+ SRR['empty_good'],
+ SRR['end_of_sequence']
+ ]
+ with pytest.raises(AnsibleFailJson) as exc:
+ self.get_invoke_mock_object().apply()
+ msg = "Error on sending autosupport message to node %s: Expected error." % data['name']
+ assert exc.value.args[0]['msg'] == msg
+
+ def test_zapi_send_error(self):
+ '''Test rest send error'''
+ data = self.mock_args()
+ data['use_rest'] = 'Never'
+ set_module_args(data)
+ with pytest.raises(AnsibleFailJson) as exc:
+ self.get_invoke_mock_object(use_rest=False).apply()
+ msg = "Error on sending autosupport message to node %s: NetApp API failed. Reason - test:Expected error." % data['name']
+ assert exc.value.args[0]['msg'] == msg
diff --git a/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_broadcast_domain.py b/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_broadcast_domain.py
new file mode 100644
index 00000000..86a0b8d2
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_broadcast_domain.py
@@ -0,0 +1,309 @@
+# (c) 2018, NetApp, Inc
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+''' unit test template for ONTAP Ansible module '''
+
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+import json
+import pytest
+
+from ansible_collections.netapp.ontap.tests.unit.compat import unittest
+from ansible_collections.netapp.ontap.tests.unit.compat.mock import patch, Mock
+from ansible.module_utils import basic
+from ansible.module_utils._text import to_bytes
+import ansible_collections.netapp.ontap.plugins.module_utils.netapp as netapp_utils
+
+from ansible_collections.netapp.ontap.plugins.modules.na_ontap_broadcast_domain \
+ import NetAppOntapBroadcastDomain as broadcast_domain_module # module under test
+
+if not netapp_utils.has_netapp_lib():
+ pytestmark = pytest.mark.skip('skipping as missing required netapp_lib')
+
+
+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 MockONTAPConnection(object):
+ ''' mock server connection to ONTAP host '''
+
+ def __init__(self, kind=None, data=None):
+ ''' save arguments '''
+ self.type = kind
+ self.params = data
+ self.xml_in = None
+ self.xml_out = None
+
+ def invoke_successfully(self, xml, enable_tunneling): # pylint: disable=unused-argument
+ ''' mock invoke_successfully returning xml data '''
+ self.xml_in = xml
+ if self.type == 'broadcast_domain':
+ xml = self.build_broadcast_domain_info(self.params)
+ self.xml_out = xml
+ return xml
+
+ @staticmethod
+ def build_broadcast_domain_info(broadcast_domain_details):
+ ''' build xml data for broadcast_domain info '''
+ xml = netapp_utils.zapi.NaElement('xml')
+ attributes = {
+ 'num-records': 1,
+ 'attributes-list': {
+ 'net-port-broadcast-domain-info': {
+ 'broadcast-domain': broadcast_domain_details['name'],
+ 'ipspace': broadcast_domain_details['ipspace'],
+ 'mtu': broadcast_domain_details['mtu'],
+ 'ports': {
+ 'port-info': {
+ 'port': 'test_port_1'
+ }
+ }
+ }
+
+ }
+ }
+ xml.translate_struct(attributes)
+ return xml
+
+
+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)
+ self.server = MockONTAPConnection()
+ self.mock_broadcast_domain = {
+ 'name': 'test_broadcast_domain',
+ 'mtu': '1000',
+ 'ipspace': 'Default',
+ 'ports': 'test_port_1'
+ }
+
+ def mock_args(self):
+ return {
+ 'name': self.mock_broadcast_domain['name'],
+ 'ipspace': self.mock_broadcast_domain['ipspace'],
+ 'mtu': self.mock_broadcast_domain['mtu'],
+ 'ports': self.mock_broadcast_domain['ports'],
+ 'hostname': 'test',
+ 'username': 'test_user',
+ 'password': 'test_pass!'
+ }
+
+ def get_broadcast_domain_mock_object(self, kind=None, data=None):
+ """
+ Helper method to return an na_ontap_volume object
+ :param kind: passes this param to MockONTAPConnection()
+ :param data: passes this param to MockONTAPConnection()
+ :return: na_ontap_volume object
+ """
+ broadcast_domain_obj = broadcast_domain_module()
+ broadcast_domain_obj.asup_log_for_cserver = Mock(return_value=None)
+ broadcast_domain_obj.cluster = Mock()
+ broadcast_domain_obj.cluster.invoke_successfully = Mock()
+ if kind is None:
+ broadcast_domain_obj.server = MockONTAPConnection()
+ else:
+ if data is None:
+ broadcast_domain_obj.server = MockONTAPConnection(kind='broadcast_domain', data=self.mock_broadcast_domain)
+ else:
+ broadcast_domain_obj.server = MockONTAPConnection(kind='broadcast_domain', data=data)
+ return broadcast_domain_obj
+
+ def test_module_fail_when_required_args_missing(self):
+ ''' required arguments are reported as errors '''
+ with pytest.raises(AnsibleFailJson) as exc:
+ set_module_args({})
+ broadcast_domain_module()
+ print('Info: %s' % exc.value.args[0]['msg'])
+
+ def test_get_nonexistent_net_route(self):
+ ''' Test if get_broadcast_domain returns None for non-existent broadcast_domain '''
+ set_module_args(self.mock_args())
+ result = self.get_broadcast_domain_mock_object().get_broadcast_domain()
+ assert result is None
+
+ def test_create_error_missing_broadcast_domain(self):
+ ''' Test if create throws an error if broadcast_domain is not specified'''
+ data = self.mock_args()
+ del data['name']
+ set_module_args(data)
+ with pytest.raises(AnsibleFailJson) as exc:
+ self.get_broadcast_domain_mock_object('broadcast_domain').create_broadcast_domain()
+ msg = 'missing required arguments: name'
+ assert exc.value.args[0]['msg'] == msg
+
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_broadcast_domain.NetAppOntapBroadcastDomain.create_broadcast_domain')
+ def test_successful_create(self, create_broadcast_domain):
+ ''' Test successful create '''
+ data = self.mock_args()
+ set_module_args(data)
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_broadcast_domain_mock_object().apply()
+ assert exc.value.args[0]['changed']
+ create_broadcast_domain.assert_called_with()
+
+ def test_create_idempotency(self):
+ ''' Test create idempotency '''
+ set_module_args(self.mock_args())
+ obj = self.get_broadcast_domain_mock_object('broadcast_domain')
+ with pytest.raises(AnsibleExitJson) as exc:
+ obj.apply()
+ assert not exc.value.args[0]['changed']
+
+ def test_modify_mtu(self):
+ ''' Test successful modify mtu '''
+ data = self.mock_args()
+ data['mtu'] = '1200'
+ set_module_args(data)
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_broadcast_domain_mock_object('broadcast_domain').apply()
+ assert exc.value.args[0]['changed']
+
+ def test_modify_ipspace_idempotency(self):
+ ''' Test modify ipsapce idempotency'''
+ data = self.mock_args()
+ data['ipspace'] = 'Cluster'
+ set_module_args(data)
+ with pytest.raises(AnsibleFailJson) as exc:
+ self.get_broadcast_domain_mock_object('broadcast_domain').apply()
+ msg = 'A domain ipspace can not be modified after the domain has been created.'
+ assert exc.value.args[0]['msg'] == msg
+
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_broadcast_domain.NetAppOntapBroadcastDomain.add_broadcast_domain_ports')
+ def test_add_ports(self, add_broadcast_domain_ports):
+ ''' Test successful modify ports '''
+ data = self.mock_args()
+ data['ports'] = 'test_port_1,test_port_2'
+ set_module_args(data)
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_broadcast_domain_mock_object('broadcast_domain').apply()
+ assert exc.value.args[0]['changed']
+ add_broadcast_domain_ports.assert_called_with(['test_port_2'])
+
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_broadcast_domain.NetAppOntapBroadcastDomain.delete_broadcast_domain_ports')
+ def test_delete_ports(self, delete_broadcast_domain_ports):
+ ''' Test successful modify ports '''
+ data = self.mock_args()
+ data['ports'] = ''
+ set_module_args(data)
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_broadcast_domain_mock_object('broadcast_domain').apply()
+ assert exc.value.args[0]['changed']
+ delete_broadcast_domain_ports.assert_called_with(['test_port_1'])
+
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_broadcast_domain.NetAppOntapBroadcastDomain.modify_broadcast_domain')
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_broadcast_domain.NetAppOntapBroadcastDomain.split_broadcast_domain')
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_broadcast_domain.NetAppOntapBroadcastDomain.get_broadcast_domain')
+ def test_split_broadcast_domain(self, get_broadcast_domain, split_broadcast_domain, modify_broadcast_domain):
+ ''' Test successful split broadcast domain '''
+ data = self.mock_args()
+ data['from_name'] = 'test_broadcast_domain'
+ data['name'] = 'test_broadcast_domain_2'
+ data['ports'] = 'test_port_2'
+ set_module_args(data)
+ current = {
+ 'name': 'test_broadcast_domain',
+ 'mtu': '1000',
+ 'ipspace': 'Default',
+ 'ports': ['test_port_1,test_port2']
+ }
+ get_broadcast_domain.side_effect = [
+ None,
+ current,
+ current
+ ]
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_broadcast_domain_mock_object().apply()
+ assert exc.value.args[0]['changed']
+ modify_broadcast_domain.assert_not_called()
+ split_broadcast_domain.assert_called_with()
+
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_broadcast_domain.NetAppOntapBroadcastDomain.delete_broadcast_domain')
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_broadcast_domain.NetAppOntapBroadcastDomain.modify_broadcast_domain')
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_broadcast_domain.NetAppOntapBroadcastDomain.get_broadcast_domain')
+ def test_split_broadcast_domain_modify_delete(self, get_broadcast_domain, modify_broadcast_domain, delete_broadcast_domain):
+ ''' Test successful split broadcast domain '''
+ data = self.mock_args()
+ data['from_name'] = 'test_broadcast_domain'
+ data['name'] = 'test_broadcast_domain_2'
+ data['ports'] = ['test_port_1', 'test_port_2']
+ data['mtu'] = '1200'
+ set_module_args(data)
+
+ current = {
+ 'name': 'test_broadcast_domain',
+ 'mtu': '1000',
+ 'ipspace': 'Default',
+ 'ports': ['test_port_1', 'test_port2']
+ }
+ get_broadcast_domain.side_effect = [
+ None,
+ current,
+ current
+ ]
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_broadcast_domain_mock_object().apply()
+ assert exc.value.args[0]['changed']
+ delete_broadcast_domain.assert_called_with('test_broadcast_domain')
+ modify_broadcast_domain.assert_called_with()
+
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_broadcast_domain.NetAppOntapBroadcastDomain.get_broadcast_domain')
+ def test_split_broadcast_domain_not_exist(self, get_broadcast_domain):
+ ''' Test successful split broadcast domain '''
+ data = self.mock_args()
+ data['from_name'] = 'test_broadcast_domain'
+ data['name'] = 'test_broadcast_domain_2'
+ data['ports'] = 'test_port_2'
+ set_module_args(data)
+
+ get_broadcast_domain.side_effect = [
+ None,
+ None,
+ ]
+ with pytest.raises(AnsibleFailJson) as exc:
+ self.get_broadcast_domain_mock_object().apply()
+ msg = 'A domain can not be split if it does not exist.'
+ assert exc.value.args[0]['msg'], msg
+
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_broadcast_domain.NetAppOntapBroadcastDomain.split_broadcast_domain')
+ def test_split_broadcast_domain_idempotency(self, split_broadcast_domain):
+ ''' Test successful split broadcast domain '''
+ data = self.mock_args()
+ data['from_name'] = 'test_broadcast_domain'
+ data['ports'] = 'test_port_1'
+ set_module_args(data)
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_broadcast_domain_mock_object('broadcast_domain').apply()
+ assert exc.value.args[0]['changed'] is False
+ split_broadcast_domain.assert_not_called()
diff --git a/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_cg_snapshot.py b/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_cg_snapshot.py
new file mode 100644
index 00000000..7bc8dfbc
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_cg_snapshot.py
@@ -0,0 +1,116 @@
+# (c) 2018, NetApp, Inc
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+''' unit tests for Ansible module: na_ontap_cg_snapshot'''
+
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+import json
+import pytest
+
+from ansible_collections.netapp.ontap.tests.unit.compat import unittest
+from ansible_collections.netapp.ontap.tests.unit.compat.mock import patch
+from ansible.module_utils import basic
+from ansible.module_utils._text import to_bytes
+import ansible_collections.netapp.ontap.plugins.module_utils.netapp as netapp_utils
+
+from ansible_collections.netapp.ontap.plugins.modules.na_ontap_cg_snapshot \
+ import NetAppONTAPCGSnapshot as my_module # module under test
+
+if not netapp_utils.has_netapp_lib():
+ pytestmark = pytest.mark.skip('skipping as missing required netapp_lib')
+
+
+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 MockONTAPConnection(object):
+ ''' mock server connection to ONTAP host '''
+
+ def __init__(self, kind=None, parm1=None):
+ ''' save arguments '''
+ self.type = kind
+ self.parm1 = parm1
+ self.xml_in = None
+ self.xml_out = None
+
+ def invoke_successfully(self, xml, enable_tunneling): # pylint: disable=unused-argument
+ ''' mock invoke_successfully returning xml data '''
+ self.xml_in = xml
+ if self.type == 'vserver':
+ xml = self.build_vserver_info(self.parm1)
+ self.xml_out = xml
+ return xml
+
+ @staticmethod
+ def build_vserver_info(vserver):
+ ''' build xml data for vserser-info '''
+ xml = netapp_utils.zapi.NaElement('xml')
+ attributes = netapp_utils.zapi.NaElement('attributes-list')
+ attributes.add_node_with_children('vserver-info',
+ **{'vserver-name': vserver})
+ xml.add_child_elem(attributes)
+ # print(xml.to_string())
+ return xml
+
+
+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)
+ self.server = MockONTAPConnection()
+
+ def test_module_fail_when_required_args_missing(self):
+ ''' required arguments are reported as errors '''
+ with pytest.raises(AnsibleFailJson) as exc:
+ set_module_args({})
+ my_module()
+ print('Info: %s' % exc.value.args[0]['msg'])
+
+ def test_ensure_command_called(self):
+ ''' a more interesting test '''
+ set_module_args({
+ 'vserver': 'vserver',
+ 'volumes': 'volumes',
+ 'snapshot': 'snapshot',
+ 'hostname': 'hostname',
+ 'username': 'username',
+ 'password': 'password',
+ })
+ my_obj = my_module()
+ my_obj.server = self.server
+ with pytest.raises(AnsibleFailJson) as exc:
+ my_obj.cgcreate()
+ msg = 'Error fetching CG ID for CG commit snapshot'
+ assert exc.value.args[0]['msg'] == msg
diff --git a/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_cifs.py b/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_cifs.py
new file mode 100644
index 00000000..d35f816c
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_cifs.py
@@ -0,0 +1,228 @@
+# (c) 2018, NetApp, Inc
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+''' unit tests ONTAP Ansible module: na_ontap_cifs '''
+
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+import json
+import pytest
+
+from ansible_collections.netapp.ontap.tests.unit.compat import unittest
+from ansible_collections.netapp.ontap.tests.unit.compat.mock import patch
+from ansible.module_utils import basic
+from ansible.module_utils._text import to_bytes
+import ansible_collections.netapp.ontap.plugins.module_utils.netapp as netapp_utils
+
+from ansible_collections.netapp.ontap.plugins.modules.na_ontap_cifs \
+ import NetAppONTAPCifsShare as my_module # module under test
+
+if not netapp_utils.has_netapp_lib():
+ pytestmark = pytest.mark.skip('skipping as missing required netapp_lib')
+
+
+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 MockONTAPConnection(object):
+ ''' mock server connection to ONTAP host '''
+
+ def __init__(self, kind=None):
+ ''' save arguments '''
+ self.type = kind
+ self.xml_in = None
+ self.xml_out = None
+
+ def invoke_successfully(self, xml, enable_tunneling): # pylint: disable=unused-argument
+ ''' mock invoke_successfully returning xml data '''
+ self.xml_in = xml
+ if self.type == 'cifs':
+ xml = self.build_cifs_info()
+ elif self.type == 'cifs_fail':
+ raise netapp_utils.zapi.NaApiError(code='TEST', message="This exception is from the unit test")
+ self.xml_out = xml
+ return xml
+
+ @staticmethod
+ def build_cifs_info():
+ ''' build xml data for cifs-info '''
+ xml = netapp_utils.zapi.NaElement('xml')
+ data = {'num-records': 1, 'attributes-list': {'cifs-share': {
+ 'share-name': 'test',
+ 'path': '/test',
+ 'vscan-fileop-profile': 'standard',
+ 'share-properties': [{'cifs-share-properties': 'browsable'},
+ {'cifs-share-properties': 'oplocks'}],
+ 'symlink-properties': [{'cifs-share-symlink-properties': 'enable'},
+ {'cifs-share-symlink-properties': 'read_only'}],
+ }}}
+ xml.translate_struct(data)
+ print(xml.to_string())
+ return xml
+
+
+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)
+ self.server = MockONTAPConnection()
+ self.onbox = False
+
+ def set_default_args(self):
+ if self.onbox:
+ hostname = '10.193.77.37'
+ username = 'admin'
+ password = 'netapp1!'
+ share_name = 'test'
+ path = '/test'
+ share_properties = 'browsable,oplocks'
+ symlink_properties = 'disable'
+ vscan_fileop_profile = 'standard'
+ vserver = 'abc'
+ else:
+ hostname = '10.193.77.37'
+ username = 'admin'
+ password = 'netapp1!'
+ share_name = 'test'
+ path = '/test'
+ share_properties = 'show_previous_versions'
+ symlink_properties = 'disable'
+ vscan_fileop_profile = 'no_scan'
+ vserver = 'abc'
+ return dict({
+ 'hostname': hostname,
+ 'username': username,
+ 'password': password,
+ 'share_name': share_name,
+ 'path': path,
+ 'share_properties': share_properties,
+ 'symlink_properties': symlink_properties,
+ 'vscan_fileop_profile': vscan_fileop_profile,
+ 'vserver': vserver
+ })
+
+ def test_module_fail_when_required_args_missing(self):
+ ''' required arguments are reported as errors '''
+ with pytest.raises(AnsibleFailJson) as exc:
+ set_module_args({})
+ my_module()
+ print('Info: %s' % exc.value.args[0]['msg'])
+
+ def test_ensure_cifs_get_called(self):
+ ''' fetching details of cifs '''
+ set_module_args(self.set_default_args())
+ my_obj = my_module()
+ my_obj.server = self.server
+ cifs_get = my_obj.get_cifs_share()
+ print('Info: test_cifs_share_get: %s' % repr(cifs_get))
+ assert not bool(cifs_get)
+
+ def test_ensure_apply_for_cifs_called(self):
+ ''' creating cifs share and checking idempotency '''
+ module_args = {}
+ module_args.update(self.set_default_args())
+ set_module_args(module_args)
+ my_obj = my_module()
+ if not self.onbox:
+ my_obj.server = self.server
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ print('Info: test_cifs_apply: %s' % repr(exc.value))
+ assert exc.value.args[0]['changed']
+ if not self.onbox:
+ my_obj.server = MockONTAPConnection('cifs')
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ print('Info: test_cifs_apply: %s' % repr(exc.value))
+ assert exc.value.args[0]['changed']
+
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_cifs.NetAppONTAPCifsShare.create_cifs_share')
+ def test_cifs_create_called(self, create_cifs_share):
+ ''' creating cifs'''
+ module_args = {}
+ module_args.update(self.set_default_args())
+ set_module_args(module_args)
+ my_obj = my_module()
+ if not self.onbox:
+ my_obj.server = MockONTAPConnection()
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ print('Info: test_cifs_apply: %s' % repr(exc.value))
+ create_cifs_share.assert_called_with()
+
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_cifs.NetAppONTAPCifsShare.delete_cifs_share')
+ def test_cifs_delete_called(self, delete_cifs_share):
+ ''' deleting cifs'''
+ module_args = {}
+ module_args.update(self.set_default_args())
+ module_args['state'] = 'absent'
+ set_module_args(module_args)
+ my_obj = my_module()
+ if not self.onbox:
+ my_obj.server = MockONTAPConnection('cifs')
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ print('Info: test_cifs_apply: %s' % repr(exc.value))
+ delete_cifs_share.assert_called_with()
+
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_cifs.NetAppONTAPCifsShare.modify_cifs_share')
+ def test_cifs_modify_called(self, modify_cifs_share):
+ ''' modifying cifs'''
+ module_args = {}
+ module_args.update(self.set_default_args())
+ set_module_args(module_args)
+ my_obj = my_module()
+ if not self.onbox:
+ my_obj.server = MockONTAPConnection('cifs')
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ print('Info: test_cifs_apply: %s' % repr(exc.value))
+ modify_cifs_share.assert_called_with()
+
+ def test_if_all_methods_catch_exception(self):
+ module_args = {}
+ module_args.update(self.set_default_args())
+ set_module_args(module_args)
+ my_obj = my_module()
+ if not self.onbox:
+ my_obj.server = MockONTAPConnection('cifs_fail')
+ with pytest.raises(AnsibleFailJson) as exc:
+ my_obj.create_cifs_share()
+ assert 'Error creating cifs-share' in exc.value.args[0]['msg']
+ with pytest.raises(AnsibleFailJson) as exc:
+ my_obj.delete_cifs_share()
+ assert 'Error deleting cifs-share' in exc.value.args[0]['msg']
+ with pytest.raises(AnsibleFailJson) as exc:
+ my_obj.modify_cifs_share()
+ assert 'Error modifying cifs-share' in exc.value.args[0]['msg']
diff --git a/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_cifs_server.py b/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_cifs_server.py
new file mode 100644
index 00000000..27b368ff
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_cifs_server.py
@@ -0,0 +1,222 @@
+# (c) 2018, NetApp, Inc
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+''' unit tests ONTAP Ansible module: na_ontap_cifs_server '''
+
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+import json
+import pytest
+
+from ansible_collections.netapp.ontap.tests.unit.compat import unittest
+from ansible_collections.netapp.ontap.tests.unit.compat.mock import patch
+from ansible.module_utils import basic
+from ansible.module_utils._text import to_bytes
+import ansible_collections.netapp.ontap.plugins.module_utils.netapp as netapp_utils
+
+from ansible_collections.netapp.ontap.plugins.modules.na_ontap_cifs_server \
+ import NetAppOntapcifsServer as my_module # module under test
+
+if not netapp_utils.has_netapp_lib():
+ pytestmark = pytest.mark.skip('skipping as missing required netapp_lib')
+
+
+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 MockONTAPConnection(object):
+ ''' mock server connection to ONTAP host '''
+
+ def __init__(self, kind=None, parm1=None, parm2=None):
+ ''' save arguments '''
+ self.type = kind
+ self.parm1 = parm1
+ self.parm2 = parm2
+ self.xml_in = None
+ self.xml_out = None
+
+ def invoke_successfully(self, xml, enable_tunneling): # pylint: disable=unused-argument
+ ''' mock invoke_successfully returning xml data '''
+ self.xml_in = xml
+ if self.type == 'cifs_server':
+ xml = self.build_vserver_info(self.parm1, self.parm2)
+ self.xml_out = xml
+ return xml
+
+ @staticmethod
+ def build_vserver_info(cifs_server, admin_status):
+ ''' build xml data for cifs-server-info '''
+ xml = netapp_utils.zapi.NaElement('xml')
+ data = {'num-records': 1,
+ 'attributes-list': {'cifs-server-config': {'cifs-server': cifs_server,
+ 'administrative-status': admin_status}}}
+ xml.translate_struct(data)
+ print(xml.to_string())
+ return xml
+
+
+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)
+ self.server = MockONTAPConnection()
+ self.use_vsim = False
+
+ def set_default_args(self):
+ if self.use_vsim:
+ hostname = '10.193.77.154'
+ username = 'admin'
+ password = 'netapp1!'
+ cifs_server = 'test'
+ vserver = 'ansible_test'
+ else:
+ hostname = 'hostname'
+ username = 'username'
+ password = 'password'
+ cifs_server = 'name'
+ vserver = 'vserver'
+ return dict({
+ 'hostname': hostname,
+ 'username': username,
+ 'password': password,
+ 'cifs_server_name': cifs_server,
+ 'vserver': vserver
+ })
+
+ def test_module_fail_when_required_args_missing(self):
+ ''' required arguments are reported as errors '''
+ with pytest.raises(AnsibleFailJson) as exc:
+ set_module_args({})
+ my_module()
+ print('Info: %s' % exc.value.args[0]['msg'])
+
+ def test_ensure_cifs_server_get_called(self):
+ ''' a more interesting test '''
+ set_module_args(self.set_default_args())
+ my_obj = my_module()
+ my_obj.server = self.server
+ cifs_server = my_obj.get_cifs_server()
+ print('Info: test_cifs_server_get: %s' % repr(cifs_server))
+ assert cifs_server is None
+
+ def test_ensure_cifs_server_apply_for_create_called(self):
+ ''' creating cifs server and checking idempotency '''
+ module_args = {}
+ module_args.update(self.set_default_args())
+ module_args.update({'cifs_server_name': 'create'})
+ set_module_args(module_args)
+ my_obj = my_module()
+ if not self.use_vsim:
+ my_obj.server = self.server
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ print('Info: test_cifs_server_apply: %s' % repr(exc.value))
+ assert exc.value.args[0]['changed']
+ if not self.use_vsim:
+ my_obj.server = MockONTAPConnection('cifs_server', 'create', 'up')
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ print('Info: test_cifs_server_apply_for_create: %s' % repr(exc.value))
+ assert not exc.value.args[0]['changed']
+
+ def test_ensure_cifs_server_apply_for_delete_called(self):
+ ''' deleting cifs server and checking idempotency '''
+ module_args = {}
+ module_args.update(self.set_default_args())
+ module_args.update({'cifs_server_name': 'delete'})
+ set_module_args(module_args)
+ my_obj = my_module()
+ if not self.use_vsim:
+ my_obj.server = MockONTAPConnection('cifs_server', 'delete', 'up')
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ print('Info: test_cifs_server_apply: %s' % repr(exc.value))
+ assert not exc.value.args[0]['changed']
+ module_args.update({'state': 'absent'})
+ set_module_args(module_args)
+ my_obj = my_module()
+ if not self.use_vsim:
+ my_obj.server = MockONTAPConnection('cifs_server', 'delete', 'up')
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ print('Info: test_cifs_server_delete: %s' % repr(exc.value))
+ assert exc.value.args[0]['changed']
+
+ def test_ensure_start_cifs_server_called(self):
+ ''' starting cifs server and checking idempotency '''
+ module_args = {}
+ module_args.update(self.set_default_args())
+ module_args.update({'cifs_server_name': 'delete'})
+ module_args.update({'service_state': 'started'})
+ set_module_args(module_args)
+ my_obj = my_module()
+ if not self.use_vsim:
+ my_obj.server = MockONTAPConnection('cifs_server', 'test', 'up')
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ print('Info: test_ensure_start_cifs_server: %s' % repr(exc.value))
+ assert not exc.value.args[0]['changed']
+ module_args.update({'service_state': 'stopped'})
+ set_module_args(module_args)
+ my_obj = my_module()
+ if not self.use_vsim:
+ my_obj.server = MockONTAPConnection('cifs_server', 'test', 'up')
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ print('Info: test_ensure_start_cifs_server: %s' % repr(exc.value))
+ assert exc.value.args[0]['changed']
+
+ def test_ensure_stop_cifs_server_called(self):
+ ''' stopping cifs server and checking idempotency '''
+ module_args = {}
+ module_args.update(self.set_default_args())
+ module_args.update({'cifs_server_name': 'delete'})
+ module_args.update({'service_state': 'stopped'})
+ set_module_args(module_args)
+ my_obj = my_module()
+ if not self.use_vsim:
+ my_obj.server = MockONTAPConnection('cifs_server', 'test', 'down')
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ print('Info: test_ensure_stop_cifs_server: %s' % repr(exc.value))
+ assert not exc.value.args[0]['changed']
+ module_args.update({'service_state': 'started'})
+ set_module_args(module_args)
+ my_obj = my_module()
+ if not self.use_vsim:
+ my_obj.server = MockONTAPConnection('cifs_server', 'test', 'down')
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ print('Info: test_ensure_stop_cifs_server: %s' % repr(exc.value))
+ assert exc.value.args[0]['changed']
diff --git a/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_cluster.py b/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_cluster.py
new file mode 100644
index 00000000..8d90c477
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_cluster.py
@@ -0,0 +1,429 @@
+# (c) 2018, NetApp, Inc
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+''' unit tests ONTAP Ansible module: na_ontap_cluster '''
+
+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.ontap.tests.unit.compat import unittest
+from ansible_collections.netapp.ontap.tests.unit.compat.mock import patch, Mock
+import ansible_collections.netapp.ontap.plugins.module_utils.netapp as netapp_utils
+
+from ansible_collections.netapp.ontap.plugins.modules.na_ontap_cluster \
+ import NetAppONTAPCluster as my_module # module under test
+
+if not netapp_utils.has_netapp_lib():
+ pytestmark = pytest.mark.skip('skipping as missing required netapp_lib')
+
+
+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"""
+
+
+class AnsibleFailJson(Exception):
+ """Exception class to be raised by module.fail_json and caught by the test case"""
+
+
+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 MockONTAPConnection(object):
+ ''' mock server connection to ONTAP host '''
+
+ def __init__(self, kind=None):
+ ''' save arguments '''
+ self.type = kind
+ self.xml_in = None
+ self.xml_out = None
+
+ def invoke_successfully(self, xml, enable_tunneling): # pylint: disable=unused-argument
+ ''' mock invoke_successfully returning xml data '''
+ self.xml_in = xml
+ if self.type == 'cluster':
+ xml = self.build_cluster_info()
+ if self.type == 'cluster_success':
+ xml = self.build_cluster_info_success()
+ elif self.type == 'cluster_add':
+ xml = self.build_add_node_info()
+ elif self.type == 'cluster_extra_input':
+ self.type = 'cluster' # success on second call
+ raise netapp_utils.zapi.NaApiError(code='TEST1', message="Extra input: single-node-cluster")
+ elif self.type == 'cluster_extra_input_loop':
+ raise netapp_utils.zapi.NaApiError(code='TEST2', message="Extra input: single-node-cluster")
+ elif self.type == 'cluster_extra_input_other':
+ raise netapp_utils.zapi.NaApiError(code='TEST3', message="Extra input: other-unexpected-element")
+ elif self.type == 'cluster_fail':
+ raise netapp_utils.zapi.NaApiError(code='TEST4', message="This exception is from the unit test")
+ self.xml_out = xml
+ return xml
+
+ def autosupport_log(self):
+ ''' mock autosupport log'''
+ return None
+
+ @staticmethod
+ def build_cluster_info():
+ ''' build xml data for cluster-create-join-progress-info '''
+ xml = netapp_utils.zapi.NaElement('xml')
+ attributes = {
+ 'attributes': {
+ 'cluster-create-join-progress-info': {
+ 'is-complete': 'true',
+ 'status': 'whatever'
+ }
+ }
+ }
+ xml.translate_struct(attributes)
+ return xml
+
+ @staticmethod
+ def build_cluster_info_success():
+ ''' build xml data for cluster-create-join-progress-info '''
+ xml = netapp_utils.zapi.NaElement('xml')
+ attributes = {
+ 'attributes': {
+ 'cluster-create-join-progress-info': {
+ 'is-complete': 'false',
+ 'status': 'success'
+ }
+ }
+ }
+ xml.translate_struct(attributes)
+ return xml
+
+ @staticmethod
+ def build_add_node_info():
+ ''' build xml data for cluster-create-add-node-status-info '''
+ xml = netapp_utils.zapi.NaElement('xml')
+ attributes = {
+ 'attributes-list': {
+ 'cluster-create-add-node-status-info': {
+ 'failure-msg': '',
+ 'status': 'success'
+ }
+ }
+ }
+ xml.translate_struct(attributes)
+ return xml
+
+
+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)
+ self.server = MockONTAPConnection()
+ self.use_vsim = False
+
+ def set_default_args(self):
+ if self.use_vsim:
+ hostname = '10.10.10.10'
+ username = 'admin'
+ password = 'password'
+ cluster_name = 'abc'
+ else:
+ hostname = '10.10.10.10'
+ username = 'admin'
+ password = 'password'
+ cluster_name = 'abc'
+ return dict({
+ 'hostname': hostname,
+ 'username': username,
+ 'password': password,
+ 'cluster_name': cluster_name
+ })
+
+ def test_module_fail_when_required_args_missing(self):
+ ''' required arguments are reported as errors '''
+ with pytest.raises(AnsibleFailJson) as exc:
+ set_module_args({})
+ my_module()
+ print('Info: %s' % exc.value.args[0]['msg'])
+
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_cluster.NetAppONTAPCluster.get_cluster_identity')
+ def test_ensure_apply_for_cluster_called(self, get_cl_id):
+ ''' creating cluster and checking idempotency '''
+ get_cl_id.return_value = None
+ module_args = {}
+ module_args.update(self.set_default_args())
+ set_module_args(module_args)
+ my_obj = my_module()
+ my_obj.autosupport_log = Mock(return_value=None)
+ if not self.use_vsim:
+ my_obj.server = MockONTAPConnection('cluster')
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ print('Info: test_cluster_apply: %s' % repr(exc.value))
+ assert exc.value.args[0]['changed']
+
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_cluster.NetAppONTAPCluster.get_cluster_identity')
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_cluster.NetAppONTAPCluster.create_cluster')
+ def test_cluster_create_called(self, cluster_create, get_cl_id):
+ ''' creating cluster'''
+ get_cl_id.return_value = None
+ module_args = {}
+ module_args.update(self.set_default_args())
+ set_module_args(module_args)
+ my_obj = my_module()
+ my_obj.autosupport_log = Mock(return_value=None)
+ if not self.use_vsim:
+ my_obj.server = MockONTAPConnection('cluster_success')
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ print('Info: test_cluster_apply: %s' % repr(exc.value))
+ cluster_create.assert_called_with()
+
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_cluster.NetAppONTAPCluster.get_cluster_identity')
+ def test_cluster_create_old_api(self, get_cl_id):
+ ''' creating cluster'''
+ get_cl_id.return_value = None
+ module_args = {}
+ module_args.update(self.set_default_args())
+ set_module_args(module_args)
+ my_obj = my_module()
+ my_obj.autosupport_log = Mock(return_value=None)
+ if not self.use_vsim:
+ my_obj.server = MockONTAPConnection('cluster_extra_input')
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ print('Info: test_cluster_apply: %s' % repr(exc.value))
+ assert exc.value.args[0]['changed']
+
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_cluster.NetAppONTAPCluster.get_cluster_identity')
+ def test_cluster_create_old_api_loop(self, get_cl_id):
+ ''' creating cluster'''
+ get_cl_id.return_value = None
+ module_args = {}
+ module_args.update(self.set_default_args())
+ set_module_args(module_args)
+ my_obj = my_module()
+ my_obj.autosupport_log = Mock(return_value=None)
+ if not self.use_vsim:
+ my_obj.server = MockONTAPConnection('cluster_extra_input_loop')
+ with pytest.raises(AnsibleFailJson) as exc:
+ my_obj.apply()
+ msg = 'TEST2:Extra input: single-node-cluster'
+ print('Info: test_cluster_apply: %s' % repr(exc.value))
+ assert msg in exc.value.args[0]['msg']
+
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_cluster.NetAppONTAPCluster.get_cluster_identity')
+ def test_cluster_create_old_api_other_extra(self, get_cl_id):
+ ''' creating cluster'''
+ get_cl_id.return_value = None
+ module_args = {}
+ module_args.update(self.set_default_args())
+ set_module_args(module_args)
+ my_obj = my_module()
+ my_obj.autosupport_log = Mock(return_value=None)
+ if not self.use_vsim:
+ my_obj.server = MockONTAPConnection('cluster_extra_input_other')
+ with pytest.raises(AnsibleFailJson) as exc:
+ my_obj.apply()
+ msg = 'TEST3:Extra input: other-unexpected-element'
+ print('Info: test_cluster_apply: %s' % repr(exc.value))
+ assert msg in exc.value.args[0]['msg']
+
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_cluster.NetAppONTAPCluster.get_cluster_ip_addresses')
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_cluster.NetAppONTAPCluster.get_cluster_identity')
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_cluster.NetAppONTAPCluster.add_node')
+ def test_add_node_called(self, add_node, get_cl_id, get_cl_ips):
+ ''' creating add_node'''
+ get_cl_ips.return_value = list()
+ get_cl_id.return_value = None
+ data = self.set_default_args()
+ del data['cluster_name']
+ data['cluster_ip_address'] = '10.10.10.10'
+ set_module_args(data)
+ my_obj = my_module()
+ my_obj.autosupport_log = Mock(return_value=None)
+ if not self.use_vsim:
+ my_obj.server = MockONTAPConnection('cluster_add')
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ print('Info: test_cluster_apply: %s' % repr(exc.value))
+ add_node.assert_called_with()
+ assert exc.value.args[0]['changed']
+
+ def test_if_all_methods_catch_exception(self):
+ module_args = {}
+ module_args.update(self.set_default_args())
+ set_module_args(module_args)
+ my_obj = my_module()
+ if not self.use_vsim:
+ my_obj.server = MockONTAPConnection('cluster_fail')
+ with pytest.raises(AnsibleFailJson) as exc:
+ my_obj.create_cluster()
+ assert 'Error creating cluster' in exc.value.args[0]['msg']
+ data = self.set_default_args()
+ data['cluster_ip_address'] = '10.10.10.10'
+ set_module_args(data)
+ my_obj = my_module()
+ if not self.use_vsim:
+ my_obj.server = MockONTAPConnection('cluster_fail')
+ with pytest.raises(AnsibleFailJson) as exc:
+ my_obj.add_node()
+ assert 'Error adding node with ip' in exc.value.args[0]['msg']
+
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_cluster.NetAppONTAPCluster.get_cluster_ip_addresses')
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_cluster.NetAppONTAPCluster.get_cluster_identity')
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_cluster.NetAppONTAPCluster.add_node')
+ def test_add_node_idempotent(self, add_node, get_cl_id, get_cl_ips):
+ ''' creating add_node'''
+ get_cl_ips.return_value = ['10.10.10.10']
+ get_cl_id.return_value = None
+ data = self.set_default_args()
+ del data['cluster_name']
+ data['cluster_ip_address'] = '10.10.10.10'
+ set_module_args(data)
+ my_obj = my_module()
+ my_obj.autosupport_log = Mock(return_value=None)
+ if not self.use_vsim:
+ my_obj.server = MockONTAPConnection('cluster_add')
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ print('Info: test_cluster_apply: %s' % repr(exc.value))
+ try:
+ add_node.assert_not_called()
+ except AttributeError:
+ # not supported with python <= 3.4
+ pass
+ assert not exc.value.args[0]['changed']
+
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_cluster.NetAppONTAPCluster.get_cluster_ip_addresses')
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_cluster.NetAppONTAPCluster.get_cluster_identity')
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_cluster.NetAppONTAPCluster.remove_node')
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_cluster.NetAppONTAPCluster.node_remove_wait')
+ def test_remove_node_ip(self, wait, remove_node, get_cl_id, get_cl_ips):
+ ''' creating add_node'''
+ get_cl_ips.return_value = ['10.10.10.10']
+ get_cl_id.return_value = None
+ wait.return_value = None
+ data = self.set_default_args()
+ # del data['cluster_name']
+ data['cluster_ip_address'] = '10.10.10.10'
+ data['state'] = 'absent'
+ set_module_args(data)
+ my_obj = my_module()
+ my_obj.autosupport_log = Mock(return_value=None)
+ if not self.use_vsim:
+ my_obj.server = MockONTAPConnection('cluster_add')
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ print('Info: test_cluster_apply: %s' % repr(exc.value))
+ remove_node.assert_called_with()
+ assert exc.value.args[0]['changed']
+
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_cluster.NetAppONTAPCluster.get_cluster_ip_addresses')
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_cluster.NetAppONTAPCluster.get_cluster_identity')
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_cluster.NetAppONTAPCluster.remove_node')
+ def test_remove_node_ip_idempotent(self, remove_node, get_cl_id, get_cl_ips):
+ ''' creating add_node'''
+ get_cl_ips.return_value = list()
+ get_cl_id.return_value = None
+ data = self.set_default_args()
+ # del data['cluster_name']
+ data['cluster_ip_address'] = '10.10.10.10'
+ data['state'] = 'absent'
+ set_module_args(data)
+ my_obj = my_module()
+ my_obj.autosupport_log = Mock(return_value=None)
+ if not self.use_vsim:
+ my_obj.server = MockONTAPConnection('cluster_add')
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ print('Info: test_cluster_apply: %s' % repr(exc.value))
+ try:
+ remove_node.assert_not_called()
+ except AttributeError:
+ # not supported with python <= 3.4
+ pass
+ assert not exc.value.args[0]['changed']
+
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_cluster.NetAppONTAPCluster.get_cluster_nodes')
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_cluster.NetAppONTAPCluster.get_cluster_identity')
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_cluster.NetAppONTAPCluster.remove_node')
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_cluster.NetAppONTAPCluster.node_remove_wait')
+ def test_remove_node_name(self, wait, remove_node, get_cl_id, get_cl_nodes):
+ ''' creating add_node'''
+ get_cl_nodes.return_value = ['node1', 'node2']
+ get_cl_id.return_value = None
+ wait.return_value = None
+ data = self.set_default_args()
+ # del data['cluster_name']
+ data['node_name'] = 'node2'
+ data['state'] = 'absent'
+ set_module_args(data)
+ my_obj = my_module()
+ my_obj.autosupport_log = Mock(return_value=None)
+ if not self.use_vsim:
+ my_obj.server = MockONTAPConnection('cluster_add')
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ print('Info: test_cluster_apply: %s' % repr(exc.value))
+ remove_node.assert_called_with()
+ assert exc.value.args[0]['changed']
+
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_cluster.NetAppONTAPCluster.get_cluster_nodes')
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_cluster.NetAppONTAPCluster.get_cluster_identity')
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_cluster.NetAppONTAPCluster.remove_node')
+ def test_remove_node_name_idempotent(self, remove_node, get_cl_id, get_cl_nodes):
+ ''' creating add_node'''
+ get_cl_nodes.return_value = ['node1', 'node2']
+ get_cl_id.return_value = None
+ data = self.set_default_args()
+ # del data['cluster_name']
+ data['node_name'] = 'node3'
+ data['state'] = 'absent'
+ set_module_args(data)
+ my_obj = my_module()
+ my_obj.autosupport_log = Mock(return_value=None)
+ if not self.use_vsim:
+ my_obj.server = MockONTAPConnection('cluster_add')
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ print('Info: test_cluster_apply: %s' % repr(exc.value))
+ try:
+ remove_node.assert_not_called()
+ except AttributeError:
+ # not supported with python <= 3.4
+ pass
+ assert not exc.value.args[0]['changed']
+
+ def test_remove_node_name_and_id(self):
+ ''' creating add_node'''
+ data = self.set_default_args()
+ # del data['cluster_name']
+ data['cluster_ip_address'] = '10.10.10.10'
+ data['node_name'] = 'node3'
+ data['state'] = 'absent'
+ set_module_args(data)
+ with pytest.raises(AnsibleFailJson) as exc:
+ my_module()
+ print('Info: test_remove_node_name_and_id: %s' % repr(exc.value))
+ msg = 'when state is "absent", parameters are mutually exclusive: cluster_ip_address|node_name'
+ assert msg in exc.value.args[0]['msg']
diff --git a/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_cluster_peer.py b/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_cluster_peer.py
new file mode 100644
index 00000000..690563c6
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_cluster_peer.py
@@ -0,0 +1,212 @@
+''' unit tests ONTAP Ansible module: na_ontap_cluster_peer '''
+
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+import json
+import pytest
+
+from ansible_collections.netapp.ontap.tests.unit.compat import unittest
+from ansible_collections.netapp.ontap.tests.unit.compat.mock import patch, Mock
+from ansible.module_utils import basic
+from ansible.module_utils._text import to_bytes
+import ansible_collections.netapp.ontap.plugins.module_utils.netapp as netapp_utils
+
+from ansible_collections.netapp.ontap.plugins.modules.na_ontap_cluster_peer \
+ import NetAppONTAPClusterPeer as my_module # module under test
+
+if not netapp_utils.has_netapp_lib():
+ pytestmark = pytest.mark.skip('skipping as missing required netapp_lib')
+
+
+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 MockONTAPConnection(object):
+ ''' mock server connection to ONTAP host '''
+
+ def __init__(self, kind=None, parm1=None):
+ ''' save arguments '''
+ self.type = kind
+ self.data = parm1
+ self.xml_in = None
+ self.xml_out = None
+
+ def invoke_successfully(self, xml, enable_tunneling): # pylint: disable=unused-argument
+ ''' mock invoke_successfully returning xml data '''
+ self.xml_in = xml
+ if self.type == 'cluster_peer':
+ xml = self.build_cluster_peer_info(self.data)
+ self.xml_out = xml
+ return xml
+
+ @staticmethod
+ def build_cluster_peer_info(parm1):
+ ''' build xml data for vserser-info '''
+ xml = netapp_utils.zapi.NaElement('xml')
+ attributes = {
+ 'num-records': 1,
+ 'attributes-list': {
+ 'cluster-peer-info': {
+ 'cluster-name': parm1['dest_cluster_name'],
+ 'peer-addresses': parm1['dest_intercluster_lifs']
+ }
+ }
+ }
+ xml.translate_struct(attributes)
+ return xml
+
+
+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)
+ self.server = MockONTAPConnection()
+ self.mock_cluster_peer = {
+ 'source_intercluster_lifs': '1.2.3.4,1.2.3.5',
+ 'dest_intercluster_lifs': '1.2.3.6,1.2.3.7',
+ 'passphrase': 'netapp123',
+ 'dest_hostname': '10.20.30.40',
+ 'dest_cluster_name': 'cluster2',
+ 'hostname': 'hostname',
+ 'username': 'username',
+ 'password': 'password',
+
+ }
+
+ def mock_args(self):
+ return {
+ 'source_intercluster_lifs': self.mock_cluster_peer['source_intercluster_lifs'],
+ 'dest_intercluster_lifs': self.mock_cluster_peer['dest_intercluster_lifs'],
+ 'passphrase': self.mock_cluster_peer['passphrase'],
+ 'dest_hostname': self.mock_cluster_peer['dest_hostname'],
+ 'dest_cluster_name': 'cluster2',
+ 'hostname': 'hostname',
+ 'username': 'username',
+ 'password': 'password',
+ }
+
+ def get_cluster_peer_mock_object(self, kind=None):
+ """
+ Helper method to return an na_ontap_cluster_peer object
+ :param kind: passes this param to MockONTAPConnection()
+ :return: na_ontap_cluster_peer object
+ """
+ cluster_peer_obj = my_module()
+ cluster_peer_obj.asup_log_for_cserver = Mock(return_value=None)
+ cluster_peer_obj.cluster = Mock()
+ cluster_peer_obj.cluster.invoke_successfully = Mock()
+ if kind is None:
+ cluster_peer_obj.server = MockONTAPConnection()
+ cluster_peer_obj.dest_server = MockONTAPConnection()
+ else:
+ cluster_peer_obj.server = MockONTAPConnection(kind=kind, parm1=self.mock_cluster_peer)
+ cluster_peer_obj.dest_server = MockONTAPConnection(kind=kind, parm1=self.mock_cluster_peer)
+ return cluster_peer_obj
+
+ def test_module_fail_when_required_args_missing(self):
+ ''' required arguments are reported as errors '''
+ with pytest.raises(AnsibleFailJson) as exc:
+ set_module_args({})
+ my_module()
+ print('Info: %s' % exc.value.args[0]['msg'])
+
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_cluster_peer.NetAppONTAPClusterPeer.cluster_peer_get')
+ def test_successful_create(self, cluster_peer_get):
+ ''' Test successful create '''
+ set_module_args(self.mock_args())
+ cluster_peer_get.side_effect = [
+ None,
+ None
+ ]
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_cluster_peer_mock_object().apply()
+ assert exc.value.args[0]['changed']
+
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_cluster_peer.NetAppONTAPClusterPeer.cluster_peer_get')
+ def test_create_idempotency(self, cluster_peer_get):
+ ''' Test create idempotency '''
+ set_module_args(self.mock_args())
+ current1 = {
+ 'cluster_name': 'cluster1',
+ 'peer-addresses': '1.2.3.6,1.2.3.7'
+ }
+ current2 = {
+ 'cluster_name': 'cluster2',
+ 'peer-addresses': '1.2.3.4,1.2.3.5'
+ }
+ cluster_peer_get.side_effect = [
+ current1,
+ current2
+ ]
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_cluster_peer_mock_object('cluster_peer').apply()
+ assert not exc.value.args[0]['changed']
+
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_cluster_peer.NetAppONTAPClusterPeer.cluster_peer_get')
+ def test_successful_delete(self, cluster_peer_get):
+ ''' Test delete existing cluster peer '''
+ data = self.mock_args()
+ data['state'] = 'absent'
+ data['source_cluster_name'] = 'cluster1'
+ set_module_args(data)
+ current1 = {
+ 'cluster_name': 'cluster1',
+ 'peer-addresses': '1.2.3.6,1.2.3.7'
+ }
+ current2 = {
+ 'cluster_name': 'cluster2',
+ 'peer-addresses': '1.2.3.4,1.2.3.5'
+ }
+ cluster_peer_get.side_effect = [
+ current1,
+ current2
+ ]
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_cluster_peer_mock_object('cluster_peer').apply()
+ assert exc.value.args[0]['changed']
+
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_cluster_peer.NetAppONTAPClusterPeer.cluster_peer_get')
+ def test_delete_idempotency(self, cluster_peer_get):
+ ''' Test delete idempotency '''
+ data = self.mock_args()
+ data['state'] = 'absent'
+ data['source_cluster_name'] = 'cluster2'
+ set_module_args(data)
+ cluster_peer_get.side_effect = [
+ None,
+ None
+ ]
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_cluster_peer_mock_object().apply()
+ assert not exc.value.args[0]['changed']
diff --git a/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_command.py b/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_command.py
new file mode 100644
index 00000000..bd13094b
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_command.py
@@ -0,0 +1,205 @@
+# (c) 2018, NetApp, Inc
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+''' unit test for ONTAP Command Ansible module '''
+
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+import json
+import pytest
+
+from ansible_collections.netapp.ontap.tests.unit.compat import unittest
+from ansible_collections.netapp.ontap.tests.unit.compat.mock import patch, Mock
+from ansible.module_utils import basic
+from ansible.module_utils._text import to_bytes
+import ansible_collections.netapp.ontap.plugins.module_utils.netapp as netapp_utils
+
+from ansible_collections.netapp.ontap.plugins.modules.na_ontap_command \
+ import NetAppONTAPCommand as my_module # module under test
+
+if not netapp_utils.has_netapp_lib():
+ pytestmark = pytest.mark.skip('skipping as missing required netapp_lib')
+
+
+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 MockONTAPConnection(object):
+ ''' mock server connection to ONTAP host '''
+
+ def __init__(self, kind=None, parm1=None):
+ ''' save arguments '''
+ self.type = kind
+ self.parm1 = parm1
+ self.xml_in = None
+ self.xml_out = None
+
+ def invoke_successfully(self, xml, enable_tunneling): # pylint: disable=unused-argument
+ ''' mock invoke_successfully returning xml data '''
+ self.xml_in = xml
+ # print(xml.to_string())
+
+ if self.type == 'version':
+ priv = xml.get_child_content('priv')
+ xml = self.build_version(priv, self.parm1)
+
+ self.xml_out = xml
+ return xml
+
+ @staticmethod
+ def build_version(priv, result):
+ ''' build xml data for version '''
+ prefix = 'NetApp Release'
+ if priv == 'advanced':
+ prefix = '\n' + prefix
+ xml = netapp_utils.zapi.NaElement('results')
+ xml.add_attr('status', 'status_ok')
+ xml.add_new_child('cli-output', prefix)
+ if result == "u'77'":
+ xml.add_new_child('cli-result-value', u'77')
+ elif result == "b'77'":
+ xml.add_new_child('cli-result-value', b'77')
+ else:
+ xml.add_new_child('cli-result-value', b'7' if result is None else result)
+ # print('XML ut:', xml.to_string())
+ return xml
+
+
+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)
+ self.server = MockONTAPConnection(kind='version')
+ # whether to use a mock or a simulator
+ self.use_vsim = 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({})
+ my_module()
+ print('Info: %s' % exc.value.args[0]['msg'])
+
+ @staticmethod
+ def set_default_args(vsim=False):
+ ''' populate hostname/username/password '''
+ if vsim:
+ hostname = '10.10.10.10'
+ username = 'admin'
+ password = 'admin'
+ else:
+ hostname = 'hostname'
+ username = 'username'
+ password = 'password'
+ return dict({
+ 'hostname': hostname,
+ 'username': username,
+ 'password': password,
+ 'https': True,
+ 'validate_certs': False
+ })
+
+ def call_command(self, module_args, vsim=False):
+ ''' utility function to call apply '''
+ module_args.update(self.set_default_args(vsim=vsim))
+ set_module_args(module_args)
+ my_obj = my_module()
+ my_obj.asup_log_for_cserver = Mock(return_value=None)
+ if not vsim:
+ # mock the connection
+ my_obj.server = self.server
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ msg = exc.value.args[0]['msg']
+ return msg
+
+ def test_default_priv(self):
+ ''' make sure privilege is not required '''
+ module_args = {
+ 'command': 'version',
+ }
+ msg = self.call_command(module_args, vsim=self.use_vsim)
+ needle = b'<cli-output>NetApp Release'
+ assert needle in msg
+ print('Version (raw): %s' % msg)
+
+ def test_admin_priv(self):
+ ''' make sure admin is accepted '''
+ module_args = {
+ 'command': 'version',
+ 'privilege': 'admin',
+ }
+ msg = self.call_command(module_args, vsim=self.use_vsim)
+ needle = b'<cli-output>NetApp Release'
+ assert needle in msg
+ print('Version (raw): %s' % msg)
+
+ def test_advanced_priv(self):
+ ''' make sure advanced is not required '''
+ module_args = {
+ 'command': 'version',
+ 'privilege': 'advanced',
+ }
+ msg = self.call_command(module_args, vsim=self.use_vsim)
+ # Interestingly, the ZAPI returns a slightly different response
+ needle = b'<cli-output>\nNetApp Release'
+ assert needle in msg
+ print('Version (raw): %s' % msg)
+
+ def get_dict_output(self, result):
+ ''' get result value after calling command module '''
+ print('In:', result)
+ module_args = {
+ 'command': 'version',
+ 'return_dict': 'true',
+ }
+ self.server = MockONTAPConnection(kind='version', parm1=result)
+ dict_output = self.call_command(module_args, vsim=self.use_vsim)
+ print('dict_output: %s' % repr(dict_output))
+ return dict_output['result_value']
+
+ def test_dict_output_77(self):
+ ''' make sure correct value is returned '''
+ result = '77'
+ assert self.get_dict_output(result) == int(result)
+
+ def test_dict_output_b77(self):
+ ''' make sure correct value is returned '''
+ result = b'77'
+ assert self.get_dict_output(result) == int(result)
+
+ def test_dict_output_u77(self):
+ ''' make sure correct value is returned '''
+ result = "u'77'"
+ assert self.get_dict_output(result) == int(eval(result))
diff --git a/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_dns.py b/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_dns.py
new file mode 100644
index 00000000..b514742b
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_dns.py
@@ -0,0 +1,321 @@
+# (c) 2018, NetApp, Inc
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+''' unit tests for Ansible module: na_ontap_dns'''
+
+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.ontap.tests.unit.compat import unittest
+from ansible_collections.netapp.ontap.tests.unit.compat.mock import patch
+import ansible_collections.netapp.ontap.plugins.module_utils.netapp as netapp_utils
+
+from ansible_collections.netapp.ontap.plugins.modules.na_ontap_dns \
+ import NetAppOntapDns as dns_module # module under test
+
+if not netapp_utils.has_netapp_lib():
+ pytestmark = pytest.mark.skip('skipping as missing required netapp_lib')
+HAS_NETAPP_ZAPI_MSG = "pip install netapp_lib is required"
+
+
+# REST API canned responses when mocking send_request
+SRR = {
+ # common responses
+ 'is_rest': (200, {}, None),
+ 'is_zapi': (400, {}, "Unreachable"),
+ 'empty_good': (200, {}, None),
+ 'end_of_sequence': (500, None, "Unexpected call to send_request"),
+ 'generic_error': (400, None, "Expected error"),
+ 'dns_record': (200, {"records": [{"domains": ['test.com'],
+ "servers": ['0.0.0.0'],
+ "svm": {"name": "svm1", "uuid": "02c9e252-41be-11e9-81d5-00a0986138f7"}}]}, None),
+ 'cluster_data': (200, {"dns_domains": ['test.com'],
+ "name_servers": ['0.0.0.0'],
+ "name": "cserver",
+ "uuid": "C2c9e252-41be-11e9-81d5-00a0986138f7"}, None),
+ 'cluster_name': (200, {"name": "cserver"}, 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"""
+
+
+class AnsibleFailJson(Exception):
+ """Exception class to be raised by module.fail_json and caught by the test case"""
+
+
+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 MockONTAPConnection(object):
+ ''' mock server connection to ONTAP host '''
+
+ def __init__(self, kind=None, data=None):
+ ''' save arguments '''
+ self.kind = kind
+ self.params = data
+ self.xml_in = None
+ self.xml_out = None
+
+ def invoke_successfully(self, xml, enable_tunneling): # pylint: disable=unused-argument
+ ''' mock invoke_successfully returning xml data '''
+ self.xml_in = xml
+ request = xml.to_string().decode('utf-8')
+ if request.startswith("<ems-autosupport-log>"):
+ xml = None # or something that may the logger happy, and you don't need @patch anymore
+ # or
+ # xml = build_ems_log_response()
+ elif request == "<net-dns-get/>":
+ if self.kind == 'create':
+ raise netapp_utils.zapi.NaApiError(code="15661")
+ else:
+ xml = self.build_dns_status_info()
+ elif request.startswith("<net-dns-create>"):
+ xml = self.build_dns_status_info()
+ if self.kind == 'enable':
+ xml = self.build_dns_status_info()
+ self.xml_out = xml
+ return xml
+
+ @staticmethod
+ def build_dns_status_info():
+ xml = netapp_utils.zapi.NaElement('xml')
+ nameservers = [{'ip-address': '0.0.0.0'}]
+ domains = [{'string': 'test.com'}]
+ attributes = {'num-records': 1,
+ 'attributes': {'net-dns-info': {'name-servers': nameservers,
+ 'domains': domains,
+ 'skip-config-validation': 'false'}}}
+ xml.translate_struct(attributes)
+ return xml
+
+
+class TestMyModule(unittest.TestCase):
+ ''' Unit tests for na_ontap_job_schedule '''
+
+ 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 mock_args(self):
+ return {
+ 'state': 'present',
+ 'vserver': 'vserver',
+ 'nameservers': ['0.0.0.0'],
+ 'domains': ['test.com'],
+ 'hostname': 'test',
+ 'username': 'test_user',
+ 'password': 'test_pass!'
+ }
+
+ def get_dns_mock_object(self, cx_type='zapi', kind=None, status=None):
+ dns_obj = dns_module()
+ if cx_type == 'zapi':
+ if kind is None:
+ dns_obj.server = MockONTAPConnection()
+ else:
+ dns_obj.server = MockONTAPConnection(kind=kind, data=status)
+ return dns_obj
+
+ def test_module_fail_when_required_args_missing(self):
+ ''' required arguments are reported as errors '''
+ with pytest.raises(AnsibleFailJson) as exc:
+ set_module_args({})
+ dns_module()
+ print('Info: %s' % exc.value.args[0]['msg'])
+
+ def test_idempotent_modify_dns(self):
+ data = self.mock_args()
+ data['use_rest'] = 'never'
+ set_module_args(data)
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_dns_mock_object('zapi', 'enable', 'false').apply()
+ assert not exc.value.args[0]['changed']
+
+ def test_successfully_modify_dns(self):
+ data = self.mock_args()
+ data['domains'] = ['new_test.com']
+ data['use_rest'] = 'never'
+ set_module_args(data)
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_dns_mock_object('zapi', 'enable', 'false').apply()
+ assert exc.value.args[0]['changed']
+
+ @patch('ansible_collections.netapp.ontap.plugins.module_utils.netapp.ems_log_event')
+ def test_idempotent_create_dns(self, mock_ems_log_event):
+ data = self.mock_args()
+ data['use_rest'] = 'never'
+ set_module_args(data)
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_dns_mock_object('zapi').apply()
+ assert not exc.value.args[0]['changed']
+
+ @patch('ansible_collections.netapp.ontap.plugins.module_utils.netapp.ems_log_event')
+ def test_successfully_create_dns(self, mock_ems_log_event):
+ data = self.mock_args()
+ print("create dns")
+ data['domains'] = ['new_test.com']
+ data['use_rest'] = 'never'
+ set_module_args(data)
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_dns_mock_object('zapi', 'create').apply()
+ assert exc.value.args[0]['changed']
+
+ @patch('ansible_collections.netapp.ontap.plugins.module_utils.netapp.OntapRestAPI.send_request')
+ def test_rest_error(self, mock_request):
+ data = self.mock_args()
+ set_module_args(data)
+ mock_request.side_effect = [
+ SRR['is_rest'],
+ SRR['generic_error'],
+ SRR['end_of_sequence']
+ ]
+ with pytest.raises(AnsibleFailJson) as exc:
+ self.get_dns_mock_object(cx_type='rest').apply()
+ assert exc.value.args[0]['msg'] == SRR['generic_error'][2]
+
+ @patch('ansible_collections.netapp.ontap.plugins.module_utils.netapp.OntapRestAPI.send_request')
+ def test_rest_successfully_create(self, mock_request):
+ data = self.mock_args()
+ set_module_args(data)
+ mock_request.side_effect = [
+ SRR['is_rest'],
+ SRR['empty_good'], # get
+ SRR['cluster_data'], # get cluster
+ SRR['empty_good'], # post
+ SRR['end_of_sequence']
+ ]
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_dns_mock_object(cx_type='rest').apply()
+ assert exc.value.args[0]['changed']
+
+ @patch('ansible_collections.netapp.ontap.plugins.module_utils.netapp.OntapRestAPI.send_request')
+ def test_rest_successfully_create_is_cluster_vserver(self, mock_request):
+ data = self.mock_args()
+ data['vserver'] = 'cvserver'
+ set_module_args(data)
+ mock_request.side_effect = [
+ SRR['is_rest'],
+ SRR['empty_good'], # get
+ SRR['cluster_name'], # get cluster name
+ SRR['empty_good'], # post
+ SRR['end_of_sequence']
+ ]
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_dns_mock_object(cx_type='rest').apply()
+ assert exc.value.args[0]['changed']
+
+ @patch('ansible_collections.netapp.ontap.plugins.module_utils.netapp.OntapRestAPI.send_request')
+ def test_rest_idempotent_create_dns(self, mock_request):
+ data = self.mock_args()
+ set_module_args(data)
+ mock_request.side_effect = [
+ SRR['is_rest'],
+ SRR['dns_record'], # get
+ SRR['end_of_sequence']
+ ]
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_dns_mock_object(cx_type='rest').apply()
+ assert not exc.value.args[0]['changed']
+
+ @patch('ansible_collections.netapp.ontap.plugins.module_utils.netapp.OntapRestAPI.send_request')
+ def test_rest_successfully_destroy(self, mock_request):
+ data = self.mock_args()
+ data['state'] = 'absent'
+ set_module_args(data)
+ mock_request.side_effect = [
+ SRR['is_rest'],
+ SRR['dns_record'], # get
+ SRR['empty_good'], # delete
+ SRR['end_of_sequence']
+ ]
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_dns_mock_object(cx_type='rest').apply()
+ assert exc.value.args[0]['changed']
+
+ @patch('ansible_collections.netapp.ontap.plugins.module_utils.netapp.OntapRestAPI.send_request')
+ def test_rest_idempotently_destroy(self, mock_request):
+ data = self.mock_args()
+ data['state'] = 'absent'
+ set_module_args(data)
+ mock_request.side_effect = [
+ SRR['is_rest'],
+ SRR['empty_good'], # get
+ SRR['cluster_data'], # get cluster
+ SRR['end_of_sequence']
+ ]
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_dns_mock_object(cx_type='rest').apply()
+ assert not exc.value.args[0]['changed']
+
+ @patch('ansible_collections.netapp.ontap.plugins.module_utils.netapp.OntapRestAPI.send_request')
+ def test_rest_successfully_modify(self, mock_request):
+ data = self.mock_args()
+ data['state'] = 'present'
+ set_module_args(data)
+ mock_request.side_effect = [
+ SRR['is_rest'],
+ SRR['empty_good'], # get
+ SRR['cluster_data'], # get cluster
+ SRR['empty_good'], # patch
+ SRR['end_of_sequence']
+ ]
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_dns_mock_object(cx_type='rest').apply()
+ assert exc.value.args[0]['changed']
+
+ @patch('ansible_collections.netapp.ontap.plugins.module_utils.netapp.OntapRestAPI.send_request')
+ def test_rest_successfully_modify_is_cluster_vserver(self, mock_request):
+ data = self.mock_args()
+ data['vserver'] = 'cvserver'
+ data['state'] = 'present'
+ data['domains'] = 'new_test.com'
+ set_module_args(data)
+ mock_request.side_effect = [
+ SRR['is_rest'],
+ SRR['empty_good'], # get
+ SRR['cluster_data'], # get cluster data
+ SRR['empty_good'], # patch
+ SRR['end_of_sequence']
+ ]
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_dns_mock_object(cx_type='rest').apply()
+ assert exc.value.args[0]['changed']
+
+ @patch('ansible_collections.netapp.ontap.plugins.module_utils.netapp.OntapRestAPI.send_request')
+ def test_rest_idempotently_modify(self, mock_request):
+ data = self.mock_args()
+ data['state'] = 'present'
+ set_module_args(data)
+ mock_request.side_effect = [
+ SRR['is_rest'],
+ SRR['dns_record'], # get
+ SRR['end_of_sequence']
+ ]
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_dns_mock_object(cx_type='rest').apply()
+ assert not exc.value.args[0]['changed']
diff --git a/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_efficiency_policy.py b/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_efficiency_policy.py
new file mode 100644
index 00000000..9432ff96
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_efficiency_policy.py
@@ -0,0 +1,232 @@
+# (c) 2018, NetApp, Inc
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+''' unit tests for Ansible module: na_ontap_vscan_scanner_pool '''
+
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+import json
+import pytest
+
+from ansible_collections.netapp.ontap.tests.unit.compat import unittest
+from ansible_collections.netapp.ontap.tests.unit.compat.mock import patch, Mock
+from ansible.module_utils import basic
+from ansible.module_utils._text import to_bytes
+import ansible_collections.netapp.ontap.plugins.module_utils.netapp as netapp_utils
+
+from ansible_collections.netapp.ontap.plugins.modules.na_ontap_efficiency_policy \
+ import NetAppOntapEfficiencyPolicy as efficiency_module # module under test
+
+if not netapp_utils.has_netapp_lib():
+ pytestmark = pytest.mark.skip('skipping as missing required netapp_lib')
+
+
+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 MockONTAPConnection(object):
+ ''' mock server connection to ONTAP host '''
+
+ def __init__(self, kind=None, data=None):
+ ''' save arguments '''
+ self.kind = kind
+ self.params = data
+ self.xml_in = None
+ self.xml_out = None
+
+ def invoke_successfully(self, xml, enable_tunneling): # pylint: disable=unused-argument
+ ''' mock invoke_successfully returning xml data '''
+ self.xml_in = xml
+ if self.kind == 'threshold':
+ xml = self.build_threshold_info(self.params)
+ elif self.kind == 'scheduled':
+ xml = self.build_schedule_info(self.params)
+ self.xml_out = xml
+ return xml
+
+ @staticmethod
+ def build_threshold_info(details):
+ xml = netapp_utils.zapi.NaElement('xml')
+ attributes = {'num-records': 1,
+ 'attributes-list': {
+ 'sis-policy-info': {
+ 'changelog-threshold-percent': 10,
+ 'comment': details['comment'],
+ 'enabled': 'true',
+ 'policy-name': details['policy_name'],
+ 'policy-type': 'threshold',
+ 'qos-policy': details['qos_policy'],
+ 'vserver': details['vserver']
+ }
+ }
+ }
+ xml.translate_struct(attributes)
+ return xml
+
+ @staticmethod
+ def build_schedule_info(details):
+ xml = netapp_utils.zapi.NaElement('xml')
+ attributes = {'num-records': 1,
+ 'attributes-list': {
+ 'sis-policy-info': {
+ 'comment': details['comment'],
+ 'duration': 10,
+ 'enabled': 'true',
+ 'policy-name': details['policy_name'],
+ 'policy-type': 'scheduled',
+ 'qos-policy': details['qos_policy'],
+ 'vserver': details['vserver']
+ }
+ }
+ }
+ xml.translate_struct(attributes)
+ return xml
+
+
+class TestMyModule(unittest.TestCase):
+ ''' Unit tests for na_ontap_efficiency_policy '''
+
+ 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)
+ self.mock_efficiency_policy = {
+ 'state': 'present',
+ 'vserver': 'test_vserver',
+ 'policy_name': 'test_policy',
+ 'comment': 'This policy is for x and y',
+ 'enabled': True,
+ 'qos_policy': 'background'
+ }
+
+ def mock_args(self):
+ return {
+ 'state': self.mock_efficiency_policy['state'],
+ 'vserver': self.mock_efficiency_policy['vserver'],
+ 'policy_name': self.mock_efficiency_policy['policy_name'],
+ 'comment': self.mock_efficiency_policy['comment'],
+ 'enabled': self.mock_efficiency_policy['enabled'],
+ 'qos_policy': self.mock_efficiency_policy['qos_policy'],
+ 'hostname': 'test',
+ 'username': 'test_user',
+ 'password': 'test_pass!'
+ }
+
+ def get_efficiency_mock_object(self, kind=None):
+ efficiency_obj = efficiency_module()
+ if kind is None:
+ efficiency_obj.server = MockONTAPConnection()
+ elif kind == 'threshold':
+ efficiency_obj.server = MockONTAPConnection(kind='threshold', data=self.mock_efficiency_policy)
+ elif kind == 'scheduled':
+ efficiency_obj.server = MockONTAPConnection(kind='scheduled', data=self.mock_efficiency_policy)
+ return efficiency_obj
+
+ def test_module_fail_when_required_args_missing(self):
+ ''' required arguments are reported as errors '''
+ with pytest.raises(AnsibleFailJson) as exc:
+ set_module_args({})
+ efficiency_module()
+ print('Info: %s' % exc.value.args[0]['msg'])
+
+ def test_get_nonexistent_efficiency_policy(self):
+ set_module_args(self.mock_args())
+ result = self.get_efficiency_mock_object().get_efficiency_policy()
+ assert not result
+
+ def test_get_existing_scanner(self):
+ set_module_args(self.mock_args())
+ result = self.get_efficiency_mock_object('threshold').get_efficiency_policy()
+ assert result
+
+ def test_successfully_create(self):
+ data = self.mock_args()
+ data['policy_type'] = 'threshold'
+ set_module_args(data)
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_efficiency_mock_object().apply()
+ assert exc.value.args[0]['changed']
+
+ def test_create_idempotency(self):
+ data = self.mock_args()
+ data['policy_type'] = 'threshold'
+ set_module_args(data)
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_efficiency_mock_object('threshold').apply()
+ assert not exc.value.args[0]['changed']
+
+ def test_threshold_duration_failure(self):
+ data = self.mock_args()
+ data['duration'] = 1
+ set_module_args(data)
+ with pytest.raises(AnsibleFailJson) as exc:
+ self.get_efficiency_mock_object('threshold').apply()
+ assert exc.value.args[0]['msg'] == "duration cannot be set if policy_type is threshold"
+
+ def test_threshold_schedule_failure(self):
+ data = self.mock_args()
+ data['schedule'] = 'test_job_schedule'
+ set_module_args(data)
+ with pytest.raises(AnsibleFailJson) as exc:
+ self.get_efficiency_mock_object('threshold').apply()
+ assert exc.value.args[0]['msg'] == "schedule cannot be set if policy_type is threshold"
+
+ def test_scheduled_threshold_percent_failure(self):
+ data = self.mock_args()
+ data['changelog_threshold_percent'] = 30
+ set_module_args(data)
+ with pytest.raises(AnsibleFailJson) as exc:
+ self.get_efficiency_mock_object('scheduled').apply()
+ assert exc.value.args[0]['msg'] == "changelog_threshold_percent cannot be set if policy_type is scheduled"
+
+ def test_successfully_delete(self):
+ data = self.mock_args()
+ data['state'] = 'absent'
+ set_module_args(data)
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_efficiency_mock_object('threshold').apply()
+ assert exc.value.args[0]['changed']
+
+ def test_delete_idempotency(self):
+ data = self.mock_args()
+ data['state'] = 'absent'
+ set_module_args(data)
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_efficiency_mock_object().apply()
+ assert not exc.value.args[0]['changed']
+
+ def test_successful_modify(self):
+ data = self.mock_args()
+ data['policy_type'] = 'threshold'
+ set_module_args(data)
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_efficiency_mock_object('scheduled').apply()
+ assert exc.value.args[0]['changed']
diff --git a/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_export_policy.py b/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_export_policy.py
new file mode 100644
index 00000000..04620856
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_export_policy.py
@@ -0,0 +1,289 @@
+# (c) 2019, NetApp, Inc
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+''' unit tests for Ansible module: na_ontap_volume_export_policy '''
+
+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.ontap.tests.unit.compat import unittest
+from ansible_collections.netapp.ontap.tests.unit.compat.mock import patch
+import ansible_collections.netapp.ontap.plugins.module_utils.netapp as netapp_utils
+
+from ansible_collections.netapp.ontap.plugins.modules.na_ontap_export_policy \
+ import NetAppONTAPExportPolicy as policy_module # module under test
+
+if not netapp_utils.has_netapp_lib():
+ pytestmark = pytest.mark.skip('skipping as missing required netapp_lib')
+
+# REST API canned responses when mocking send_request
+SRR = {
+ # common responses
+ 'is_rest': (200, {}, None),
+ 'is_zapi': (400, {}, "Unreachable"),
+ 'empty_good': (200, {}, None),
+ 'end_of_sequence': (500, None, "Unexpected call to send_request"),
+ 'generic_error': (400, None, "Expected error"),
+ # module specific responses
+ 'get_uuid_policy_id_export_policy': (
+ 200,
+ {
+ "records": [{
+ "svm": {
+ "uuid": "uuid",
+ "name": "svm"},
+ "id": 123,
+ "name": "ansible"
+ }],
+ "num_records": 1}, None),
+ "no_record": (
+ 200,
+ {"num_records": 0},
+ 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"""
+
+
+class AnsibleFailJson(Exception):
+ """Exception class to be raised by module.fail_json and caught by the test case"""
+
+
+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 MockONTAPConnection(object):
+ ''' mock server connection to ONTAP host '''
+
+ def __init__(self, kind=None, data=None):
+ ''' save arguments '''
+ self.kind = kind
+ self.params = data
+ self.xml_in = None
+ self.xml_out = None
+
+ def invoke_successfully(self, xml, enable_tunneling): # pylint: disable=unused-argument
+ ''' mock invoke_successfully returning xml data '''
+ self.xml_in = xml
+ if self.kind == 'export_policy':
+ xml = self.build_export_policy_info(self.params)
+ self.xml_out = xml
+ return xml
+
+ @staticmethod
+ def build_export_policy_info(export_policy_details):
+ xml = netapp_utils.zapi.NaElement('xml')
+ data = {'num-records': 1,
+ 'attributes-list': {'export-policy-info': {'name': export_policy_details['name']
+ }}}
+ xml.translate_struct(data)
+ return xml
+
+
+class TestMyModule(unittest.TestCase):
+ ''' Unit tests for na_ontap_job_schedule '''
+
+ 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)
+ self.mock_export_policy = {
+ 'name': 'test_policy',
+ 'vserver': 'test_vserver'
+ }
+
+ def mock_args(self, rest=False):
+ if rest:
+ return {
+ 'vserver': self.mock_export_policy['vserver'],
+ 'name': self.mock_export_policy['name'],
+ 'hostname': 'test',
+ 'username': 'test_user',
+ 'password': 'test_pass!'
+ }
+ else:
+ return {
+ 'vserver': self.mock_export_policy['vserver'],
+ 'name': self.mock_export_policy['name'],
+ 'hostname': 'test',
+ 'username': 'test_user',
+ 'password': 'test_pass!',
+ 'use_rest': 'never'
+ }
+
+ def get_export_policy_mock_object(self, cx_type='zapi', kind=None):
+ policy_obj = policy_module()
+ if cx_type == 'zapi':
+ if kind is None:
+ policy_obj.server = MockONTAPConnection()
+ elif kind == 'export_policy':
+ policy_obj.server = MockONTAPConnection(kind='export_policy', data=self.mock_export_policy)
+ return policy_obj
+
+ def test_module_fail_when_required_args_missing(self):
+ ''' required arguments are reported as errors '''
+ with pytest.raises(AnsibleFailJson) as exc:
+ set_module_args({})
+ policy_module()
+ print('Info: %s' % exc.value.args[0]['msg'])
+
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_export_policy.NetAppONTAPExportPolicy.create_export_policy')
+ def test_successful_create(self, create_export_policy):
+ ''' Test successful create '''
+ data = self.mock_args()
+ set_module_args(data)
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_export_policy_mock_object().apply()
+ assert exc.value.args[0]['changed']
+ create_export_policy.assert_called_with(uuid=None)
+
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_export_policy.NetAppONTAPExportPolicy.get_export_policy')
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_export_policy.NetAppONTAPExportPolicy.rename_export_policy')
+ def test_successful_rename(self, rename_export_policy, get_export_policy):
+ ''' Test successful rename '''
+ data = self.mock_args()
+ data['from_name'] = 'old_policy'
+ set_module_args(data)
+ get_export_policy.side_effect = [
+ None,
+ {'policy-name': 'old_policy'}
+ ]
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_export_policy_mock_object().apply()
+ assert exc.value.args[0]['changed']
+ rename_export_policy.assert_called_with(policy_id=None)
+
+ @patch('ansible_collections.netapp.ontap.plugins.module_utils.netapp.OntapRestAPI.send_request')
+ def test_rest_successful_create(self, mock_request):
+ '''Test successful rest create'''
+ data = self.mock_args(rest=True)
+ set_module_args(data)
+ mock_request.side_effect = [
+ SRR['is_rest'],
+ SRR['get_uuid_policy_id_export_policy'],
+ SRR['get_uuid_policy_id_export_policy'],
+ SRR['no_record'],
+ SRR['empty_good'],
+ SRR['end_of_sequence']
+ ]
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_export_policy_mock_object(cx_type='rest').apply()
+ assert exc.value.args[0]['changed']
+
+ @patch('ansible_collections.netapp.ontap.plugins.module_utils.netapp.OntapRestAPI.send_request')
+ def test_rest_successful_delete(self, mock_request):
+ '''Test successful rest delete'''
+ data = self.mock_args(rest=True)
+ data['state'] = 'absent'
+ set_module_args(data)
+ mock_request.side_effect = [
+ SRR['is_rest'],
+ SRR['get_uuid_policy_id_export_policy'],
+ SRR['get_uuid_policy_id_export_policy'],
+ SRR['get_uuid_policy_id_export_policy'],
+ SRR['empty_good'],
+ SRR['end_of_sequence']
+ ]
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_export_policy_mock_object(cx_type='rest').apply()
+ assert exc.value.args[0]['changed']
+
+ @patch('ansible_collections.netapp.ontap.plugins.module_utils.netapp.OntapRestAPI.send_request')
+ def test_rest_successful_rename(self, mock_request):
+ '''Test successful rest rename'''
+ data = self.mock_args(rest=True)
+ data['from_name'] = 'ansible'
+ set_module_args(data)
+ mock_request.side_effect = [
+ SRR['is_rest'],
+ SRR['get_uuid_policy_id_export_policy'],
+ SRR['get_uuid_policy_id_export_policy'],
+ SRR['no_record'],
+ SRR['get_uuid_policy_id_export_policy'],
+ SRR['empty_good'],
+ SRR['end_of_sequence']
+ ]
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_export_policy_mock_object(cx_type='rest').apply()
+ assert exc.value.args[0]['changed']
+
+ @patch('ansible_collections.netapp.ontap.plugins.module_utils.netapp.OntapRestAPI.send_request')
+ def test_rest_error_create(self, mock_request):
+ '''Test error rest create'''
+ data = self.mock_args(rest=True)
+ set_module_args(data)
+ mock_request.side_effect = [
+ SRR['is_rest'],
+ SRR['get_uuid_policy_id_export_policy'],
+ SRR['get_uuid_policy_id_export_policy'],
+ SRR['no_record'],
+ SRR['generic_error'],
+ SRR['empty_good'],
+ SRR['end_of_sequence']
+ ]
+ with pytest.raises(AnsibleFailJson) as exc:
+ self.get_export_policy_mock_object(cx_type='rest').apply()
+ assert 'Error on creating export policy: Expected error' in exc.value.args[0]['msg']
+
+ @patch('ansible_collections.netapp.ontap.plugins.module_utils.netapp.OntapRestAPI.send_request')
+ def test_rest_error_delete(self, mock_request):
+ '''Test error rest delete'''
+ data = self.mock_args(rest=True)
+ data['state'] = 'absent'
+ set_module_args(data)
+ mock_request.side_effect = [
+ SRR['is_rest'],
+ SRR['get_uuid_policy_id_export_policy'],
+ SRR['get_uuid_policy_id_export_policy'],
+ SRR['get_uuid_policy_id_export_policy'],
+ SRR['generic_error'],
+ SRR['empty_good'],
+ SRR['end_of_sequence']
+ ]
+ with pytest.raises(AnsibleFailJson) as exc:
+ self.get_export_policy_mock_object(cx_type='rest').apply()
+ assert 'Error on deleting export policy: Expected error' in exc.value.args[0]['msg']
+
+ @patch('ansible_collections.netapp.ontap.plugins.module_utils.netapp.OntapRestAPI.send_request')
+ def test_rest_error_rename(self, mock_request):
+ '''Test error rest rename'''
+ data = self.mock_args(rest=True)
+ data['from_name'] = 'ansible'
+ set_module_args(data)
+ mock_request.side_effect = [
+ SRR['is_rest'],
+ SRR['get_uuid_policy_id_export_policy'],
+ SRR['get_uuid_policy_id_export_policy'],
+ SRR['no_record'],
+ SRR['get_uuid_policy_id_export_policy'],
+ SRR['generic_error'],
+ SRR['empty_good'],
+ SRR['end_of_sequence']
+ ]
+ with pytest.raises(AnsibleFailJson) as exc:
+ self.get_export_policy_mock_object(cx_type='rest').apply()
+ assert 'Error on renaming export policy: Expected error' in exc.value.args[0]['msg']
diff --git a/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_export_policy_rule.py b/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_export_policy_rule.py
new file mode 100644
index 00000000..016dd68a
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_export_policy_rule.py
@@ -0,0 +1,269 @@
+# (c) 2018, NetApp, Inc
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+''' unit test template for ONTAP Ansible module '''
+
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+import json
+import pytest
+
+from ansible_collections.netapp.ontap.tests.unit.compat import unittest
+from ansible_collections.netapp.ontap.tests.unit.compat.mock import patch, Mock
+from ansible.module_utils import basic
+from ansible.module_utils._text import to_bytes
+import ansible_collections.netapp.ontap.plugins.module_utils.netapp as netapp_utils
+
+from ansible_collections.netapp.ontap.plugins.modules.na_ontap_export_policy_rule \
+ import NetAppontapExportRule as policy_rule # module under test
+
+if not netapp_utils.has_netapp_lib():
+ pytestmark = pytest.mark.skip('skipping as missing required netapp_lib')
+
+
+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 MockONTAPConnection(object):
+ ''' mock server connection to ONTAP host '''
+
+ def __init__(self, kind=None, data=None):
+ ''' save arguments '''
+ self.kind = kind
+ self.data = data
+ self.xml_in = None
+ self.xml_out = None
+
+ def invoke_successfully(self, xml, enable_tunneling): # pylint: disable=unused-argument
+ ''' mock invoke_successfully returning xml data '''
+ self.xml_in = xml
+ if self.kind == 'rule':
+ xml = self.build_policy_rule(self.data)
+ if self.kind == 'rules':
+ xml = self.build_policy_rule(self.data, multiple=True)
+ if self.kind == 'policy':
+ xml = self.build_policy()
+ self.xml_out = xml
+ return xml
+
+ @staticmethod
+ def build_policy_rule(policy, multiple=False):
+ ''' build xml data for export-rule-info '''
+ xml = netapp_utils.zapi.NaElement('xml')
+ attributes = {'attributes-list': {
+ 'export-rule-info': {
+ 'policy-name': policy['name'],
+ 'client-match': policy['client_match'],
+ 'ro-rule': {
+ 'security-flavor': 'any'
+ },
+ 'rw-rule': {
+ 'security-flavor': 'any'
+ },
+ 'protocol': {
+ 'access-protocol': policy['protocol']
+ },
+ 'super-user-security': {
+ 'security-flavor': 'any'
+ },
+ 'is-allow-set-uid-enabled': 'false',
+ 'rule-index': policy['rule_index'],
+ 'anonymous-user-id': policy['anonymous_user_id'],
+
+ }
+ }, 'num-records': 2 if multiple is True else 1}
+ xml.translate_struct(attributes)
+ return xml
+
+ @staticmethod
+ def build_policy():
+ ''' build xml data for export-policy-get-iter '''
+ xml = netapp_utils.zapi.NaElement('xml')
+ attributes = {
+ 'num-records': 1,
+
+ }
+ xml.translate_struct(attributes)
+ return xml
+
+
+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)
+ self.server = MockONTAPConnection()
+ self.mock_rule = {
+ 'name': 'test',
+ 'protocol': 'nfs',
+ 'client_match': '1.1.1.0',
+ 'rule_index': 10,
+ 'anonymous_user_id': '65534'
+ }
+
+ def mock_rule_args(self):
+ return {
+ 'name': self.mock_rule['name'],
+ 'client_match': self.mock_rule['client_match'],
+ 'vserver': 'test',
+ 'protocol': self.mock_rule['protocol'],
+ 'rule_index': self.mock_rule['rule_index'],
+ 'anonymous_user_id': self.mock_rule['anonymous_user_id'],
+ 'ro_rule': 'any',
+ 'rw_rule': 'any',
+ 'hostname': 'test',
+ 'username': 'test_user',
+ 'password': 'test_pass!'
+ }
+
+ def get_mock_object(self, kind=None):
+ """
+ Helper method to return an na_ontap_firewall_policy object
+ :param kind: passes this param to MockONTAPConnection()
+ :return: na_ontap_firewall_policy object
+ """
+ obj = policy_rule()
+ obj.autosupport_log = Mock(return_value=None)
+ if kind is None:
+ obj.server = MockONTAPConnection()
+ else:
+ obj.server = MockONTAPConnection(kind=kind, data=self.mock_rule_args())
+ return obj
+
+ def test_module_fail_when_required_args_missing(self):
+ ''' required arguments are reported as errors '''
+ with pytest.raises(AnsibleFailJson) as exc:
+ set_module_args({})
+ policy_rule()
+ print('Info: %s' % exc.value.args[0]['msg'])
+
+ def test_get_nonexistent_rule(self):
+ ''' Test if get_export_policy_rule returns None for non-existent policy '''
+ set_module_args(self.mock_rule_args())
+ result = self.get_mock_object().get_export_policy_rule()
+ assert result is None
+
+ def test_get_nonexistent_policy(self):
+ ''' Test if get_export_policy returns None for non-existent policy '''
+ set_module_args(self.mock_rule_args())
+ result = self.get_mock_object().get_export_policy()
+ assert result is None
+
+ def test_get_existing_rule(self):
+ ''' Test if get_export_policy_rule returns rule details for existing policy '''
+ data = self.mock_rule_args()
+ set_module_args(data)
+ result = self.get_mock_object('rule').get_export_policy_rule()
+ assert result['name'] == data['name']
+ assert result['client_match'] == data['client_match']
+ assert result['ro_rule'] == ['any'] # from build_rule()
+
+ def test_get_existing_policy(self):
+ ''' Test if get_export_policy returns policy details for existing policy '''
+ data = self.mock_rule_args()
+ set_module_args(data)
+ result = self.get_mock_object('policy').get_export_policy()
+ assert result is not None
+
+ def test_create_missing_param_error(self):
+ ''' Test validation error from create '''
+ data = self.mock_rule_args()
+ del data['ro_rule']
+ set_module_args(data)
+ with pytest.raises(AnsibleFailJson) as exc:
+ self.get_mock_object().apply()
+ msg = 'Error: Missing required param for creating export policy rule ro_rule'
+ assert exc.value.args[0]['msg'] == msg
+
+ def test_successful_create(self):
+ ''' Test successful create '''
+ set_module_args(self.mock_rule_args())
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_mock_object().apply()
+ assert exc.value.args[0]['changed']
+
+ def test_create_idempotency(self):
+ ''' Test create idempotency '''
+ set_module_args(self.mock_rule_args())
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_mock_object('rule').apply()
+ assert not exc.value.args[0]['changed']
+
+ def test_successful_delete_without_rule_index(self):
+ ''' Test delete existing job '''
+ data = self.mock_rule_args()
+ data['state'] = 'absent'
+ del data['rule_index']
+ set_module_args(data)
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_mock_object('rule').apply()
+ assert exc.value.args[0]['changed']
+
+ def test_delete_idempotency(self):
+ ''' Test delete idempotency '''
+ data = self.mock_rule_args()
+ data['state'] = 'absent'
+ set_module_args(data)
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_mock_object().apply()
+ assert not exc.value.args[0]['changed']
+
+ def test_successful_modify(self):
+ ''' Test successful modify protocol '''
+ data = self.mock_rule_args()
+ data['protocol'] = ['cifs']
+ data['allow_suid'] = 'true'
+ set_module_args(data)
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_mock_object('rule').apply()
+ assert exc.value.args[0]['changed']
+
+ def test_error_on_ambiguous_delete(self):
+ ''' Test error if multiple entries match for a delete '''
+ data = self.mock_rule_args()
+ data['state'] = 'absent'
+ set_module_args(data)
+ with pytest.raises(AnsibleFailJson) as exc:
+ self.get_mock_object('rules').apply()
+ msg = "Multiple export policy rules exist.Please specify a rule_index to delete"
+ assert exc.value.args[0]['msg'] == msg
+
+ def test_helper_query_parameters(self):
+ ''' Test helper method set_query_parameters() '''
+ data = self.mock_rule_args()
+ set_module_args(data)
+ result = self.get_mock_object('rule').set_query_parameters()
+ print(str(result))
+ assert 'query' in result
+ assert 'export-rule-info' in result['query']
+ assert result['query']['export-rule-info']['rule-index'] == data['rule_index']
diff --git a/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_file_directory_policy.py b/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_file_directory_policy.py
new file mode 100644
index 00000000..70354edc
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_file_directory_policy.py
@@ -0,0 +1,173 @@
+# (c) 2020, NetApp, Inc
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+''' unit tests for Ansible module: na_ontap_file_directory_policy '''
+
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+import json
+import pytest
+
+from ansible_collections.netapp.ontap.tests.unit.compat import unittest
+from ansible_collections.netapp.ontap.tests.unit.compat.mock import patch, Mock
+from ansible.module_utils import basic
+from ansible.module_utils._text import to_bytes
+import ansible_collections.netapp.ontap.plugins.module_utils.netapp as netapp_utils
+
+from ansible_collections.netapp.ontap.plugins.modules.na_ontap_file_directory_policy \
+ import NetAppOntapFilePolicy as policy_module # module under test
+
+if not netapp_utils.has_netapp_lib():
+ pytestmark = pytest.mark.skip('skipping as missing required netapp_lib')
+
+
+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 MockONTAPConnection(object):
+ ''' mock server connection to ONTAP host '''
+
+ def __init__(self, kind=None, data=None):
+ ''' save arguments '''
+ self.kind = kind
+ self.params = data
+ self.xml_in = None
+ self.xml_out = None
+
+ def invoke_successfully(self, xml, enable_tunneling): # pylint: disable=unused-argument
+ ''' mock invoke_successfully returning xml data '''
+ self.xml_in = xml
+ request = xml.to_string().decode('utf-8')
+ if self.kind == 'error':
+ raise netapp_utils.zapi.NaApiError('test', 'expect error')
+ elif request.startswith("<ems-autosupport-log>"):
+ xml = None # or something that may the logger happy, and you don't need @patch anymore
+ # or
+ # xml = build_ems_log_response()
+ elif request.startswith("<file-directory-security-policy-get-iter>"):
+ if self.kind == 'create':
+ xml = self.build_sd_info()
+ else:
+ xml = self.build_sd_info(self.params)
+ elif request.startswith("<file-directory-security-ntfs-modify>"):
+ xml = self.build_sd_info(self.params)
+ self.xml_out = xml
+ return xml
+
+ @staticmethod
+ def build_sd_info(data=None):
+ xml = netapp_utils.zapi.NaElement('xml')
+ attributes = {}
+ if data is not None:
+ attributes = {'num-records': 1,
+ 'attributes-list': {'file-directory-security-policy': {'policy-name': data['policy_name']}}}
+ xml.translate_struct(attributes)
+ return xml
+
+
+class TestMyModule(unittest.TestCase):
+ ''' Unit tests for na_ontap_file_directory_policy '''
+
+ 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 mock_args(self):
+ return {
+ 'vserver': 'vserver',
+ 'hostname': 'test',
+ 'username': 'test_user',
+ 'password': 'test_pass!'
+ }
+
+ def get_policy_mock_object(self, type='zapi', kind=None, status=None):
+ policy_obj = policy_module()
+ netapp_utils.ems_log_event = Mock(return_value=None)
+ if type == 'zapi':
+ if kind is None:
+ policy_obj.server = MockONTAPConnection()
+ else:
+ policy_obj.server = MockONTAPConnection(kind=kind, data=status)
+ return policy_obj
+
+ def test_module_fail_when_required_args_missing(self):
+ ''' required arguments are reported as errors '''
+ with pytest.raises(AnsibleFailJson) as exc:
+ set_module_args({})
+ policy_module()
+ print('Info: %s' % exc.value.args[0]['msg'])
+
+ def test_successfully_create_policy(self):
+ data = self.mock_args()
+ data['policy_name'] = 'test_policy'
+ set_module_args(data)
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_policy_mock_object('zapi', 'create', data).apply()
+ assert exc.value.args[0]['changed']
+
+ def test_error(self):
+ data = self.mock_args()
+ data['policy_name'] = 'test_policy'
+ set_module_args(data)
+ with pytest.raises(AnsibleFailJson) as exc:
+ self.get_policy_mock_object('zapi', 'error', data).get_policy_iter()
+ assert exc.value.args[0]['msg'] == 'Error fetching file-directory policy test_policy: NetApp API failed. Reason - test:expect error'
+
+ with pytest.raises(AnsibleFailJson) as exc:
+ self.get_policy_mock_object('zapi', 'error', data).create_policy()
+ assert exc.value.args[0]['msg'] == 'Error creating file-directory policy test_policy: NetApp API failed. Reason - test:expect error'
+
+ with pytest.raises(AnsibleFailJson) as exc:
+ self.get_policy_mock_object('zapi', 'error', data).remove_policy()
+ assert exc.value.args[0]['msg'] == 'Error removing file-directory policy test_policy: NetApp API failed. Reason - test:expect error'
+
+ data['path'] = '/vol'
+ set_module_args(data)
+ with pytest.raises(AnsibleFailJson) as exc:
+ self.get_policy_mock_object('zapi', 'error', data).get_task_iter()
+ assert exc.value.args[0]['msg'] == 'Error fetching task from file-directory policy test_policy: NetApp API failed. Reason - test:expect error'
+
+ with pytest.raises(AnsibleFailJson) as exc:
+ self.get_policy_mock_object('zapi', 'error', data).add_task_to_policy()
+ assert exc.value.args[0]['msg'] == 'Error adding task to file-directory policy test_policy: NetApp API failed. Reason - test:expect error'
+
+ with pytest.raises(AnsibleFailJson) as exc:
+ self.get_policy_mock_object('zapi', 'error', data).remove_task_from_policy()
+ assert exc.value.args[0]['msg'] == 'Error removing task from file-directory policy test_policy: NetApp API failed. Reason - test:expect error'
+
+ with pytest.raises(AnsibleFailJson) as exc:
+ self.get_policy_mock_object('zapi', 'error', data).modify_task(dict())
+ assert exc.value.args[0]['msg'] == 'Error modifying task in file-directory policy test_policy: NetApp API failed. Reason - test:expect error'
+
+ with pytest.raises(AnsibleFailJson) as exc:
+ self.get_policy_mock_object('zapi', 'error', data).set_sd()
+ assert exc.value.args[0]['msg'] == 'Error applying file-directory policy test_policy: NetApp API failed. Reason - test:expect error'
diff --git a/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_firewall_policy.py b/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_firewall_policy.py
new file mode 100644
index 00000000..51e4d06d
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_firewall_policy.py
@@ -0,0 +1,296 @@
+# (c) 2018, NetApp, Inc
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+''' unit test template for ONTAP Ansible module '''
+
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+import json
+import pytest
+
+from ansible_collections.netapp.ontap.tests.unit.compat import unittest
+from ansible_collections.netapp.ontap.tests.unit.compat.mock import patch, Mock
+from ansible.module_utils import basic
+from ansible.module_utils._text import to_bytes
+import ansible_collections.netapp.ontap.plugins.module_utils.netapp as netapp_utils
+
+from ansible_collections.netapp.ontap.plugins.modules.na_ontap_firewall_policy \
+ import NetAppONTAPFirewallPolicy as fp_module # module under test
+
+if not netapp_utils.has_netapp_lib():
+ pytestmark = pytest.mark.skip('skipping as missing required netapp_lib')
+
+
+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 MockONTAPConnection(object):
+ ''' mock server connection to ONTAP host '''
+
+ def __init__(self, kind=None, data=None):
+ ''' save arguments '''
+ self.kind = kind
+ self.data = data
+ self.xml_in = None
+ self.xml_out = None
+
+ def invoke_successfully(self, xml, enable_tunneling): # pylint: disable=unused-argument
+ ''' mock invoke_successfully returning xml data '''
+ self.xml_in = xml
+ if self.kind == 'policy':
+ xml = self.build_policy_info(self.data)
+ if self.kind == 'config':
+ xml = self.build_firewall_config_info(self.data)
+ self.xml_out = xml
+ return xml
+
+ @staticmethod
+ def build_policy_info(data):
+ ''' build xml data for net-firewall-policy-info '''
+ xml = netapp_utils.zapi.NaElement('xml')
+ attributes = {
+ 'num-records': 1,
+ 'attributes-list': {
+ 'net-firewall-policy-info': {
+ 'policy': data['policy'],
+ 'service': data['service'],
+ 'allow-list': [
+ {'ip-and-mask': '1.2.3.0/24'}
+ ]
+ }
+ }
+ }
+
+ xml.translate_struct(attributes)
+ return xml
+
+ @staticmethod
+ def build_firewall_config_info(data):
+ ''' build xml data for net-firewall-config-info '''
+ xml = netapp_utils.zapi.NaElement('xml')
+ attributes = {
+ 'attributes': {
+ 'net-firewall-config-info': {
+ 'is-enabled': 'true',
+ 'is-logging': 'false'
+ }
+ }
+ }
+ xml.translate_struct(attributes)
+ return xml
+
+
+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)
+ self.mock_policy = {
+ 'policy': 'test',
+ 'service': 'http',
+ 'vserver': 'my_vserver',
+ 'allow_list': '1.2.3.0/24'
+ }
+ self.mock_config = {
+ 'node': 'test',
+ 'enable': 'enable',
+ 'logging': 'enable'
+ }
+
+ def mock_policy_args(self):
+ return {
+ 'policy': self.mock_policy['policy'],
+ 'service': self.mock_policy['service'],
+ 'vserver': self.mock_policy['vserver'],
+ 'allow_list': [self.mock_policy['allow_list']],
+ 'hostname': 'test',
+ 'username': 'test_user',
+ 'password': 'test_pass!'
+ }
+
+ def mock_config_args(self):
+ return {
+ 'node': self.mock_config['node'],
+ 'enable': self.mock_config['enable'],
+ 'logging': self.mock_config['logging'],
+ 'hostname': 'test',
+ 'username': 'test_user',
+ 'password': 'test_pass!'
+ }
+
+ def get_mock_object(self, kind=None):
+ """
+ Helper method to return an na_ontap_firewall_policy object
+ :param kind: passes this param to MockONTAPConnection()
+ :return: na_ontap_firewall_policy object
+ """
+ obj = fp_module()
+ obj.autosupport_log = Mock(return_value=None)
+ if kind is None:
+ obj.server = MockONTAPConnection()
+ else:
+ mock_data = self.mock_config if kind == 'config' else self.mock_policy
+ obj.server = MockONTAPConnection(kind=kind, data=mock_data)
+ return obj
+
+ def test_module_fail_when_required_args_missing(self):
+ ''' required arguments are reported as errors '''
+ with pytest.raises(AnsibleFailJson) as exc:
+ set_module_args({})
+ fp_module()
+ print('Info: %s' % exc.value.args[0]['msg'])
+
+ def test_helper_firewall_policy_attributes(self):
+ ''' helper returns dictionary with vserver, service and policy details '''
+ data = self.mock_policy
+ set_module_args(self.mock_policy_args())
+ result = self.get_mock_object('policy').firewall_policy_attributes()
+ del data['allow_list']
+ assert data == result
+
+ def test_helper_validate_ip_addresses_positive(self):
+ ''' test if helper validates if IP is a network address '''
+ data = self.mock_policy_args()
+ data['allow_list'] = ['1.2.0.0/16', '1.2.3.0/24']
+ set_module_args(data)
+ result = self.get_mock_object().validate_ip_addresses()
+ assert result is None
+
+ def test_helper_validate_ip_addresses_negative(self):
+ ''' test if helper validates if IP is a network address '''
+ data = self.mock_policy_args()
+ data['allow_list'] = ['1.2.0.10/16', '1.2.3.0/24']
+ set_module_args(data)
+ with pytest.raises(AnsibleFailJson) as exc:
+ self.get_mock_object().validate_ip_addresses()
+ msg = 'Error: Invalid IP address value for allow_list parameter.' \
+ 'Please specify a network address without host bits set: ' \
+ '1.2.0.10/16 has host bits set'
+ assert exc.value.args[0]['msg'] == msg
+
+ def test_get_nonexistent_policy(self):
+ ''' Test if get_firewall_policy returns None for non-existent policy '''
+ set_module_args(self.mock_policy_args())
+ result = self.get_mock_object().get_firewall_policy()
+ assert result is None
+
+ def test_get_existing_policy(self):
+ ''' Test if get_firewall_policy returns policy details for existing policy '''
+ data = self.mock_policy_args()
+ set_module_args(data)
+ result = self.get_mock_object('policy').get_firewall_policy()
+ assert result['service'] == data['service']
+ assert result['allow_list'] == ['1.2.3.0/24'] # from build_policy_info()
+
+ def test_successful_create(self):
+ ''' Test successful create '''
+ set_module_args(self.mock_policy_args())
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_mock_object().apply()
+ assert exc.value.args[0]['changed']
+
+ def test_create_idempotency(self):
+ ''' Test create idempotency '''
+ set_module_args(self.mock_policy_args())
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_mock_object('policy').apply()
+ assert not exc.value.args[0]['changed']
+
+ def test_successful_delete(self):
+ ''' Test delete existing job '''
+ data = self.mock_policy_args()
+ data['state'] = 'absent'
+ set_module_args(data)
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_mock_object('policy').apply()
+ assert exc.value.args[0]['changed']
+
+ def test_delete_idempotency(self):
+ ''' Test delete idempotency '''
+ data = self.mock_policy_args()
+ data['state'] = 'absent'
+ set_module_args(data)
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_mock_object().apply()
+ assert not exc.value.args[0]['changed']
+
+ def test_successful_modify(self):
+ ''' Test successful modify allow_list '''
+ data = self.mock_policy_args()
+ data['allow_list'] = ['1.2.0.0/16']
+ set_module_args(data)
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_mock_object('policy').apply()
+ assert exc.value.args[0]['changed']
+
+ def test_successful_modify_mutiple_ips(self):
+ ''' Test successful modify allow_list '''
+ data = self.mock_policy_args()
+ data['allow_list'] = ['1.2.0.0/16', '1.0.0.0/8']
+ set_module_args(data)
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_mock_object('policy').apply()
+ assert exc.value.args[0]['changed']
+
+ def test_successful_modify_mutiple_ips_contain_existing(self):
+ ''' Test successful modify allow_list '''
+ data = self.mock_policy_args()
+ data['allow_list'] = ['1.2.3.0/24', '1.0.0.0/8']
+ set_module_args(data)
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_mock_object('policy').apply()
+ assert exc.value.args[0]['changed']
+
+ def test_get_nonexistent_config(self):
+ ''' Test if get_firewall_config returns None for non-existent node '''
+ set_module_args(self.mock_config_args())
+ result = self.get_mock_object().get_firewall_config_for_node()
+ assert result is None
+
+ def test_get_existing_config(self):
+ ''' Test if get_firewall_config returns policy details for existing node '''
+ data = self.mock_config_args()
+ set_module_args(data)
+ result = self.get_mock_object('config').get_firewall_config_for_node()
+ assert result['enable'] == 'enable' # from build_config_info()
+ assert result['logging'] == 'disable' # from build_config_info()
+
+ def test_successful_modify_config(self):
+ ''' Test successful modify allow_list '''
+ data = self.mock_config_args()
+ data['enable'] = 'disable'
+ data['logging'] = 'enable'
+ set_module_args(data)
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_mock_object('config').apply()
+ assert exc.value.args[0]['changed']
diff --git a/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_firmware_upgrade.py b/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_firmware_upgrade.py
new file mode 100644
index 00000000..223bb5b6
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_firmware_upgrade.py
@@ -0,0 +1,436 @@
+# (c) 2018, NetApp, Inc
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+''' unit tests ONTAP Ansible module: na_ontap_firmware_upgrade '''
+
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+import json
+import pytest
+
+from ansible_collections.netapp.ontap.tests.unit.compat import unittest
+from ansible_collections.netapp.ontap.tests.unit.compat.mock import patch, Mock
+from ansible.module_utils import basic
+from ansible.module_utils._text import to_bytes
+import ansible_collections.netapp.ontap.plugins.module_utils.netapp as netapp_utils
+
+from ansible_collections.netapp.ontap.plugins.modules.na_ontap_firmware_upgrade\
+ import NetAppONTAPFirmwareUpgrade as my_module # module under test
+
+if not netapp_utils.has_netapp_lib():
+ pytestmark = pytest.mark.skip('skipping as missing required netapp_lib')
+
+
+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 MockONTAPConnection(object):
+ ''' mock server connection to ONTAP host '''
+
+ def __init__(self, kind=None, parm1=None, parm2=None, parm3=None):
+ ''' save arguments '''
+ self.type = kind
+ self.parm1 = parm1
+ self.parm2 = parm2
+ # self.parm3 = parm3
+ self.xml_in = None
+ self.xml_out = None
+ self.firmware_type = 'None'
+
+ def invoke_successfully(self, xml, enable_tunneling): # pylint: disable=unused-argument
+ ''' mock invoke_successfully returning xml data '''
+ self.xml_in = xml
+ # print('xml_in', xml.to_string())
+ if self.type == 'firmware_upgrade':
+ xml = self.build_firmware_upgrade_info(self.parm1, self.parm2)
+ if self.type == 'acp':
+ xml = self.build_acp_firmware_info(self.firmware_type)
+ if self.type == 'firmware_download':
+ xml = self.build_system_cli_info(error=self.parm1)
+ if self.type == 'firmware_download_exception':
+ raise netapp_utils.zapi.NaApiError(self.parm1, self.parm2)
+ self.xml_out = xml
+ return xml
+
+ def autosupport_log(self):
+ ''' mock autosupport log'''
+ return None
+
+ @staticmethod
+ def build_firmware_upgrade_info(version, node):
+ ''' build xml data for service-processor firmware info '''
+ xml = netapp_utils.zapi.NaElement('xml')
+ data = {
+ 'num-records': 1,
+ 'attributes-list': {'service-processor-info': {'firmware-version': '3.4'}}
+ }
+ xml.translate_struct(data)
+ print(xml.to_string())
+ return xml
+
+ @staticmethod
+ def build_acp_firmware_info(firmware_type):
+ ''' build xml data for acp firmware info '''
+ xml = netapp_utils.zapi.NaElement('xml')
+ data = {
+ # 'num-records': 1,
+ 'attributes-list': {'storage-shelf-acp-module': {'state': 'firmware_update_required'}}
+ }
+ xml.translate_struct(data)
+ print(xml.to_string())
+ return xml
+
+ @staticmethod
+ def build_system_cli_info(error=None):
+ ''' build xml data for system-cli info '''
+ if error is None:
+ # make it a string, to be able to compare easily
+ error = ""
+ xml = netapp_utils.zapi.NaElement('results')
+ if error == 'empty_output':
+ output = ""
+ else:
+ output = 'Download complete.'
+ data = {
+ 'cli-output': output,
+ 'cli-result-value': 1
+ }
+ xml.translate_struct(data)
+ if error == 'status_failed':
+ status = "failed"
+ else:
+ status = "passed"
+ if error != 'no_status_attr':
+ xml.add_attr('status', status)
+ return xml
+
+
+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)
+ self.server = MockONTAPConnection()
+ self.use_vsim = False
+
+ def set_default_args(self):
+ if self.use_vsim:
+ hostname = '10.10.10.10'
+ username = 'admin'
+ password = 'admin'
+ node = 'vsim1'
+ clear_logs = True
+ package = 'test1.zip'
+ install_baseline_image = False
+ update_type = 'serial_full'
+ force_disruptive_update = False
+ else:
+ hostname = 'hostname'
+ username = 'username'
+ password = 'password'
+ node = 'abc'
+ package = 'test1.zip'
+ clear_logs = True
+ install_baseline_image = False
+ update_type = 'serial_full'
+ force_disruptive_update = False
+
+ return dict({
+ 'hostname': hostname,
+ 'username': username,
+ 'password': password,
+ 'node': node,
+ 'package': package,
+ 'clear_logs': clear_logs,
+ 'install_baseline_image': install_baseline_image,
+ 'update_type': update_type,
+ 'https': 'true',
+ 'force_disruptive_update': force_disruptive_update
+ })
+
+ def test_module_fail_when_required_args_missing(self):
+ ''' required arguments are reported as errors '''
+ with pytest.raises(AnsibleFailJson) as exc:
+ set_module_args({})
+ my_module()
+ print('Info: %s' % exc.value.args[0]['msg'])
+
+ def test_invalid_firmware_type_parameters(self):
+ ''' fail if firmware_type is missing '''
+ module_args = {}
+ module_args.update(self.set_default_args())
+ module_args.update({'firmware_type': 'service_test'})
+ set_module_args(module_args)
+ with pytest.raises(AnsibleFailJson) as exc:
+ set_module_args(module_args)
+ my_module()
+ msg = 'value of firmware_type must be one of: service-processor, shelf, acp, disk, got: %s' % module_args['firmware_type']
+ print('Info: %s' % exc.value.args[0]['msg'])
+ assert exc.value.args[0]['msg'] == msg
+
+ def test_ensure_sp_firmware_get_called(self):
+ ''' a more interesting test '''
+ module_args = {}
+ module_args.update(self.set_default_args())
+ module_args.update({'firmware_type': 'service-processor'})
+ set_module_args(module_args)
+ my_obj = my_module()
+ my_obj.server = self.server
+ firmware_image_get = my_obj.firmware_image_get('node')
+ print('Info: test_firmware_upgrade_get: %s' % repr(firmware_image_get))
+ assert firmware_image_get is None
+
+ def test_ensure_firmware_get_with_package_baseline_called(self):
+ ''' a more interesting test '''
+ module_args = {}
+ module_args.update(self.set_default_args())
+ module_args.update({'firmware_type': 'service-processor'})
+ module_args.update({'package': 'test1.zip'})
+ module_args.update({'install_baseline_image': True})
+ with pytest.raises(AnsibleFailJson) as exc:
+ set_module_args(module_args)
+ my_module()
+ msg = 'Do not specify both package and install_baseline_image: true'
+ print('info: ' + exc.value.args[0]['msg'])
+ assert exc.value.args[0]['msg'] == msg
+
+ def test_ensure_acp_firmware_required_get_called(self):
+ ''' a test tp verify acp firmware upgrade is required or not '''
+ module_args = {}
+ module_args.update(self.set_default_args())
+ module_args.update({'firmware_type': 'acp'})
+ set_module_args(module_args)
+ my_obj = my_module()
+ # my_obj.server = self.server
+ my_obj.server = MockONTAPConnection(kind='acp')
+ acp_firmware_required_get = my_obj.acp_firmware_required_get()
+ print('Info: test_acp_firmware_upgrade_required_get: %s' % repr(acp_firmware_required_get))
+ assert acp_firmware_required_get is True
+
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_firmware_upgrade.NetAppONTAPFirmwareUpgrade.sp_firmware_image_update')
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_firmware_upgrade.NetAppONTAPFirmwareUpgrade.sp_firmware_image_update_progress_get')
+ def test_ensure_apply_for_firmware_upgrade_called(self, get_mock, update_mock):
+ ''' updgrading firmware and checking idempotency '''
+ module_args = {}
+ module_args.update(self.set_default_args())
+ module_args.update({'package': 'test1.zip'})
+ module_args.update({'firmware_type': 'service-processor'})
+ module_args.update({'force_disruptive_update': True})
+ set_module_args(module_args)
+ my_obj = my_module()
+ my_obj.autosupport_log = Mock(return_value=None)
+ if not self.use_vsim:
+ my_obj.server = self.server
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ print('Info: test_firmware_upgrade_apply: %s' % repr(exc.value))
+ assert not exc.value.args[0]['changed']
+ if not self.use_vsim:
+ my_obj.server = MockONTAPConnection('firmware_upgrade', '3.5', 'true')
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ print('Info: test_firmware_upgrade_apply: %s' % repr(exc.value))
+ assert exc.value.args[0]['changed']
+ update_mock.assert_called_with()
+
+ def test_shelf_firmware_upgrade(self):
+ ''' Test shelf firmware upgrade '''
+ module_args = {}
+ module_args.update(self.set_default_args())
+ module_args.update({'firmware_type': 'shelf'})
+ set_module_args(module_args)
+ my_obj = my_module()
+ my_obj.autosupport_log = Mock(return_value=None)
+ if not self.use_vsim:
+ my_obj.server = self.server
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ print('Info: test_firmware_upgrade_apply: %s' % repr(exc.value))
+ assert not exc.value.args[0]['changed']
+
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_firmware_upgrade.NetAppONTAPFirmwareUpgrade.acp_firmware_upgrade')
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_firmware_upgrade.NetAppONTAPFirmwareUpgrade.acp_firmware_required_get')
+ def test_acp_firmware_upgrade(self, get_mock, update_mock):
+ ''' Test ACP firmware upgrade '''
+ module_args = {}
+ module_args.update(self.set_default_args())
+ module_args.update({'firmware_type': 'acp'})
+ set_module_args(module_args)
+ my_obj = my_module()
+ my_obj.autosupport_log = Mock(return_value=None)
+ if not self.use_vsim:
+ my_obj.server = self.server
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ print('Info: test_firmware_upgrade_apply: %s' % repr(exc.value))
+ assert not exc.value.args[0]['changed']
+
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_firmware_upgrade.NetAppONTAPFirmwareUpgrade.disk_firmware_upgrade')
+ def test_disk_firmware_upgrade(self, get_mock):
+ ''' Test disk firmware upgrade '''
+ module_args = {}
+ module_args.update(self.set_default_args())
+ module_args.update({'firmware_type': 'disk'})
+ set_module_args(module_args)
+ my_obj = my_module()
+ my_obj.autosupport_log = Mock(return_value=None)
+ if not self.use_vsim:
+ my_obj.server = self.server
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ print('Info: test_firmware_upgrade_apply: %s' % repr(exc.value))
+ assert not exc.value.args[0]['changed']
+
+ def test_firmware_download(self):
+ ''' Test firmware download '''
+ module_args = {}
+ module_args.update(self.set_default_args())
+ module_args.update({'package_url': 'dummy_url'})
+ set_module_args(module_args)
+ my_obj = my_module()
+ my_obj.autosupport_log = Mock(return_value=None)
+ if not self.use_vsim:
+ my_obj.server = MockONTAPConnection('firmware_download')
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ assert exc.value.args[0]['changed']
+ msg = "Firmware download completed. Extra info: Download complete."
+ assert exc.value.args[0]['msg'] == msg
+
+ def test_firmware_download_60(self):
+ ''' Test firmware download '''
+ module_args = {}
+ module_args.update(self.set_default_args())
+ module_args.update({'package_url': 'dummy_url'})
+ set_module_args(module_args)
+ my_obj = my_module()
+ my_obj.autosupport_log = Mock(return_value=None)
+ if not self.use_vsim:
+ my_obj.server = MockONTAPConnection('firmware_download_exception', 60, 'ZAPI timeout')
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ assert exc.value.args[0]['changed']
+ msg = "Firmware download completed, slowly."
+ assert exc.value.args[0]['msg'] == msg
+
+ def test_firmware_download_502(self):
+ ''' Test firmware download '''
+ module_args = {}
+ module_args.update(self.set_default_args())
+ module_args.update({'package_url': 'dummy_url'})
+ set_module_args(module_args)
+ my_obj = my_module()
+ my_obj.autosupport_log = Mock(return_value=None)
+ if not self.use_vsim:
+ my_obj.server = MockONTAPConnection('firmware_download_exception', 502, 'Bad GW')
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ assert exc.value.args[0]['changed']
+ msg = "Firmware download still in progress."
+ assert exc.value.args[0]['msg'] == msg
+
+ def test_firmware_download_502_as_error(self):
+ ''' Test firmware download '''
+ module_args = {}
+ module_args.update(self.set_default_args())
+ module_args.update({'package_url': 'dummy_url'})
+ module_args.update({'fail_on_502_error': True})
+ set_module_args(module_args)
+ my_obj = my_module()
+ my_obj.autosupport_log = Mock(return_value=None)
+ if not self.use_vsim:
+ my_obj.server = MockONTAPConnection('firmware_download_exception', 502, 'Bad GW')
+ with pytest.raises(AnsibleFailJson) as exc:
+ my_obj.apply()
+ msg = "NetApp API failed. Reason - 502:Bad GW"
+ assert msg in exc.value.args[0]['msg']
+
+ def test_firmware_download_no_num_error(self):
+ ''' Test firmware download '''
+ module_args = {}
+ module_args.update(self.set_default_args())
+ module_args.update({'package_url': 'dummy_url'})
+ set_module_args(module_args)
+ my_obj = my_module()
+ my_obj.autosupport_log = Mock(return_value=None)
+ if not self.use_vsim:
+ my_obj.server = MockONTAPConnection('firmware_download_exception', 'some error string', 'whatever')
+ with pytest.raises(AnsibleFailJson) as exc:
+ my_obj.apply()
+ msg = "NetApp API failed. Reason - some error string:whatever"
+ assert msg in exc.value.args[0]['msg']
+
+ def test_firmware_download_no_status_attr(self):
+ ''' Test firmware download '''
+ module_args = {}
+ module_args.update(self.set_default_args())
+ module_args.update({'package_url': 'dummy_url'})
+ set_module_args(module_args)
+ my_obj = my_module()
+ my_obj.autosupport_log = Mock(return_value=None)
+ if not self.use_vsim:
+ my_obj.server = MockONTAPConnection('firmware_download', 'no_status_attr')
+ with pytest.raises(AnsibleFailJson) as exc:
+ my_obj.apply()
+ msg = "unable to download package from dummy_url: 'status' attribute missing."
+ assert exc.value.args[0]['msg'].startswith(msg)
+
+ def test_firmware_download_status_failed(self):
+ ''' Test firmware download '''
+ module_args = {}
+ module_args.update(self.set_default_args())
+ module_args.update({'package_url': 'dummy_url'})
+ set_module_args(module_args)
+ my_obj = my_module()
+ my_obj.autosupport_log = Mock(return_value=None)
+ if not self.use_vsim:
+ my_obj.server = MockONTAPConnection('firmware_download', 'status_failed')
+ with pytest.raises(AnsibleFailJson) as exc:
+ my_obj.apply()
+ msg = "unable to download package from dummy_url: check 'status' value."
+ assert exc.value.args[0]['msg'].startswith(msg)
+
+ def test_firmware_download_empty_output(self):
+ ''' Test firmware download '''
+ module_args = {}
+ module_args.update(self.set_default_args())
+ module_args.update({'package_url': 'dummy_url'})
+ set_module_args(module_args)
+ my_obj = my_module()
+ my_obj.autosupport_log = Mock(return_value=None)
+ if not self.use_vsim:
+ my_obj.server = MockONTAPConnection('firmware_download', 'empty_output')
+ with pytest.raises(AnsibleFailJson) as exc:
+ my_obj.apply()
+ msg = "unable to download package from dummy_url: check console permissions."
+ assert exc.value.args[0]['msg'].startswith(msg)
diff --git a/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_flexcache.py b/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_flexcache.py
new file mode 100644
index 00000000..ae93f142
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_flexcache.py
@@ -0,0 +1,531 @@
+# (c) 2018, NetApp, Inc
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+''' unit test for ONTAP FlexCache Ansible module '''
+
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+import json
+import pytest
+
+from ansible_collections.netapp.ontap.tests.unit.compat import unittest
+from ansible_collections.netapp.ontap.tests.unit.compat.mock import patch
+from ansible.module_utils import basic
+from ansible.module_utils._text import to_bytes
+import ansible_collections.netapp.ontap.plugins.module_utils.netapp as netapp_utils
+
+from ansible_collections.netapp.ontap.plugins.modules.na_ontap_flexcache \
+ import NetAppONTAPFlexCache as my_module # module under test
+
+if not netapp_utils.has_netapp_lib():
+ pytestmark = pytest.mark.skip('skipping as missing required netapp_lib')
+
+
+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 MockONTAPConnection(object):
+ ''' mock server connection to ONTAP host '''
+
+ def __init__(self, kind=None, parm1=None, api_error=None, job_error=None):
+ ''' save arguments '''
+ self.type = kind
+ self.parm1 = parm1
+ self.api_error = api_error
+ self.job_error = job_error
+ self.xml_in = None
+ self.xml_out = None
+
+ def invoke_successfully(self, xml, enable_tunneling): # pylint: disable=unused-argument
+ ''' mock invoke_successfully returning xml data '''
+ self.xml_in = xml
+ tag = xml.get_name()
+ if tag == 'flexcache-get-iter' and self.type == 'vserver':
+ xml = self.build_flexcache_info(self.parm1)
+ elif tag == 'flexcache-create-async':
+ xml = self.build_flexcache_create_destroy_rsp()
+ elif tag == 'flexcache-destroy-async':
+ if self.api_error:
+ code, message = self.api_error.split(':', 2)
+ raise netapp_utils.zapi.NaApiError(code, message)
+ xml = self.build_flexcache_create_destroy_rsp()
+ elif tag == 'job-get':
+ xml = self.build_job_info(self.job_error)
+ self.xml_out = xml
+ return xml
+
+ @staticmethod
+ def build_flexcache_info(vserver):
+ ''' build xml data for vserser-info '''
+ xml = netapp_utils.zapi.NaElement('xml')
+ attributes = netapp_utils.zapi.NaElement('attributes-list')
+ count = 2 if vserver == 'repeats' else 1
+ for dummy in range(count):
+ attributes.add_node_with_children('flexcache-info', **{
+ 'vserver': vserver,
+ 'origin-vserver': 'ovserver',
+ 'origin-volume': 'ovolume',
+ 'origin-cluster': 'ocluster',
+ 'volume': 'volume',
+ })
+ xml.add_child_elem(attributes)
+ xml.add_new_child('num-records', str(count))
+ return xml
+
+ @staticmethod
+ def build_flexcache_create_destroy_rsp():
+ ''' build xml data for a create or destroy response '''
+ xml = netapp_utils.zapi.NaElement('xml')
+ xml.add_new_child('result-status', 'in_progress')
+ xml.add_new_child('result-jobid', '1234')
+ return xml
+
+ @staticmethod
+ def build_job_info(error):
+ ''' build xml data for a job '''
+ xml = netapp_utils.zapi.NaElement('xml')
+ attributes = netapp_utils.zapi.NaElement('attributes')
+ if error is None:
+ state = 'success'
+ elif error == 'time_out':
+ state = 'running'
+ else:
+ state = 'failure'
+ attributes.add_node_with_children('job-info', **{
+ 'job-state': state,
+ 'job-progress': 'dummy',
+ 'job-completion': error,
+ })
+ xml.add_child_elem(attributes)
+ xml.add_new_child('result-status', 'in_progress')
+ xml.add_new_child('result-jobid', '1234')
+ return xml
+
+
+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)
+ # make sure to change this to False before submitting
+ self.onbox = False
+ self.dummy_args = dict()
+ for arg in ('hostname', 'username', 'password'):
+ self.dummy_args[arg] = arg
+ if self.onbox:
+ self.args = {
+ 'hostname': '10.193.78.219',
+ 'username': 'admin',
+ 'password': 'netapp1!',
+ }
+ else:
+ self.args = self.dummy_args
+ self.server = MockONTAPConnection()
+
+ def create_flexcache(self, vserver, volume, junction_path):
+ ''' create flexcache '''
+ if not self.onbox:
+ return
+ args = {
+ 'state': 'present',
+ 'volume': volume,
+ 'size': '90', # 80MB minimum
+ 'size_unit': 'mb', # 80MB minimum
+ 'vserver': vserver,
+ 'aggr_list': 'aggr1',
+ 'origin_volume': 'fc_vol_origin',
+ 'origin_vserver': 'ansibleSVM',
+ 'junction_path': junction_path,
+ }
+ args.update(self.args)
+ set_module_args(args)
+ my_obj = my_module()
+ try:
+ my_obj.apply()
+ except AnsibleExitJson as exc:
+ print('Create util: ' + repr(exc))
+ except AnsibleFailJson as exc:
+ print('Create util: ' + repr(exc))
+
+ def delete_flexcache(self, vserver, volume):
+ ''' delete flexcache '''
+ if not self.onbox:
+ return
+ args = {'volume': volume, 'vserver': vserver, 'state': 'absent', 'force_offline': 'true'}
+ args.update(self.args)
+ set_module_args(args)
+ my_obj = my_module()
+ try:
+ my_obj.apply()
+ except AnsibleExitJson as exc:
+ print('Delete util: ' + repr(exc))
+ except AnsibleFailJson as exc:
+ print('Delete util: ' + repr(exc))
+
+ def test_module_fail_when_required_args_missing(self):
+ ''' required arguments are reported as errors '''
+ with pytest.raises(AnsibleFailJson) as exc:
+ set_module_args({})
+ my_module()
+ print('Info: %s' % exc.value.args[0]['msg'])
+
+ def test_missing_parameters(self):
+ ''' fail if origin volume and origin verser are missing '''
+ args = {
+ 'vserver': 'vserver',
+ 'volume': 'volume'
+ }
+ args.update(self.dummy_args)
+ set_module_args(args)
+ my_obj = my_module()
+ my_obj.server = self.server
+ with pytest.raises(AnsibleFailJson) as exc:
+ # It may not be a good idea to start with apply
+ # More atomic methods can be easier to mock
+ # Hint: start with get methods, as they are called first
+ my_obj.apply()
+ msg = 'Missing parameters: origin_volume, origin_vserver'
+ assert exc.value.args[0]['msg'] == msg
+
+ def test_missing_parameter(self):
+ ''' fail if origin verser parameter is missing '''
+ args = {
+ 'vserver': 'vserver',
+ 'origin_volume': 'origin_volume',
+ 'volume': 'volume'
+ }
+ args.update(self.dummy_args)
+ set_module_args(args)
+ my_obj = my_module()
+ my_obj.server = self.server
+ with pytest.raises(AnsibleFailJson) as exc:
+ my_obj.apply()
+ msg = 'Missing parameter: origin_vserver'
+ assert exc.value.args[0]['msg'] == msg
+
+ def test_get_flexcache(self):
+ ''' get flexcache info '''
+ args = {
+ 'vserver': 'ansibleSVM',
+ 'origin_volume': 'origin_volume',
+ 'volume': 'volume'
+ }
+ args.update(self.args)
+ set_module_args(args)
+ my_obj = my_module()
+ if not self.onbox:
+ my_obj.server = MockONTAPConnection('vserver')
+ info = my_obj.flexcache_get()
+ print('info: ' + repr(info))
+
+ def test_get_flexcache_double(self):
+ ''' get flexcache info returns 2 entries! '''
+ args = {
+ 'vserver': 'ansibleSVM',
+ 'origin_volume': 'origin_volume',
+ 'volume': 'volume'
+ }
+ args.update(self.dummy_args)
+ set_module_args(args)
+ my_obj = my_module()
+ my_obj.server = MockONTAPConnection('vserver', 'repeats')
+ with pytest.raises(AnsibleFailJson) as exc:
+ my_obj.flexcache_get()
+ msg = 'Error fetching FlexCache info: Multiple records found for %s:' % args['volume']
+ assert exc.value.args[0]['msg'] == msg
+
+ def test_create_flexcache(self):
+ ''' create flexcache '''
+ args = {
+ 'volume': 'volume',
+ 'size': '90', # 80MB minimum
+ 'size_unit': 'mb', # 80MB minimum
+ 'vserver': 'ansibleSVM',
+ 'aggr_list': 'aggr1',
+ 'origin_volume': 'fc_vol_origin',
+ 'origin_vserver': 'ansibleSVM',
+ }
+ self.delete_flexcache(args['vserver'], args['volume'])
+ args.update(self.args)
+ set_module_args(args)
+ my_obj = my_module()
+ if not self.onbox:
+ my_obj.server = MockONTAPConnection()
+ with patch.object(my_module, 'flexcache_create', wraps=my_obj.flexcache_create) as mock_create:
+ # with patch('__main__.my_module.flexcache_create', wraps=my_obj.flexcache_create) as mock_create:
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ print('Create: ' + repr(exc.value))
+ assert exc.value.args[0]['changed']
+ mock_create.assert_called_with()
+
+ def test_create_flexcache_idempotent(self):
+ ''' create flexcache - already exists '''
+ args = {
+ 'volume': 'volume',
+ 'vserver': 'ansibleSVM',
+ 'aggr_list': 'aggr1',
+ 'origin_volume': 'fc_vol_origin',
+ 'origin_vserver': 'ansibleSVM',
+ }
+ args.update(self.args)
+ set_module_args(args)
+ my_obj = my_module()
+ if not self.onbox:
+ my_obj.server = MockONTAPConnection('vserver')
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ print('Create: ' + repr(exc.value))
+ assert exc.value.args[0]['changed'] is False
+
+ def test_create_flexcache_autoprovision(self):
+ ''' create flexcache with autoprovision'''
+ args = {
+ 'volume': 'volume',
+ 'size': '90', # 80MB minimum
+ 'size_unit': 'mb', # 80MB minimum
+ 'vserver': 'ansibleSVM',
+ 'auto_provision_as': 'flexgroup',
+ 'origin_volume': 'fc_vol_origin',
+ 'origin_vserver': 'ansibleSVM',
+ }
+ self.delete_flexcache(args['vserver'], args['volume'])
+ args.update(self.args)
+ set_module_args(args)
+ my_obj = my_module()
+ if not self.onbox:
+ my_obj.server = MockONTAPConnection()
+ with patch.object(my_module, 'flexcache_create', wraps=my_obj.flexcache_create) as mock_create:
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ print('Create: ' + repr(exc.value))
+ assert exc.value.args[0]['changed']
+ mock_create.assert_called_with()
+
+ def test_create_flexcache_autoprovision_idempotent(self):
+ ''' create flexcache with autoprovision - already exists '''
+ args = {
+ 'volume': 'volume',
+ 'vserver': 'ansibleSVM',
+ 'origin_volume': 'fc_vol_origin',
+ 'origin_vserver': 'ansibleSVM',
+ 'auto_provision_as': 'flexgroup',
+ }
+ args.update(self.args)
+ set_module_args(args)
+ my_obj = my_module()
+ if not self.onbox:
+ my_obj.server = MockONTAPConnection('vserver')
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ print('Create: ' + repr(exc.value))
+ assert exc.value.args[0]['changed'] is False
+
+ def test_create_flexcache_multiplier(self):
+ ''' create flexcache with aggregate multiplier'''
+ args = {
+ 'volume': 'volume',
+ 'size': '90', # 80MB minimum
+ 'size_unit': 'mb', # 80MB minimum
+ 'vserver': 'ansibleSVM',
+ 'aggr_list': 'aggr1',
+ 'origin_volume': 'fc_vol_origin',
+ 'origin_vserver': 'ansibleSVM',
+ 'aggr_list_multiplier': '2',
+ }
+ self.delete_flexcache(args['vserver'], args['volume'])
+ args.update(self.args)
+ set_module_args(args)
+ my_obj = my_module()
+ if not self.onbox:
+ my_obj.server = MockONTAPConnection()
+ with patch.object(my_module, 'flexcache_create', wraps=my_obj.flexcache_create) as mock_create:
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ print('Create: ' + repr(exc.value))
+ assert exc.value.args[0]['changed']
+ mock_create.assert_called_with()
+
+ def test_create_flexcache_multiplier_idempotent(self):
+ ''' create flexcache with aggregate multiplier - already exists '''
+ args = {
+ 'volume': 'volume',
+ 'vserver': 'ansibleSVM',
+ 'aggr_list': 'aggr1',
+ 'origin_volume': 'fc_vol_origin',
+ 'origin_vserver': 'ansibleSVM',
+ 'aggr_list_multiplier': '2',
+ }
+ args.update(self.args)
+ set_module_args(args)
+ my_obj = my_module()
+ if not self.onbox:
+ my_obj.server = MockONTAPConnection('vserver')
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ print('Create: ' + repr(exc.value))
+ assert exc.value.args[0]['changed'] is False
+
+ def test_delete_flexcache_exists_no_force(self):
+ ''' delete flexcache '''
+ args = {'volume': 'volume', 'vserver': 'ansibleSVM', 'state': 'absent'}
+ args.update(self.args)
+ set_module_args(args)
+ my_obj = my_module()
+ error = '13001:Volume volume in Vserver ansibleSVM must be offline to be deleted. ' \
+ 'Use "volume offline -vserver ansibleSVM -volume volume" command to offline ' \
+ 'the volume'
+ if not self.onbox:
+ my_obj.server = MockONTAPConnection('vserver', 'flex', api_error=error)
+ with patch.object(my_module, 'flexcache_delete', wraps=my_obj.flexcache_delete) as mock_delete:
+ with pytest.raises(AnsibleFailJson) as exc:
+ my_obj.apply()
+ print('Delete: ' + repr(exc.value))
+ msg = 'Error deleting FlexCache : NetApp API failed. Reason - %s' % error
+ assert exc.value.args[0]['msg'] == msg
+ mock_delete.assert_called_with()
+
+ def test_delete_flexcache_exists_with_force(self):
+ ''' delete flexcache '''
+ args = {'volume': 'volume', 'vserver': 'ansibleSVM', 'state': 'absent', 'force_offline': 'true'}
+ args.update(self.args)
+ set_module_args(args)
+ my_obj = my_module()
+ if not self.onbox:
+ my_obj.server = MockONTAPConnection('vserver', 'flex')
+ with patch.object(my_module, 'flexcache_delete', wraps=my_obj.flexcache_delete) as mock_delete:
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ print('Delete: ' + repr(exc.value))
+ assert exc.value.args[0]['changed']
+ mock_delete.assert_called_with()
+
+ def test_delete_flexcache_exists_junctionpath_no_force(self):
+ ''' delete flexcache '''
+ args = {'volume': 'volume', 'vserver': 'ansibleSVM', 'junction_path': 'jpath', 'state': 'absent', 'force_offline': 'true'}
+ self.create_flexcache(args['vserver'], args['volume'], args['junction_path'])
+ args.update(self.args)
+ set_module_args(args)
+ my_obj = my_module()
+ error = '160:Volume volume on Vserver ansibleSVM must be unmounted before being taken offline or restricted.'
+ if not self.onbox:
+ my_obj.server = MockONTAPConnection('vserver', 'flex', api_error=error)
+ with patch.object(my_module, 'flexcache_delete', wraps=my_obj.flexcache_delete) as mock_delete:
+ with pytest.raises(AnsibleFailJson) as exc:
+ my_obj.apply()
+ print('Delete: ' + repr(exc.value))
+ msg = 'Error deleting FlexCache : NetApp API failed. Reason - %s' % error
+ assert exc.value.args[0]['msg'] == msg
+ mock_delete.assert_called_with()
+
+ def test_delete_flexcache_exists_junctionpath_with_force(self):
+ ''' delete flexcache '''
+ args = {'volume': 'volume', 'vserver': 'ansibleSVM', 'junction_path': 'jpath', 'state': 'absent', 'force_offline': 'true', 'force_unmount': 'true'}
+ self.create_flexcache(args['vserver'], args['volume'], args['junction_path'])
+ args.update(self.args)
+ set_module_args(args)
+ my_obj = my_module()
+ if not self.onbox:
+ my_obj.server = MockONTAPConnection('vserver', 'flex')
+ with patch.object(my_module, 'flexcache_delete', wraps=my_obj.flexcache_delete) as mock_delete:
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ print('Delete: ' + repr(exc.value))
+ assert exc.value.args[0]['changed']
+ mock_delete.assert_called_with()
+
+ def test_delete_flexcache_not_exist(self):
+ ''' delete flexcache '''
+ args = {'volume': 'volume', 'vserver': 'ansibleSVM', 'state': 'absent'}
+ args.update(self.args)
+ set_module_args(args)
+ my_obj = my_module()
+ if not self.onbox:
+ my_obj.server = MockONTAPConnection()
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ print('Delete: ' + repr(exc.value))
+ assert exc.value.args[0]['changed'] is False
+
+ def test_create_flexcache_size_error(self):
+ ''' create flexcache '''
+ args = {
+ 'volume': 'volume_err',
+ 'size': '50', # 80MB minimum
+ 'size_unit': 'mb', # 80MB minimum
+ 'vserver': 'ansibleSVM',
+ 'aggr_list': 'aggr1',
+ 'origin_volume': 'fc_vol_origin',
+ 'origin_vserver': 'ansibleSVM',
+ }
+ args.update(self.args)
+ set_module_args(args)
+ my_obj = my_module()
+ error = 'Size "50MB" ("52428800B") is too small. Minimum size is "80MB" ("83886080B"). '
+ if not self.onbox:
+ my_obj.server = MockONTAPConnection(job_error=error)
+ with patch.object(my_module, 'flexcache_create', wraps=my_obj.flexcache_create) as mock_create:
+ with pytest.raises(AnsibleFailJson) as exc:
+ my_obj.apply()
+ print('Create: ' + repr(exc.value))
+ msg = 'Error when creating flexcache: %s' % error
+ assert exc.value.args[0]['msg'] == msg
+ mock_create.assert_called_with()
+
+ def test_create_flexcache_time_out(self):
+ ''' create flexcache '''
+ args = {
+ 'volume': 'volume_err',
+ 'size': '50', # 80MB minimum
+ 'size_unit': 'mb', # 80MB minimum
+ 'vserver': 'ansibleSVM',
+ 'aggr_list': 'aggr1',
+ 'origin_volume': 'fc_vol_origin',
+ 'origin_vserver': 'ansibleSVM',
+ 'time_out': '2'
+ }
+ args.update(self.args)
+ set_module_args(args)
+ my_obj = my_module()
+ if not self.onbox:
+ my_obj.server = MockONTAPConnection(job_error='time_out')
+ with patch.object(my_module, 'flexcache_create', wraps=my_obj.flexcache_create) as mock_create:
+ # replace time.sleep with a noop
+ with patch('time.sleep', lambda a: None):
+ with pytest.raises(AnsibleFailJson) as exc:
+ my_obj.apply()
+ print('Create: ' + repr(exc.value))
+ msg = 'Error when creating flexcache: job completion exceeded expected timer of: %s seconds' \
+ % args['time_out']
+ assert exc.value.args[0]['msg'] == msg
+ mock_create.assert_called_with()
diff --git a/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_igroup.py b/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_igroup.py
new file mode 100644
index 00000000..57609fc6
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_igroup.py
@@ -0,0 +1,260 @@
+# (c) 2018, NetApp, Inc
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+''' unit test template for ONTAP Ansible module '''
+
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+import json
+import pytest
+
+from ansible_collections.netapp.ontap.tests.unit.compat import unittest
+from ansible_collections.netapp.ontap.tests.unit.compat.mock import patch, Mock
+from ansible.module_utils import basic
+from ansible.module_utils._text import to_bytes
+import ansible_collections.netapp.ontap.plugins.module_utils.netapp as netapp_utils
+
+from ansible_collections.netapp.ontap.plugins.modules.na_ontap_igroup \
+ import NetAppOntapIgroup as igroup # module under test
+
+if not netapp_utils.has_netapp_lib():
+ pytestmark = pytest.mark.skip('skipping as missing required netapp_lib')
+
+
+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 MockONTAPConnection(object):
+ ''' mock server connection to ONTAP host '''
+
+ def __init__(self, kind=None, data=None):
+ ''' save arguments '''
+ self.kind = kind
+ self.data = data
+ self.xml_in = None
+ self.xml_out = None
+
+ def invoke_successfully(self, xml, enable_tunneling): # pylint: disable=unused-argument
+ ''' mock invoke_successfully returning xml data '''
+ self.xml_in = xml
+ if self.kind == 'igroup':
+ xml = self.build_igroup()
+ if self.kind == 'igroup_no_initiators':
+ xml = self.build_igroup_no_initiators()
+ self.xml_out = xml
+ return xml
+
+ @staticmethod
+ def build_igroup():
+ ''' build xml data for initiator '''
+ xml = netapp_utils.zapi.NaElement('xml')
+ attributes = {
+ 'num-records': 1,
+ 'attributes-list': {
+ 'initiator-group-info': {
+ 'initiators': [
+ {
+ 'initiator-info': {
+ 'initiator-name': 'init1'
+ }},
+ {
+ 'initiator-info': {
+ 'initiator-name': 'init2'
+ }}
+ ]
+ }
+ }
+ }
+ xml.translate_struct(attributes)
+ return xml
+
+ @staticmethod
+ def build_igroup_no_initiators():
+ ''' build xml data for igroup with no initiators '''
+ xml = netapp_utils.zapi.NaElement('xml')
+ attributes = {
+ 'num-records': 1,
+ 'attributes-list': {
+ 'initiator-group-info': {
+ 'vserver': 'test'
+ }
+ }
+ }
+ xml.translate_struct(attributes)
+ return xml
+
+
+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)
+ self.server = MockONTAPConnection()
+
+ def mock_args(self):
+ return {
+ 'vserver': 'vserver',
+ 'name': 'test',
+ 'initiators': 'init1',
+ 'ostype': 'linux',
+ 'initiator_group_type': 'fcp',
+ 'bind_portset': 'true',
+ 'hostname': 'hostname',
+ 'username': 'username',
+ 'password': 'password'
+ }
+
+ def get_igroup_mock_object(self, kind=None):
+ """
+ Helper method to return an na_ontap_igroup object
+ :param kind: passes this param to MockONTAPConnection()
+ :return: na_ontap_igroup object
+ """
+ obj = igroup()
+ obj.autosupport_log = Mock(return_value=None)
+ if kind is None:
+ obj.server = MockONTAPConnection()
+ else:
+ obj.server = MockONTAPConnection(kind=kind)
+ return obj
+
+ def test_module_fail_when_required_args_missing(self):
+ ''' required arguments are reported as errors '''
+ with pytest.raises(AnsibleFailJson) as exc:
+ set_module_args({})
+ igroup()
+
+ def test_get_nonexistent_igroup(self):
+ ''' Test if get_igroup returns None for non-existent igroup '''
+ data = self.mock_args()
+ set_module_args(data)
+ result = self.get_igroup_mock_object().get_igroup('dummy')
+ assert result is None
+
+ def test_get_existing_igroup_with_initiators(self):
+ ''' Test if get_igroup returns list of existing initiators '''
+ data = self.mock_args()
+ set_module_args(data)
+ result = self.get_igroup_mock_object('igroup').get_igroup(data['name'])
+ assert data['initiators'] in result['initiators']
+ assert result['initiators'] == ['init1', 'init2']
+
+ def test_get_existing_igroup_without_initiators(self):
+ ''' Test if get_igroup returns empty list() '''
+ data = self.mock_args()
+ set_module_args(data)
+ result = self.get_igroup_mock_object('igroup_no_initiators').get_igroup(data['name'])
+ assert result['initiators'] == []
+
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_igroup.NetAppOntapIgroup.add_initiators')
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_igroup.NetAppOntapIgroup.remove_initiators')
+ def test_modify_initiator_calls_add_and_remove(self, remove, add):
+ '''Test remove_initiator() is called followed by add_initiator() on modify operation'''
+ data = self.mock_args()
+ data['initiators'] = 'replacewithme'
+ set_module_args(data)
+ obj = self.get_igroup_mock_object('igroup')
+ with pytest.raises(AnsibleExitJson) as exc:
+ current = obj.get_igroup(data['name'])
+ obj.apply()
+ remove.assert_called_with(current['initiators'])
+ add.assert_called_with()
+
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_igroup.NetAppOntapIgroup.modify_initiator')
+ def test_modify_called_from_add(self, modify):
+ '''Test remove_initiator() and add_initiator() calls modify'''
+ data = self.mock_args()
+ data['initiators'] = 'replacewithme'
+ add, remove = 'igroup-add', 'igroup-remove'
+ set_module_args(data)
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_igroup_mock_object('igroup_no_initiators').apply()
+ modify.assert_called_with('replacewithme', add)
+ assert modify.call_count == 1 # remove nothing, add 1 new
+
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_igroup.NetAppOntapIgroup.modify_initiator')
+ def test_modify_called_from_remove(self, modify):
+ '''Test remove_initiator() and add_initiator() calls modify'''
+ data = self.mock_args()
+ data['initiators'] = ''
+ remove = 'igroup-remove'
+ set_module_args(data)
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_igroup_mock_object('igroup').apply()
+ modify.assert_called_with('init2', remove)
+ assert modify.call_count == 2 # remove existing 2, add nothing
+
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_igroup.NetAppOntapIgroup.add_initiators')
+ def test_successful_create(self, add):
+ ''' Test successful create '''
+ set_module_args(self.mock_args())
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_igroup_mock_object().apply()
+ assert exc.value.args[0]['changed']
+ add.assert_called_with()
+
+ def test_successful_delete(self):
+ ''' Test successful delete '''
+ data = self.mock_args()
+ data['state'] = 'absent'
+ set_module_args(self.mock_args())
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_igroup_mock_object('igroup').apply()
+ assert exc.value.args[0]['changed']
+
+ def test_successful_modify(self):
+ ''' Test successful modify '''
+ data = self.mock_args()
+ data['initiators'] = 'new'
+ set_module_args(self.mock_args())
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_igroup_mock_object('igroup').apply()
+ assert exc.value.args[0]['changed']
+
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_igroup.NetAppOntapIgroup.get_igroup')
+ def test_successful_rename(self, get_vserver):
+ '''Test successful rename'''
+ data = self.mock_args()
+ data['from_name'] = 'test'
+ data['name'] = 'test_new'
+ set_module_args(data)
+ current = {
+ 'initiators': ['init1', 'init2']
+ }
+ get_vserver.side_effect = [
+ None,
+ current
+ ]
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_igroup_mock_object().apply()
+ assert exc.value.args[0]['changed']
diff --git a/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_igroup_initiator.py b/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_igroup_initiator.py
new file mode 100644
index 00000000..d7e3bc68
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_igroup_initiator.py
@@ -0,0 +1,218 @@
+# (c) 2018, NetApp, Inc
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+''' unit test template for ONTAP Ansible module '''
+
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+import json
+import pytest
+
+from ansible_collections.netapp.ontap.tests.unit.compat import unittest
+from ansible_collections.netapp.ontap.tests.unit.compat.mock import patch, Mock
+from ansible.module_utils import basic
+from ansible.module_utils._text import to_bytes
+import ansible_collections.netapp.ontap.plugins.module_utils.netapp as netapp_utils
+
+from ansible_collections.netapp.ontap.plugins.modules.na_ontap_igroup_initiator \
+ import NetAppOntapIgroupInitiator as initiator # module under test
+
+if not netapp_utils.has_netapp_lib():
+ pytestmark = pytest.mark.skip('skipping as missing required netapp_lib')
+
+
+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 MockONTAPConnection(object):
+ ''' mock server connection to ONTAP host '''
+
+ def __init__(self, kind=None, data=None):
+ ''' save arguments '''
+ self.kind = kind
+ self.data = data
+ self.xml_in = None
+ self.xml_out = None
+
+ def invoke_successfully(self, xml, enable_tunneling): # pylint: disable=unused-argument
+ ''' mock invoke_successfully returning xml data '''
+ self.xml_in = xml
+ if self.kind == 'initiator':
+ xml = self.build_igroup_initiator()
+ elif self.kind == 'initiator_fail':
+ raise netapp_utils.zapi.NaApiError(code='TEST', message="This exception is from the unit test")
+ self.xml_out = xml
+ return xml
+
+ @staticmethod
+ def build_igroup_initiator():
+ ''' build xml data for initiator '''
+ xml = netapp_utils.zapi.NaElement('xml')
+ attributes = {
+ 'num-records': 1,
+ 'attributes-list': {
+ 'initiator-group-info': {
+ 'initiators': [
+ {'initiator-info': {
+ 'initiator-name': 'init1'
+ }},
+ {'initiator-info': {
+ 'initiator-name': 'init2'
+ }}
+ ]
+ }
+ }
+ }
+ xml.translate_struct(attributes)
+ return xml
+
+
+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)
+ self.server = MockONTAPConnection()
+
+ def mock_args(self):
+ return {
+ 'vserver': 'vserver',
+ 'name': 'init1',
+ 'initiator_group': 'test',
+ 'hostname': 'hostname',
+ 'username': 'username',
+ 'password': 'password'
+ }
+
+ def get_initiator_mock_object(self, kind=None):
+ """
+ Helper method to return an na_ontap_initiator object
+ :param kind: passes this param to MockONTAPConnection()
+ :return: na_ontap_initiator object
+ """
+ obj = initiator()
+ obj.autosupport_log = Mock(return_value=None)
+ if kind is None:
+ obj.server = MockONTAPConnection()
+ else:
+ obj.server = MockONTAPConnection(kind=kind)
+ return obj
+
+ def test_module_fail_when_required_args_missing(self):
+ ''' required arguments are reported as errors '''
+ with pytest.raises(AnsibleFailJson) as exc:
+ set_module_args({})
+ initiator()
+
+ def test_get_nonexistent_initiator(self):
+ ''' Test if get_initiators returns None for non-existent initiator '''
+ data = self.mock_args()
+ data['name'] = 'idontexist'
+ set_module_args(data)
+ result = self.get_initiator_mock_object('initiator').get_initiators()
+ assert data['name'] not in result
+
+ def test_get_nonexistent_igroup(self):
+ ''' Test if get_initiators returns None for non-existent igroup '''
+ data = self.mock_args()
+ data['name'] = 'idontexist'
+ set_module_args(data)
+ result = self.get_initiator_mock_object().get_initiators()
+ assert result == []
+
+ def test_get_existing_initiator(self):
+ ''' Test if get_initiator returns None for existing initiator '''
+ data = self.mock_args()
+ set_module_args(data)
+ result = self.get_initiator_mock_object(kind='initiator').get_initiators()
+ assert data['name'] in result
+ assert result == ['init1', 'init2'] # from build_igroup_initiators()
+
+ def test_successful_add(self):
+ ''' Test successful add'''
+ data = self.mock_args()
+ data['name'] = 'iamnew'
+ set_module_args(data)
+ obj = self.get_initiator_mock_object('initiator')
+ with pytest.raises(AnsibleExitJson) as exc:
+ current = obj.get_initiators()
+ obj.apply()
+ assert data['name'] not in current
+ assert exc.value.args[0]['changed']
+
+ def test_successful_add_idempotency(self):
+ ''' Test successful add idempotency '''
+ data = self.mock_args()
+ set_module_args(data)
+ obj = self.get_initiator_mock_object('initiator')
+ with pytest.raises(AnsibleExitJson) as exc:
+ current_list = obj.get_initiators()
+ obj.apply()
+ assert data['name'] in current_list
+ assert not exc.value.args[0]['changed']
+
+ def test_successful_remove(self):
+ ''' Test successful remove '''
+ data = self.mock_args()
+ data['state'] = 'absent'
+ set_module_args(data)
+ obj = self.get_initiator_mock_object('initiator')
+ with pytest.raises(AnsibleExitJson) as exc:
+ current_list = obj.get_initiators()
+ obj.apply()
+ assert data['name'] in current_list
+ assert exc.value.args[0]['changed']
+
+ def test_successful_remove_idempotency(self):
+ ''' Test successful remove idempotency'''
+ data = self.mock_args()
+ data['state'] = 'absent'
+ data['name'] = 'alreadyremoved'
+ set_module_args(data)
+ obj = self.get_initiator_mock_object('initiator')
+ with pytest.raises(AnsibleExitJson) as exc:
+ current_list = obj.get_initiators()
+ obj.apply()
+ assert data['name'] not in current_list
+ assert not exc.value.args[0]['changed']
+
+ def test_if_all_methods_catch_exception(self):
+ data = self.mock_args()
+ set_module_args(data)
+ my_obj = self.get_initiator_mock_object('initiator_fail')
+ with pytest.raises(AnsibleFailJson) as exc:
+ my_obj.get_initiators()
+ assert 'Error fetching igroup info ' in exc.value.args[0]['msg']
+ with pytest.raises(AnsibleFailJson) as exc:
+ my_obj.modify_initiator(data['name'], 'igroup-add')
+ assert 'Error modifying igroup initiator ' in exc.value.args[0]['msg']
diff --git a/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_info.py b/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_info.py
new file mode 100644
index 00000000..625fbb2b
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_info.py
@@ -0,0 +1,557 @@
+# (c) 2018-2019, NetApp, Inc
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+''' unit tests for ONTAP Ansible module na_ontap_info '''
+
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+import json
+import pytest
+import sys
+
+from ansible_collections.netapp.ontap.tests.unit.compat import unittest
+from ansible_collections.netapp.ontap.tests.unit.compat.mock import patch
+from ansible.module_utils import basic
+from ansible.module_utils._text import to_bytes
+import ansible_collections.netapp.ontap.plugins.module_utils.netapp as netapp_utils
+
+from ansible_collections.netapp.ontap.plugins.modules.na_ontap_info import main as info_main
+from ansible_collections.netapp.ontap.plugins.modules.na_ontap_info import __finditem as info_finditem
+from ansible_collections.netapp.ontap.plugins.modules.na_ontap_info \
+ import NetAppONTAPGatherInfo as info_module # module under test
+from ansible_collections.netapp.ontap.plugins.modules.na_ontap_info \
+ import convert_keys as info_convert_keys # function under test
+
+if not netapp_utils.has_netapp_lib():
+ pytestmark = pytest.mark.skip('skipping as missing required netapp_lib')
+
+
+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 MockONTAPConnection(object):
+ ''' mock server connection to ONTAP host '''
+
+ def __init__(self, kind=None):
+ ''' save arguments '''
+ self.type = kind
+ self.xml_in = None
+ self.xml_out = None
+
+ def invoke_successfully(self, xml, enable_tunneling): # pylint: disable=unused-argument
+ ''' mock invoke_successfully returning xml data '''
+ self.xml_in = xml
+ if self.type == 'vserver':
+ xml = self.build_vserver_info()
+ elif self.type == 'net_port':
+ xml = self.build_net_port_info()
+ elif self.type == 'net_port_no_ifgrp':
+ xml = self.build_net_port_info('no_ifgrp')
+ elif self.type == 'net_port_with_ifgrp':
+ xml = self.build_net_port_info('with_ifgrp')
+ # for the next calls
+ self.type = 'net_ifgrp'
+ elif self.type == 'net_ifgrp':
+ xml = self.build_net_ifgrp_info()
+ elif self.type == 'zapi_error':
+ error = netapp_utils.zapi.NaApiError('test', 'error')
+ raise error
+ elif self.type == 'list_of_one':
+ xml = self.list_of_one()
+ elif self.type == 'list_of_two':
+ xml = self.list_of_two()
+ elif self.type == 'list_of_two_dups':
+ xml = self.list_of_two_dups()
+ else:
+ raise KeyError(self.type)
+ self.xml_out = xml
+ return xml
+
+ @staticmethod
+ def build_vserver_info():
+ ''' build xml data for vserser-info '''
+ xml = netapp_utils.zapi.NaElement('xml')
+ attributes = netapp_utils.zapi.NaElement('attributes-list')
+ attributes.add_node_with_children('vserver-info',
+ **{'vserver-name': 'test_vserver'})
+ xml.add_child_elem(attributes)
+ return xml
+
+ @staticmethod
+ def build_net_port_info(with_type=None):
+ ''' build xml data for net-port-info '''
+ xml = netapp_utils.zapi.NaElement('xml')
+ attributes_list = netapp_utils.zapi.NaElement('attributes-list')
+ num_net_port_info = 2
+ for i in range(num_net_port_info):
+ net_port_info = netapp_utils.zapi.NaElement('net-port-info')
+ net_port_info.add_new_child('node', 'node_' + str(i))
+ net_port_info.add_new_child('port', 'port_' + str(i))
+ net_port_info.add_new_child('broadcast_domain', 'test_domain_' + str(i))
+ net_port_info.add_new_child('ipspace', 'ipspace' + str(i))
+ if with_type == 'with_ifgrp':
+ net_port_info.add_new_child('port_type', 'if_group')
+ elif with_type == 'no_ifgrp':
+ net_port_info.add_new_child('port_type', 'whatever')
+ attributes_list.add_child_elem(net_port_info)
+ xml.add_child_elem(attributes_list)
+ return xml
+
+ @staticmethod
+ def build_net_ifgrp_info():
+ ''' build xml data for net-ifgrp-info '''
+ xml = netapp_utils.zapi.NaElement('xml')
+ attributes_list = netapp_utils.zapi.NaElement('attributes')
+ num_net_ifgrp_info = 2
+ for i in range(num_net_ifgrp_info):
+ net_ifgrp_info = netapp_utils.zapi.NaElement('net-ifgrp-info')
+ net_ifgrp_info.add_new_child('ifgrp-name', 'ifgrp_' + str(i))
+ net_ifgrp_info.add_new_child('node', 'node_' + str(i))
+ attributes_list.add_child_elem(net_ifgrp_info)
+ xml.add_child_elem(attributes_list)
+ return xml
+
+ @staticmethod
+ def list_of_one():
+ ''' build xml data for list of one info element '''
+ xml = netapp_utils.zapi.NaElement('xml')
+ list_of_one = [{'k1': 'v1', 'k2': 'v2'}]
+ xml.translate_struct(list_of_one)
+ return xml
+
+ @staticmethod
+ def list_of_two():
+ ''' build xml data for list of two info elements '''
+ xml = netapp_utils.zapi.NaElement('xml')
+ list_of_two = [{'k1': 'v1'}, {'k2': 'v2'}]
+ xml.translate_struct(list_of_two)
+ return xml
+
+ @staticmethod
+ def list_of_two_dups():
+ ''' build xml data for list of two info elements with same key '''
+ xml = netapp_utils.zapi.NaElement('xml')
+ list_of_two = [{'k1': 'v1'}, {'k1': 'v2'}]
+ xml.translate_struct(list_of_two)
+ return xml
+
+
+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)
+ self.server = MockONTAPConnection()
+
+ def mock_args(self):
+ return {
+ 'hostname': 'hostname',
+ 'username': 'username',
+ 'password': 'password',
+ 'vserver': None
+ }
+
+ def get_info_mock_object(self, kind=None):
+ """
+ Helper method to return an na_ontap_info object
+ """
+ argument_spec = netapp_utils.na_ontap_host_argument_spec()
+ argument_spec.update(dict(
+ state=dict(type='str', default='info', choices=['info']),
+ gather_subset=dict(default=['all'], type='list'),
+ vserver=dict(type='str', default=None, required=False),
+ max_records=dict(type='int', default=1024, required=False),
+ desired_attributes=dict(type='dict', required=False),
+ use_native_zapi_tags=dict(type='bool', required=False, default=False),
+ continue_on_error=dict(type='list', required=False, default=['never']),
+ query=dict(type='dict', required=False),
+ ))
+ module = basic.AnsibleModule(
+ argument_spec=argument_spec,
+ supports_check_mode=True
+ )
+ max_records = module.params['max_records']
+ obj = info_module(module, max_records)
+ obj.netapp_info = dict()
+ if kind is None:
+ obj.server = MockONTAPConnection()
+ else:
+ obj.server = MockONTAPConnection(kind)
+ return obj
+
+ 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.get_info_mock_object()
+ print('Info: %s' % exc.value.args[0]['msg'])
+
+ @patch('ansible_collections.netapp.ontap.plugins.module_utils.netapp.ems_log_event')
+ def test_ensure_command_called(self, mock_ems_log):
+ ''' calling get_all will raise a KeyError exception '''
+ set_module_args(self.mock_args())
+ my_obj = self.get_info_mock_object('vserver')
+ with pytest.raises(KeyError) as exc:
+ my_obj.get_all(['net_interface_info'])
+ if sys.version_info >= (2, 7):
+ msg = 'net-interface-info'
+ print(exc.value.args[0])
+ assert exc.value.args[0] == msg
+
+ @patch('ansible_collections.netapp.ontap.plugins.module_utils.netapp.ems_log_event')
+ def test_get_generic_get_iter(self, mock_ems_log):
+ '''calling get_generic_get_iter will return expected dict'''
+ set_module_args(self.mock_args())
+ obj = self.get_info_mock_object('net_port')
+ result = obj.get_generic_get_iter(
+ 'net-port-get-iter',
+ attribute='net-port-info',
+ key_fields=('node', 'port'),
+ query={'max-records': '1024'}
+ )
+ assert result.get('node_0:port_0')
+ assert result.get('node_1:port_1')
+
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_info.NetAppONTAPGatherInfo.get_all')
+ def test_main(self, get_all):
+ '''test main method.'''
+ set_module_args(self.mock_args())
+ get_all.side_effect = [
+ {'test_get_all':
+ {'vserver_login_banner_info': 'test_vserver_login_banner_info', 'vserver_info': 'test_vserver_info'}}
+ ]
+ with pytest.raises(AnsibleExitJson) as exc:
+ info_main()
+ assert exc.value.args[0]['state'] == 'info'
+
+ def test_get_ifgrp_info_no_ifgrp(self):
+ '''test get_ifgrp_info with empty ifgrp_info'''
+ set_module_args(self.mock_args())
+ obj = self.get_info_mock_object('net_port_no_ifgrp')
+ result = obj.get_ifgrp_info()
+ assert result == {}
+
+ def test_get_ifgrp_info_with_ifgrp(self):
+ '''test get_ifgrp_info with empty ifgrp_info'''
+ set_module_args(self.mock_args())
+ obj = self.get_info_mock_object('net_port_with_ifgrp')
+ result = obj.get_ifgrp_info()
+ assert result.get('node_0:ifgrp_0')
+ assert result.get('node_1:ifgrp_1')
+
+ def test_ontapi_error(self):
+ '''test ontapi will raise zapi error'''
+ set_module_args(self.mock_args())
+ obj = self.get_info_mock_object('zapi_error')
+ with pytest.raises(AnsibleFailJson) as exc:
+ obj.ontapi()
+ # The new version of nettap-lib adds a space after :
+ # Keep both versions to keep the pipeline happy
+ assert exc.value.args[0]['msg'] == 'Error calling API system-get-ontapi-version: NetApp API failed. Reason - test:error'
+
+ def test_call_api_error(self):
+ '''test call_api will raise zapi error'''
+ set_module_args(self.mock_args())
+ obj = self.get_info_mock_object('zapi_error')
+ with pytest.raises(AnsibleFailJson) as exc:
+ obj.call_api('nvme-get-iter')
+ # The new version of nettap-lib adds a space after :
+ # Keep both versions to keep the pipeline happy
+ assert exc.value.args[0]['msg'] == 'Error calling API nvme-get-iter: NetApp API failed. Reason - test:error'
+
+ def test_find_item(self):
+ '''test __find_item return expected key value'''
+ obj = {"A": 1, "B": {"C": {"D": 2}}}
+ key = "D"
+ result = info_finditem(obj, key)
+ assert result == 2
+
+ def test_subset_return_all_complete(self):
+ ''' Check all returns all of the entries if version is high enough '''
+ version = '170' # change this if new ZAPIs are supported
+ set_module_args(self.mock_args())
+ obj = self.get_info_mock_object('vserver')
+ subset = obj.get_subset(['all'], version)
+ assert set(obj.info_subsets.keys()) == subset
+
+ def test_subset_return_all_partial(self):
+ ''' Check all returns a subset of the entries if version is low enough '''
+ version = '120' # low enough so that some ZAPIs are not supported
+ set_module_args(self.mock_args())
+ obj = self.get_info_mock_object('vserver')
+ subset = obj.get_subset(['all'], version)
+ all_keys = obj.info_subsets.keys()
+ assert set(all_keys) > subset
+ supported_keys = filter(lambda key: obj.info_subsets[key]['min_version'] <= version, all_keys)
+ assert set(supported_keys) == subset
+
+ def test_subset_return_one(self):
+ ''' Check single entry returns one '''
+ version = '120' # low enough so that some ZAPIs are not supported
+ set_module_args(self.mock_args())
+ obj = self.get_info_mock_object('vserver')
+ subset = obj.get_subset(['net_interface_info'], version)
+ assert len(subset) == 1
+
+ def test_subset_return_multiple(self):
+ ''' Check that more than one entry returns the same number '''
+ version = '120' # low enough so that some ZAPIs are not supported
+ set_module_args(self.mock_args())
+ obj = self.get_info_mock_object('vserver')
+ subset_entries = ['net_interface_info', 'net_port_info']
+ subset = obj.get_subset(subset_entries, version)
+ assert len(subset) == len(subset_entries)
+
+ def test_subset_return_bad(self):
+ ''' Check that a bad subset entry will error out '''
+ version = '120' # low enough so that some ZAPIs are not supported
+ set_module_args(self.mock_args())
+ obj = self.get_info_mock_object('vserver')
+ with pytest.raises(AnsibleFailJson) as exc:
+ obj.get_subset(['net_interface_info', 'my_invalid_subset'], version)
+ print('Info: %s' % exc.value.args[0]['msg'])
+ assert exc.value.args[0]['msg'] == 'Bad subset: my_invalid_subset'
+
+ def test_subset_return_unsupported(self):
+ ''' Check that a new subset entry will error out on an older system '''
+ version = '120' # low enough so that some ZAPIs are not supported
+ key = 'nvme_info' # only supported starting at 140
+ set_module_args(self.mock_args())
+ obj = self.get_info_mock_object('vserver')
+ with pytest.raises(AnsibleFailJson) as exc:
+ obj.get_subset(['net_interface_info', key], version)
+ print('Info: %s' % exc.value.args[0]['msg'])
+ msg = 'Remote system at version %s does not support %s' % (version, key)
+ assert exc.value.args[0]['msg'] == msg
+
+ def test_subset_return_none(self):
+ ''' Check usable subset can be empty '''
+ version = '!' # lower then 0, so that no ZAPI is supported
+ set_module_args(self.mock_args())
+ obj = self.get_info_mock_object('vserver')
+ subset = obj.get_subset(['all'], version)
+ assert len(subset) == 0
+
+ def test_subset_return_all_expect_one(self):
+ ''' Check !x returns all of the entries except x if version is high enough '''
+ version = '170' # change this if new ZAPIs are supported
+ set_module_args(self.mock_args())
+ obj = self.get_info_mock_object('vserver')
+ subset = obj.get_subset(['!net_interface_info'], version)
+ assert len(obj.info_subsets.keys()) == len(subset) + 1
+ subset.add('net_interface_info')
+ assert set(obj.info_subsets.keys()) == subset
+
+ def test_subset_return_all_expect_three(self):
+ ''' Check !x,!y,!z returns all of the entries except x, y, z if version is high enough '''
+ version = '170' # change this if new ZAPIs are supported
+ set_module_args(self.mock_args())
+ obj = self.get_info_mock_object('vserver')
+ subset = obj.get_subset(['!net_interface_info', '!nvme_info', '!ontap_version'], version)
+ assert len(obj.info_subsets.keys()) == len(subset) + 3
+ subset.update(['net_interface_info', 'nvme_info', 'ontap_version'])
+ assert set(obj.info_subsets.keys()) == subset
+
+ def test_subset_return_none_with_exclusion(self):
+ ''' Check usable subset can be empty with !x '''
+ version = '!' # lower then 0, so that no ZAPI is supported
+ key = 'net_interface_info'
+ set_module_args(self.mock_args())
+ obj = self.get_info_mock_object('vserver')
+ with pytest.raises(AnsibleFailJson) as exc:
+ obj.get_subset(['!' + key], version)
+ print('Info: %s' % exc.value.args[0]['msg'])
+ msg = 'Remote system at version %s does not support %s' % (version, key)
+ assert exc.value.args[0]['msg'] == msg
+
+ @patch('ansible_collections.netapp.ontap.plugins.module_utils.netapp.ems_log_event')
+ def test_get_generic_get_iter_flatten_list_of_one(self, mock_ems_log):
+ '''calling get_generic_get_iter will return expected dict'''
+ set_module_args(self.mock_args())
+ obj = self.get_info_mock_object('list_of_one')
+ result = obj.get_generic_get_iter(
+ 'list_of_one',
+ attributes_list_tag=None,
+ )
+ assert isinstance(result, dict)
+ assert result.get('k1') == 'v1'
+ assert result.get('k2') == 'v2'
+
+ @patch('ansible_collections.netapp.ontap.plugins.module_utils.netapp.ems_log_event')
+ def test_get_generic_get_iter_flatten_list_of_two(self, mock_ems_log):
+ '''calling get_generic_get_iter will return expected dict'''
+ set_module_args(self.mock_args())
+ obj = self.get_info_mock_object('list_of_two')
+ result = obj.get_generic_get_iter(
+ 'list_of_two',
+ attributes_list_tag=None,
+ )
+ assert isinstance(result, dict)
+ assert result.get('k1') == 'v1'
+ assert result.get('k2') == 'v2'
+
+ @patch('ansible_collections.netapp.ontap.plugins.module_utils.netapp.ems_log_event')
+ def test_get_generic_get_iter_flatten_list_of_two_dups(self, mock_ems_log):
+ '''calling get_generic_get_iter will return expected dict'''
+ set_module_args(self.mock_args())
+ obj = self.get_info_mock_object('list_of_two_dups')
+ result = obj.get_generic_get_iter(
+ 'list_of_two_dups',
+ attributes_list_tag=None,
+ )
+ assert isinstance(result, list)
+ assert result[0].get('k1') == 'v1'
+ assert result[1].get('k1') == 'v2'
+
+ def test_check_underscore(self):
+ ''' Check warning is recorded if '_' is found in key '''
+ test_dict = dict(
+ bad_key='something'
+ )
+ test_dict['good-key'] = [dict(
+ other_bad_key=dict(
+ yet_another_bad_key=1
+ ),
+ somekey=dict(
+ more_bad_key=2
+ )
+ )]
+ set_module_args(self.mock_args())
+ obj = self.get_info_mock_object('vserver')
+ obj.check_for___in_keys(test_dict)
+ print('Info: %s' % repr(obj.warnings))
+ for key in ['bad_key', 'other_bad_key', 'yet_another_bad_key', 'more_bad_key']:
+ msg = "Underscore in ZAPI tag: %s, do you mean '-'?" % key
+ assert msg in obj.warnings
+ obj.warnings.remove(msg)
+ # make sure there is no extra warnings (eg we found and removed all of them)
+ assert obj.warnings == list()
+
+ @staticmethod
+ def d2us(astr):
+ return str.replace(astr, '-', '_')
+
+ def test_convert_keys_string(self):
+ ''' no conversion '''
+ key = 'a-b-c'
+ assert info_convert_keys(key) == key
+
+ def test_convert_keys_tuple(self):
+ ''' no conversion '''
+ key = 'a-b-c'
+ anobject = (key, key)
+ assert info_convert_keys(anobject) == anobject
+
+ def test_convert_keys_list(self):
+ ''' no conversion '''
+ key = 'a-b-c'
+ anobject = [key, key]
+ assert info_convert_keys(anobject) == anobject
+
+ def test_convert_keys_simple_dict(self):
+ ''' conversion of keys '''
+ key = 'a-b-c'
+ anobject = {key: 1}
+ assert list(info_convert_keys(anobject).keys())[0] == self.d2us(key)
+
+ def test_convert_keys_list_of_dict(self):
+ ''' conversion of keys '''
+ key = 'a-b-c'
+ anobject = [{key: 1}, {key: 2}]
+ converted = info_convert_keys(anobject)
+ for adict in converted:
+ for akey in adict:
+ assert akey == self.d2us(key)
+
+ def test_set_error_flags_error_n(self):
+ ''' Check set_error__flags return correct dict '''
+ args = dict(self.mock_args())
+ args['continue_on_error'] = ['never', 'whatever']
+ set_module_args(args)
+ with pytest.raises(AnsibleFailJson) as exc:
+ obj = self.get_info_mock_object('vserver')
+ print('Info: %s' % exc.value.args[0]['msg'])
+ msg = "never needs to be the only keyword in 'continue_on_error' option."
+ assert exc.value.args[0]['msg'] == msg
+
+ def test_set_error_flags_error_a(self):
+ ''' Check set_error__flags return correct dict '''
+ args = dict(self.mock_args())
+ args['continue_on_error'] = ['whatever', 'always']
+ set_module_args(args)
+ with pytest.raises(AnsibleFailJson) as exc:
+ obj = self.get_info_mock_object('vserver')
+ print('Info: %s' % exc.value.args[0]['msg'])
+ msg = "always needs to be the only keyword in 'continue_on_error' option."
+ assert exc.value.args[0]['msg'] == msg
+
+ def test_set_error_flags_error_u(self):
+ ''' Check set_error__flags return correct dict '''
+ args = dict(self.mock_args())
+ args['continue_on_error'] = ['whatever', 'else']
+ set_module_args(args)
+ with pytest.raises(AnsibleFailJson) as exc:
+ obj = self.get_info_mock_object('vserver')
+ print('Info: %s' % exc.value.args[0]['msg'])
+ msg = "whatever is not a valid keyword in 'continue_on_error' option."
+ assert exc.value.args[0]['msg'] == msg
+
+ def test_set_error_flags_1_flag(self):
+ ''' Check set_error__flags return correct dict '''
+ args = dict(self.mock_args())
+ args['continue_on_error'] = ['missing_vserver_api_error']
+ set_module_args(args)
+ obj = self.get_info_mock_object('vserver')
+ assert not obj.error_flags['missing_vserver_api_error']
+ assert obj.error_flags['rpc_error']
+ assert obj.error_flags['other_error']
+
+ def test_set_error_flags_2_flags(self):
+ ''' Check set_error__flags return correct dict '''
+ args = dict(self.mock_args())
+ args['continue_on_error'] = ['missing_vserver_api_error', 'rpc_error']
+ set_module_args(args)
+ obj = self.get_info_mock_object('vserver')
+ assert not obj.error_flags['missing_vserver_api_error']
+ assert not obj.error_flags['rpc_error']
+ assert obj.error_flags['other_error']
+
+ def test_set_error_flags_3_flags(self):
+ ''' Check set_error__flags return correct dict '''
+ args = dict(self.mock_args())
+ args['continue_on_error'] = ['missing_vserver_api_error', 'rpc_error', 'other_error']
+ set_module_args(args)
+ obj = self.get_info_mock_object('vserver')
+ assert not obj.error_flags['missing_vserver_api_error']
+ assert not obj.error_flags['rpc_error']
+ assert not obj.error_flags['other_error']
diff --git a/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_interface.py b/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_interface.py
new file mode 100644
index 00000000..67c04c2c
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_interface.py
@@ -0,0 +1,312 @@
+# (c) 2018, NetApp, Inc
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+''' unit test template for ONTAP Ansible module '''
+
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+import json
+import pytest
+
+from ansible_collections.netapp.ontap.tests.unit.compat import unittest
+from ansible_collections.netapp.ontap.tests.unit.compat.mock import patch, Mock
+from ansible.module_utils import basic
+from ansible.module_utils._text import to_bytes
+import ansible_collections.netapp.ontap.plugins.module_utils.netapp as netapp_utils
+
+from ansible_collections.netapp.ontap.plugins.modules.na_ontap_interface \
+ import NetAppOntapInterface as interface_module # module under test
+
+if not netapp_utils.has_netapp_lib():
+ pytestmark = pytest.mark.skip('skipping as missing required netapp_lib')
+
+
+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 MockONTAPConnection(object):
+ ''' mock server connection to ONTAP host '''
+
+ def __init__(self, kind=None, data=None):
+ ''' save arguments '''
+ self.type = kind
+ self.params = data
+ self.xml_in = None
+ self.xml_out = None
+
+ def invoke_successfully(self, xml, enable_tunneling): # pylint: disable=unused-argument
+ ''' mock invoke_successfully returning xml data '''
+ self.xml_in = xml
+ if self.type == 'interface':
+ xml = self.build_interface_info(self.params)
+ elif self.type == 'zapi_error':
+ error = netapp_utils.zapi.NaApiError('test', 'error')
+ raise error
+ self.xml_out = xml
+ return xml
+
+ @staticmethod
+ def build_interface_info(data):
+ ''' build xml data for vserser-info '''
+ xml = netapp_utils.zapi.NaElement('xml')
+ attributes = {
+ 'num-records': 1,
+ 'attributes-list': {
+ 'net-interface-info': {
+ 'interface-name': data['name'],
+ 'administrative-status': data['administrative-status'],
+ 'failover-policy': data['failover-policy'],
+ 'firewall-policy': data['firewall-policy'],
+ 'is-auto-revert': data['is-auto-revert'],
+ 'home-node': data['home_node'],
+ 'home-port': data['home_port'],
+ 'address': data['address'],
+ 'netmask': data['netmask'],
+ 'role': data['role'],
+ 'protocols': data['protocols'] if data.get('protocols') else None,
+ 'dns-domain-name': data['dns_domain_name'],
+ 'listen-for-dns_query': data['listen_for_dns_query'],
+ 'is-dns-update-enabled': data['is_dns_update_enabled']
+ }
+ }
+ }
+ xml.translate_struct(attributes)
+ return xml
+
+
+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)
+ self.mock_interface = {
+ 'name': 'test_lif',
+ 'administrative-status': 'up',
+ 'failover-policy': 'up',
+ 'firewall-policy': 'up',
+ 'is-auto-revert': 'true',
+ 'home_node': 'node',
+ 'role': 'data',
+ 'home_port': 'e0c',
+ 'address': '2.2.2.2',
+ 'netmask': '1.1.1.1',
+ 'dns_domain_name': 'test.com',
+ 'listen_for_dns_query': True,
+ 'is_dns_update_enabled': True,
+ 'admin_status': 'up'
+ }
+
+ def mock_args(self):
+ return {
+ 'vserver': 'vserver',
+ 'interface_name': self.mock_interface['name'],
+ 'home_node': self.mock_interface['home_node'],
+ 'role': self.mock_interface['role'],
+ 'home_port': self.mock_interface['home_port'],
+ 'address': self.mock_interface['address'],
+ 'netmask': self.mock_interface['netmask'],
+ 'hostname': 'hostname',
+ 'username': 'username',
+ 'password': 'password',
+ }
+
+ def get_interface_mock_object(self, kind=None):
+ """
+ Helper method to return an na_ontap_interface object
+ :param kind: passes this param to MockONTAPConnection()
+ :return: na_ontap_interface object
+ """
+ interface_obj = interface_module()
+ interface_obj.autosupport_log = Mock(return_value=None)
+ if kind is None:
+ interface_obj.server = MockONTAPConnection()
+ else:
+ interface_obj.server = MockONTAPConnection(kind=kind, data=self.mock_interface)
+ return interface_obj
+
+ def test_module_fail_when_required_args_missing(self):
+ ''' required arguments are reported as errors '''
+ with pytest.raises(AnsibleFailJson) as exc:
+ set_module_args({})
+ interface_module()
+ print('Info: %s' % exc.value.args[0]['msg'])
+
+ def test_create_error_missing_param(self):
+ ''' Test if create throws an error if required param 'role' is not specified'''
+ data = self.mock_args()
+ del data['role']
+ set_module_args(data)
+ with pytest.raises(AnsibleFailJson) as exc:
+ self.get_interface_mock_object('interface').create_interface()
+ msg = 'Error: Missing one or more required parameters for creating interface: ' \
+ 'home_port, netmask, role, home_node, address'
+ expected = sorted(','.split(msg))
+ received = sorted(','.split(exc.value.args[0]['msg']))
+ assert expected == received
+
+ def test_get_nonexistent_interface(self):
+ ''' Test if get_interface returns None for non-existent interface '''
+ set_module_args(self.mock_args())
+ result = self.get_interface_mock_object().get_interface()
+ assert result is None
+
+ def test_get_existing_interface(self):
+ ''' Test if get_interface returns None for existing interface '''
+ set_module_args(self.mock_args())
+ result = self.get_interface_mock_object(kind='interface').get_interface()
+ assert result['interface_name'] == self.mock_interface['name']
+
+ def test_successful_create(self):
+ ''' Test successful create '''
+ set_module_args(self.mock_args())
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_interface_mock_object().apply()
+ assert exc.value.args[0]['changed']
+
+ def test_successful_create_for_NVMe(self):
+ ''' Test successful create for NVMe protocol'''
+ data = self.mock_args()
+ data['protocols'] = 'fc-nvme'
+ del data['address']
+ del data['netmask']
+ del data['home_port']
+ set_module_args(data)
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_interface_mock_object().apply()
+ assert exc.value.args[0]['changed']
+
+ def test_create_idempotency_for_NVMe(self):
+ ''' Test create idempotency for NVMe protocol '''
+ data = self.mock_args()
+ data['protocols'] = 'fc-nvme'
+ del data['address']
+ del data['netmask']
+ del data['home_port']
+ set_module_args(data)
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_interface_mock_object('interface').apply()
+ assert not exc.value.args[0]['changed']
+
+ def test_create_error_for_NVMe(self):
+ ''' Test if create throws an error if required param 'protocols' uses NVMe'''
+ data = self.mock_args()
+ data['protocols'] = 'fc-nvme'
+ set_module_args(data)
+ with pytest.raises(AnsibleFailJson) as exc:
+ self.get_interface_mock_object('interface').create_interface()
+ msg = 'Error: Following parameters for creating interface are not supported for data-protocol fc-nvme: ' \
+ 'netmask, firewall_policy, address'
+ expected = sorted(','.split(msg))
+ received = sorted(','.split(exc.value.args[0]['msg']))
+ assert expected == received
+
+ def test_create_idempotency(self):
+ ''' Test create idempotency '''
+ set_module_args(self.mock_args())
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_interface_mock_object('interface').apply()
+ assert not exc.value.args[0]['changed']
+
+ def test_successful_delete(self):
+ ''' Test delete existing interface '''
+ data = self.mock_args()
+ data['state'] = 'absent'
+ set_module_args(data)
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_interface_mock_object('interface').apply()
+ assert exc.value.args[0]['changed']
+
+ def test_delete_idempotency(self):
+ ''' Test delete idempotency '''
+ data = self.mock_args()
+ data['state'] = 'absent'
+ set_module_args(data)
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_interface_mock_object().apply()
+ assert not exc.value.args[0]['changed']
+
+ def test_successful_modify(self):
+ ''' Test successful modify interface_minutes '''
+ data = self.mock_args()
+ data['home_port'] = 'new_port'
+ data['dns_domain_name'] = 'test2.com'
+ data['listen_for_dns_query'] = False
+ data['is_dns_update_enabled'] = False
+ set_module_args(data)
+ with pytest.raises(AnsibleExitJson) as exc:
+ interface_obj = self.get_interface_mock_object('interface')
+ interface_obj.apply()
+ assert exc.value.args[0]['changed']
+
+ def test_modify_idempotency(self):
+ ''' Test modify idempotency '''
+ data = self.mock_args()
+ set_module_args(data)
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_interface_mock_object('interface').apply()
+ assert not exc.value.args[0]['changed']
+
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_interface.NetAppOntapInterface.get_interface')
+ def test_error_message(self, get_interface):
+ ''' Test modify idempotency '''
+ data = self.mock_args()
+ set_module_args(data)
+ get_interface.side_effect = [None]
+ with pytest.raises(AnsibleFailJson) as exc:
+ self.get_interface_mock_object('zapi_error').apply()
+ assert exc.value.args[0]['msg'] == 'Error Creating interface test_lif: NetApp API failed. Reason - test:error'
+
+ data = self.mock_args()
+ data['home_port'] = 'new_port'
+ data['dns_domain_name'] = 'test2.com'
+ data['listen_for_dns_query'] = False
+ data['is_dns_update_enabled'] = False
+ set_module_args(data)
+ get_interface.side_effect = [
+ self.mock_interface
+ ]
+ with pytest.raises(AnsibleFailJson) as exc:
+ self.get_interface_mock_object('zapi_error').apply()
+ assert exc.value.args[0]['msg'] == 'Error modifying interface test_lif: NetApp API failed. Reason - test:error'
+
+ data = self.mock_args()
+ data['state'] = 'absent'
+ set_module_args(data)
+ current = self.mock_interface
+ current['admin_status'] = 'down'
+ get_interface.side_effect = [
+ current
+ ]
+ with pytest.raises(AnsibleFailJson) as exc:
+ self.get_interface_mock_object('zapi_error').apply()
+ assert exc.value.args[0]['msg'] == 'Error deleting interface test_lif: NetApp API failed. Reason - test:error'
diff --git a/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_ipspace.py b/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_ipspace.py
new file mode 100644
index 00000000..641e1414
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_ipspace.py
@@ -0,0 +1,269 @@
+# (c) 2018, NTT Europe Ltd.
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+""" unit test for Ansible module: na_ontap_ipspace """
+
+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.ontap.tests.unit.compat import unittest
+from ansible_collections.netapp.ontap.tests.unit.compat.mock import patch
+import ansible_collections.netapp.ontap.plugins.module_utils.netapp as netapp_utils
+
+from ansible_collections.netapp.ontap.plugins.modules.na_ontap_ipspace \
+ import NetAppOntapIpspace as my_module # module under test
+
+if not netapp_utils.has_netapp_lib():
+ pytestmark = pytest.mark.skip('skipping as missing required netapp_lib')
+
+# REST API canned responses when mocking send_request
+SRR = {
+ # common responses
+ 'is_rest': (200, {}, None),
+ 'is_zapi': (400, {}, "Unreachable"),
+ 'empty_good': (200, {}, None),
+ 'end_of_sequence': (500, None, "Ooops, the UT needs one more SRR response"),
+ 'generic_error': (400, None, "Expected error"),
+ # module specific responses
+ 'ipspace_record': (200, {'records': [{"name": "test_ipspace",
+ "uuid": "1cd8a442-86d1-11e0-ae1c-123478563412"}]}, 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"""
+
+
+class AnsibleFailJson(Exception):
+ """Exception class to be raised by module.fail_json and caught by the test case"""
+
+
+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 MockONTAPConnection(object):
+ ''' mock server connection to ONTAP host '''
+ def __init__(self, kind=None, parm1=None):
+ ''' save arguments '''
+ self.type = kind
+ self.parm1 = parm1
+ self.xml_in = None
+ self.xml_out = None
+
+ def invoke_successfully(self, xml, enable_tunneling): # pylint: disable=unused-argument
+ ''' mock invoke_successfully returning xml data '''
+ self.xml_in = xml
+ if self.type == 'ipspace':
+ xml = self.build_ipspace_info(self.parm1)
+ self.xml_out = xml
+ return xml
+
+ @staticmethod
+ def build_ipspace_info(ipspace):
+ ''' build xml data for ipspace '''
+ xml = netapp_utils.zapi.NaElement('xml')
+ data = {'num-records': 1,
+ 'attributes-list': {'net-ipspaces-info': {'ipspace': ipspace}}}
+ xml.translate_struct(data)
+ print(xml.to_string())
+ return xml
+
+
+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)
+ self.server = MockONTAPConnection()
+
+ def set_default_args(self):
+ return dict({
+ 'name': 'test_ipspace',
+ 'hostname': 'hostname',
+ 'username': 'username',
+ 'password': 'password'
+ })
+
+ @staticmethod
+ def get_ipspace_mock_object(cx_type='zapi', kind=None, status=None):
+ ipspace_obj = my_module()
+ if cx_type == 'zapi':
+ if kind is None:
+ ipspace_obj.server = MockONTAPConnection()
+ else:
+ ipspace_obj.server = MockONTAPConnection(kind=kind, parm1=status)
+ return ipspace_obj
+
+ def test_fail_requiredargs_missing(self):
+ ''' required arguments are reported as errors '''
+ with pytest.raises(AnsibleFailJson) as exc:
+ set_module_args({})
+ my_module()
+ print('Info: %s' % exc.value.args[0]['msg'])
+
+ @patch('ansible_collections.netapp.ontap.plugins.module_utils.netapp.OntapRestAPI.send_request')
+ def test_get_ipspace_iscalled(self, mock_request):
+ ''' test if get_ipspace() is called '''
+ mock_request.side_effect = [
+ SRR['is_zapi'],
+ SRR['end_of_sequence']
+ ]
+ set_module_args(self.set_default_args())
+ my_obj = my_module()
+ my_obj.server = self.server
+ ipspace = my_obj.get_ipspace()
+ print('Info: test_get_ipspace: %s' % repr(ipspace))
+ assert ipspace is None
+
+ @patch('ansible_collections.netapp.ontap.plugins.module_utils.netapp.OntapRestAPI.send_request')
+ def test_ipspace_apply_iscalled(self, mock_request):
+ ''' test if apply() is called '''
+ mock_request.side_effect = [
+ SRR['is_zapi'],
+ SRR['end_of_sequence']
+ ]
+ module_args = {'name': 'test_apply_ips'}
+ module_args.update(self.set_default_args())
+ set_module_args(module_args)
+ my_obj = my_module()
+ my_obj.server = self.server
+ ipspace = my_obj.get_ipspace()
+ print('Info: test_get_ipspace: %s' % repr(ipspace))
+ assert ipspace is None
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ print('Info: test_ipspace_apply: %s' % repr(exc.value))
+ assert exc.value.args[0]['changed']
+ my_obj.server = MockONTAPConnection('ipspace', 'test_apply_ips')
+ ipspace = my_obj.get_ipspace()
+ print('Info: test_get_ipspace: %s' % repr(ipspace))
+ assert ipspace is not None
+ assert ipspace['name'] == 'test_apply_ips'
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ print('Info: test_ipspace_apply: %s' % repr(exc.value))
+ assert exc.value.args[0]['changed']
+ ipspace = my_obj.get_ipspace()
+ assert ipspace['name'] == 'test_apply_ips'
+
+ @patch('ansible_collections.netapp.ontap.plugins.module_utils.netapp.OntapRestAPI.send_request')
+ def test_rest_error(self, mock_request):
+ data = self.set_default_args()
+ set_module_args(data)
+ mock_request.side_effect = [
+ SRR['is_rest'],
+ SRR['generic_error'],
+ SRR['end_of_sequence']
+ ]
+ with pytest.raises(AnsibleFailJson) as exc:
+ self.get_ipspace_mock_object(cx_type='rest').apply()
+ assert exc.value.args[0]['msg'] == SRR['generic_error'][2]
+
+ @patch('ansible_collections.netapp.ontap.plugins.module_utils.netapp.OntapRestAPI.send_request')
+ def test_successful_create_rest(self, mock_request):
+ data = self.set_default_args()
+ set_module_args(data)
+ mock_request.side_effect = [
+ SRR['is_rest'],
+ SRR['empty_good'], # get
+ SRR['empty_good'], # post
+ SRR['end_of_sequence']
+ ]
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_ipspace_mock_object(cx_type='rest').apply()
+ assert exc.value.args[0]['changed']
+
+ @patch('ansible_collections.netapp.ontap.plugins.module_utils.netapp.OntapRestAPI.send_request')
+ def test_idempotent_create_rest(self, mock_request):
+ data = self.set_default_args()
+ set_module_args(data)
+ mock_request.side_effect = [
+ SRR['is_rest'],
+ SRR['ipspace_record'], # get
+ SRR['end_of_sequence']
+ ]
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_ipspace_mock_object(cx_type='rest').apply()
+ assert not exc.value.args[0]['changed']
+
+ @patch('ansible_collections.netapp.ontap.plugins.module_utils.netapp.OntapRestAPI.send_request')
+ def test_successful_delete_rest(self, mock_request):
+ data = self.set_default_args()
+ data['state'] = 'absent'
+ set_module_args(data)
+ mock_request.side_effect = [
+ SRR['is_rest'],
+ SRR['ipspace_record'], # get
+ SRR['empty_good'], # delete
+ SRR['end_of_sequence']
+ ]
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_ipspace_mock_object(cx_type='rest').apply()
+ assert exc.value.args[0]['changed']
+
+ @patch('ansible_collections.netapp.ontap.plugins.module_utils.netapp.OntapRestAPI.send_request')
+ def test_idempotent_delete_rest(self, mock_request):
+ data = self.set_default_args()
+ data['state'] = 'absent'
+ set_module_args(data)
+ mock_request.side_effect = [
+ SRR['is_rest'],
+ SRR['empty_good'], # get
+ SRR['end_of_sequence']
+ ]
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_ipspace_mock_object(cx_type='rest').apply()
+ assert not exc.value.args[0]['changed']
+
+ @patch('ansible_collections.netapp.ontap.plugins.module_utils.netapp.OntapRestAPI.send_request')
+ def test_successful_modify_rest(self, mock_request):
+ data = self.set_default_args()
+ data['state'] = 'present'
+ set_module_args(data)
+ mock_request.side_effect = [
+ SRR['is_rest'],
+ SRR['empty_good'], # get
+ SRR['empty_good'], # patch
+ SRR['end_of_sequence']
+ ]
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_ipspace_mock_object(cx_type='rest').apply()
+ assert exc.value.args[0]['changed']
+
+ @patch('ansible_collections.netapp.ontap.plugins.module_utils.netapp.OntapRestAPI.send_request')
+ def test_idempotent_modify_rest(self, mock_request):
+ data = self.set_default_args()
+ data['state'] = 'present'
+ set_module_args(data)
+ mock_request.side_effect = [
+ SRR['is_rest'],
+ SRR['ipspace_record'], # get
+ SRR['end_of_sequence']
+ ]
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_ipspace_mock_object(cx_type='rest').apply()
+ assert not exc.value.args[0]['changed']
diff --git a/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_iscsi_security.py b/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_iscsi_security.py
new file mode 100644
index 00000000..bb31fd65
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_iscsi_security.py
@@ -0,0 +1,256 @@
+# (c) 2018, NetApp, Inc
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+''' unit tests for Ansible module: na_ontap_iscsi_security '''
+
+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.ontap.tests.unit.compat import unittest
+from ansible_collections.netapp.ontap.tests.unit.compat.mock import patch
+
+from ansible_collections.netapp.ontap.plugins.modules.na_ontap_iscsi_security \
+ import NetAppONTAPIscsiSecurity as iscsi_module # module under test
+
+# REST API canned responses when mocking send_request
+SRR = {
+ # common responses
+ 'is_rest': (200, {}, None),
+ 'is_zapi': (400, {}, "Unreachable"),
+ 'empty_good': (200, {}, None),
+ 'end_of_sequence': (500, None, "Unexpected call to send_request"),
+ 'generic_error': (400, None, "Expected error"),
+ # module specific responses
+ 'get_uuid': (
+ 200,
+ {
+ "records": [
+ {
+ "uuid": "e2e89ccc-db35-11e9-0000-000000000000"
+ }
+ ],
+ "num_records": 1
+ }, None),
+ 'get_initiator': (
+ 200,
+ {
+ "records": [
+ {
+ "svm": {
+ "uuid": "e2e89ccc-db35-11e9-0000-000000000000",
+ "name": "test_ansible"
+ },
+ "initiator": "eui.0123456789abcdef",
+ "authentication_type": "chap",
+ "chap": {
+ "inbound": {
+ "user": "test_user_1"
+ },
+ "outbound": {
+ "user": "test_user_2"
+ }
+ },
+ "initiator_address": {
+ "ranges": [
+ {
+ "start": "10.125.10.0",
+ "end": "10.125.10.10",
+ "family": "ipv4"
+ },
+ {
+ "start": "10.10.10.7",
+ "end": "10.10.10.7",
+ "family": "ipv4"
+ }
+ ]
+ }
+ }
+ ],
+ "num_records": 1
+ }, None),
+ "no_record": (
+ 200,
+ {
+ "num_records": 0
+ }, 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"""
+
+
+class AnsibleFailJson(Exception):
+ """Exception class to be raised by module.fail_json and caught by the test case"""
+
+
+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):
+ ''' Unit tests for na_ontap_iscsi_security '''
+
+ 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)
+ self.mock_iscsi = {
+ "initiator": "eui.0123456789abcdef",
+ "inbound_username": "test_user_1",
+ "inbound_password": "123",
+ "outbound_username": "test_user_2",
+ "outbound_password": "321",
+ "auth_type": "chap",
+ "address_ranges": ["10.125.10.0-10.125.10.10", "10.10.10.7"]
+ }
+
+ def mock_args(self):
+ return {
+ 'initiator': self.mock_iscsi['initiator'],
+ 'inbound_username': self.mock_iscsi['inbound_username'],
+ 'inbound_password': self.mock_iscsi['inbound_password'],
+ 'outbound_username': self.mock_iscsi['outbound_username'],
+ 'outbound_password': self.mock_iscsi['outbound_password'],
+ 'auth_type': self.mock_iscsi['auth_type'],
+ 'address_ranges': self.mock_iscsi['address_ranges'],
+ 'hostname': 'test',
+ 'vserver': 'test_vserver',
+ 'username': 'test_user',
+ 'password': 'test_pass!'
+ }
+
+ def get_iscsi_mock_object(self):
+ """
+ Helper method to return an na_ontap_iscsi_security object
+ :return: na_ontap_iscsi_security object
+ """
+ iscsi_obj = iscsi_module()
+ return iscsi_obj
+
+ @patch('ansible_collections.netapp.ontap.plugins.module_utils.netapp.OntapRestAPI.send_request')
+ def test_rest_successful_create(self, mock_request):
+ '''Test successful rest create'''
+ data = self.mock_args()
+ set_module_args(data)
+ mock_request.side_effect = [
+ SRR['get_uuid'],
+ SRR['no_record'],
+ SRR['empty_good'],
+ SRR['end_of_sequence']
+ ]
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_iscsi_mock_object().apply()
+ assert exc.value.args[0]['changed']
+
+ @patch('ansible_collections.netapp.ontap.plugins.module_utils.netapp.OntapRestAPI.send_request')
+ def test_rest_create_idempotency(self, mock_request):
+ '''Test rest create idempotency'''
+ data = self.mock_args()
+ set_module_args(data)
+ mock_request.side_effect = [
+ SRR['get_uuid'],
+ SRR['get_initiator'],
+ SRR['empty_good'],
+ SRR['end_of_sequence']
+ ]
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_iscsi_mock_object().apply()
+ assert not exc.value.args[0]['changed']
+
+ @patch('ansible_collections.netapp.ontap.plugins.module_utils.netapp.OntapRestAPI.send_request')
+ def test_rest_successful_modify_address(self, mock_request):
+ '''Test successful rest modify'''
+ data = self.mock_args()
+ data['address_ranges'] = ['10.10.10.8']
+ set_module_args(data)
+ mock_request.side_effect = [
+ SRR['get_uuid'],
+ SRR['get_initiator'],
+ SRR['empty_good'],
+ SRR['end_of_sequence']
+ ]
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_iscsi_mock_object().apply()
+ assert exc.value.args[0]['changed']
+
+ @patch('ansible_collections.netapp.ontap.plugins.module_utils.netapp.OntapRestAPI.send_request')
+ def test_rest_successful_modify_user(self, mock_request):
+ '''Test successful rest modify'''
+ data = self.mock_args()
+ data['inbound_username'] = 'test_user_3'
+ set_module_args(data)
+ mock_request.side_effect = [
+ SRR['get_uuid'],
+ SRR['get_initiator'],
+ SRR['empty_good'],
+ SRR['end_of_sequence']
+ ]
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_iscsi_mock_object().apply()
+ assert exc.value.args[0]['changed']
+
+ @patch('ansible_collections.netapp.ontap.plugins.module_utils.netapp.OntapRestAPI.send_request')
+ def test_rest_error(self, mock_request):
+ '''Test rest error'''
+ data = self.mock_args()
+ set_module_args(data)
+ mock_request.side_effect = [
+ SRR['get_uuid'],
+ SRR['no_record'],
+ SRR['generic_error'],
+ SRR['empty_good'],
+ SRR['end_of_sequence']
+ ]
+ with pytest.raises(AnsibleFailJson) as exc:
+ self.get_iscsi_mock_object().apply()
+ assert 'Error on creating initiator: Expected error' in exc.value.args[0]['msg']
+
+ data = self.mock_args()
+ data['inbound_username'] = 'test_user_3'
+ set_module_args(data)
+ mock_request.side_effect = [
+ SRR['get_uuid'],
+ SRR['get_initiator'],
+ SRR['generic_error'],
+ SRR['empty_good'],
+ SRR['end_of_sequence']
+ ]
+ with pytest.raises(AnsibleFailJson) as exc:
+ self.get_iscsi_mock_object().apply()
+ assert 'Error on modifying initiator: Expected error' in exc.value.args[0]['msg']
+
+ data = self.mock_args()
+ data['state'] = 'absent'
+ set_module_args(data)
+ mock_request.side_effect = [
+ SRR['get_uuid'],
+ SRR['get_initiator'],
+ SRR['generic_error'],
+ SRR['empty_good'],
+ SRR['end_of_sequence']
+ ]
+ with pytest.raises(AnsibleFailJson) as exc:
+ self.get_iscsi_mock_object().apply()
+ assert 'Error on deleting initiator: Expected error' in exc.value.args[0]['msg']
diff --git a/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_job_schedule.py b/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_job_schedule.py
new file mode 100644
index 00000000..c7196677
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_job_schedule.py
@@ -0,0 +1,369 @@
+# (c) 2018, NetApp, Inc
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+''' unit tests for Ansible module: na_ontap_job_schedule '''
+
+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.ontap.tests.unit.compat import unittest
+from ansible_collections.netapp.ontap.tests.unit.compat.mock import patch, Mock
+import ansible_collections.netapp.ontap.plugins.module_utils.netapp as netapp_utils
+
+from ansible_collections.netapp.ontap.plugins.modules.na_ontap_job_schedule \
+ import NetAppONTAPJob as job_module # module under test
+
+if not netapp_utils.has_netapp_lib():
+ pytestmark = pytest.mark.skip('skipping as missing required netapp_lib')
+
+# REST API canned responses when mocking send_request
+SRR = {
+ # common responses
+ 'is_rest': (200, {}, None),
+ 'is_zapi': (400, {}, "Unreachable"),
+ 'empty_good': (200, {}, None),
+ 'end_of_sequence': (500, None, "Unexpected call to send_request"),
+ 'generic_error': (400, None, "Expected error"),
+ # module specific responses
+ 'get_schedule': (
+ 200,
+ {
+ "records": [
+ {
+ "uuid": "010df156-e0a9-11e9-9f70-005056b3df08",
+ "name": "test_job",
+ "cron": {
+ "minutes": [
+ 25
+ ],
+ "hours": [
+ 0
+ ],
+ "weekdays": [
+ 0
+ ]
+ }
+ }
+ ],
+ "num_records": 1
+ }, None),
+ "no_record": (
+ 200,
+ {"num_records": 0},
+ 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"""
+
+
+class AnsibleFailJson(Exception):
+ """Exception class to be raised by module.fail_json and caught by the test case"""
+
+
+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 MockONTAPConnection(object):
+ ''' mock server connection to ONTAP host '''
+
+ def __init__(self, kind=None, data=None):
+ ''' save arguments '''
+ self.kind = kind
+ self.params = data
+ self.xml_in = None
+ self.xml_out = None
+
+ def invoke_successfully(self, xml, enable_tunneling): # pylint: disable=unused-argument
+ ''' mock invoke_successfully returning xml data '''
+ self.xml_in = xml
+ if self.kind == 'job':
+ xml = self.build_job_schedule_cron_info(self.params)
+ elif self.kind == 'job_multiple':
+ xml = self.build_job_schedule_multiple_cron_info(self.params)
+ self.xml_out = xml
+ return xml
+
+ def autosupport_log(self):
+ ''' Mock autosupport log method, returns None '''
+ return None
+
+ @staticmethod
+ def build_job_schedule_cron_info(job_details):
+ ''' build xml data for vserser-info '''
+ xml = netapp_utils.zapi.NaElement('xml')
+ attributes = {
+ 'num-records': 1,
+ 'attributes-list': {
+ 'job-schedule-cron-info': {
+ 'job-schedule-name': job_details['name'],
+ 'job-schedule-cron-minute': {
+ 'cron-minute': job_details['minutes']
+ }
+ }
+ }
+ }
+ xml.translate_struct(attributes)
+ return xml
+
+ @staticmethod
+ def build_job_schedule_multiple_cron_info(job_details):
+ ''' build xml data for vserser-info '''
+ print("CALLED MULTIPLE BUILD")
+ xml = netapp_utils.zapi.NaElement('xml')
+ attributes = {
+ 'num-records': 1,
+ 'attributes-list': {
+ 'job-schedule-cron-info': {
+ 'job-schedule-name': job_details['name'],
+ 'job-schedule-cron-minute': [
+ {'cron-minute': '25'},
+ {'cron-minute': '35'}
+ ],
+ 'job-schedule-cron-month': [
+ {'cron-month': '5'},
+ {'cron-month': '10'}
+ ]
+ }
+ }
+ }
+ xml.translate_struct(attributes)
+ return xml
+
+
+class TestMyModule(unittest.TestCase):
+ ''' Unit tests for na_ontap_job_schedule '''
+
+ 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)
+ self.mock_job = {
+ 'name': 'test_job',
+ 'minutes': '25',
+ 'job_hours': ['0'],
+ 'weekdays': ['0']
+ }
+
+ def mock_args(self, rest=False):
+ if rest:
+ return {
+ 'name': self.mock_job['name'],
+ 'job_minutes': [self.mock_job['minutes']],
+ 'job_hours': self.mock_job['job_hours'],
+ 'job_days_of_week': self.mock_job['weekdays'],
+ 'hostname': 'test',
+ 'username': 'test_user',
+ 'password': 'test_pass!'
+ }
+ else:
+ return {
+ 'name': self.mock_job['name'],
+ 'job_minutes': [self.mock_job['minutes']],
+ 'hostname': 'test',
+ 'username': 'test_user',
+ 'password': 'test_pass!',
+ 'use_rest': 'never'
+ }
+
+ def get_job_mock_object(self, kind=None, call_type='zapi'):
+ """
+ Helper method to return an na_ontap_job_schedule object
+ :param kind: passes this param to MockONTAPConnection()
+ :param call_type:
+ :return: na_ontap_job_schedule object
+ """
+ job_obj = job_module()
+ job_obj.autosupport_log = Mock(return_value=None)
+ if call_type == 'zapi':
+ if kind is None:
+ job_obj.server = MockONTAPConnection()
+ else:
+ job_obj.server = MockONTAPConnection(kind=kind, data=self.mock_job)
+ return job_obj
+
+ def test_module_fail_when_required_args_missing(self):
+ ''' required arguments are reported as errors '''
+ with pytest.raises(AnsibleFailJson) as exc:
+ set_module_args({})
+ job_module()
+ print('Info: %s' % exc.value.args[0]['msg'])
+
+ def test_get_nonexistent_job(self):
+ ''' Test if get_job_schedule returns None for non-existent job '''
+ set_module_args(self.mock_args())
+ result = self.get_job_mock_object().get_job_schedule()
+ assert result is None
+
+ def test_get_existing_job(self):
+ ''' Test if get_job_schedule retuns job details for existing job '''
+ data = self.mock_args()
+ set_module_args(data)
+ result = self.get_job_mock_object('job').get_job_schedule()
+ assert result['name'] == self.mock_job['name']
+ assert result['job_minutes'] == data['job_minutes']
+
+ def test_get_existing_job_multiple_minutes(self):
+ ''' Test if get_job_schedule retuns job details for existing job '''
+ set_module_args(self.mock_args())
+ result = self.get_job_mock_object('job_multiple').get_job_schedule()
+ print(str(result))
+ assert result['name'] == self.mock_job['name']
+ assert result['job_minutes'] == ['25', '35']
+ assert result['job_months'] == ['5', '10']
+
+ def test_create_error_missing_param(self):
+ ''' Test if create throws an error if job_minutes is not specified'''
+ data = self.mock_args()
+ del data['job_minutes']
+ set_module_args(data)
+ with pytest.raises(AnsibleFailJson) as exc:
+ self.get_job_mock_object('job').create_job_schedule()
+ msg = 'Error: missing required parameter job_minutes for create'
+ assert exc.value.args[0]['msg'] == msg
+
+ def test_successful_create(self):
+ ''' Test successful create '''
+ set_module_args(self.mock_args())
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_job_mock_object().apply()
+ assert exc.value.args[0]['changed']
+
+ def test_create_idempotency(self):
+ ''' Test create idempotency '''
+ set_module_args(self.mock_args())
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_job_mock_object('job').apply()
+ assert not exc.value.args[0]['changed']
+
+ def test_successful_delete(self):
+ ''' Test delete existing job '''
+ data = self.mock_args()
+ data['state'] = 'absent'
+ set_module_args(data)
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_job_mock_object('job').apply()
+ assert exc.value.args[0]['changed']
+
+ def test_delete_idempotency(self):
+ ''' Test delete idempotency '''
+ data = self.mock_args()
+ data['state'] = 'absent'
+ set_module_args(data)
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_job_mock_object().apply()
+ assert not exc.value.args[0]['changed']
+
+ def test_successful_modify(self):
+ ''' Test successful modify job_minutes '''
+ data = self.mock_args()
+ data['job_minutes'] = ['20']
+ set_module_args(data)
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_job_mock_object('job').apply()
+ assert exc.value.args[0]['changed']
+
+ def test_modify_idempotency(self):
+ ''' Test modify idempotency '''
+ data = self.mock_args()
+ set_module_args(data)
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_job_mock_object('job').apply()
+ assert not exc.value.args[0]['changed']
+
+ @patch('ansible_collections.netapp.ontap.plugins.module_utils.netapp.OntapRestAPI.send_request')
+ def test_rest_successful_create(self, mock_request):
+ '''Test successful rest create'''
+ data = self.mock_args(rest=True)
+ set_module_args(data)
+ mock_request.side_effect = [
+ SRR['is_rest'],
+ SRR['no_record'],
+ SRR['empty_good'],
+ SRR['end_of_sequence']
+ ]
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_job_mock_object(call_type='rest').apply()
+ assert exc.value.args[0]['changed']
+
+ @patch('ansible_collections.netapp.ontap.plugins.module_utils.netapp.OntapRestAPI.send_request')
+ def test_rest_create_idempotency(self, mock_request):
+ '''Test rest create idempotency'''
+ data = self.mock_args(rest=True)
+ set_module_args(data)
+ mock_request.side_effect = [
+ SRR['is_rest'],
+ SRR['get_schedule'],
+ SRR['empty_good'],
+ SRR['end_of_sequence']
+ ]
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_job_mock_object(call_type='rest').apply()
+ assert not exc.value.args[0]['changed']
+
+ @patch('ansible_collections.netapp.ontap.plugins.module_utils.netapp.OntapRestAPI.send_request')
+ def test_rest_error(self, mock_request):
+ '''Test rest create idempotency'''
+ data = self.mock_args(rest=True)
+ set_module_args(data)
+ mock_request.side_effect = [
+ SRR['is_rest'],
+ SRR['no_record'],
+ SRR['generic_error'],
+ SRR['empty_good'],
+ SRR['end_of_sequence']
+ ]
+ with pytest.raises(AnsibleFailJson) as exc:
+ self.get_job_mock_object(call_type='rest').apply()
+ assert 'Error on creating job schedule: Expected error' in exc.value.args[0]['msg']
+
+ data = self.mock_args(rest=True)
+ data['job_minutes'] = ['20']
+ set_module_args(data)
+ mock_request.side_effect = [
+ SRR['is_rest'],
+ SRR['get_schedule'],
+ SRR['generic_error'],
+ SRR['empty_good'],
+ SRR['end_of_sequence']
+ ]
+ with pytest.raises(AnsibleFailJson) as exc:
+ self.get_job_mock_object(call_type='rest').apply()
+ assert 'Error on modifying job schedule: Expected error' in exc.value.args[0]['msg']
+
+ data = self.mock_args(rest=True)
+ data['state'] = 'absent'
+ set_module_args(data)
+ mock_request.side_effect = [
+ SRR['is_rest'],
+ SRR['get_schedule'],
+ SRR['generic_error'],
+ SRR['empty_good'],
+ SRR['end_of_sequence']
+ ]
+ with pytest.raises(AnsibleFailJson) as exc:
+ self.get_job_mock_object(call_type='rest').apply()
+ assert 'Error on deleting job schedule: Expected error' in exc.value.args[0]['msg']
diff --git a/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_kerberos_realm.py b/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_kerberos_realm.py
new file mode 100644
index 00000000..5cdcb75a
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_kerberos_realm.py
@@ -0,0 +1,269 @@
+# (c) 2018, NetApp, Inc
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+''' unit test for ONTAP Kerberos Realm module '''
+
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+
+import json
+import pytest
+import ansible_collections.netapp.ontap.plugins.module_utils.netapp as netapp_utils
+from ansible.module_utils import basic
+from ansible.module_utils._text import to_bytes
+from ansible_collections.netapp.ontap.plugins.modules.na_ontap_kerberos_realm \
+ import NetAppOntapKerberosRealm as my_module # module under test
+from ansible_collections.netapp.ontap.tests.unit.compat import unittest
+from ansible_collections.netapp.ontap.tests.unit.compat.mock import patch
+from ansible_collections.netapp.ontap.tests.unit.compat.mock import Mock
+
+
+if not netapp_utils.has_netapp_lib():
+ pytestmark = pytest.mark.skip('skipping as missing required netapp_lib')
+
+
+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"""
+ print(Exception)
+ # pass
+
+
+class AnsibleFailJson(Exception):
+ """Exception class to be raised by module.fail_json and caught by the test case"""
+ print(Exception)
+ # 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 MockONTAPConnection(object):
+ ''' mock server connection to ONTAP host '''
+
+ def __init__(self, kind=None):
+ ''' save arguments '''
+ self.type = kind
+ self.xml_in = None
+ self.xml_out = None
+
+ def invoke_successfully(self, xml, enable_tunneling): # pylint: disable=unused-argument
+ ''' mock invoke_successfully returning xml data '''
+ self.xml_in = xml
+
+ if self.type == 'present_realm':
+ xml = self.build_realm()
+ else:
+ xml = self.build_empty_realm()
+
+ self.xml_out = xml
+ return xml
+
+ @staticmethod
+ def build_realm():
+ ''' build xml data for kerberos realm '''
+ xml = netapp_utils.zapi.NaElement('xml')
+ attributes = {
+ 'num-records': "1",
+ 'attributes-list': {
+ 'kerberos-realm': {
+ 'admin-server-ip': "192.168.0.1",
+ 'admin-server-port': "749",
+ 'clock-skew': "5",
+ 'kdc-ip': "192.168.0.1",
+ 'kdc-port': "88",
+ 'kdc-vendor': "other",
+ 'password-server-ip': "192.168.0.1",
+ 'password-server-port': "464",
+ "permitted-enc-types": {
+ "string": ["des", "des3", "aes_128", "aes_256"]
+ },
+ 'realm': "EXAMPLE.COM",
+ 'vserver-name': "vserver0"
+ }
+ }
+ }
+ xml.translate_struct(attributes)
+ return xml
+
+ @staticmethod
+ def build_empty_realm():
+ ''' build xml data for kerberos realm '''
+ xml = netapp_utils.zapi.NaElement('xml')
+ attributes = {
+ 'num-records': "0",
+ }
+ xml.translate_struct(attributes)
+ return xml
+
+
+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)
+ self.server = MockONTAPConnection(kind='present_realm')
+
+ @staticmethod
+ def get_kerberos_realm_mock_object(kind=None):
+ """
+ Helper method to return an na_ontap_volume object
+ :param kind: passes this param to MockONTAPConnection()
+ :return: na_ontap_volume object
+ """
+ krbrealm_obj = my_module()
+ krbrealm_obj.asup_log_for_cserver = Mock(return_value=None)
+ krbrealm_obj.cluster = Mock()
+ krbrealm_obj.cluster.invoke_successfully = Mock()
+ if kind is None:
+ krbrealm_obj.server = MockONTAPConnection()
+ else:
+ krbrealm_obj.server = MockONTAPConnection(kind=kind)
+ return krbrealm_obj
+
+ @staticmethod
+ def mock_args():
+ '''Set default arguments'''
+ return dict({
+ 'hostname': 'test',
+ 'username': 'test_user',
+ 'password': 'test_pass!',
+ 'https': True,
+ 'validate_certs': False
+ })
+
+ @staticmethod
+ def test_module_fail_when_required_args_missing():
+ ''' required arguments are reported as errors '''
+ with pytest.raises(AnsibleFailJson) as exc:
+ set_module_args({})
+ my_module()
+ print('Info: %s' % exc.value.args[0]['msg'])
+
+ def test_module_fail_when_state_present_required_args_missing(self):
+ ''' required arguments are reported as errors '''
+ data = self.mock_args()
+ data['state'] = 'present'
+ data['vserver'] = 'vserver1'
+ data['realm'] = 'NETAPP.COM'
+ with pytest.raises(AnsibleFailJson) as exc:
+ set_module_args(data)
+ my_module()
+ msg = "state is present but all of the following are missing: kdc_vendor, kdc_ip"
+ assert exc.value.args[0]['msg'] == msg
+
+ def test_get_nonexistent_realm(self):
+ ''' Test if get_krbrealm returns None for non-existent kerberos realm '''
+ data = self.mock_args()
+ data['vserver'] = 'none'
+ data['realm'] = 'none'
+ data['state'] = 'present'
+ data['kdc_ip'] = 'none'
+ data['kdc_vendor'] = 'other'
+ set_module_args(data)
+ result = self.get_kerberos_realm_mock_object().get_krbrealm()
+ assert result is None
+
+ def test_get_existing_realm(self):
+ ''' Test if get_krbrealm returns details for existing kerberos realm '''
+ data = self.mock_args()
+ data['vserver'] = 'vserver0'
+ data['realm'] = 'EXAMPLE.COM'
+ data['state'] = 'present'
+ data['kdc_ip'] = '10.0.0.1'
+ data['kdc_vendor'] = 'other'
+ set_module_args(data)
+ result = self.get_kerberos_realm_mock_object('present_realm').get_krbrealm()
+ assert result['realm']
+
+ def test_successfully_modify_realm(self):
+ ''' Test modify realm successful for modifying kdc_ip. '''
+ data = self.mock_args()
+ data['vserver'] = 'vserver0'
+ data['realm'] = 'EXAMPLE.COM'
+ data['kdc_ip'] = '192.168.10.10'
+ data['state'] = 'present'
+ data['kdc_ip'] = '10.0.0.1'
+ data['kdc_vendor'] = 'other'
+ set_module_args(data)
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_kerberos_realm_mock_object('present_realm').apply()
+ assert exc.value.args[0]['changed']
+
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_kerberos_realm.NetAppOntapKerberosRealm.delete_krbrealm')
+ def test_successfully_delete_realm(self, delete_krbrealm):
+ ''' Test successfully delete realm '''
+ data = self.mock_args()
+ data['state'] = 'absent'
+ data['vserver'] = 'vserver0'
+ data['realm'] = 'EXAMPLE.COM'
+ set_module_args(data)
+ obj = self.get_kerberos_realm_mock_object('present_realm')
+ with pytest.raises(AnsibleExitJson) as exc:
+ obj.apply()
+ assert exc.value.args[0]['changed']
+ delete_krbrealm.assert_called_with()
+
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_kerberos_realm.NetAppOntapKerberosRealm.create_krbrealm')
+ def test_successfully_create_realm(self, create_krbrealm):
+ ''' Test successfully create realm '''
+ data = self.mock_args()
+ data['state'] = 'present'
+ data['vserver'] = 'vserver1'
+ data['realm'] = 'NETAPP.COM'
+ data['kdc_ip'] = '10.0.0.1'
+ data['kdc_vendor'] = 'other'
+ set_module_args(data)
+ obj = self.get_kerberos_realm_mock_object()
+ with pytest.raises(AnsibleExitJson) as exc:
+ obj.apply()
+ assert exc.value.args[0]['changed']
+ create_krbrealm.assert_called_with()
+
+ def test_required_if(self):
+ ''' required arguments are reported as errors '''
+ data = self.mock_args()
+ data['state'] = 'present'
+ data['vserver'] = 'vserver1'
+ data['realm'] = 'NETAPP.COM'
+ data['kdc_ip'] = '10.0.0.1'
+ data['kdc_vendor'] = 'microsoft'
+ with pytest.raises(AnsibleFailJson) as exc:
+ set_module_args(data)
+ my_module()
+ msg = "kdc_vendor is microsoft but all of the following are missing: ad_server_ip, ad_server_name"
+ assert exc.value.args[0]['msg'] == msg
+
+ def test_required_if_single(self):
+ ''' required arguments are reported as errors '''
+ data = self.mock_args()
+ data['state'] = 'present'
+ data['vserver'] = 'vserver1'
+ data['realm'] = 'NETAPP.COM'
+ data['kdc_ip'] = '10.0.0.1'
+ data['kdc_vendor'] = 'microsoft'
+ data['ad_server_ip'] = '10.0.0.1'
+ with pytest.raises(AnsibleFailJson) as exc:
+ set_module_args(data)
+ my_module()
+ msg = "kdc_vendor is microsoft but all of the following are missing: ad_server_name"
+ assert exc.value.args[0]['msg'] == msg
diff --git a/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_ldap_client.py b/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_ldap_client.py
new file mode 100644
index 00000000..d8502342
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_ldap_client.py
@@ -0,0 +1,185 @@
+# (c) 2018, NetApp, Inc
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+''' unit tests for Ansible module: na_ontap_ldap_client '''
+
+from __future__ import (absolute_import, division, print_function)
+
+__metaclass__ = type
+
+import json
+import pytest
+
+from ansible_collections.netapp.ontap.tests.unit.compat import unittest
+from ansible_collections.netapp.ontap.tests.unit.compat.mock import patch, Mock
+from ansible.module_utils import basic
+from ansible.module_utils._text import to_bytes
+import ansible_collections.netapp.ontap.plugins.module_utils.netapp as netapp_utils
+
+from ansible_collections.netapp.ontap.plugins.modules.na_ontap_ldap_client \
+ import NetAppOntapLDAPClient as client_module # module under test
+
+if not netapp_utils.has_netapp_lib():
+ pytestmark = pytest.mark.skip('skipping as missing required netapp_lib')
+
+
+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 MockONTAPConnection(object):
+ ''' mock server connection to ONTAP host '''
+
+ def __init__(self, kind=None, data=None):
+ ''' save arguments '''
+ self.kind = kind
+ self.params = data
+ self.xml_in = None
+ self.xml_out = None
+
+ def invoke_successfully(self, xml, enable_tunneling): # pylint: disable=unused-argument
+ ''' mock invoke_successfully returning xml data '''
+ self.xml_in = xml
+ if self.kind == 'client':
+ xml = self.build_ldap_client_info(self.params)
+ self.xml_out = xml
+ return xml
+
+ @staticmethod
+ def build_ldap_client_info(client_details):
+ xml = netapp_utils.zapi.NaElement('xml')
+ attributes = {
+ 'num-records': 1,
+ 'attributes-list': {
+ 'ldap-client': {
+ 'ldap-client-config': client_details['name'],
+ 'schema': client_details['schema'],
+ 'ldap-servers': [
+ {"ldap-server": client_details['ldap_servers'][0]},
+ {"ldap-server": client_details['ldap_servers'][1]}
+ ]
+ }
+ }
+ }
+ xml.translate_struct(attributes)
+ return xml
+
+
+class TestMyModule(unittest.TestCase):
+ ''' Unit tests for na_ontap_job_schedule '''
+
+ 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)
+ self.mock_client = {
+ 'state': 'present',
+ 'name': 'test_ldap',
+ 'ldap_servers': ['ldap1.example.company.com', 'ldap2.example.company.com'],
+ 'schema': 'RFC-2307',
+ 'vserver': 'test_vserver',
+ }
+
+ def mock_args(self):
+ return {
+ 'state': self.mock_client['state'],
+ 'name': self.mock_client['name'],
+ 'ldap_servers': self.mock_client['ldap_servers'],
+ 'schema': self.mock_client['schema'],
+ 'hostname': 'test',
+ 'username': 'test_user',
+ 'password': 'test_pass!',
+ 'vserver': 'test_vserver',
+ }
+
+ def get_client_mock_object(self, kind=None):
+ client_obj = client_module()
+ client_obj.asup_log_for_cserver = Mock(return_value=None)
+ if kind is None:
+ client_obj.server = MockONTAPConnection()
+ else:
+ client_obj.server = MockONTAPConnection(kind='client', data=self.mock_client)
+ return client_obj
+
+ def test_module_fail_when_required_args_missing(self):
+ ''' required arguments are reported as errors '''
+ with pytest.raises(AnsibleFailJson) as exc:
+ set_module_args({})
+ client_module()
+ print('Info: %s' % exc.value.args[0]['msg'])
+
+ def test_get_nonexistent_client(self):
+ ''' Test if get ldap client returns None for non-existent job '''
+ set_module_args(self.mock_args())
+ result = self.get_client_mock_object().get_ldap_client()
+ assert not result
+
+ def test_get_existing_client(self):
+ ''' Test if get ldap client returns None for non-existent job '''
+ set_module_args(self.mock_args())
+ result = self.get_client_mock_object('client').get_ldap_client()
+ assert result
+
+ def test_successfully_create(self):
+ set_module_args(self.mock_args())
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_client_mock_object().apply()
+ assert exc.value.args[0]['changed']
+
+ def test_create_idempotency(self):
+ set_module_args(self.mock_args())
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_client_mock_object('client').apply()
+ assert not exc.value.args[0]['changed']
+
+ def test_successfully_delete(self):
+ data = self.mock_args()
+ data['state'] = 'absent'
+ set_module_args(data)
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_client_mock_object('client').apply()
+ assert exc.value.args[0]['changed']
+
+ def test_delete_idempotency(self):
+ data = self.mock_args()
+ data['state'] = 'absent'
+ set_module_args(data)
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_client_mock_object().apply()
+ assert not exc.value.args[0]['changed']
+
+ def test_successfully_modify(self):
+ data = self.mock_args()
+ data['ldap_servers'] = ["ldap1.example.company.com"]
+ set_module_args(data)
+ print(self.get_client_mock_object('client').get_ldap_client())
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_client_mock_object('client').apply()
+ assert exc.value.args[0]['changed']
diff --git a/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_login_messages.py b/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_login_messages.py
new file mode 100644
index 00000000..f7b81ae4
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_login_messages.py
@@ -0,0 +1,287 @@
+# (c) 2019, NetApp, Inc
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+''' unit tests for Ansible module: na_ontap_login_messages'''
+
+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.ontap.tests.unit.compat import unittest
+from ansible_collections.netapp.ontap.tests.unit.compat.mock import patch, Mock
+import ansible_collections.netapp.ontap.plugins.module_utils.netapp as netapp_utils
+
+from ansible_collections.netapp.ontap.plugins.modules.na_ontap_login_messages \
+ import NetAppOntapLoginMessages as messages_module # module under test
+
+if not netapp_utils.has_netapp_lib():
+ pytestmark = pytest.mark.skip('skipping as missing required netapp_lib')
+HAS_NETAPP_ZAPI_MSG = "pip install netapp_lib is required"
+
+
+# REST API canned responses when mocking send_request
+SRR = {
+ # common responses
+ 'is_rest': (200, {}, None),
+ 'is_zapi': (400, {}, "Unreachable"),
+ 'empty_good': (200, {}, None),
+ 'end_of_sequence': (500, None, "Unexpected call to send_request"),
+ 'generic_error': (400, None, "Expected error"),
+ # 'dns_record': ({"records": [{"message": "test message",
+ # "uuid": "02c9e252-41be-11e9-81d5-00a0986138f7"}]}, None),
+ 'svm_uuid': (200, {"records": [{"uuid": "test_uuid"}], "num_records": 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"""
+
+
+class AnsibleFailJson(Exception):
+ """Exception class to be raised by module.fail_json and caught by the test case"""
+
+
+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 MockONTAPConnection(object):
+ ''' mock server connection to ONTAP host '''
+
+ def __init__(self, kind=None, data=None):
+ ''' save arguments '''
+ self.kind = kind
+ self.params = data
+ self.xml_in = None
+ self.xml_out = None
+
+ def invoke_successfully(self, xml, enable_tunneling): # pylint: disable=unused-argument
+ ''' mock invoke_successfully returning xml data '''
+ self.xml_in = xml
+ request = xml.to_string().decode('utf-8')
+ print(request)
+ if self.kind == 'error':
+ raise netapp_utils.zapi.NaApiError('test', 'expect error')
+ elif request.startswith("<ems-autosupport-log>"):
+ xml = None # or something that may the logger happy, and you don't need @patch anymore
+ # or
+ # xml = build_ems_log_response()
+ elif request.startswith("<vserver-login-banner-get-iter>"):
+ if self.kind == 'create':
+ xml = self.build_banner_info()
+ # elif self.kind == 'create_idempotency':
+ # xml = self.build_banner_info(self.params)
+ else:
+ xml = self.build_banner_info(self.params)
+ elif request.startswith("<vserver-login-banner-modify-iter>"):
+ xml = self.build_banner_info(self.params)
+ elif request.startswith("<vserver-motd-modify-iter>"):
+ xml = self.build_motd_info(self.params)
+ elif request.startswith("<vserver-motd-get-iter>"):
+ if self.kind == 'create':
+ xml = self.build_motd_info()
+ # elif self.kind == 'create_idempotency':
+ # xml = self.build_banner_info(self.params)
+ else:
+ xml = self.build_motd_info(self.params)
+
+ self.xml_out = xml
+ return xml
+
+ @staticmethod
+ def build_banner_info(data=None):
+ xml = netapp_utils.zapi.NaElement('xml')
+ vserver = 'vserver'
+ attributes = {'num-records': 1,
+ 'attributes-list': {'vserver-login-banner-info': {'vserver': vserver}}}
+ if data is not None and data.get('banner'):
+ attributes['attributes-list']['vserver-login-banner-info']['message'] = data['banner']
+ xml.translate_struct(attributes)
+ return xml
+
+ @staticmethod
+ def build_motd_info(data=None):
+ xml = netapp_utils.zapi.NaElement('xml')
+ vserver = 'vserver'
+ attributes = {'num-records': 1,
+ 'attributes-list': {'vserver-motd-info': {'vserver': vserver}}}
+ if data is not None and data.get('motd_message'):
+ attributes['attributes-list']['vserver-motd-info']['message'] = data['motd_message']
+ if data is not None and data.get('show_cluster_motd') is False:
+ attributes['attributes-list']['vserver-motd-info']['is-cluster-message-enabled'] = 'false'
+ else:
+ attributes['attributes-list']['vserver-motd-info']['is-cluster-message-enabled'] = 'true'
+ xml.translate_struct(attributes)
+ return xml
+
+
+class TestMyModule(unittest.TestCase):
+ ''' Unit tests for na_ontap_login_banner '''
+
+ 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 mock_args(self):
+ return {
+ 'vserver': 'vserver',
+ 'hostname': 'test',
+ 'username': 'test_user',
+ 'password': 'test_pass!'
+ }
+
+ def get_login_mock_object(self, cx_type='zapi', kind=None, status=None):
+ banner_obj = messages_module()
+ netapp_utils.ems_log_event = Mock(return_value=None)
+ if cx_type == 'zapi':
+ if kind is None:
+ banner_obj.server = MockONTAPConnection()
+ else:
+ banner_obj.server = MockONTAPConnection(kind=kind, data=status)
+ return banner_obj
+
+ def test_module_fail_when_required_args_missing(self):
+ ''' required arguments are reported as errors '''
+ with pytest.raises(AnsibleFailJson) as exc:
+ set_module_args({})
+ messages_module()
+ print('Info: %s' % exc.value.args[0]['msg'])
+
+ def test_successfully_create_banner(self):
+ data = self.mock_args()
+ data['banner'] = 'test banner'
+ data['use_rest'] = 'never'
+ set_module_args(data)
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_login_mock_object('zapi', 'create', data).apply()
+ assert exc.value.args[0]['changed']
+
+ def test_create_banner_idempotency(self):
+ data = self.mock_args()
+ data['banner'] = 'test banner'
+ data['use_rest'] = 'never'
+ set_module_args(data)
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_login_mock_object('zapi', 'create_idempotency', data).apply()
+ assert not exc.value.args[0]['changed']
+
+ def test_successfully_create_motd(self):
+ data = self.mock_args()
+ data['motd_message'] = 'test message'
+ data['use_rest'] = 'never'
+ set_module_args(data)
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_login_mock_object('zapi', 'create', data).apply()
+ assert exc.value.args[0]['changed']
+
+ def test_create_motd_idempotency(self):
+ data = self.mock_args()
+ data['motd_message'] = 'test message'
+ data['use_rest'] = 'never'
+ set_module_args(data)
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_login_mock_object('zapi', 'create_idempotency', data).apply()
+ assert not exc.value.args[0]['changed']
+
+ def test_get_banner_error(self):
+ data = self.mock_args()
+ data['use_rest'] = 'never'
+ set_module_args(data)
+ with pytest.raises(AnsibleFailJson) as exc:
+ self.get_login_mock_object('zapi', 'error', data).apply()
+ assert exc.value.args[0]['msg'] == 'Error fetching login_banner info: NetApp API failed. Reason - test:expect error'
+
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_login_messages.NetAppOntapLoginMessages.get_banner_motd')
+ def test_modify_banner_error(self, get_info):
+ data = self.mock_args()
+ data['banner'] = 'modify to new banner'
+ data['use_rest'] = 'never'
+ set_module_args(data)
+ get_info.side_effect = [
+ {
+ 'banner': 'old banner',
+ 'motd': ''
+ }
+ ]
+ with pytest.raises(AnsibleFailJson) as exc:
+ self.get_login_mock_object('zapi', 'error', data).apply()
+ assert exc.value.args[0]['msg'] == 'Error modifying login_banner: NetApp API failed. Reason - test:expect error'
+
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_login_messages.NetAppOntapLoginMessages.get_banner_motd')
+ def test_modify_motd_error(self, get_info):
+ data = self.mock_args()
+ data['motd_message'] = 'modify to new motd'
+ data['use_rest'] = 'never'
+ set_module_args(data)
+ get_info.side_effect = [
+ {
+ 'motd': 'old motd',
+ 'show_cluster_motd': False
+ }
+ ]
+ with pytest.raises(AnsibleFailJson) as exc:
+ self.get_login_mock_object('zapi', 'error', data).apply()
+ assert exc.value.args[0]['msg'] == 'Error modifying motd: NetApp API failed. Reason - test:expect error'
+
+ @patch('ansible_collections.netapp.ontap.plugins.module_utils.netapp.OntapRestAPI.send_request')
+ def test_successfully_create_banner_rest(self, mock_request):
+ data = self.mock_args()
+ data['banner'] = 'test banner'
+ set_module_args(data)
+ mock_request.side_effect = [
+ SRR['is_rest'],
+ SRR['svm_uuid'],
+ SRR['empty_good'], # get
+ SRR['empty_good'], # patch
+ SRR['end_of_sequence']
+ ]
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_login_mock_object(cx_type='rest').apply()
+ assert exc.value.args[0]['changed']
+
+ @patch('ansible_collections.netapp.ontap.plugins.module_utils.netapp.OntapRestAPI.send_request')
+ def test_banner_error_rest(self, mock_request):
+ data = self.mock_args()
+ data['banner'] = 'test banner'
+ set_module_args(data)
+ mock_request.side_effect = [
+ SRR['is_rest'],
+ SRR['svm_uuid'],
+ SRR['generic_error'], # get
+ SRR['end_of_sequence']
+ ]
+ with pytest.raises(AnsibleFailJson) as exc:
+ self.get_login_mock_object(cx_type='rest').apply()
+ assert exc.value.args[0]['msg'] == 'Error when fetching login_banner info: Expected error'
+
+ mock_request.side_effect = [
+ SRR['is_rest'],
+ SRR['svm_uuid'],
+ SRR['empty_good'], # get
+ SRR['generic_error'], # patch
+ SRR['end_of_sequence']
+ ]
+ with pytest.raises(AnsibleFailJson) as exc:
+ self.get_login_mock_object(cx_type='rest').apply()
+ assert exc.value.args[0]['msg'] == 'Error when modifying banner: Expected error'
diff --git a/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_lun.py b/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_lun.py
new file mode 100644
index 00000000..07d3c2f3
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_lun.py
@@ -0,0 +1,177 @@
+# (c) 2020, NetApp, Inc
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+''' unit test template for ONTAP Ansible module '''
+
+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.ontap.tests.unit.compat import unittest
+from ansible_collections.netapp.ontap.tests.unit.compat.mock import patch, Mock
+import ansible_collections.netapp.ontap.plugins.module_utils.netapp as netapp_utils
+
+from ansible_collections.netapp.ontap.plugins.modules.na_ontap_lun \
+ import NetAppOntapLUN as my_module # module under test
+
+if not netapp_utils.has_netapp_lib():
+ pytestmark = pytest.mark.skip('skipping as missing required netapp_lib')
+
+
+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"""
+
+
+class AnsibleFailJson(Exception):
+ """Exception class to be raised by module.fail_json and caught by the test case"""
+
+
+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 MockONTAPConnection(object):
+ ''' mock server connection to ONTAP host '''
+
+ def __init__(self, kind=None, parm1=None):
+ ''' save arguments '''
+ self.type = kind
+ self.parm1 = parm1
+ self.xml_in = None
+ self.xml_out = None
+
+ def invoke_successfully(self, xml, enable_tunneling): # pylint: disable=unused-argument
+ ''' mock invoke_successfully returning xml data '''
+ self.xml_in = xml
+ if self.type == 'lun':
+ xml = self.build_lun_info(self.parm1)
+ self.xml_out = xml
+ return xml
+
+ @staticmethod
+ def build_lun_info(lun_name):
+ ''' build xml data for lun-info '''
+ xml = netapp_utils.zapi.NaElement('xml')
+ lun = dict(
+ lun_info=dict(
+ path="/what/ever/%s" % lun_name,
+ size=10
+ )
+ )
+ attributes = {
+ 'num-records': 1,
+ 'attributes-list': [lun]
+ }
+ xml.translate_struct(attributes)
+ return xml
+
+
+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)
+ self.mock_lun_args = {
+ 'vserver': 'ansible',
+ 'name': 'lun_name',
+ 'flexvol_name': 'vol_name',
+ 'state': 'present'
+ }
+
+ def mock_args(self):
+
+ return {
+ 'vserver': self.mock_lun_args['vserver'],
+ 'name': self.mock_lun_args['name'],
+ 'flexvol_name': self.mock_lun_args['flexvol_name'],
+ 'state': self.mock_lun_args['state'],
+ 'hostname': 'hostname',
+ 'username': 'username',
+ 'password': 'password',
+ }
+ # self.server = MockONTAPConnection()
+
+ def get_lun_mock_object(self, kind=None, parm1=None):
+ """
+ Helper method to return an na_ontap_lun object
+ :param kind: passes this param to MockONTAPConnection()
+ :return: na_ontap_interface object
+ """
+ lun_obj = my_module()
+ lun_obj.autosupport_log = Mock(return_value=None)
+ lun_obj.server = MockONTAPConnection(kind=kind, parm1=parm1)
+ return lun_obj
+
+ def test_module_fail_when_required_args_missing(self):
+ ''' required arguments are reported as errors '''
+ with pytest.raises(AnsibleFailJson) as exc:
+ set_module_args({})
+ my_module()
+ print('Info: %s' % exc.value.args[0]['msg'])
+
+ def test_create_error_missing_param(self):
+ ''' Test if create throws an error if required param 'destination_vserver' is not specified'''
+ data = self.mock_args()
+ set_module_args(data)
+ with pytest.raises(AnsibleFailJson) as exc:
+ self.get_lun_mock_object().apply()
+ msg = 'size is a required parameter for create.'
+ assert msg == exc.value.args[0]['msg']
+
+ def test_successful_create(self):
+ ''' Test successful create '''
+ data = dict(self.mock_args())
+ data['size'] = 5
+ set_module_args(data)
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_lun_mock_object().apply()
+ assert exc.value.args[0]['changed']
+
+ def test_create_rename_idempotency(self):
+ ''' Test create idempotency '''
+ set_module_args(self.mock_args())
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_lun_mock_object('lun', 'lun_name').apply()
+ assert not exc.value.args[0]['changed']
+
+ def test_successful_rename(self):
+ ''' Test successful create '''
+ data = dict(self.mock_args())
+ data['from_name'] = 'lun_from_name'
+ set_module_args(data)
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_lun_mock_object('lun', 'lun_from_name').apply()
+ assert exc.value.args[0]['changed']
+ assert 'renamed' in exc.value.args[0]
+
+ def test_failed_rename(self):
+ ''' Test failed rename '''
+ data = dict(self.mock_args())
+ data['from_name'] = 'lun_from_name'
+ set_module_args(data)
+ with pytest.raises(AnsibleFailJson) as exc:
+ self.get_lun_mock_object('lun', 'other_lun_name').apply()
+ msg = 'Error renaming lun: lun_from_name does not exist'
+ assert msg == exc.value.args[0]['msg']
diff --git a/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_lun_copy.py b/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_lun_copy.py
new file mode 100644
index 00000000..cafa9105
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_lun_copy.py
@@ -0,0 +1,155 @@
+# (c) 2018, NetApp, Inc
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+''' unit test template for ONTAP Ansible module '''
+
+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.ontap.tests.unit.compat import unittest
+from ansible_collections.netapp.ontap.tests.unit.compat.mock import patch, Mock
+import ansible_collections.netapp.ontap.plugins.module_utils.netapp as netapp_utils
+
+from ansible_collections.netapp.ontap.plugins.modules.na_ontap_lun_copy \
+ import NetAppOntapLUNCopy as my_module # module under test
+
+if not netapp_utils.has_netapp_lib():
+ pytestmark = pytest.mark.skip('skipping as missing required netapp_lib')
+
+
+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"""
+
+
+class AnsibleFailJson(Exception):
+ """Exception class to be raised by module.fail_json and caught by the test case"""
+
+
+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 MockONTAPConnection(object):
+ ''' mock server connection to ONTAP host '''
+
+ def __init__(self, kind=None, parm1=None):
+ ''' save arguments '''
+ self.type = kind
+ self.parm1 = parm1
+ self.xml_in = None
+ self.xml_out = None
+
+ def invoke_successfully(self, xml, enable_tunneling): # pylint: disable=unused-argument
+ ''' mock invoke_successfully returning xml data '''
+ self.xml_in = xml
+ if self.type == 'destination_vserver':
+ xml = self.build_lun_info()
+ self.xml_out = xml
+ return xml
+
+ @staticmethod
+ def build_lun_info():
+ ''' build xml data for lun-info '''
+ xml = netapp_utils.zapi.NaElement('xml')
+ attributes = {
+ 'num-records': 1,
+ }
+ xml.translate_struct(attributes)
+ return xml
+
+
+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)
+ self.mock_lun_copy = {
+ 'source_vserver': 'ansible',
+ 'destination_path': '/vol/test/test_copy_dest_dest_new_reviewd_new',
+ 'source_path': '/vol/test/test_copy_1',
+ 'destination_vserver': 'ansible',
+ 'state': 'present'
+ }
+
+ def mock_args(self):
+
+ return {
+ 'source_vserver': self.mock_lun_copy['source_vserver'],
+ 'destination_path': self.mock_lun_copy['destination_path'],
+ 'source_path': self.mock_lun_copy['source_path'],
+ 'destination_vserver': self.mock_lun_copy['destination_vserver'],
+ 'state': self.mock_lun_copy['state'],
+ 'hostname': 'hostname',
+ 'username': 'username',
+ 'password': 'password',
+ }
+ # self.server = MockONTAPConnection()
+
+ def get_lun_copy_mock_object(self, kind=None):
+ """
+ Helper method to return an na_ontap_lun_copy object
+ :param kind: passes this param to MockONTAPConnection()
+ :return: na_ontap_interface object
+ """
+ lun_copy_obj = my_module()
+ lun_copy_obj.autosupport_log = Mock(return_value=None)
+ if kind is None:
+ lun_copy_obj.server = MockONTAPConnection()
+ else:
+ lun_copy_obj.server = MockONTAPConnection(kind=kind)
+ return lun_copy_obj
+
+ def test_module_fail_when_required_args_missing(self):
+ ''' required arguments are reported as errors '''
+ with pytest.raises(AnsibleFailJson) as exc:
+ set_module_args({})
+ my_module()
+ print('Info: %s' % exc.value.args[0]['msg'])
+
+ def test_create_error_missing_param(self):
+ ''' Test if create throws an error if required param 'destination_vserver' is not specified'''
+ data = self.mock_args()
+ del data['destination_vserver']
+ set_module_args(data)
+ with pytest.raises(AnsibleFailJson) as exc:
+ self.get_lun_copy_mock_object('lun_copy').copy_lun()
+ msg = 'missing required arguments: destination_vserver'
+ assert msg == exc.value.args[0]['msg']
+
+ def test_successful_copy(self):
+ ''' Test successful create '''
+ # data = self.mock_args()
+ set_module_args(self.mock_args())
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_lun_copy_mock_object().apply()
+ assert exc.value.args[0]['changed']
+
+ def test_copy_idempotency(self):
+ ''' Test create idempotency '''
+ set_module_args(self.mock_args())
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_lun_copy_mock_object('destination_vserver').apply()
+ assert not exc.value.args[0]['changed']
diff --git a/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_lun_map.py b/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_lun_map.py
new file mode 100644
index 00000000..a904a516
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_lun_map.py
@@ -0,0 +1,192 @@
+''' unit tests ONTAP Ansible module: na_ontap_lun_map '''
+
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+import json
+import pytest
+
+from ansible_collections.netapp.ontap.tests.unit.compat import unittest
+from ansible_collections.netapp.ontap.tests.unit.compat.mock import patch
+from ansible.module_utils import basic
+from ansible.module_utils._text import to_bytes
+import ansible_collections.netapp.ontap.plugins.module_utils.netapp as netapp_utils
+
+from ansible_collections.netapp.ontap.plugins.modules.na_ontap_lun_map \
+ import NetAppOntapLUNMap as my_module
+
+if not netapp_utils.has_netapp_lib():
+ pytestmark = pytest.mark.skip('skipping as missing required netapp_lib')
+
+
+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 MockONTAPConnection(object):
+ ''' mock server connection to ONTAP host '''
+
+ def __init__(self, kind=None):
+ ''' save arguments '''
+ self.type = kind
+ self.xml_in = None
+ self.xml_out = None
+
+ def invoke_successfully(self, xml, enable_tunneling): # pylint: disable=unused-argument
+ ''' mock invoke_successfully returning xml data '''
+ self.xml_in = xml
+ if self.type == 'lun_map':
+ xml = self.build_lun_info()
+ elif self.type == 'lun_map_fail':
+ raise netapp_utils.zapi.NaApiError(code='TEST', message="This exception is from the unit test")
+ self.xml_out = xml
+ return xml
+
+ @staticmethod
+ def build_lun_info():
+ ''' build xml data for lun-map-entry '''
+ xml = netapp_utils.zapi.NaElement('xml')
+ data = {'initiator-groups': [{'initiator-group-info': {'initiator-group-name': 'ansible', 'lun-id': 2}}]}
+ xml.translate_struct(data)
+ return xml
+
+
+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)
+ self.server = MockONTAPConnection()
+ self.onbox = False
+
+ def set_default_args(self):
+ if self.onbox:
+ hostname = '10.10.10.10'
+ username = 'admin'
+ password = 'password'
+ initiator_group_name = 'ansible'
+ vserver = 'ansible'
+ path = '/vol/ansible/test'
+ lun_id = 2
+ else:
+ hostname = 'hostname'
+ username = 'username'
+ password = 'password'
+ initiator_group_name = 'ansible'
+ vserver = 'ansible'
+ path = '/vol/ansible/test'
+ lun_id = 2
+ return dict({
+ 'hostname': hostname,
+ 'username': username,
+ 'password': password,
+ 'initiator_group_name': initiator_group_name,
+ 'vserver': vserver,
+ 'path': path,
+ 'lun_id': lun_id
+ })
+
+ def test_module_fail_when_required_args_missing(self):
+ ''' required arguments are reported as errors '''
+ with pytest.raises(AnsibleFailJson) as exc:
+ set_module_args({})
+ my_module()
+ print('Info: %s' % exc.value.args[0]['msg'])
+
+ def test_ensure_get_called(self):
+ ''' test get_lun_map for non-existent lun'''
+ set_module_args(self.set_default_args())
+ my_obj = my_module()
+ my_obj.server = self.server
+ assert my_obj.get_lun_map is not None
+
+ def test_ensure_get_called_existing(self):
+ ''' test get_lun_map for existing lun'''
+ set_module_args(self.set_default_args())
+ my_obj = my_module()
+ my_obj.server = MockONTAPConnection(kind='lun_map')
+ assert my_obj.get_lun_map()
+
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_lun_map.NetAppOntapLUNMap.create_lun_map')
+ def test_successful_create(self, create_lun_map):
+ ''' mapping lun and testing idempotency '''
+ data = self.set_default_args()
+ set_module_args(data)
+ my_obj = my_module()
+ if not self.onbox:
+ my_obj.server = self.server
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ assert exc.value.args[0]['changed']
+ create_lun_map.assert_called_with()
+ # to reset na_helper from remembering the previous 'changed' value
+ set_module_args(self.set_default_args())
+ my_obj = my_module()
+ if not self.onbox:
+ my_obj.server = MockONTAPConnection('lun_map')
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ assert not exc.value.args[0]['changed']
+
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_lun_map.NetAppOntapLUNMap.delete_lun_map')
+ def test_successful_delete(self, delete_lun_map):
+ ''' unmapping lun and testing idempotency '''
+ data = self.set_default_args()
+ data['state'] = 'absent'
+ set_module_args(data)
+ my_obj = my_module()
+ if not self.onbox:
+ my_obj.server = MockONTAPConnection('lun_map')
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ assert exc.value.args[0]['changed']
+ delete_lun_map.assert_called_with()
+ # to reset na_helper from remembering the previous 'changed' value
+ my_obj = my_module()
+ if not self.onbox:
+ my_obj.server = self.server
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ assert not exc.value.args[0]['changed']
+
+ def test_if_all_methods_catch_exception(self):
+ module_args = {}
+ module_args.update(self.set_default_args())
+ set_module_args(module_args)
+ my_obj = my_module()
+ if not self.onbox:
+ my_obj.server = MockONTAPConnection('lun_map_fail')
+ with pytest.raises(AnsibleFailJson) as exc:
+ my_obj.create_lun_map()
+ assert 'Error mapping lun' in exc.value.args[0]['msg']
+ with pytest.raises(AnsibleFailJson) as exc:
+ my_obj.delete_lun_map()
+ assert 'Error unmapping lun' in exc.value.args[0]['msg']
diff --git a/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_lun_rest.py b/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_lun_rest.py
new file mode 100644
index 00000000..9ed07535
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_lun_rest.py
@@ -0,0 +1,277 @@
+# (c) 2020, NetApp, Inc
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+''' unit test template for ONTAP Ansible module '''
+
+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.ontap.tests.unit.compat import unittest
+from ansible_collections.netapp.ontap.tests.unit.compat.mock import patch, Mock
+import ansible_collections.netapp.ontap.plugins.module_utils.netapp as netapp_utils
+
+from ansible_collections.netapp.ontap.plugins.modules.na_ontap_lun \
+ import NetAppOntapLUN as my_module # module under test
+
+if not netapp_utils.has_netapp_lib():
+ pytestmark = pytest.mark.skip('skipping as missing required netapp_lib')
+
+
+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"""
+
+
+class AnsibleFailJson(Exception):
+ """Exception class to be raised by module.fail_json and caught by the test case"""
+
+
+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)
+
+
+# REST API canned responses when mocking send_request
+SRR = {
+ # common responses
+ 'is_rest': (200, {}, None),
+ 'is_zapi': (400, {}, "Unreachable"),
+ 'empty_good': (200, {}, None),
+ 'end_of_sequence': (500, None, "Unexpected call to send_request"),
+ 'generic_error': (400, None, "Expected error"),
+ # module specific responses
+ 'get_apps_empty': (200,
+ {'records': [],
+ 'num_records': 0
+ },
+ None
+ ),
+ 'get_apps_found': (200,
+ {'records': [dict(name='san_appli', uuid='1234')],
+ 'num_records': 1
+ },
+ None
+ ),
+ 'get_app_components': (200,
+ {'records': [dict(name='san_appli', uuid='1234')],
+ 'num_records': 1
+ },
+ None
+ ),
+ 'get_app_component_details': (200,
+ {'backing_storage': dict(luns=[]),
+ },
+ None
+ ),
+}
+
+
+class MockONTAPConnection(object):
+ ''' mock server connection to ONTAP host '''
+
+ def __init__(self, kind=None, parm1=None):
+ ''' save arguments '''
+ self.type = kind
+ self.parm1 = parm1
+ self.xml_in = None
+ self.xml_out = None
+
+ def invoke_successfully(self, xml, enable_tunneling): # pylint: disable=unused-argument
+ ''' mock invoke_successfully returning xml data '''
+ self.xml_in = xml
+ if self.type == 'lun':
+ xml = self.build_lun_info(self.parm1)
+ self.xml_out = xml
+ return xml
+
+ @staticmethod
+ def build_lun_info(lun_name):
+ ''' build xml data for lun-info '''
+ xml = netapp_utils.zapi.NaElement('xml')
+ lun = dict(
+ lun_info=dict(
+ path="/what/ever/%s" % lun_name,
+ size=10
+ )
+ )
+ attributes = {
+ 'num-records': 1,
+ 'attributes-list': [lun]
+ }
+ xml.translate_struct(attributes)
+ return xml
+
+
+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)
+ self.mock_lun_args = {
+ 'vserver': 'ansible',
+ 'name': 'lun_name',
+ 'flexvol_name': 'vol_name',
+ 'state': 'present'
+ }
+
+ def mock_args(self):
+ return {
+ 'vserver': self.mock_lun_args['vserver'],
+ 'name': self.mock_lun_args['name'],
+ 'flexvol_name': self.mock_lun_args['flexvol_name'],
+ 'state': self.mock_lun_args['state'],
+ 'hostname': 'hostname',
+ 'username': 'username',
+ 'password': 'password',
+ }
+ # self.server = MockONTAPConnection()
+
+ def get_lun_mock_object(self, kind=None, parm1=None):
+ """
+ Helper method to return an na_ontap_lun object
+ :param kind: passes this param to MockONTAPConnection()
+ :return: na_ontap_interface object
+ """
+ lun_obj = my_module()
+ lun_obj.autosupport_log = Mock(return_value=None)
+ lun_obj.server = MockONTAPConnection(kind=kind, parm1=parm1)
+ return lun_obj
+
+ def test_module_fail_when_required_args_missing(self):
+ ''' required arguments are reported as errors '''
+ with pytest.raises(AnsibleFailJson) as exc:
+ set_module_args({})
+ my_module()
+ print('Info: %s' % exc.value.args[0]['msg'])
+
+ def test_create_error_missing_param(self):
+ ''' Test if create throws an error if required param 'destination_vserver' is not specified'''
+ data = self.mock_args()
+ set_module_args(data)
+ data.pop('flexvol_name')
+ data['san_application_template'] = dict(name='san_appli')
+ with pytest.raises(AnsibleFailJson) as exc:
+ self.get_lun_mock_object().apply()
+ msg = 'size is a required parameter for create.'
+ assert msg == exc.value.args[0]['msg']
+
+ def test_create_error_missing_param2(self):
+ ''' Test if create throws an error if required param 'destination_vserver' is not specified'''
+ data = self.mock_args()
+ data.pop('flexvol_name')
+ data['size'] = 5
+ data['san_application_template'] = dict(lun_count=6)
+ set_module_args(data)
+ with pytest.raises(AnsibleFailJson) as exc:
+ self.get_lun_mock_object().apply()
+ msg = 'missing required arguments: name found in san_application_template'
+ assert msg == exc.value.args[0]['msg']
+
+ @patch('ansible_collections.netapp.ontap.plugins.module_utils.netapp.OntapRestAPI.send_request')
+ def test_successful_create_appli(self, mock_request):
+ ''' Test successful create '''
+ mock_request.side_effect = [
+ SRR['get_apps_empty'], # GET application/applications
+ SRR['empty_good'], # POST application/applications
+ SRR['end_of_sequence']
+ ]
+ data = dict(self.mock_args())
+ data['size'] = 5
+ data.pop('flexvol_name')
+ data['san_application_template'] = dict(name='san_appli')
+ set_module_args(data)
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_lun_mock_object().apply()
+ assert exc.value.args[0]['changed']
+
+ @patch('ansible_collections.netapp.ontap.plugins.module_utils.netapp.OntapRestAPI.send_request')
+ def test_successful_create_appli_idem(self, mock_request):
+ ''' Test successful create idempotent '''
+ mock_request.side_effect = [
+ SRR['get_apps_found'], # GET application/applications
+ SRR['get_apps_found'], # GET application/applications/<uuid>/components
+ SRR['get_app_component_details'], # GET application/applications/<uuid>/components/<cuuid>
+ SRR['end_of_sequence']
+ ]
+ data = dict(self.mock_args())
+ data['size'] = 5
+ data.pop('flexvol_name')
+ data['san_application_template'] = dict(name='san_appli')
+ set_module_args(data)
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_lun_mock_object().apply()
+ assert not exc.value.args[0]['changed']
+
+ @patch('ansible_collections.netapp.ontap.plugins.module_utils.netapp.OntapRestAPI.send_request')
+ def test_successful_create_appli_idem_no_comp(self, mock_request):
+ ''' Test successful create idempotent '''
+ mock_request.side_effect = [
+ SRR['get_apps_found'], # GET application/applications
+ SRR['get_apps_empty'], # GET application/applications/<uuid>/components
+ SRR['end_of_sequence']
+ ]
+ data = dict(self.mock_args())
+ data['size'] = 5
+ data.pop('flexvol_name')
+ data['san_application_template'] = dict(name='san_appli')
+ set_module_args(data)
+ with pytest.raises(AnsibleFailJson) as exc:
+ self.get_lun_mock_object().apply()
+ msg = 'Error: no component for application san_appli'
+ assert msg == exc.value.args[0]['msg']
+
+ @patch('ansible_collections.netapp.ontap.plugins.module_utils.netapp.OntapRestAPI.send_request')
+ def test_successful_delete_appli(self, mock_request):
+ ''' Test successful create '''
+ mock_request.side_effect = [
+ SRR['get_apps_found'], # GET application/applications
+ SRR['empty_good'], # POST application/applications
+ SRR['end_of_sequence']
+ ]
+ data = dict(self.mock_args())
+ data['size'] = 5
+ data.pop('flexvol_name')
+ data['san_application_template'] = dict(name='san_appli')
+ data['state'] = 'absent'
+ set_module_args(data)
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_lun_mock_object().apply()
+ assert exc.value.args[0]['changed']
+
+ @patch('ansible_collections.netapp.ontap.plugins.module_utils.netapp.OntapRestAPI.send_request')
+ def test_successful_delete_appli_idem(self, mock_request):
+ ''' Test successful deelte idempotent '''
+ mock_request.side_effect = [
+ SRR['get_apps_empty'], # GET application/applications
+ SRR['end_of_sequence']
+ ]
+ data = dict(self.mock_args())
+ data['size'] = 5
+ data.pop('flexvol_name')
+ data['san_application_template'] = dict(name='san_appli')
+ data['state'] = 'absent'
+ set_module_args(data)
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_lun_mock_object().apply()
+ assert not exc.value.args[0]['changed']
diff --git a/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_mcc_mediator.py b/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_mcc_mediator.py
new file mode 100644
index 00000000..33da1819
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_mcc_mediator.py
@@ -0,0 +1,156 @@
+# (c) 2020, NetApp, Inc
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+''' unit tests for Ansible module: na_ontap_metrocluster '''
+
+from __future__ import (absolute_import, division, print_function)
+
+__metaclass__ = type
+
+import json
+import pytest
+
+from ansible_collections.netapp.ontap.tests.unit.compat import unittest
+from ansible_collections.netapp.ontap.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.ontap.plugins.modules.na_ontap_mcc_mediator \
+ import NetAppOntapMccipMediator as mediator_module # module under test
+
+SRR = {
+ # common responses
+ 'is_rest': (200, {}, None),
+ 'is_zapi': (400, {}, "Unreachable"),
+ 'empty_good': (200, {}, None),
+ 'end_of_sequence': (500, None, "Unexpected call to send_request"),
+ 'generic_error': (400, None, "Expected error"),
+ # module specific responses
+ 'get_mediator_with_no_results': (200, {'num_records': 0}, None),
+ 'get_mediator_with_results': (200, {
+ 'num_records': 1,
+ 'records': [{
+ 'ip_address': '10.10.10.10',
+ 'uuid': 'ebe27c49-1adf-4496-8335-ab862aebebf2'
+ }]
+ }, 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"""
+
+
+class AnsibleFailJson(Exception):
+ """Exception class to be raised by module.fail_json and caught by the test case"""
+
+
+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):
+ """ Unit tests for na_ontap_metrocluster """
+
+ 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)
+ self.mock_mediator = {
+ 'mediator_address': '10.10.10.10',
+ 'mediator_user': 'carchi',
+ 'mediator_password': 'netapp1!'
+ }
+
+ def mock_args(self):
+ return {
+ 'mediator_address': self.mock_mediator['mediator_address'],
+ 'mediator_user': self.mock_mediator['mediator_user'],
+ 'mediator_password': self.mock_mediator['mediator_password'],
+ 'hostname': 'test_host',
+ 'username': 'test_user',
+ 'password': 'test_pass!'
+ }
+
+ def get_alias_mock_object(self):
+ alias_obj = mediator_module()
+ return alias_obj
+
+ @patch('ansible_collections.netapp.ontap.plugins.module_utils.netapp.OntapRestAPI.send_request')
+ def test_rest_successful_create(self, mock_request):
+ """Test successful rest create"""
+ data = self.mock_args()
+ set_module_args(data)
+ mock_request.side_effect = [
+ SRR['is_rest'],
+ SRR['get_mediator_with_no_results'],
+ SRR['empty_good'],
+ SRR['end_of_sequence']
+ ]
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_alias_mock_object().apply()
+ assert exc.value.args[0]['changed']
+
+ @patch('ansible_collections.netapp.ontap.plugins.module_utils.netapp.OntapRestAPI.send_request')
+ def test_rest_successful_create_idempotency(self, mock_request):
+ """Test successful rest create"""
+ data = self.mock_args()
+ set_module_args(data)
+ mock_request.side_effect = [
+ SRR['is_rest'],
+ SRR['get_mediator_with_results'],
+ SRR['empty_good'],
+ SRR['end_of_sequence']
+ ]
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_alias_mock_object().apply()
+ assert not exc.value.args[0]['changed']
+
+ @patch('ansible_collections.netapp.ontap.plugins.module_utils.netapp.OntapRestAPI.send_request')
+ def test_rest_successful_delete(self, mock_request):
+ """Test successful rest create"""
+ data = self.mock_args()
+ data['state'] = 'absent'
+ set_module_args(data)
+ mock_request.side_effect = [
+ SRR['is_rest'],
+ SRR['get_mediator_with_results'],
+ SRR['empty_good'],
+ SRR['end_of_sequence']
+ ]
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_alias_mock_object().apply()
+ assert exc.value.args[0]['changed']
+
+ @patch('ansible_collections.netapp.ontap.plugins.module_utils.netapp.OntapRestAPI.send_request')
+ def test_rest_successful_delete(self, mock_request):
+ """Test successful rest create"""
+ data = self.mock_args()
+ data['state'] = 'absent'
+ set_module_args(data)
+ mock_request.side_effect = [
+ SRR['is_rest'],
+ SRR['get_mediator_with_no_results'],
+ SRR['empty_good'],
+ SRR['end_of_sequence']
+ ]
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_alias_mock_object().apply()
+ assert not exc.value.args[0]['changed']
diff --git a/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_metrocluster.py b/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_metrocluster.py
new file mode 100644
index 00000000..169ab9c3
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_metrocluster.py
@@ -0,0 +1,149 @@
+# (c) 2020, NetApp, Inc
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+''' unit tests for Ansible module: na_ontap_metrocluster '''
+
+from __future__ import (absolute_import, division, print_function)
+
+__metaclass__ = type
+
+import json
+import pytest
+
+from ansible_collections.netapp.ontap.tests.unit.compat import unittest
+from ansible_collections.netapp.ontap.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.ontap.plugins.modules.na_ontap_metrocluster \
+ import NetAppONTAPMetroCluster as metrocluster_module # module under test
+
+# REST API canned responses when mocking send_request
+SRR = {
+ # common responses
+ 'is_rest': (200, {}, None),
+ 'is_zapi': (400, {}, "Unreachable"),
+ 'empty_good': (200, {}, None),
+ 'end_of_sequence': (500, None, "Unexpected call to send_request"),
+ 'generic_error': (400, None, "Expected error"),
+ # module specific responses
+ 'get_metrocluster_with_results': (200, {"local": {
+ "cluster": {
+ 'name': 'cluster1'
+ },
+ "configuration_state": "configuration_error", # TODO: put correct state
+ "partner_cluster_reachable": "true",
+ }}, None),
+ 'get_metrocluster_with_no_results': (200, None, None),
+ 'metrocluster_post': (200, {'job': {
+ 'uuid': 'fde79888-692a-11ea-80c2-005056b39fe7',
+ '_links': {
+ 'self': {
+ 'href': '/api/cluster/jobs/fde79888-692a-11ea-80c2-005056b39fe7'}}}
+ }, None),
+ 'job': (200, {
+ "uuid": "cca3d070-58c6-11ea-8c0c-005056826c14",
+ "description": "POST /api/cluster/metrocluster",
+ "state": "success",
+ "message": "There are not enough disks in Pool1.",
+ "code": 2432836,
+ "start_time": "2020-02-26T10:35:44-08:00",
+ "end_time": "2020-02-26T10:47:38-08:00",
+ "_links": {
+ "self": {
+ "href": "/api/cluster/jobs/cca3d070-58c6-11ea-8c0c-005056826c14"
+ }
+ }
+ }, 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"""
+
+
+class AnsibleFailJson(Exception):
+ """Exception class to be raised by module.fail_json and caught by the test case"""
+
+
+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):
+ """ Unit tests for na_ontap_metrocluster """
+
+ 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)
+ self.mock_metrocluster = {
+ 'partner_cluster_name': 'cluster1',
+ 'node_name': 'carchi_vsim1',
+ 'partner_node_name': 'carchi_vsim3'
+ }
+
+ def mock_args(self):
+ return {
+ 'dr_pairs': [{
+ 'node_name': self.mock_metrocluster['node_name'],
+ 'partner_node_name': self.mock_metrocluster['partner_node_name'],
+ }],
+ 'partner_cluster_name': self.mock_metrocluster['partner_cluster_name'],
+ 'hostname': 'test_host',
+ 'username': 'test_user',
+ 'password': 'test_pass!'
+ }
+
+ def get_alias_mock_object(self):
+ alias_obj = metrocluster_module()
+ return alias_obj
+
+ @patch('ansible_collections.netapp.ontap.plugins.module_utils.netapp.OntapRestAPI.send_request')
+ def test_rest_successful_create(self, mock_request):
+ """Test successful rest create"""
+ data = self.mock_args()
+ set_module_args(data)
+ mock_request.side_effect = [
+ SRR['is_rest'],
+ SRR['get_metrocluster_with_no_results'],
+ SRR['metrocluster_post'],
+ SRR['job'],
+ SRR['empty_good'],
+ SRR['end_of_sequence']
+ ]
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_alias_mock_object().apply()
+ assert exc.value.args[0]['changed']
+
+ @patch('ansible_collections.netapp.ontap.plugins.module_utils.netapp.OntapRestAPI.send_request')
+ def test_rest_create_idempotency(self, mock_request):
+ """Test rest create idempotency"""
+ data = self.mock_args()
+ set_module_args(data)
+ mock_request.side_effect = [
+ SRR['is_rest'],
+ SRR['get_metrocluster_with_results'],
+ SRR['empty_good'],
+ SRR['end_of_sequence']
+ ]
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_alias_mock_object().apply()
+ assert not exc.value.args[0]['changed']
diff --git a/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_metrocluster_dr_group.py b/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_metrocluster_dr_group.py
new file mode 100644
index 00000000..df349da4
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_metrocluster_dr_group.py
@@ -0,0 +1,196 @@
+# (c) 2020, NetApp, Inc
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+''' unit tests for Ansible module: na_ontap_metrocluster '''
+
+from __future__ import (absolute_import, division, print_function)
+
+__metaclass__ = type
+
+import json
+import pytest
+
+from ansible_collections.netapp.ontap.tests.unit.compat import unittest
+from ansible_collections.netapp.ontap.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.ontap.plugins.modules.na_ontap_metrocluster_dr_group \
+ import NetAppONTAPMetroClusterDRGroup as mcc_dr_pairs_module # module under test
+
+# REST API canned responses when mocking send_request
+SRR = {
+ # common responses
+ 'is_rest': (200, {}, None),
+ 'is_zapi': (400, {}, "Unreachable"),
+ 'empty_good': (200, {}, None),
+ 'end_of_sequence': (500, None, "Unexpected call to send_request"),
+ 'generic_error': (400, None, "Expected error"),
+ # module specific responses
+ 'get_mcc_dr_pair_with_no_results': (200, {'records': [], 'num_records': 0}, None),
+ 'get_mcc_dr_pair_with_results': (200, {'records': [{'partner_cluster': {'name': 'rha2-b2b1_siteB'},
+ 'dr_pairs': [{'node': {'name': 'rha17-a2'},
+ 'partner': {'name': 'rha17-b2'}},
+ {'node': {'name': 'rha17-b2'},
+ 'partner': {'name': 'rha17-b1'}}],
+ 'id': '2'}],
+ 'num_records': 1}, None),
+ 'mcc_dr_pair_post': (200, {'job': {
+ 'uuid': 'fde79888-692a-11ea-80c2-005056b39fe7',
+ '_links': {
+ 'self': {
+ 'href': '/api/cluster/jobs/fde79888-692a-11ea-80c2-005056b39fe7'}}}
+ }, None),
+ 'get_mcc_dr_node': (200, {'records': [{'dr_group_id': '1'}], 'num_records': 1}, None),
+ 'get_mcc_dr_node_none': (200, {'records': [], 'num_records': 0}, None),
+ 'job': (200, {
+ "uuid": "cca3d070-58c6-11ea-8c0c-005056826c14",
+ "description": "POST /api/cluster/metrocluster",
+ "state": "success",
+ "message": "There are not enough disks in Pool1.",
+ "code": 2432836,
+ "start_time": "2020-02-26T10:35:44-08:00",
+ "end_time": "2020-02-26T10:47:38-08:00",
+ "_links": {
+ "self": {
+ "href": "/api/cluster/jobs/cca3d070-58c6-11ea-8c0c-005056826c14"
+ }
+ }
+ }, 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"""
+
+
+class AnsibleFailJson(Exception):
+ """Exception class to be raised by module.fail_json and caught by the test case"""
+
+
+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):
+ """ Unit tests for na_ontap_metrocluster """
+
+ 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)
+ self.mock_mcc_dr_pair = {
+ 'partner_cluster_name': 'rha2-b2b1_siteB',
+ 'node_name': 'rha17-a2',
+ 'partner_node_name': 'rha17-b2',
+ 'node_name2': 'rha17-b2',
+ 'partner_node_name2': 'rha17-b1'
+
+ }
+
+ def mock_args(self):
+ return {
+ 'dr_pairs': [{
+ 'node_name': self.mock_mcc_dr_pair['node_name'],
+ 'partner_node_name': self.mock_mcc_dr_pair['partner_node_name'],
+ }, {
+ 'node_name': self.mock_mcc_dr_pair['node_name2'],
+ 'partner_node_name': self.mock_mcc_dr_pair['partner_node_name2'],
+ }],
+ 'partner_cluster_name': self.mock_mcc_dr_pair['partner_cluster_name'],
+ 'hostname': 'test_host',
+ 'username': 'test_user',
+ 'password': 'test_pass!'
+ }
+
+ def get_alias_mock_object(self):
+ alias_obj = mcc_dr_pairs_module()
+ return alias_obj
+
+ @patch('ansible_collections.netapp.ontap.plugins.module_utils.netapp.OntapRestAPI.send_request')
+ def test_rest_successful_create(self, mock_request):
+ """Test successful rest create"""
+ data = self.mock_args()
+ set_module_args(data)
+ mock_request.side_effect = [
+ SRR['is_rest'],
+ SRR['get_mcc_dr_pair_with_no_results'],
+ SRR['get_mcc_dr_pair_with_no_results'],
+ SRR['mcc_dr_pair_post'],
+ SRR['job'],
+ SRR['empty_good'],
+ SRR['end_of_sequence']
+ ]
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_alias_mock_object().apply()
+ assert exc.value.args[0]['changed']
+
+ @patch('ansible_collections.netapp.ontap.plugins.module_utils.netapp.OntapRestAPI.send_request')
+ def test_rest_create_idempotency(self, mock_request):
+ """Test rest create idempotency"""
+ data = self.mock_args()
+ set_module_args(data)
+ mock_request.side_effect = [
+ SRR['is_rest'],
+ SRR['get_mcc_dr_pair_with_results'],
+ SRR['empty_good'],
+ SRR['end_of_sequence']
+ ]
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_alias_mock_object().apply()
+ assert not exc.value.args[0]['changed']
+
+ @patch('ansible_collections.netapp.ontap.plugins.module_utils.netapp.OntapRestAPI.send_request')
+ def test_rest_successful_delete(self, mock_request):
+ """Test successful rest delete"""
+ data = self.mock_args()
+ data['state'] = 'absent'
+ set_module_args(data)
+ mock_request.side_effect = [
+ SRR['is_rest'],
+ SRR['get_mcc_dr_pair_with_results'],
+ SRR['mcc_dr_pair_post'],
+ SRR['job'],
+ SRR['empty_good'],
+ SRR['end_of_sequence']
+ ]
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_alias_mock_object().apply()
+ assert exc.value.args[0]['changed']
+
+ @patch('ansible_collections.netapp.ontap.plugins.module_utils.netapp.OntapRestAPI.send_request')
+ def test_rest_delete_idempotency(self, mock_request):
+ """Test rest delete idempotency"""
+ data = self.mock_args()
+ data['state'] = 'absent'
+ set_module_args(data)
+ mock_request.side_effect = [
+ SRR['is_rest'],
+ SRR['get_mcc_dr_pair_with_no_results'],
+ SRR['get_mcc_dr_pair_with_no_results'],
+ SRR['get_mcc_dr_node_none'],
+ SRR['get_mcc_dr_node_none'],
+ SRR['job'],
+ SRR['empty_good'],
+ SRR['end_of_sequence']
+ ]
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_alias_mock_object().apply()
+ assert not exc.value.args[0]['changed']
diff --git a/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_motd.py b/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_motd.py
new file mode 100644
index 00000000..5522986d
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_motd.py
@@ -0,0 +1,182 @@
+# (c) 2019, NetApp, Inc
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+""" unit tests for Ansible module: na_ontap_motd """
+
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+import json
+import pytest
+
+from ansible_collections.netapp.ontap.tests.unit.compat import unittest
+from ansible_collections.netapp.ontap.tests.unit.compat.mock import patch, Mock
+from ansible.module_utils import basic
+from ansible.module_utils._text import to_bytes
+import ansible_collections.netapp.ontap.plugins.module_utils.netapp as netapp_utils
+
+from ansible_collections.netapp.ontap.plugins.modules.na_ontap_motd \
+ import NetAppONTAPMotd as my_module # module under test
+
+if not netapp_utils.has_netapp_lib():
+ pytestmark = pytest.mark.skip('skipping as missing required netapp_lib')
+
+
+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 MockONTAPConnection(object):
+ ''' mock server connection to ONTAP host '''
+
+ def __init__(self, kind=None):
+ ''' save arguments '''
+ self.type = kind
+ self.xml_in = None
+ self.xml_out = None
+
+ def invoke_successfully(self, xml, enable_tunneling): # pylint: disable=unused-argument
+ ''' mock invoke_successfully returning xml data '''
+ self.xml_in = xml
+ if self.type == 'motd':
+ xml = self.build_motd_info()
+ elif self.type == 'motd_fail':
+ raise netapp_utils.zapi.NaApiError(code='TEST', message="This exception is from the unit test")
+ self.xml_out = xml
+ return xml
+
+ @staticmethod
+ def build_motd_info():
+ ''' build xml data for motd '''
+ xml = netapp_utils.zapi.NaElement('xml')
+ data = {'num-records': 1,
+ 'attributes-list': {'vserver-motd-info': {'message': 'ansible',
+ 'vserver': 'ansible',
+ 'is-cluster-message-enabled': 'true'}}}
+ xml.translate_struct(data)
+ print(xml.to_string())
+ return xml
+
+
+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)
+ self.server = MockONTAPConnection()
+ # whether to use a mock or a simulator
+ self.onbox = False
+
+ def set_default_args(self):
+ if self.onbox:
+ hostname = '10.10.10.10'
+ username = 'admin'
+ password = 'password'
+ message = 'ansible'
+ vserver = 'ansible'
+ show_cluster_motd = 'true'
+ else:
+ hostname = 'hostname'
+ username = 'username'
+ password = 'password'
+ message = 'ansible'
+ vserver = 'ansible'
+ show_cluster_motd = 'true'
+ return dict({
+ 'hostname': hostname,
+ 'username': username,
+ 'password': password,
+ 'message': message,
+ 'vserver': vserver,
+ 'show_cluster_motd': show_cluster_motd
+ })
+
+ def call_command(self, module_args):
+ ''' utility function to call apply '''
+ module_args.update(self.set_default_args())
+ set_module_args(module_args)
+ my_obj = my_module()
+ if not self.onbox:
+ my_obj.server = MockONTAPConnection('motd')
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ return exc.value.args[0]['changed']
+
+ def test_module_fail_when_required_args_missing(self):
+ ''' required arguments are reported as errors '''
+ with pytest.raises(AnsibleFailJson) as exc:
+ set_module_args({})
+ my_module()
+ print('Info: %s' % exc.value.args[0]['msg'])
+
+ def test_ensure_motd_get_called(self):
+ ''' fetching details of motd '''
+ set_module_args(self.set_default_args())
+ my_obj = my_module()
+ my_obj.server = self.server
+ assert my_obj.motd_get() is None
+
+ def test_ensure_get_called_existing(self):
+ ''' test for existing motd'''
+ set_module_args(self.set_default_args())
+ my_obj = my_module()
+ my_obj.server = MockONTAPConnection(kind='motd')
+ assert my_obj.motd_get()
+
+ def test_motd_create(self):
+ ''' test for creating motd'''
+ set_module_args(self.set_default_args())
+ my_obj = my_module()
+ if not self.onbox:
+ my_obj.server = MockONTAPConnection(kind='motd')
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ assert not exc.value.args[0]['changed']
+
+ def test_motd_delete(self):
+ ''' test for deleting motd'''
+ module_args = {
+ 'state': 'absent',
+ }
+ changed = self.call_command(module_args)
+ assert changed
+
+ def test_if_all_methods_catch_exception(self):
+ set_module_args(self.set_default_args())
+ my_obj = my_module()
+ if not self.onbox:
+ my_obj.server = MockONTAPConnection('motd_fail')
+ with pytest.raises(AnsibleFailJson) as exc:
+ my_obj.motd_get()
+ assert '' in exc.value.args[0]['msg']
+ with pytest.raises(AnsibleFailJson) as exc:
+ my_obj.modify_motd()
+ assert 'Error creating motd: ' in exc.value.args[0]['msg']
diff --git a/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_name_service_switch.py b/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_name_service_switch.py
new file mode 100644
index 00000000..d6b0ce72
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_name_service_switch.py
@@ -0,0 +1,180 @@
+# (c) 2019, NetApp, Inc
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+''' unit tests for Ansible module: na_ontap_volume_export_policy '''
+
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+import json
+import pytest
+
+from ansible_collections.netapp.ontap.tests.unit.compat import unittest
+from ansible_collections.netapp.ontap.tests.unit.compat.mock import patch, Mock
+from ansible.module_utils import basic
+from ansible.module_utils._text import to_bytes
+import ansible_collections.netapp.ontap.plugins.module_utils.netapp as netapp_utils
+
+from ansible_collections.netapp.ontap.plugins.modules.na_ontap_name_service_switch \
+ import NetAppONTAPNsswitch as nss_module # module under test
+
+if not netapp_utils.has_netapp_lib():
+ pytestmark = pytest.mark.skip('skipping as missing required netapp_lib')
+
+
+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 MockONTAPConnection(object):
+ ''' mock server connection to ONTAP host '''
+
+ def __init__(self, kind=None, data=None):
+ ''' save arguments '''
+ self.kind = kind
+ self.params = data
+ self.xml_in = None
+ self.xml_out = None
+
+ def invoke_successfully(self, xml, enable_tunneling): # pylint: disable=unused-argument
+ ''' mock invoke_successfully returning xml data '''
+ self.xml_in = xml
+ if self.kind == 'nss':
+ xml = self.build_nss_info(self.params)
+ if self.kind == 'error':
+ error = netapp_utils.zapi.NaApiError('test', 'error')
+ raise error
+ self.xml_out = xml
+ return xml
+
+ @staticmethod
+ def build_nss_info(nss_details):
+ xml = netapp_utils.zapi.NaElement('xml')
+ attributes = {
+ 'num-records': 1,
+ 'attributes-list': {
+ 'namservice-nsswitch-config-info': {
+ 'nameservice-database': nss_details['database_type'],
+ 'nameservice-sources': {
+ 'nss-source-type': nss_details['sources']
+ }
+ }
+ }
+ }
+ xml.translate_struct(attributes)
+ return xml
+
+
+class TestMyModule(unittest.TestCase):
+ ''' Unit tests for na_ontap_name_service_switch '''
+
+ 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)
+ self.mock_nss = {
+ 'state': 'present',
+ 'vserver': 'test_vserver',
+ 'database_type': 'namemap',
+ 'sources': 'files,ldap',
+ }
+
+ def mock_args(self):
+ return {
+ 'state': self.mock_nss['state'],
+ 'vserver': self.mock_nss['vserver'],
+ 'database_type': self.mock_nss['database_type'],
+ 'sources': self.mock_nss['sources'],
+ 'hostname': 'test',
+ 'username': 'test_user',
+ 'password': 'test_pass!'
+ }
+
+ def get_nss_object(self, kind=None):
+ nss_obj = nss_module()
+ nss_obj.asup_log_for_cserver = Mock(return_value=None)
+ if kind is None:
+ nss_obj.server = MockONTAPConnection()
+ else:
+ nss_obj.server = MockONTAPConnection(kind=kind, data=self.mock_nss)
+ return nss_obj
+
+ def test_module_fail_when_required_args_missing(self):
+ with pytest.raises(AnsibleFailJson) as exc:
+ set_module_args({})
+ nss_module()
+ print('Info: %s' % exc.value.args[0]['msg'])
+
+ def test_get_nonexistent_nss(self):
+ set_module_args(self.mock_args())
+ result = self.get_nss_object().get_name_service_switch()
+ assert result is None
+
+ def test_get_existing_nss(self):
+ set_module_args(self.mock_args())
+ result = self.get_nss_object('nss').get_name_service_switch()
+ assert result
+
+ def test_successfully_create(self):
+ set_module_args(self.mock_args())
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_nss_object().apply()
+ assert exc.value.args[0]['changed']
+
+ def test_successfully_modify(self):
+ data = self.mock_args()
+ data['sources'] = 'files'
+ set_module_args(data)
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_nss_object('nss').apply()
+ assert exc.value.args[0]['changed']
+
+ def test_successfully_delete(self):
+ data = self.mock_args()
+ data['state'] = 'absent'
+ set_module_args(data)
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_nss_object('nss').apply()
+ assert exc.value.args[0]['changed']
+
+ def test_error(self):
+ data = self.mock_args()
+ set_module_args(data)
+ with pytest.raises(AnsibleFailJson) as exc:
+ self.get_nss_object('error').create_name_service_switch()
+ print(exc)
+ assert exc.value.args[0]['msg'] == 'Error on creating name service switch config on vserver test_vserver: NetApp API failed. Reason - test:error'
+ with pytest.raises(AnsibleFailJson) as exc:
+ self.get_nss_object('error').modify_name_service_switch({})
+ print(exc)
+ assert exc.value.args[0]['msg'] == 'Error on modifying name service switch config on vserver test_vserver: NetApp API failed. Reason - test:error'
+ with pytest.raises(AnsibleFailJson) as exc:
+ self.get_nss_object('error').delete_name_service_switch()
+ print(exc)
+ assert exc.value.args[0]['msg'] == 'Error on deleting name service switch config on vserver test_vserver: NetApp API failed. Reason - test:error'
diff --git a/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_ndmp.py b/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_ndmp.py
new file mode 100644
index 00000000..6fc9b89e
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_ndmp.py
@@ -0,0 +1,227 @@
+# (c) 2019, NetApp, Inc
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+''' unit test template for ONTAP Ansible module '''
+
+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.ontap.tests.unit.compat import unittest
+from ansible_collections.netapp.ontap.tests.unit.compat.mock import patch, Mock
+import ansible_collections.netapp.ontap.plugins.module_utils.netapp as netapp_utils
+
+from ansible_collections.netapp.ontap.plugins.modules.na_ontap_ndmp \
+ import NetAppONTAPNdmp as ndmp_module # module under test
+
+if not netapp_utils.has_netapp_lib():
+ pytestmark = pytest.mark.skip('skipping as missing required netapp_lib')
+
+# REST API canned responses when mocking send_request
+SRR = {
+ # common responses
+ 'is_rest': (200, {}, None),
+ 'is_zapi': (400, {}, "Unreachable"),
+ 'get_uuid': (200, {'records': [{'uuid': 'testuuid'}]}, None),
+ 'empty_good': (200, {}, None),
+ 'end_of_sequence': (500, None, "Unexpected call to send_request"),
+ 'generic_error': (400, None, 'Error fetching ndmp from ansible: NetApp API failed. Reason - Unexpected error:',
+ "REST API currently does not support 'backup_log_enable, ignore_ctime_enabled'"),
+ 'get_ndmp_uuid': (200, {"records": [{"svm": {"name": "svm1", "uuid": "02c9e252-41be-11e9-81d5-00a0986138f7"}}]}, None),
+ 'get_ndmp': (200, {"enabled": True, "authentication_types": ["test"],
+ "records": [{"svm": {"name": "svm1", "uuid": "02c9e252-41be-11e9-81d5-00a0986138f7"}}]}, 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"""
+
+
+class AnsibleFailJson(Exception):
+ """Exception class to be raised by module.fail_json and caught by the test case"""
+
+
+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 MockONTAPConnection(object):
+ ''' mock server connection to ONTAP host '''
+
+ def __init__(self, kind=None, data=None):
+ ''' save arguments '''
+ self.type = kind
+ self.data = data
+ self.xml_in = None
+ self.xml_out = None
+
+ def invoke_successfully(self, xml, enable_tunneling): # pylint: disable=unused-argument
+ ''' mock invoke_successfully returning xml data '''
+ self.xml_in = xml
+ if self.type == 'ndmp':
+ xml = self.build_ndmp_info(self.data)
+ if self.type == 'error':
+ error = netapp_utils.zapi.NaApiError('test', 'error')
+ raise error
+ self.xml_out = xml
+ return xml
+
+ @staticmethod
+ def build_ndmp_info(ndmp_details):
+ ''' build xml data for ndmp '''
+ xml = netapp_utils.zapi.NaElement('xml')
+ attributes = {
+ 'num-records': 1,
+ 'attributes-list': {
+ 'ndmp-vserver-attributes-info': {
+ 'ignore_ctime_enabled': ndmp_details['ignore_ctime_enabled'],
+ 'backup_log_enable': ndmp_details['backup_log_enable'],
+
+ 'authtype': [
+ {'ndmpd-authtypes': 'plaintext'},
+ {'ndmpd-authtypes': 'challenge'}
+ ]
+ }
+ }
+ }
+ xml.translate_struct(attributes)
+ return xml
+
+
+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)
+ self.mock_ndmp = {
+ 'ignore_ctime_enabled': True,
+ 'backup_log_enable': 'false',
+ 'authtype': 'plaintext',
+ 'enable': True
+ }
+
+ def mock_args(self, rest=False):
+ if rest:
+ return {
+ 'authtype': self.mock_ndmp['authtype'],
+ 'enable': True,
+ 'vserver': 'ansible',
+ 'hostname': 'test',
+ 'username': 'test_user',
+ 'password': 'test_pass!',
+ 'https': 'False'
+ }
+ else:
+ return {
+ 'vserver': 'ansible',
+ 'authtype': self.mock_ndmp['authtype'],
+ 'ignore_ctime_enabled': self.mock_ndmp['ignore_ctime_enabled'],
+ 'backup_log_enable': self.mock_ndmp['backup_log_enable'],
+ 'enable': self.mock_ndmp['enable'],
+ 'hostname': 'test',
+ 'username': 'test_user',
+ 'password': 'test_pass!'
+ }
+
+ def get_ndmp_mock_object(self, kind=None, cx_type='zapi'):
+ """
+ Helper method to return an na_ontap_ndmp object
+ :param kind: passes this param to MockONTAPConnection()
+ :return: na_ontap_ndmp object
+ """
+ obj = ndmp_module()
+ if cx_type == 'zapi':
+ obj.asup_log_for_cserver = Mock(return_value=None)
+ obj.server = Mock()
+ obj.server.invoke_successfully = Mock()
+ if kind is None:
+ obj.server = MockONTAPConnection()
+ else:
+ obj.server = MockONTAPConnection(kind=kind, data=self.mock_ndmp)
+ return obj
+
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_ndmp.NetAppONTAPNdmp.ndmp_get_iter')
+ def test_successful_modify(self, ger_ndmp):
+ ''' Test successful modify ndmp'''
+ data = self.mock_args()
+ set_module_args(data)
+ current = {
+ 'ignore_ctime_enabled': False,
+ 'backup_log_enable': True
+ }
+ ger_ndmp.side_effect = [
+ current
+ ]
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_ndmp_mock_object('ndmp').apply()
+ assert exc.value.args[0]['changed']
+
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_ndmp.NetAppONTAPNdmp.ndmp_get_iter')
+ def test_modify_error(self, ger_ndmp):
+ ''' Test modify error '''
+ data = self.mock_args()
+ set_module_args(data)
+ current = {
+ 'ignore_ctime_enabled': False,
+ 'backup_log_enable': True
+ }
+ ger_ndmp.side_effect = [
+ current
+ ]
+ with pytest.raises(AnsibleFailJson) as exc:
+ self.get_ndmp_mock_object('error').apply()
+ assert exc.value.args[0]['msg'] == 'Error modifying ndmp on ansible: NetApp API failed. Reason - test:error'
+
+ @patch('ansible_collections.netapp.ontap.plugins.module_utils.netapp.OntapRestAPI.send_request')
+ def test_rest_error(self, mock_request):
+ data = self.mock_args()
+ data['use_rest'] = 'Always'
+ set_module_args(data)
+ mock_request.side_effect = [
+ SRR['is_rest'],
+ SRR['generic_error'],
+ SRR['end_of_sequence']
+ ]
+ with pytest.raises(AnsibleFailJson) as exc:
+ self.get_ndmp_mock_object(cx_type='rest').apply()
+ assert exc.value.args[0]['msg'] == SRR['generic_error'][3]
+
+ @patch('ansible_collections.netapp.ontap.plugins.module_utils.netapp.OntapRestAPI.send_request')
+ def test_rest_successfully_modify(self, mock_request):
+ data = self.mock_args(rest=True)
+ data['use_rest'] = 'Always'
+ set_module_args(data)
+ mock_request.side_effect = [
+ # SRR['is_rest'], # WHY IS IT NOT CALLED HERE?
+ SRR['get_ndmp_uuid'], # for get svm uuid: protocols/ndmp/svms
+ SRR['get_ndmp'], # for get ndmp details: '/protocols/ndmp/svms/' + uuid
+ SRR['get_ndmp_uuid'], # for get svm uuid: protocols/ndmp/svms (before modify)
+ SRR['empty_good'], # modify (patch)
+ SRR['end_of_sequence'],
+ ]
+ my_obj = ndmp_module()
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ assert exc.value.args[0]['changed']
diff --git a/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_net_ifgrp.py b/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_net_ifgrp.py
new file mode 100644
index 00000000..f849c35a
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_net_ifgrp.py
@@ -0,0 +1,299 @@
+# (c) 2018, NetApp, Inc
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+''' unit test template for ONTAP Ansible module '''
+
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+import json
+import pytest
+
+from ansible_collections.netapp.ontap.tests.unit.compat import unittest
+from ansible_collections.netapp.ontap.tests.unit.compat.mock import patch, Mock
+from ansible.module_utils import basic
+from ansible.module_utils._text import to_bytes
+import ansible_collections.netapp.ontap.plugins.module_utils.netapp as netapp_utils
+
+from ansible_collections.netapp.ontap.plugins.modules.na_ontap_net_ifgrp \
+ import NetAppOntapIfGrp as ifgrp_module # module under test
+
+if not netapp_utils.has_netapp_lib():
+ pytestmark = pytest.mark.skip('skipping as missing required netapp_lib')
+
+
+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 MockONTAPConnection(object):
+ ''' mock server connection to ONTAP host '''
+
+ def __init__(self, kind=None, data=None):
+ ''' save arguments '''
+ self.kind = kind
+ self.params = data
+ self.xml_in = None
+ self.xml_out = None
+
+ def invoke_successfully(self, xml, enable_tunneling): # pylint: disable=unused-argument
+ ''' mock invoke_successfully returning xml data '''
+ self.xml_in = xml
+ if self.kind == 'ifgrp':
+ xml = self.build_ifgrp_info(self.params)
+ elif self.kind == 'ifgrp-ports':
+ xml = self.build_ifgrp_ports_info(self.params)
+ elif self.kind == 'ifgrp-fail':
+ raise netapp_utils.zapi.NaApiError(code='TEST', message="This exception is from the unit test")
+ self.xml_out = xml
+ return xml
+
+ @staticmethod
+ def build_ifgrp_info(ifgrp_details):
+ ''' build xml data for ifgrp-info '''
+ xml = netapp_utils.zapi.NaElement('xml')
+ attributes = {
+ 'num-records': 1,
+ 'attributes-list': {
+ 'net-port-info': {
+ 'port': ifgrp_details['name'],
+ 'ifgrp-distribution-function': 'mac',
+ 'ifgrp-mode': ifgrp_details['mode'],
+ 'node': ifgrp_details['node']
+ }
+ }
+ }
+ xml.translate_struct(attributes)
+ return xml
+
+ @staticmethod
+ def build_ifgrp_ports_info(data):
+ ''' build xml data for ifgrp-ports '''
+ xml = netapp_utils.zapi.NaElement('xml')
+ attributes = {
+ 'attributes': {
+ 'net-ifgrp-info': {
+ 'ports': [
+ {'lif-bindable': data['ports'][0]},
+ {'lif-bindable': data['ports'][1]},
+ {'lif-bindable': data['ports'][2]}
+ ]
+ }
+ }
+ }
+ xml.translate_struct(attributes)
+ return xml
+
+
+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)
+ self.mock_ifgrp = {
+ 'name': 'test',
+ 'port': 'a1',
+ 'node': 'test_vserver',
+ 'mode': 'something'
+ }
+
+ def mock_args(self):
+ return {
+ 'name': self.mock_ifgrp['name'],
+ 'distribution_function': 'mac',
+ 'ports': [self.mock_ifgrp['port']],
+ 'node': self.mock_ifgrp['node'],
+ 'mode': self.mock_ifgrp['mode'],
+ 'hostname': 'test',
+ 'username': 'test_user',
+ 'password': 'test_pass!'
+ }
+
+ def get_ifgrp_mock_object(self, kind=None, data=None):
+ """
+ Helper method to return an na_ontap_net_ifgrp object
+ :param kind: passes this param to MockONTAPConnection()
+ :return: na_ontap_net_ifgrp object
+ """
+ obj = ifgrp_module()
+ obj.autosupport_log = Mock(return_value=None)
+ if data is None:
+ data = self.mock_ifgrp
+ obj.server = MockONTAPConnection(kind=kind, data=data)
+ return obj
+
+ def test_module_fail_when_required_args_missing(self):
+ ''' required arguments are reported as errors '''
+ with pytest.raises(AnsibleFailJson) as exc:
+ set_module_args({})
+ ifgrp_module()
+ print('Info: %s' % exc.value.args[0]['msg'])
+
+ def test_get_nonexistent_ifgrp(self):
+ ''' Test if get_ifgrp returns None for non-existent ifgrp '''
+ set_module_args(self.mock_args())
+ result = self.get_ifgrp_mock_object().get_if_grp()
+ assert result is None
+
+ def test_get_existing_ifgrp(self):
+ ''' Test if get_ifgrp returns details for existing ifgrp '''
+ set_module_args(self.mock_args())
+ result = self.get_ifgrp_mock_object('ifgrp').get_if_grp()
+ assert result['name'] == self.mock_ifgrp['name']
+
+ def test_successful_create(self):
+ ''' Test successful create '''
+ data = self.mock_args()
+ set_module_args(data)
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_ifgrp_mock_object().apply()
+ assert exc.value.args[0]['changed']
+
+ def test_successful_delete(self):
+ ''' Test delete existing volume '''
+ data = self.mock_args()
+ data['state'] = 'absent'
+ set_module_args(data)
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_ifgrp_mock_object('ifgrp').apply()
+ assert exc.value.args[0]['changed']
+
+ def test_successful_modify(self):
+ ''' Test delete existing volume '''
+ data = self.mock_args()
+ data['ports'] = ['1', '2', '3']
+ set_module_args(data)
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_ifgrp_mock_object('ifgrp').apply()
+ assert exc.value.args[0]['changed']
+
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_net_ifgrp.NetAppOntapIfGrp.get_if_grp')
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_net_ifgrp.NetAppOntapIfGrp.create_if_grp')
+ def test_create_called(self, create_ifgrp, get_ifgrp):
+ data = self.mock_args()
+ set_module_args(data)
+ get_ifgrp.return_value = None
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_ifgrp_mock_object().apply()
+ get_ifgrp.assert_called_with()
+ create_ifgrp.assert_called_with()
+
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_net_ifgrp.NetAppOntapIfGrp.add_port_to_if_grp')
+ def test_if_ports_are_added_after_create(self, add_ports):
+ ''' Test successful create '''
+ data = self.mock_args()
+ set_module_args(data)
+ self.get_ifgrp_mock_object().create_if_grp()
+ add_ports.assert_called_with('a1')
+
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_net_ifgrp.NetAppOntapIfGrp.get_if_grp')
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_net_ifgrp.NetAppOntapIfGrp.delete_if_grp')
+ def test_delete_called(self, delete_ifgrp, get_ifgrp):
+ data = self.mock_args()
+ data['state'] = 'absent'
+ set_module_args(data)
+ get_ifgrp.return_value = Mock()
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_ifgrp_mock_object().apply()
+ get_ifgrp.assert_called_with()
+ delete_ifgrp.assert_called_with()
+
+ def test_get_return_value(self):
+ data = self.mock_args()
+ set_module_args(data)
+ result = self.get_ifgrp_mock_object('ifgrp').get_if_grp()
+ assert result['name'] == data['name']
+ assert result['mode'] == data['mode']
+ assert result['node'] == data['node']
+
+ def test_get_ports_list(self):
+ data = self.mock_args()
+ data['ports'] = ['e0a', 'e0b', 'e0c']
+ set_module_args(data)
+ result = self.get_ifgrp_mock_object('ifgrp-ports', data).get_if_grp_ports()
+ assert result['ports'] == data['ports']
+
+ def test_add_port_packet(self):
+ data = self.mock_args()
+ set_module_args(data)
+ obj = self.get_ifgrp_mock_object('ifgrp')
+ obj.add_port_to_if_grp('addme')
+ assert obj.server.xml_in['port'] == 'addme'
+
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_net_ifgrp.NetAppOntapIfGrp.remove_port_to_if_grp')
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_net_ifgrp.NetAppOntapIfGrp.add_port_to_if_grp')
+ def test_modify_ports_calls_remove_existing_ports(self, add_port, remove_port):
+ ''' Test if already existing ports are not being added again '''
+ data = self.mock_args()
+ data['ports'] = ['1', '2']
+ set_module_args(data)
+ self.get_ifgrp_mock_object('ifgrp').modify_ports(current_ports=['1', '2', '3'])
+ assert remove_port.call_count == 1
+ assert add_port.call_count == 0
+
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_net_ifgrp.NetAppOntapIfGrp.remove_port_to_if_grp')
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_net_ifgrp.NetAppOntapIfGrp.add_port_to_if_grp')
+ def test_modify_ports_calls_add_new_ports(self, add_port, remove_port):
+ ''' Test new ports are added '''
+ data = self.mock_args()
+ data['ports'] = ['1', '2', '3', '4']
+ set_module_args(data)
+ self.get_ifgrp_mock_object('ifgrp').modify_ports(current_ports=['1', '2'])
+ assert remove_port.call_count == 0
+ assert add_port.call_count == 2
+
+ def test_get_ports_returns_none(self):
+ set_module_args(self.mock_args())
+ result = self.get_ifgrp_mock_object().get_if_grp_ports()
+ assert result['ports'] == []
+ result = self.get_ifgrp_mock_object().get_if_grp()
+ assert result is None
+
+ def test_if_all_methods_catch_exception(self):
+ set_module_args(self.mock_args())
+ with pytest.raises(AnsibleFailJson) as exc:
+ self.get_ifgrp_mock_object('ifgrp-fail').get_if_grp()
+ assert 'Error getting if_group test' in exc.value.args[0]['msg']
+ with pytest.raises(AnsibleFailJson) as exc:
+ self.get_ifgrp_mock_object('ifgrp-fail').create_if_grp()
+ assert 'Error creating if_group test' in exc.value.args[0]['msg']
+ with pytest.raises(AnsibleFailJson) as exc:
+ self.get_ifgrp_mock_object('ifgrp-fail').get_if_grp_ports()
+ assert 'Error getting if_group ports test' in exc.value.args[0]['msg']
+ with pytest.raises(AnsibleFailJson) as exc:
+ self.get_ifgrp_mock_object('ifgrp-fail').add_port_to_if_grp('test-port')
+ assert 'Error adding port test-port to if_group test' in exc.value.args[0]['msg']
+ with pytest.raises(AnsibleFailJson) as exc:
+ self.get_ifgrp_mock_object('ifgrp-fail').remove_port_to_if_grp('test-port')
+ assert 'Error removing port test-port to if_group test' in exc.value.args[0]['msg']
+ with pytest.raises(AnsibleFailJson) as exc:
+ self.get_ifgrp_mock_object('ifgrp-fail').delete_if_grp()
+ assert 'Error deleting if_group test' in exc.value.args[0]['msg']
diff --git a/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_net_port.py b/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_net_port.py
new file mode 100644
index 00000000..7c16c243
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_net_port.py
@@ -0,0 +1,180 @@
+# (c) 2018, NetApp, Inc
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+''' unit test template for ONTAP Ansible module '''
+
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+import json
+import pytest
+
+from ansible_collections.netapp.ontap.tests.unit.compat import unittest
+from ansible_collections.netapp.ontap.tests.unit.compat.mock import patch, Mock
+from ansible.module_utils import basic
+from ansible.module_utils._text import to_bytes
+import ansible_collections.netapp.ontap.plugins.module_utils.netapp as netapp_utils
+
+from ansible_collections.netapp.ontap.plugins.modules.na_ontap_net_port \
+ import NetAppOntapNetPort as port_module # module under test
+
+if not netapp_utils.has_netapp_lib():
+ pytestmark = pytest.mark.skip('skipping as missing required netapp_lib')
+
+
+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 MockONTAPConnection(object):
+ ''' mock server connection to ONTAP host '''
+
+ def __init__(self, kind=None, data=None):
+ ''' save arguments '''
+ self.type = kind
+ self.data = data
+ self.xml_in = None
+ self.xml_out = None
+
+ def invoke_successfully(self, xml, enable_tunneling): # pylint: disable=unused-argument
+ ''' mock invoke_successfully returning xml data '''
+ self.xml_in = xml
+ if self.type == 'port':
+ xml = self.build_port_info(self.data)
+ self.xml_out = xml
+ return xml
+
+ @staticmethod
+ def build_port_info(port_details):
+ ''' build xml data for net-port-info '''
+ xml = netapp_utils.zapi.NaElement('xml')
+ attributes = {
+ 'num-records': 1,
+ 'attributes-list': {
+ 'net-port-info': {
+ # 'port': port_details['port'],
+ 'mtu': port_details['mtu'],
+ 'is-administrative-auto-negotiate': 'true',
+ 'ipspace': 'default',
+ 'administrative-flowcontrol': port_details['flowcontrol_admin'],
+ 'node': port_details['node']
+ }
+ }
+ }
+ xml.translate_struct(attributes)
+ return xml
+
+
+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)
+ self.server = MockONTAPConnection()
+ self.mock_port = {
+ 'node': 'test',
+ 'ports': 'a1',
+ 'flowcontrol_admin': 'something',
+ 'mtu': '1000'
+ }
+
+ def mock_args(self):
+ return {
+ 'node': self.mock_port['node'],
+ 'flowcontrol_admin': self.mock_port['flowcontrol_admin'],
+ 'ports': [self.mock_port['ports']],
+ 'mtu': self.mock_port['mtu'],
+ 'hostname': 'test',
+ 'username': 'test_user',
+ 'password': 'test_pass!'
+ }
+
+ def get_port_mock_object(self, kind=None, data=None):
+ """
+ Helper method to return an na_ontap_net_port object
+ :param kind: passes this param to MockONTAPConnection()
+ :return: na_ontap_net_port object
+ """
+ obj = port_module()
+ obj.autosupport_log = Mock(return_value=None)
+ if data is None:
+ data = self.mock_port
+ obj.server = MockONTAPConnection(kind=kind, data=data)
+ return obj
+
+ def test_module_fail_when_required_args_missing(self):
+ ''' required arguments are reported as errors '''
+ with pytest.raises(AnsibleFailJson) as exc:
+ set_module_args({})
+ port_module()
+ print('Info: %s' % exc.value.args[0]['msg'])
+
+ def test_get_nonexistent_port(self):
+ ''' Test if get_net_port returns None for non-existent port '''
+ set_module_args(self.mock_args())
+ result = self.get_port_mock_object().get_net_port('test')
+ assert result is None
+
+ def test_get_existing_port(self):
+ ''' Test if get_net_port returns details for existing port '''
+ set_module_args(self.mock_args())
+ result = self.get_port_mock_object('port').get_net_port('test')
+ assert result['mtu'] == self.mock_port['mtu']
+ assert result['flowcontrol_admin'] == self.mock_port['flowcontrol_admin']
+
+ def test_successful_modify(self):
+ ''' Test modify_net_port '''
+ data = self.mock_args()
+ data['mtu'] = '2000'
+ set_module_args(data)
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_port_mock_object('port').apply()
+ assert exc.value.args[0]['changed']
+
+ def test_successful_modify_multiple_ports(self):
+ ''' Test modify_net_port '''
+ data = self.mock_args()
+ data['ports'] = ['a1', 'a2']
+ data['mtu'] = '2000'
+ set_module_args(data)
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_port_mock_object('port').apply()
+ assert exc.value.args[0]['changed']
+
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_net_port.NetAppOntapNetPort.get_net_port')
+ def test_get_called(self, get_port):
+ ''' Test get_net_port '''
+ data = self.mock_args()
+ data['ports'] = ['a1', 'a2']
+ set_module_args(data)
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_port_mock_object('port').apply()
+ assert get_port.call_count == 2
diff --git a/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_net_routes.py b/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_net_routes.py
new file mode 100644
index 00000000..ab7d57bc
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_net_routes.py
@@ -0,0 +1,461 @@
+# (c) 2018, NetApp, Inc
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+''' unit test template for ONTAP Ansible module '''
+
+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.ontap.tests.unit.compat import unittest
+from ansible_collections.netapp.ontap.tests.unit.compat.mock import patch, Mock
+import ansible_collections.netapp.ontap.plugins.module_utils.netapp as netapp_utils
+
+from ansible_collections.netapp.ontap.plugins.modules.na_ontap_net_routes \
+ import NetAppOntapNetRoutes as net_route_module # module under test
+
+if not netapp_utils.has_netapp_lib():
+ pytestmark = pytest.mark.skip('skipping as missing required netapp_lib')
+
+# REST API canned responses when mocking send_request
+SRR = {
+ # common responses
+ 'is_rest': (200, {}, None),
+ 'is_zapi': (400, {}, "Unreachable"),
+ 'empty_good': (200, {}, None),
+ 'end_of_sequence': (500, None, "Unexpected call to send_request"),
+ 'generic_error': (400, None, "Expected error"),
+ # module specific responses
+ 'net_routes_record': (200,
+ {'records': [{"destination": {"address": "176.0.0.0",
+ "netmask": "24",
+ "family": "ipv4"},
+ "gateway": '10.193.72.1',
+ "uuid": '1cd8a442-86d1-11e0-ae1c-123478563412',
+ "svm": {"name": "test_vserver"}}]}, None),
+ 'modified_record': (200,
+ {'records': [{"destination": {"address": "0.0.0.0",
+ "netmask": "0",
+ "family": "ipv4"},
+ "gateway": "10.193.72.1",
+ "uuid": '1cd8a442-86d1-11e0-ae1c-123478563412',
+ "svm": {"name": "test_vserver"}}]}, 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"""
+
+
+class AnsibleFailJson(Exception):
+ """Exception class to be raised by module.fail_json and caught by the test case"""
+
+
+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 MockONTAPConnection(object):
+ ''' mock server connection to ONTAP host '''
+
+ def __init__(self, kind=None, data=None):
+ ''' save arguments '''
+ self.kind = kind
+ self.params = data
+ self.xml_in = None
+ self.xml_out = None
+
+ def invoke_successfully(self, xml, enable_tunneling): # pylint: disable=unused-argument
+ ''' mock invoke_successfully returning xml data '''
+ self.xml_in = xml
+ if self.kind == 'net_route':
+ xml = self.build_net_route_info(self.params)
+ self.xml_out = xml
+ return xml
+
+ @staticmethod
+ def build_net_route_info(net_route_details):
+ ''' build xml data for net_route-attributes '''
+ xml = netapp_utils.zapi.NaElement('xml')
+ attributes = {
+ 'attributes': {
+ 'net-vs-routes-info': {
+ 'address-family': 'ipv4',
+ 'destination': net_route_details['destination'],
+ 'gateway': net_route_details['gateway'],
+ 'metric': net_route_details['metric'],
+ 'vserver': net_route_details['vserver']
+ }
+ }
+ }
+ xml.translate_struct(attributes)
+ return xml
+
+
+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)
+ self.server = MockONTAPConnection()
+ self.mock_net_route = {
+ 'destination': '176.0.0.0/24',
+ 'gateway': '10.193.72.1',
+ 'vserver': 'test_vserver',
+ 'metric': 70
+ }
+
+ def mock_args(self, rest=False, modify=False):
+ if rest:
+ return {
+ 'vserver': self.mock_net_route['vserver'],
+ 'destination': self.mock_net_route['destination'],
+ 'gateway': self.mock_net_route['gateway'],
+ 'hostname': 'test',
+ 'username': 'test_user',
+ 'password': 'test_pass!'
+ }
+ elif modify:
+ return {
+ 'vserver': self.mock_net_route['vserver'],
+ 'destination': '0.0.0.0/0',
+ 'gateway': '10.193.72.1',
+ 'from_destination': self.mock_net_route['destination'],
+ 'from_gateway': self.mock_net_route['gateway'],
+ 'hostname': 'test',
+ 'username': 'test_user',
+ 'password': 'test_pass!'
+ }
+ else:
+ return {
+ 'vserver': self.mock_net_route['vserver'],
+ 'destination': self.mock_net_route['destination'],
+ 'gateway': self.mock_net_route['gateway'],
+ 'metric': self.mock_net_route['metric'],
+ 'hostname': 'test',
+ 'username': 'test_user',
+ 'password': 'test_pass!'
+ }
+
+ def get_net_route_mock_object(self, kind=None, data=None, cx_type='zapi'):
+ """
+ Helper method to return an na_ontap_net_route object
+ :param kind: passes this param to MockONTAPConnection()
+ :param data: passes this data to MockONTAPConnection()
+ :param type: differentiates zapi and rest procedure
+ :return: na_ontap_net_route object
+ """
+ net_route_obj = net_route_module()
+ if cx_type == 'zapi':
+ net_route_obj.ems_log_event = Mock(return_value=None)
+ net_route_obj.cluster = Mock()
+ net_route_obj.cluster.invoke_successfully = Mock()
+ if kind is None:
+ net_route_obj.server = MockONTAPConnection()
+ else:
+ if data is None:
+ net_route_obj.server = MockONTAPConnection(kind='net_route', data=self.mock_net_route)
+ else:
+ net_route_obj.server = MockONTAPConnection(kind='net_route', data=data)
+ return net_route_obj
+
+ def test_module_fail_when_required_args_missing(self):
+ ''' required arguments are reported as errors '''
+ with pytest.raises(AnsibleFailJson) as exc:
+ set_module_args({})
+ net_route_module()
+ print('Info: %s' % exc.value.args[0]['msg'])
+
+ def test_get_nonexistent_net_route(self):
+ ''' Test if get_net_route returns None for non-existent net_route '''
+ set_module_args(self.mock_args())
+ result = self.get_net_route_mock_object().get_net_route()
+ assert result is None
+
+ def test_get_existing_job(self):
+ ''' Test if get_net_route returns details for existing net_route '''
+ set_module_args(self.mock_args())
+ result = self.get_net_route_mock_object('net_route').get_net_route()
+ assert result['destination'] == self.mock_net_route['destination']
+ assert result['gateway'] == self.mock_net_route['gateway']
+
+ def test_create_error_missing_param(self):
+ ''' Test if create throws an error if destination is not specified'''
+ data = self.mock_args()
+ del data['destination']
+ set_module_args(data)
+ with pytest.raises(AnsibleFailJson) as exc:
+ self.get_net_route_mock_object('net_route').create_net_route()
+ msg = 'missing required arguments: destination'
+ assert exc.value.args[0]['msg'] == msg
+
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_net_routes.NetAppOntapNetRoutes.create_net_route')
+ def test_successful_create(self, create_net_route):
+ ''' Test successful create '''
+ data = self.mock_args()
+ set_module_args(data)
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_net_route_mock_object().apply()
+ assert exc.value.args[0]['changed']
+ create_net_route.assert_called_with()
+
+ def test_create_idempotency(self):
+ ''' Test create idempotency '''
+ set_module_args(self.mock_args())
+ obj = self.get_net_route_mock_object('net_route')
+ with pytest.raises(AnsibleExitJson) as exc:
+ obj.apply()
+ assert not exc.value.args[0]['changed']
+
+ def test_successful_delete(self):
+ ''' Test successful delete '''
+ data = self.mock_args()
+ data['state'] = 'absent'
+ set_module_args(data)
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_net_route_mock_object('net_route').apply()
+ assert exc.value.args[0]['changed']
+
+ def test_delete_idempotency(self):
+ ''' Test delete idempotency '''
+ data = self.mock_args()
+ data['state'] = 'absent'
+ set_module_args(data)
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_net_route_mock_object().apply()
+ assert not exc.value.args[0]['changed']
+
+ def test_successful_modify_metric(self):
+ ''' Test successful modify metric '''
+ data = self.mock_args()
+ del data['metric']
+ data['from_metric'] = 70
+ data['metric'] = 40
+ set_module_args(data)
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_net_route_mock_object('net_route').apply()
+ assert exc.value.args[0]['changed']
+
+ def test_modify_metric_idempotency(self):
+ ''' Test modify metric idempotency'''
+ data = self.mock_args()
+ data['metric'] = 70
+ set_module_args(data)
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_net_route_mock_object('net_route').apply()
+ assert not exc.value.args[0]['changed']
+
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_net_routes.NetAppOntapNetRoutes.get_net_route')
+ def test_successful_modify_gateway(self, get_net_route):
+ ''' Test successful modify gateway '''
+ data = self.mock_args()
+ del data['gateway']
+ data['from_gateway'] = '10.193.72.1'
+ data['gateway'] = '10.193.0.1'
+ set_module_args(data)
+ current = {
+ 'destination': '176.0.0.0/24',
+ 'gateway': '10.193.72.1',
+ 'metric': 70,
+ 'vserver': 'test_server'
+ }
+ get_net_route.side_effect = [
+ None,
+ current
+ ]
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_net_route_mock_object().apply()
+ assert exc.value.args[0]['changed']
+
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_net_routes.NetAppOntapNetRoutes.get_net_route')
+ def test__modify_gateway_idempotency(self, get_net_route):
+ ''' Test modify gateway idempotency '''
+ data = self.mock_args()
+ del data['gateway']
+ data['from_gateway'] = '10.193.72.1'
+ data['gateway'] = '10.193.0.1'
+ set_module_args(data)
+ current = {
+ 'destination': '178.0.0.1/24',
+ 'gateway': '10.193.72.1',
+ 'metric': 70,
+ 'vserver': 'test_server'
+ }
+ get_net_route.side_effect = [
+ current,
+ None
+ ]
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_net_route_mock_object('net_route', current).apply()
+ assert not exc.value.args[0]['changed']
+
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_net_routes.NetAppOntapNetRoutes.get_net_route')
+ def test_successful_modify_destination(self, get_net_route):
+ ''' Test successful modify destination '''
+ data = self.mock_args()
+ del data['destination']
+ data['from_destination'] = '176.0.0.0/24'
+ data['destination'] = '178.0.0.1/24'
+ set_module_args(data)
+ current = {
+ 'destination': '176.0.0.0/24',
+ 'gateway': '10.193.72.1',
+ 'metric': 70,
+ 'vserver': 'test_server'
+ }
+ get_net_route.side_effect = [
+ None,
+ current
+ ]
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_net_route_mock_object().apply()
+ assert exc.value.args[0]['changed']
+
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_net_routes.NetAppOntapNetRoutes.get_net_route')
+ def test__modify_destination_idempotency(self, get_net_route):
+ ''' Test modify destination idempotency'''
+ data = self.mock_args()
+ del data['destination']
+ data['from_destination'] = '176.0.0.0/24'
+ data['destination'] = '178.0.0.1/24'
+ set_module_args(data)
+ current = {
+ 'destination': '178.0.0.1/24',
+ 'gateway': '10.193.72.1',
+ 'metric': 70,
+ 'vserver': 'test_server'
+ }
+ get_net_route.side_effect = [
+ current,
+ None
+ ]
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_net_route_mock_object('net_route', current).apply()
+ assert not exc.value.args[0]['changed']
+
+ @patch('ansible_collections.netapp.ontap.plugins.module_utils.netapp.OntapRestAPI.send_request')
+ def test_rest_error(self, mock_request):
+ data = self.mock_args(rest=True)
+ set_module_args(data)
+ mock_request.side_effect = [
+ SRR['is_rest'],
+ SRR['generic_error'],
+ SRR['end_of_sequence']
+ ]
+ with pytest.raises(AnsibleFailJson) as exc:
+ self.get_net_route_mock_object(cx_type='rest').apply()
+ assert exc.value.args[0]['msg'] == SRR['generic_error'][2]
+
+ @patch('ansible_collections.netapp.ontap.plugins.module_utils.netapp.OntapRestAPI.send_request')
+ def test_rest_successfully_create(self, mock_request):
+ data = self.mock_args(rest=True)
+ data['state'] = 'present'
+ set_module_args(data)
+ mock_request.side_effect = [
+ SRR['is_rest'],
+ SRR['empty_good'], # get
+ SRR['empty_good'], # post
+ SRR['end_of_sequence']
+ ]
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_net_route_mock_object(cx_type='rest').apply()
+ assert exc.value.args[0]['changed']
+
+ @patch('ansible_collections.netapp.ontap.plugins.module_utils.netapp.OntapRestAPI.send_request')
+ def test_rest_idempotent_create_dns(self, mock_request):
+ data = self.mock_args(rest=True)
+ data['state'] = 'present'
+ set_module_args(data)
+ mock_request.side_effect = [
+ SRR['is_rest'],
+ SRR['net_routes_record'], # get
+ SRR['end_of_sequence']
+ ]
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_net_route_mock_object(cx_type='rest').apply()
+ assert not exc.value.args[0]['changed']
+
+ @patch('ansible_collections.netapp.ontap.plugins.module_utils.netapp.OntapRestAPI.send_request')
+ def test_rest_successfully_destroy(self, mock_request):
+ data = self.mock_args(rest=True)
+ data['state'] = 'absent'
+ set_module_args(data)
+ mock_request.side_effect = [
+ SRR['is_rest'],
+ SRR['net_routes_record'], # get
+ SRR['empty_good'], # delete
+ SRR['end_of_sequence']
+ ]
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_net_route_mock_object(cx_type='rest').apply()
+ assert exc.value.args[0]['changed']
+
+ @patch('ansible_collections.netapp.ontap.plugins.module_utils.netapp.OntapRestAPI.send_request')
+ def test_rest_idempotently_destroy(self, mock_request):
+ data = self.mock_args(rest=True)
+ data['state'] = 'absent'
+ set_module_args(data)
+ mock_request.side_effect = [
+ SRR['is_rest'],
+ SRR['empty_good'], # get
+ SRR['end_of_sequence']
+ ]
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_net_route_mock_object(cx_type='rest').apply()
+ assert not exc.value.args[0]['changed']
+
+ @patch('ansible_collections.netapp.ontap.plugins.module_utils.netapp.OntapRestAPI.send_request')
+ def test_rest_successfully_modify(self, mock_request):
+ data = self.mock_args(modify=True)
+ data['state'] = 'present'
+ set_module_args(data)
+ mock_request.side_effect = [
+ SRR['is_rest'],
+ SRR['empty_good'], # get
+ SRR['net_routes_record'], # get
+ SRR['net_routes_record'], # get
+ SRR['empty_good'], # get
+ SRR['empty_good'], # delete
+ SRR['empty_good'], # post
+ SRR['end_of_sequence']
+ ]
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_net_route_mock_object(cx_type='rest').apply()
+ assert exc.value.args[0]['changed']
+
+ @patch('ansible_collections.netapp.ontap.plugins.module_utils.netapp.OntapRestAPI.send_request')
+ def test_rest_idempotently_modify(self, mock_request):
+ data = self.mock_args(modify=True)
+ data['state'] = 'present'
+ set_module_args(data)
+ mock_request.side_effect = [
+ SRR['is_rest'],
+ SRR['modified_record'], # get
+ SRR['modified_record'], # get
+ SRR['end_of_sequence']
+ ]
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_net_route_mock_object(cx_type='rest').apply()
+ assert not exc.value.args[0]['changed']
diff --git a/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_net_subnet.py b/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_net_subnet.py
new file mode 100644
index 00000000..80d27e1b
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_net_subnet.py
@@ -0,0 +1,265 @@
+# (c) 2018, NetApp, Inc
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+''' unit test template for ONTAP Ansible module '''
+
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+import json
+import pytest
+
+from ansible_collections.netapp.ontap.tests.unit.compat import unittest
+from ansible_collections.netapp.ontap.tests.unit.compat.mock import patch
+from ansible.module_utils import basic
+from ansible.module_utils._text import to_bytes
+import ansible_collections.netapp.ontap.plugins.module_utils.netapp as netapp_utils
+
+from ansible_collections.netapp.ontap.plugins.modules.na_ontap_net_subnet \
+ import NetAppOntapSubnet as my_module # module under test
+
+if not netapp_utils.has_netapp_lib():
+ pytestmark = pytest.mark.skip('skipping as missing required netapp_lib')
+
+
+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 MockONTAPConnection(object):
+ ''' mock server connection to ONTAP host '''
+
+ def __init__(self, kind=None, data=None):
+ ''' save arguments '''
+ self.type = kind
+ self.params = data
+ self.xml_in = None
+ self.xml_out = None
+
+ def invoke_successfully(self, xml, enable_tunneling): # pylint: disable=unused-argument
+ ''' mock invoke_successfully returning xml data '''
+ self.xml_in = xml
+ if xml.get_child_by_name('query') is not None and \
+ xml.get_child_by_name('query').get_child_by_name('vserver-info') is not None:
+ # assume this a a cserver request
+ xml = self.build_cserver_info()
+ elif self.type == 'subnet':
+ if xml.get_child_by_name('query'):
+ name_obj = xml.get_child_by_name('query').get_child_by_name('net-subnet-info').get_child_by_name('subnet-name')
+ xml_name = name_obj.get_content()
+ if xml_name == self.params.get('name'):
+ xml = self.build_subnet_info(self.params)
+ elif self.type == 'subnet_fail':
+ raise netapp_utils.zapi.NaApiError(code='TEST', message="This exception is from the unit test")
+ self.xml_out = xml
+ return xml
+
+ @staticmethod
+ def build_cserver_info():
+ ''' build xml data for vserver-info '''
+ xml = netapp_utils.zapi.NaElement('xml')
+ attributes = {
+ 'num-records': 1,
+ 'attributes-list': {
+ 'vserver-info': {
+ 'vserver-name': 'cserver',
+ }
+ }
+ }
+ xml.translate_struct(attributes)
+ return xml
+
+ @staticmethod
+ def build_subnet_info(data):
+ ''' build xml data for subnet-info '''
+ xml = netapp_utils.zapi.NaElement('xml')
+ ip_ranges = []
+ for elem in data['ip_ranges']:
+ ip_ranges.append({'ip-range': elem})
+ attributes = {
+ 'num-records': 1,
+ 'attributes-list': {
+ 'net-subnet-info': {
+ 'broadcast-domain': data['broadcast_domain'],
+ 'gateway': data['gateway'],
+ 'ip-ranges': ip_ranges,
+ 'ipspace': data['ipspace'],
+ 'subnet': data['subnet'],
+ 'subnet-name': data['name'],
+ }
+ }
+ }
+ xml.translate_struct(attributes)
+ return xml
+
+
+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)
+ self.server = MockONTAPConnection()
+
+ def set_default_args(self):
+ return dict({
+ 'name': 'test_subnet',
+ 'hostname': 'hostname',
+ 'username': 'username',
+ 'password': 'password',
+ 'broadcast_domain': 'Default',
+ 'gateway': '10.0.0.1',
+ 'ipspace': 'Default',
+ 'subnet': '10.0.0.0/24',
+ 'ip_ranges': ['10.0.0.10-10.0.0.20', '10.0.0.30']
+ })
+
+ def test_module_fail_when_required_args_missing(self):
+ ''' required arguments are reported as errors '''
+ with pytest.raises(AnsibleFailJson) as exc:
+ set_module_args({})
+ my_module()
+ print('Info: %s' % exc.value.args[0]['msg'])
+
+ def test_ensure_get_called(self):
+ ''' test get_subnet for non-existent subnet'''
+ set_module_args(self.set_default_args())
+ my_obj = my_module()
+ my_obj.server = self.server
+ assert my_obj.get_subnet() is None
+
+ def test_ensure_get_called_existing(self):
+ ''' test get_subnet for existing subnet'''
+ data = self.set_default_args()
+ set_module_args(data)
+ my_obj = my_module()
+ my_obj.server = MockONTAPConnection(kind='subnet', data=data)
+ assert my_obj.get_subnet() is not None
+
+ @patch('ansible_collections.netapp.ontap.plugins.module_utils.netapp.ems_log_event')
+ def test_fail_broadcast_domain_modify(self, mock_ems_log):
+ ''' test that boradcast_domain is not alterable '''
+ data = self.set_default_args()
+ data.update({'broadcast_domain': 'Test'})
+ set_module_args(data)
+ my_obj = my_module()
+ my_obj.server = MockONTAPConnection(kind='subnet', data=self.set_default_args())
+ with pytest.raises(AnsibleFailJson) as exc:
+ my_obj.apply()
+ assert 'cannot modify broadcast_domain parameter' in exc.value.args[0]['msg']
+
+ @patch('ansible_collections.netapp.ontap.plugins.module_utils.netapp.ems_log_event')
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_net_subnet.NetAppOntapSubnet.create_subnet')
+ def test_successful_create(self, create_subnet, mock_ems_log):
+ ''' creating subnet and testing idempotency '''
+ print("Create:")
+ data = self.set_default_args()
+ set_module_args(data)
+ my_obj = my_module()
+ my_obj.server = self.server
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ assert exc.value.args[0]['changed']
+ create_subnet.assert_called_with()
+
+ # to reset na_helper from remembering the previous 'changed' value
+ print("reset:")
+ data = self.set_default_args()
+ set_module_args(data)
+ my_obj = my_module()
+ my_obj.server = MockONTAPConnection(kind='subnet', data=data)
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ assert not exc.value.args[0]['changed']
+
+ @patch('ansible_collections.netapp.ontap.plugins.module_utils.netapp.ems_log_event')
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_net_subnet.NetAppOntapSubnet.rename_subnet')
+ def test_successful_rename(self, rename_subnet, mock_ems_log):
+ ''' renaming subnet '''
+ data = self.set_default_args()
+ data.update({'from_name': data['name'], 'name': 'new_test_subnet'})
+ set_module_args(data)
+ my_obj = my_module()
+ my_obj.server = MockONTAPConnection(kind='subnet', data=self.set_default_args())
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ assert exc.value.args[0]['changed']
+
+ @patch('ansible_collections.netapp.ontap.plugins.module_utils.netapp.ems_log_event')
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_net_subnet.NetAppOntapSubnet.delete_subnet')
+ def test_successful_delete(self, delete_subnet, mock_ems_log):
+ ''' deleting subnet and testing idempotency '''
+ data = self.set_default_args()
+ data['state'] = 'absent'
+ set_module_args(data)
+ my_obj = my_module()
+ my_obj.server = MockONTAPConnection(kind='subnet', data=data)
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ assert exc.value.args[0]['changed']
+ delete_subnet.assert_called_with()
+
+ # to reset na_helper from remembering the previous 'changed' value
+ my_obj = my_module()
+ my_obj.server = self.server
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ assert not exc.value.args[0]['changed']
+
+ @patch('ansible_collections.netapp.ontap.plugins.module_utils.netapp.ems_log_event')
+ def test_successful_modify(self, mock_ems_log):
+ ''' modifying subnet and testing idempotency '''
+ data = self.set_default_args()
+ data.update({'ip_ranges': ['10.0.0.10-10.0.0.25', '10.0.0.30']})
+ set_module_args(data)
+ my_obj = my_module()
+ my_obj.server = MockONTAPConnection(kind='subnet', data=self.set_default_args())
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ assert exc.value.args[0]['changed']
+
+ @patch('ansible_collections.netapp.ontap.plugins.module_utils.netapp.ems_log_event')
+ def test_if_all_methods_catch_exception(self, mock_ems_log):
+ data = self.set_default_args()
+ set_module_args(data)
+ my_obj = my_module()
+ my_obj.server = MockONTAPConnection(kind='subnet_fail', data=data)
+ with pytest.raises(AnsibleFailJson) as exc:
+ my_obj.create_subnet()
+ assert 'Error creating subnet' in exc.value.args[0]['msg']
+ with pytest.raises(AnsibleFailJson) as exc:
+ my_obj.delete_subnet()
+ assert 'Error deleting subnet' in exc.value.args[0]['msg']
+ with pytest.raises(AnsibleFailJson) as exc:
+ my_obj.modify_subnet()
+ assert 'Error modifying subnet' in exc.value.args[0]['msg']
+ with pytest.raises(AnsibleFailJson) as exc:
+ my_obj.rename_subnet()
+ assert 'Error renaming subnet' in exc.value.args[0]['msg']
diff --git a/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_nfs.py b/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_nfs.py
new file mode 100644
index 00000000..c6cd5ed8
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_nfs.py
@@ -0,0 +1,309 @@
+# (c) 2018, NetApp, Inc
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+''' unit test template for ONTAP Ansible module '''
+
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+import json
+import pytest
+
+from ansible_collections.netapp.ontap.tests.unit.compat import unittest
+from ansible_collections.netapp.ontap.tests.unit.compat.mock import patch, Mock
+from ansible.module_utils import basic
+from ansible.module_utils._text import to_bytes
+import ansible_collections.netapp.ontap.plugins.module_utils.netapp as netapp_utils
+
+from ansible_collections.netapp.ontap.plugins.modules.na_ontap_nfs \
+ import NetAppONTAPNFS as nfs_module # module under test
+
+if not netapp_utils.has_netapp_lib():
+ pytestmark = pytest.mark.skip('skipping as missing required netapp_lib')
+
+
+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 MockONTAPConnection(object):
+ ''' mock server connection to ONTAP host '''
+
+ def __init__(self, kind=None, data=None, job_error=None):
+ ''' save arguments '''
+ self.kind = kind
+ self.params = data
+ self.xml_in = None
+ self.xml_out = None
+
+ def invoke_successfully(self, xml, enable_tunneling): # pylint: disable=unused-argument
+ ''' mock invoke_successfully returning xml data '''
+ self.xml_in = xml
+ if self.kind == 'nfs':
+ xml = self.build_nfs_info(self.params)
+ self.xml_out = xml
+ if self.kind == 'nfs_status':
+ xml = self.build_nfs_status_info(self.params)
+ return xml
+
+ @staticmethod
+ def build_nfs_info(nfs_details):
+ ''' build xml data for volume-attributes '''
+ xml = netapp_utils.zapi.NaElement('xml')
+ attributes = {
+ "attributes-list": {
+ "nfs-info": {
+ "auth-sys-extended-groups": "false",
+ "cached-cred-harvest-timeout": "86400000",
+ "cached-cred-negative-ttl": "7200000",
+ "cached-cred-positive-ttl": "86400000",
+ "cached-transient-err-ttl": "30000",
+ "chown-mode": "use_export_policy",
+ "enable-ejukebox": "true",
+ "extended-groups-limit": "32",
+ "file-session-io-grouping-count": "5000",
+ "file-session-io-grouping-duration": "120",
+ "ignore-nt-acl-for-root": "false",
+ "is-checksum-enabled-for-replay-cache": "true",
+ "is-mount-rootonly-enabled": "true",
+ "is-netgroup-dns-domain-search": "true",
+ "is-nfs-access-enabled": "false",
+ "is-nfs-rootonly-enabled": "false",
+ "is-nfsv2-enabled": "false",
+ "is-nfsv3-64bit-identifiers-enabled": "false",
+ "is-nfsv3-connection-drop-enabled": "true",
+ "is-nfsv3-enabled": "true",
+ "is-nfsv3-fsid-change-enabled": "true",
+ "is-nfsv4-fsid-change-enabled": "true",
+ "is-nfsv4-numeric-ids-enabled": "true",
+ "is-nfsv40-acl-enabled": "false",
+ "is-nfsv40-enabled": "true",
+ "is-nfsv40-migration-enabled": "false",
+ "is-nfsv40-read-delegation-enabled": "false",
+ "is-nfsv40-referrals-enabled": "false",
+ "is-nfsv40-req-open-confirm-enabled": "false",
+ "is-nfsv40-write-delegation-enabled": "false",
+ "is-nfsv41-acl-enabled": "false",
+ "is-nfsv41-acl-preserve-enabled": "true",
+ "is-nfsv41-enabled": "true",
+ "is-nfsv41-migration-enabled": "false",
+ "is-nfsv41-pnfs-enabled": "true",
+ "is-nfsv41-read-delegation-enabled": "false",
+ "is-nfsv41-referrals-enabled": "false",
+ "is-nfsv41-state-protection-enabled": "true",
+ "is-nfsv41-write-delegation-enabled": "false",
+ "is-qtree-export-enabled": "false",
+ "is-rquota-enabled": "false",
+ "is-tcp-enabled": "false",
+ "is-udp-enabled": "false",
+ "is-v3-ms-dos-client-enabled": "false",
+ "is-validate-qtree-export-enabled": "true",
+ "is-vstorage-enabled": "false",
+ "map-unknown-uid-to-default-windows-user": "true",
+ "mountd-port": "635",
+ "name-service-lookup-protocol": "udp",
+ "netgroup-trust-any-ns-switch-no-match": "false",
+ "nfsv4-acl-max-aces": "400",
+ "nfsv4-grace-seconds": "45",
+ "nfsv4-id-domain": "defaultv4iddomain.com",
+ "nfsv4-lease-seconds": "30",
+ "nfsv41-implementation-id-domain": "netapp.com",
+ "nfsv41-implementation-id-name": "NetApp Release Kalyaniblack__9.4.0",
+ "nfsv41-implementation-id-time": "1541070767",
+ "nfsv4x-session-num-slots": "180",
+ "nfsv4x-session-slot-reply-cache-size": "640",
+ "nlm-port": "4045",
+ "nsm-port": "4046",
+ "ntacl-display-permissive-perms": "false",
+ "ntfs-unix-security-ops": "use_export_policy",
+ "permitted-enc-types": {
+ "string": ["des", "des3", "aes_128", "aes_256"]
+ },
+ "rpcsec-ctx-high": "0",
+ "rpcsec-ctx-idle": "0",
+ "rquotad-port": "4049",
+ "showmount": "true",
+ "showmount-timestamp": "1548372452",
+ "skip-root-owner-write-perm-check": "false",
+ "tcp-max-xfer-size": "1048576",
+ "udp-max-xfer-size": "32768",
+ "v3-search-unconverted-filename": "false",
+ "v4-inherited-acl-preserve": "false",
+ "vserver": "ansible"
+ }
+ },
+ "num-records": "1"
+ }
+ xml.translate_struct(attributes)
+ return xml
+
+ @staticmethod
+ def build_nfs_status_info(nfs_status_details):
+ ''' build xml data for volume-attributes '''
+ xml = netapp_utils.zapi.NaElement('xml')
+ attributes = {
+ 'is-enabled': "true"
+ }
+ xml.translate_struct(attributes)
+ return xml
+
+
+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)
+ self.mock_nfs_group = {
+ 'vserver': 'nfs_vserver',
+ }
+
+ def mock_args(self):
+ return {
+ 'vserver': self.mock_nfs_group['vserver'],
+ 'hostname': 'test',
+ 'username': 'test_user',
+ 'password': 'test_pass!',
+ 'https': 'False'
+ }
+
+ def get_nfs_mock_object(self, kind=None):
+ """
+ Helper method to return an na_ontap_volume object
+ :param kind: passes this param to MockONTAPConnection()
+ :return: na_ontap_volume object
+ """
+ nfsy_obj = nfs_module()
+ nfsy_obj.asup_log_for_cserver = Mock(return_value=None)
+ nfsy_obj.cluster = Mock()
+ nfsy_obj.cluster.invoke_successfully = Mock()
+ if kind is None:
+ nfsy_obj.server = MockONTAPConnection()
+ else:
+ nfsy_obj.server = MockONTAPConnection(kind=kind, data=self.mock_nfs_group)
+ return nfsy_obj
+
+ def test_module_fail_when_required_args_missing(self):
+ ''' required arguments are reported as errors '''
+ with pytest.raises(AnsibleFailJson) as exc:
+ set_module_args({})
+ nfs_module()
+ print('Info: %s' % exc.value.args[0]['msg'])
+
+ def test_get_nonexistent_nfs(self):
+ ''' Test if get_nfs_service returns None for non-existent nfs '''
+ set_module_args(self.mock_args())
+ result = self.get_nfs_mock_object().get_nfs_service()
+ assert result is None
+
+ def test_get_existing_nfs(self):
+ ''' Test if get_policy_group returns details for existing nfs '''
+ set_module_args(self.mock_args())
+ result = self.get_nfs_mock_object('nfs').get_nfs_service()
+ assert result['is_nfsv3_enabled']
+
+ def test_get_nonexistent_nfs_status(self):
+ ''' Test if get__nfs_status returns None for non-existent nfs '''
+ set_module_args(self.mock_args())
+ result = self.get_nfs_mock_object().get_nfs_status()
+ assert result is None
+
+ def test_get_existing_nfs_status(self):
+ ''' Test if get__nfs_status returns details for nfs '''
+ set_module_args(self.mock_args())
+ result = self.get_nfs_mock_object('nfs_status').get_nfs_status()
+ assert result
+
+ def test_modify_nfs(self):
+ ''' Test if modify_nfs runs for existing nfs '''
+ data = self.mock_args()
+ data['nfsv3'] = 'enabled'
+ data['nfsv3_fsid_change'] = 'enabled'
+ data['nfsv4'] = 'enabled'
+ data['nfsv41'] = 'enabled'
+ data['vstorage_state'] = 'enabled'
+ data['tcp'] = 'enabled'
+ data['udp'] = 'enabled'
+ data['nfsv4_id_domain'] = 'nfsv4_id_domain'
+ data['nfsv40_acl'] = 'enabled'
+ data['nfsv40_read_delegation'] = 'enabled'
+ data['nfsv40_write_delegation'] = 'enabled'
+ data['nfsv41_acl'] = 'enabled'
+ data['nfsv41_read_delegation'] = 'enabled'
+ data['nfsv41_write_delegation'] = 'enabled'
+ data['showmount'] = 'enabled'
+ data['tcp_max_xfer_size'] = '1048576'
+ set_module_args(data)
+ self.get_nfs_mock_object('nfs_status').modify_nfs()
+
+ def test_successfully_modify_nfs(self):
+ ''' Test modify nfs successful for modifying tcp max xfer size. '''
+ data = self.mock_args()
+ data['tcp_max_xfer_size'] = '8192'
+ set_module_args(data)
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_nfs_mock_object('nfs').apply()
+ assert exc.value.args[0]['changed']
+
+ def test_modify_nfs_idempotency(self):
+ ''' Test modify nfs idempotency '''
+ data = self.mock_args()
+ data['tcp_max_xfer_size'] = '1048576'
+ set_module_args(data)
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_nfs_mock_object('nfs').apply()
+ assert not exc.value.args[0]['changed']
+
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_nfs.NetAppONTAPNFS.delete_nfs')
+ def test_successfully_delete_nfs(self, delete_nfs):
+ ''' Test successfully delete nfs '''
+ data = self.mock_args()
+ data['state'] = 'absent'
+ set_module_args(data)
+ obj = self.get_nfs_mock_object('nfs')
+ with pytest.raises(AnsibleExitJson) as exc:
+ obj.apply()
+ assert exc.value.args[0]['changed']
+ delete_nfs.assert_called_with()
+
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_nfs.NetAppONTAPNFS.get_nfs_service')
+ def test_successfully_enable_nfs(self, get_nfs_service):
+ ''' Test successfully enable nfs on non-existent nfs '''
+ data = self.mock_args()
+ data['state'] = 'present'
+ set_module_args(data)
+ get_nfs_service.side_effect = [
+ None,
+ {}
+ ]
+ obj = self.get_nfs_mock_object('nfs')
+ with pytest.raises(AnsibleExitJson) as exc:
+ obj.apply()
+ assert exc.value.args[0]['changed']
diff --git a/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_ntfs_dacl.py b/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_ntfs_dacl.py
new file mode 100644
index 00000000..405f1e43
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_ntfs_dacl.py
@@ -0,0 +1,268 @@
+# (c) 2020, NetApp, Inc
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+''' unit tests for Ansible module: na_ontap_ntfs_dacl'''
+
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+import json
+import pytest
+
+from ansible_collections.netapp.ontap.tests.unit.compat import unittest
+from ansible_collections.netapp.ontap.tests.unit.compat.mock import patch, Mock
+from ansible.module_utils import basic
+from ansible.module_utils._text import to_bytes
+import ansible_collections.netapp.ontap.plugins.module_utils.netapp as netapp_utils
+
+from ansible_collections.netapp.ontap.plugins.modules.na_ontap_ntfs_dacl \
+ import NetAppOntapNtfsDacl as dacl_module # module under test
+
+if not netapp_utils.has_netapp_lib():
+ pytestmark = pytest.mark.skip('skipping as missing required netapp_lib')
+HAS_NETAPP_ZAPI_MSG = "pip install netapp_lib is required"
+
+
+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 MockONTAPConnection(object):
+ ''' mock server connection to ONTAP host '''
+
+ def __init__(self, kind=None, data=None):
+ ''' save arguments '''
+ self.kind = kind
+ self.params = data
+ self.xml_in = None
+ self.xml_out = None
+
+ def invoke_successfully(self, xml, enable_tunneling): # pylint: disable=unused-argument
+ ''' mock invoke_successfully returning xml data '''
+ self.xml_in = xml
+ request = xml.to_string().decode('utf-8')
+ if self.kind == 'error':
+ raise netapp_utils.zapi.NaApiError('test', 'expect error')
+ elif request.startswith("<ems-autosupport-log>"):
+ xml = None # or something that may the logger happy, and you don't need @patch anymore
+ # or
+ # xml = build_ems_log_response()
+ elif request.startswith("<file-directory-security-ntfs-dacl-get-iter>"):
+ if self.kind == 'create':
+ xml = self.build_dacl_info()
+ else:
+ xml = self.build_dacl_info(self.params)
+ elif request.startswith("<file-directory-security-ntfs-dacl-modify>"):
+ xml = self.build_dacl_info(self.params)
+ self.xml_out = xml
+ return xml
+
+ @staticmethod
+ def build_dacl_info(data=None):
+ xml = netapp_utils.zapi.NaElement('xml')
+ vserver = 'vserver'
+ attributes = {'num-records': '0',
+ 'attributes-list': {'file-directory-security-ntfs-dacl': {'vserver': vserver}}}
+
+ if data is not None:
+ attributes['num-records'] = '1'
+ if data.get('access_type'):
+ attributes['attributes-list']['file-directory-security-ntfs-dacl']['access-type'] = data['access_type']
+ if data.get('account'):
+ attributes['attributes-list']['file-directory-security-ntfs-dacl']['account'] = data['account']
+ if data.get('rights'):
+ attributes['attributes-list']['file-directory-security-ntfs-dacl']['rights'] = data['rights']
+ if data.get('advanced_rights'):
+ attributes['attributes-list']['file-directory-security-ntfs-dacl']['advanced-rights'] = data['advanced_rights']
+ if data.get('apply_to'):
+ tmp = []
+ for target in data['apply_to']:
+ tmp.append({'inheritance-level': target})
+ attributes['attributes-list']['file-directory-security-ntfs-dacl']['apply-to'] = tmp
+ if data.get('security_descriptor'):
+ attributes['attributes-list']['file-directory-security-ntfs-dacl']['ntfs-sd'] = data['security_descriptor']
+ xml.translate_struct(attributes)
+ return xml
+
+
+class TestMyModule(unittest.TestCase):
+ ''' Unit tests for na_ontap_ntfs_dacl '''
+
+ 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 mock_args(self):
+ return {
+ 'vserver': 'vserver',
+ 'hostname': 'test',
+ 'username': 'test_user',
+ 'password': 'test_pass!'
+ }
+
+ def get_dacl_mock_object(self, type='zapi', kind=None, status=None):
+ dacl_obj = dacl_module()
+ dacl_obj.autosupport_log = Mock(return_value=None)
+ if type == 'zapi':
+ if kind is None:
+ dacl_obj.server = MockONTAPConnection()
+ else:
+ dacl_obj.server = MockONTAPConnection(kind=kind, data=status)
+ return dacl_obj
+
+ def test_module_fail_when_required_args_missing(self):
+ ''' required arguments are reported as errors '''
+ with pytest.raises(AnsibleFailJson) as exc:
+ set_module_args({})
+ dacl_module()
+ print('Info: %s' % exc.value.args[0]['msg'])
+
+ def test_get_dacl_error(self):
+ data = self.mock_args()
+ data['access_type'] = 'allow'
+ data['account'] = 'acc_test'
+ data['rights'] = 'full_control'
+ data['security_descriptor'] = 'sd_test'
+ data['apply_to'] = 'this_folder,files'
+ set_module_args(data)
+ with pytest.raises(AnsibleFailJson) as exc:
+ self.get_dacl_mock_object('zapi', 'error', data).apply()
+ msg = 'Error fetching allow DACL for account acc_test for security descriptor sd_test: NetApp API failed. Reason - test:expect error'
+ assert exc.value.args[0]['msg'] == msg
+
+ def test_successfully_create_dacl(self):
+ data = self.mock_args()
+ data['access_type'] = 'allow'
+ data['account'] = 'acc_test'
+ data['rights'] = 'full_control'
+ data['security_descriptor'] = 'sd_test'
+ data['apply_to'] = 'this_folder,files'
+ set_module_args(data)
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_dacl_mock_object('zapi', 'create', data).apply()
+ assert exc.value.args[0]['changed']
+
+ def test_create_dacl_idempotency(self):
+ data = self.mock_args()
+ data['access_type'] = 'allow'
+ data['account'] = 'acc_test'
+ data['rights'] = 'full_control'
+ data['security_descriptor'] = 'sd_test'
+ data['apply_to'] = ['this_folder', 'files']
+ set_module_args(data)
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_dacl_mock_object('zapi', 'create_idempotency', data).apply()
+ assert not exc.value.args[0]['changed']
+
+ def test_successfully_modify_dacl(self):
+ data = self.mock_args()
+ data['access_type'] = 'allow'
+ data['account'] = 'acc_test'
+ data['rights'] = 'full_control'
+ data['security_descriptor'] = 'sd_test'
+ data['apply_to'] = ['this_folder', 'files']
+ set_module_args(data)
+ data['advanced_rights'] = 'read_data,write_data'
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_dacl_mock_object('zapi', 'create', data).apply()
+ assert exc.value.args[0]['changed']
+
+ def test_modify_dacl_idempotency(self):
+ data = self.mock_args()
+ data['access_type'] = 'allow'
+ data['account'] = 'acc_test'
+ data['rights'] = 'full_control'
+ data['security_descriptor'] = 'sd_test'
+ data['apply_to'] = ['this_folder', 'files']
+ set_module_args(data)
+ data['rights'] = 'full_control'
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_dacl_mock_object('zapi', 'modify_idempotency', data).apply()
+ assert not exc.value.args[0]['changed']
+
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_ntfs_dacl.NetAppOntapNtfsDacl.get_dacl')
+ def test_modify_error(self, get_info):
+ data = self.mock_args()
+ data['access_type'] = 'allow'
+ data['account'] = 'acc_test'
+ data['rights'] = 'full_control'
+ data['security_descriptor'] = 'sd_test'
+ set_module_args(data)
+ get_info.side_effect = [
+ {
+ 'access_type': 'allow',
+ 'account': 'acc_test',
+ 'security_descriptor': 'sd_test',
+ 'rights': 'modify',
+ 'apply_to': ['this_folder', 'files']
+ }
+ ]
+ with pytest.raises(AnsibleFailJson) as exc:
+ self.get_dacl_mock_object('zapi', 'error', data).apply()
+ msg = 'Error modifying allow DACL for account acc_test for security descriptor sd_test: NetApp API failed. Reason - test:expect error'
+ assert exc.value.args[0]['msg'] == msg
+
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_ntfs_dacl.NetAppOntapNtfsDacl.get_dacl')
+ def test_create_error(self, get_info):
+ data = self.mock_args()
+ data['access_type'] = 'allow'
+ data['account'] = 'acc_test'
+ data['rights'] = 'full_control'
+ data['security_descriptor'] = 'sd_test'
+ set_module_args(data)
+ get_info.side_effect = [
+ None
+ ]
+ with pytest.raises(AnsibleFailJson) as exc:
+ self.get_dacl_mock_object('zapi', 'error', data).apply()
+ msg = 'Error adding allow DACL for account acc_test for security descriptor sd_test: NetApp API failed. Reason - test:expect error'
+ assert exc.value.args[0]['msg'] == msg
+
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_ntfs_dacl.NetAppOntapNtfsDacl.get_dacl')
+ def test_delete_error(self, get_info):
+ data = self.mock_args()
+ data['access_type'] = 'allow'
+ data['account'] = 'acc_test'
+ data['rights'] = 'full_control'
+ data['security_descriptor'] = 'sd_test'
+ data['state'] = 'absent'
+ set_module_args(data)
+ get_info.side_effect = [
+ {
+ 'access_type': 'allow',
+ 'account': 'acc_test',
+ 'security_descriptor': 'sd_test',
+ 'rights': 'modify'
+ }
+ ]
+ with pytest.raises(AnsibleFailJson) as exc:
+ self.get_dacl_mock_object('zapi', 'error', data).apply()
+ msg = 'Error deleting allow DACL for account acc_test for security descriptor sd_test: NetApp API failed. Reason - test:expect error'
+ assert exc.value.args[0]['msg'] == msg
diff --git a/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_ntfs_sd.py b/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_ntfs_sd.py
new file mode 100644
index 00000000..f82e3536
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_ntfs_sd.py
@@ -0,0 +1,225 @@
+# (c) 2020, NetApp, Inc
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+''' unit tests for Ansible module: na_ontap_ntfs_sd'''
+
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+import json
+import pytest
+
+from ansible_collections.netapp.ontap.tests.unit.compat import unittest
+from ansible_collections.netapp.ontap.tests.unit.compat.mock import patch, Mock
+from ansible.module_utils import basic
+from ansible.module_utils._text import to_bytes
+import ansible_collections.netapp.ontap.plugins.module_utils.netapp as netapp_utils
+
+from ansible_collections.netapp.ontap.plugins.modules.na_ontap_ntfs_sd \
+ import NetAppOntapNtfsSd as sd_module # module under test
+
+if not netapp_utils.has_netapp_lib():
+ pytestmark = pytest.mark.skip('skipping as missing required netapp_lib')
+
+
+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 MockONTAPConnection(object):
+ ''' mock server connection to ONTAP host '''
+
+ def __init__(self, kind=None, data=None):
+ ''' save arguments '''
+ self.kind = kind
+ self.params = data
+ self.xml_in = None
+ self.xml_out = None
+
+ def invoke_successfully(self, xml, enable_tunneling): # pylint: disable=unused-argument
+ ''' mock invoke_successfully returning xml data '''
+ self.xml_in = xml
+ request = xml.to_string().decode('utf-8')
+ if self.kind == 'error':
+ raise netapp_utils.zapi.NaApiError('test', 'expect error')
+ elif request.startswith("<ems-autosupport-log>"):
+ xml = None # or something that may the logger happy, and you don't need @patch anymore
+ # or
+ # xml = build_ems_log_response()
+ elif request.startswith("<file-directory-security-ntfs-get-iter>"):
+ if self.kind == 'create':
+ xml = self.build_sd_info()
+ else:
+ xml = self.build_sd_info(self.params)
+ elif request.startswith("<file-directory-security-ntfs-modify>"):
+ xml = self.build_sd_info(self.params)
+ self.xml_out = xml
+ return xml
+
+ @staticmethod
+ def build_sd_info(data=None):
+ xml = netapp_utils.zapi.NaElement('xml')
+ vserver = 'vserver'
+ attributes = {'num-records': 1,
+ 'attributes-list': {'file-directory-security-ntfs': {'vserver': vserver}}}
+ if data is not None:
+ if data.get('name'):
+ attributes['attributes-list']['file-directory-security-ntfs']['ntfs-sd'] = data['name']
+ if data.get('owner'):
+ attributes['attributes-list']['file-directory-security-ntfs']['owner'] = data['owner']
+ if data.get('group'):
+ attributes['attributes-list']['file-directory-security-ntfs']['group'] = data['group']
+ if data.get('control_flags_raw'):
+ attributes['attributes-list']['file-directory-security-ntfs']['control-flags-raw'] = str(data['control_flags_raw'])
+ xml.translate_struct(attributes)
+ return xml
+
+
+class TestMyModule(unittest.TestCase):
+ ''' Unit tests for na_ontap_ntfs_sd '''
+
+ 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 mock_args(self):
+ return {
+ 'vserver': 'vserver',
+ 'hostname': 'test',
+ 'username': 'test_user',
+ 'password': 'test_pass!'
+ }
+
+ def get_sd_mock_object(self, type='zapi', kind=None, status=None):
+ sd_obj = sd_module()
+ netapp_utils.ems_log_event = Mock(return_value=None)
+ if type == 'zapi':
+ if kind is None:
+ sd_obj.server = MockONTAPConnection()
+ else:
+ sd_obj.server = MockONTAPConnection(kind=kind, data=status)
+ return sd_obj
+
+ def test_module_fail_when_required_args_missing(self):
+ ''' required arguments are reported as errors '''
+ with pytest.raises(AnsibleFailJson) as exc:
+ set_module_args({})
+ sd_module()
+ print('Info: %s' % exc.value.args[0]['msg'])
+
+ def test_successfully_create_sd(self):
+ data = self.mock_args()
+ data['name'] = 'sd_test'
+ data['owner'] = 'user_test'
+ set_module_args(data)
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_sd_mock_object('zapi', 'create', data).apply()
+ assert exc.value.args[0]['changed']
+
+ def test_create_sd_idempotency(self):
+ data = self.mock_args()
+ data['name'] = 'sd_test'
+ data['owner'] = 'user_test'
+ set_module_args(data)
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_sd_mock_object('zapi', 'create_idempotency', data).apply()
+ assert not exc.value.args[0]['changed']
+
+ def test_successfully_modify_sd(self):
+ data = self.mock_args()
+ data['name'] = 'sd_test'
+ data['owner'] = 'user_test'
+ data['control_flags_raw'] = 1
+ set_module_args(data)
+ data['control_flags_raw'] = 2
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_sd_mock_object('zapi', 'create', data).apply()
+ assert exc.value.args[0]['changed']
+
+ def test_modify_sd_idempotency(self):
+ data = self.mock_args()
+ data['name'] = 'sd_test'
+ data['owner'] = 'user_test'
+ data['control_flags_raw'] = 2
+ set_module_args(data)
+ data['control_flags_raw'] = 2
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_sd_mock_object('zapi', 'modify_idempotency', data).apply()
+ assert not exc.value.args[0]['changed']
+
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_ntfs_sd.NetAppOntapNtfsSd.get_ntfs_sd')
+ def test_modify_error(self, get_info):
+ data = self.mock_args()
+ data['name'] = 'sd_test'
+ data['owner'] = 'user_test'
+ data['control_flags_raw'] = 2
+ set_module_args(data)
+ get_info.side_effect = [
+ {
+ 'name': 'sd_test',
+ 'control_flags_raw': 1
+ }
+ ]
+ with pytest.raises(AnsibleFailJson) as exc:
+ self.get_sd_mock_object('zapi', 'error', data).apply()
+ print(exc)
+ assert exc.value.args[0]['msg'] == 'Error modifying NTFS security descriptor sd_test: NetApp API failed. Reason - test:expect error'
+
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_ntfs_sd.NetAppOntapNtfsSd.get_ntfs_sd')
+ def test_create_error(self, get_info):
+ data = self.mock_args()
+ data['name'] = 'sd_test'
+ data['owner'] = 'user_test'
+ set_module_args(data)
+ get_info.side_effect = [
+ None
+ ]
+ with pytest.raises(AnsibleFailJson) as exc:
+ self.get_sd_mock_object('zapi', 'error', data).apply()
+ print(exc)
+ assert exc.value.args[0]['msg'] == 'Error creating NTFS security descriptor sd_test: NetApp API failed. Reason - test:expect error'
+
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_ntfs_sd.NetAppOntapNtfsSd.get_ntfs_sd')
+ def test_delete_error(self, get_info):
+ data = self.mock_args()
+ data['name'] = 'sd_test'
+ data['owner'] = 'user_test'
+ data['state'] = 'absent'
+ set_module_args(data)
+ get_info.side_effect = [
+ {
+ 'name': 'sd_test',
+ 'owner': 'user_test'
+ }
+ ]
+ with pytest.raises(AnsibleFailJson) as exc:
+ self.get_sd_mock_object('zapi', 'error', data).apply()
+ print(exc)
+ assert exc.value.args[0]['msg'] == 'Error deleting NTFS security descriptor sd_test: NetApp API failed. Reason - test:expect error'
diff --git a/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_nvme.py b/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_nvme.py
new file mode 100644
index 00000000..647a82ca
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_nvme.py
@@ -0,0 +1,217 @@
+# (c) 2018, NetApp, Inc
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+''' unit tests ONTAP Ansible module: na_ontap_nvme'''
+
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+import json
+import pytest
+
+from ansible_collections.netapp.ontap.tests.unit.compat import unittest
+from ansible_collections.netapp.ontap.tests.unit.compat.mock import patch
+from ansible.module_utils import basic
+from ansible.module_utils._text import to_bytes
+import ansible_collections.netapp.ontap.plugins.module_utils.netapp as netapp_utils
+
+from ansible_collections.netapp.ontap.plugins.modules.na_ontap_nvme \
+ import NetAppONTAPNVMe as my_module
+
+if not netapp_utils.has_netapp_lib():
+ pytestmark = pytest.mark.skip('skipping as missing required netapp_lib')
+
+
+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 MockONTAPConnection(object):
+ ''' mock server connection to ONTAP host '''
+
+ def __init__(self, kind=None):
+ ''' save arguments '''
+ self.type = kind
+ self.xml_in = None
+ self.xml_out = None
+
+ def invoke_successfully(self, xml, enable_tunneling): # pylint: disable=unused-argument
+ ''' mock invoke_successfully returning xml data '''
+ self.xml_in = xml
+ if self.type == 'nvme':
+ xml = self.build_nvme_info()
+ elif self.type == 'nvme_fail':
+ raise netapp_utils.zapi.NaApiError(code='TEST', message="This exception is from the unit test")
+ self.xml_out = xml
+ return xml
+
+ @staticmethod
+ def build_nvme_info():
+ ''' build xml data for nvme-info '''
+ xml = netapp_utils.zapi.NaElement('xml')
+ data = {'num-records': 1,
+ 'attributes-list': [{'nvme-target-service-info': {'is-available': 'true'}}]}
+ xml.translate_struct(data)
+ return xml
+
+
+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)
+ self.server = MockONTAPConnection()
+ self.onbox = False
+
+ def set_default_args(self):
+ if self.onbox:
+ hostname = '10.193.75.3'
+ username = 'admin'
+ password = 'netapp1!'
+ vserver = 'ansible'
+ status_admin = True
+ else:
+ hostname = 'hostname'
+ username = 'username'
+ password = 'password'
+ vserver = 'vserver'
+ status_admin = True
+ return dict({
+ 'hostname': hostname,
+ 'username': username,
+ 'password': password,
+ 'vserver': vserver,
+ 'status_admin': status_admin
+ })
+
+ def test_module_fail_when_required_args_missing(self):
+ ''' required arguments are reported as errors '''
+ with pytest.raises(AnsibleFailJson) as exc:
+ set_module_args({})
+ my_module()
+ print('Info: %s' % exc.value.args[0]['msg'])
+
+ def test_ensure_get_called(self):
+ ''' test get_nvme() for non-existent nvme'''
+ set_module_args(self.set_default_args())
+ my_obj = my_module()
+ my_obj.server = self.server
+ assert my_obj.get_nvme() is None
+
+ def test_ensure_get_called_existing(self):
+ ''' test get_nvme() for existing nvme'''
+ set_module_args(self.set_default_args())
+ my_obj = my_module()
+ my_obj.server = MockONTAPConnection(kind='nvme')
+ assert my_obj.get_nvme()
+
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_nvme.NetAppONTAPNVMe.create_nvme')
+ def test_successful_create(self, create_nvme):
+ ''' creating nvme and testing idempotency '''
+ set_module_args(self.set_default_args())
+ my_obj = my_module()
+ if not self.onbox:
+ my_obj.server = self.server
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ assert exc.value.args[0]['changed']
+ create_nvme.assert_called_with()
+ # to reset na_helper from remembering the previous 'changed' value
+ my_obj = my_module()
+ if not self.onbox:
+ my_obj.server = MockONTAPConnection('nvme')
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ assert not exc.value.args[0]['changed']
+
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_nvme.NetAppONTAPNVMe.delete_nvme')
+ def test_successful_delete(self, delete_nvme):
+ ''' deleting nvme and testing idempotency '''
+ data = self.set_default_args()
+ data['state'] = 'absent'
+ set_module_args(data)
+ my_obj = my_module()
+ if not self.onbox:
+ my_obj.server = MockONTAPConnection('nvme')
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ assert exc.value.args[0]['changed']
+ delete_nvme.assert_called_with()
+ # to reset na_helper from remembering the previous 'changed' value
+ my_obj = my_module()
+ if not self.onbox:
+ my_obj.server = self.server
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ assert not exc.value.args[0]['changed']
+
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_nvme.NetAppONTAPNVMe.modify_nvme')
+ def test_successful_modify(self, modify_nvme):
+ ''' modifying nvme and testing idempotency '''
+ data = self.set_default_args()
+ data['status_admin'] = False
+ set_module_args(data)
+ my_obj = my_module()
+ if not self.onbox:
+ my_obj.server = MockONTAPConnection('nvme')
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ assert exc.value.args[0]['changed']
+ modify_nvme.assert_called_with()
+ # to reset na_helper from remembering the previous 'changed' value
+ data = self.set_default_args()
+ set_module_args(data)
+ my_obj = my_module()
+ if not self.onbox:
+ my_obj.server = MockONTAPConnection('nvme')
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ assert not exc.value.args[0]['changed']
+
+ def test_if_all_methods_catch_exception(self):
+ module_args = {}
+ module_args.update(self.set_default_args())
+ set_module_args(module_args)
+ my_obj = my_module()
+ if not self.onbox:
+ my_obj.server = MockONTAPConnection('nvme_fail')
+ with pytest.raises(AnsibleFailJson) as exc:
+ my_obj.get_nvme()
+ assert 'Error fetching nvme info:' in exc.value.args[0]['msg']
+ with pytest.raises(AnsibleFailJson) as exc:
+ my_obj.create_nvme()
+ assert 'Error creating nvme' in exc.value.args[0]['msg']
+ with pytest.raises(AnsibleFailJson) as exc:
+ my_obj.delete_nvme()
+ assert 'Error deleting nvme' in exc.value.args[0]['msg']
+ with pytest.raises(AnsibleFailJson) as exc:
+ my_obj.modify_nvme()
+ assert 'Error modifying nvme' in exc.value.args[0]['msg']
diff --git a/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_nvme_namespace.py b/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_nvme_namespace.py
new file mode 100644
index 00000000..ecfaadc3
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_nvme_namespace.py
@@ -0,0 +1,201 @@
+# (c) 2018, NetApp, Inc
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+''' unit tests ONTAP Ansible module: na_ontap_nvme_namespace'''
+
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+import json
+import pytest
+
+from ansible_collections.netapp.ontap.tests.unit.compat import unittest
+from ansible_collections.netapp.ontap.tests.unit.compat.mock import patch
+from ansible.module_utils import basic
+from ansible.module_utils._text import to_bytes
+import ansible_collections.netapp.ontap.plugins.module_utils.netapp as netapp_utils
+
+from ansible_collections.netapp.ontap.plugins.modules.na_ontap_nvme_namespace \
+ import NetAppONTAPNVMENamespace as my_module
+
+if not netapp_utils.has_netapp_lib():
+ pytestmark = pytest.mark.skip('skipping as missing required netapp_lib')
+
+
+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 MockONTAPConnection(object):
+ ''' mock server connection to ONTAP host '''
+
+ def __init__(self, kind=None):
+ ''' save arguments '''
+ self.type = kind
+ self.xml_in = None
+ self.xml_out = None
+
+ def invoke_successfully(self, xml, enable_tunneling): # pylint: disable=unused-argument
+ ''' mock invoke_successfully returning xml data '''
+ self.xml_in = xml
+ if self.type == 'namespace':
+ xml = self.build_namespace_info()
+ elif self.type == 'quota_fail':
+ raise netapp_utils.zapi.NaApiError(code='TEST', message="This exception is from the unit test")
+ self.xml_out = xml
+ return xml
+
+ @staticmethod
+ def build_namespace_info():
+ ''' build xml data for namespace-info '''
+ xml = netapp_utils.zapi.NaElement('xml')
+ data = {'num-records': 2,
+ 'attributes-list': [{'nvme-namespace-info': {'path': 'abcd/vol'}},
+ {'nvme-namespace-info': {'path': 'xyz/vol'}}]}
+ xml.translate_struct(data)
+ return xml
+
+
+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)
+ self.server = MockONTAPConnection()
+ self.onbox = False
+
+ def set_default_args(self):
+ if self.onbox:
+ hostname = '10.193.75.3'
+ username = 'admin'
+ password = 'netapp1!'
+ vserver = 'ansible'
+ ostype = 'linux'
+ path = 'abcd/vol'
+ size = 20
+ size_unit = 'mb'
+ else:
+ hostname = 'hostname'
+ username = 'username'
+ password = 'password'
+ vserver = 'vserver'
+ ostype = 'linux'
+ path = 'abcd/vol'
+ size = 20
+ size_unit = 'mb'
+ return dict({
+ 'hostname': hostname,
+ 'username': username,
+ 'password': password,
+ 'ostype': ostype,
+ 'vserver': vserver,
+ 'path': path,
+ 'size': size,
+ 'size_unit': size_unit
+ })
+
+ def test_module_fail_when_required_args_missing(self):
+ ''' required arguments are reported as errors '''
+ with pytest.raises(AnsibleFailJson) as exc:
+ set_module_args({})
+ my_module()
+ print('Info: %s' % exc.value.args[0]['msg'])
+
+ def test_ensure_get_called(self):
+ ''' test get_namespace() for non-existent namespace'''
+ set_module_args(self.set_default_args())
+ my_obj = my_module()
+ my_obj.server = self.server
+ assert my_obj.get_namespace() is None
+
+ def test_ensure_get_called_existing(self):
+ ''' test get_namespace() for existing namespace'''
+ set_module_args(self.set_default_args())
+ my_obj = my_module()
+ my_obj.server = MockONTAPConnection(kind='namespace')
+ assert my_obj.get_namespace()
+
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_nvme_namespace.NetAppONTAPNVMENamespace.create_namespace')
+ def test_successful_create(self, create_namespace):
+ ''' creating namespace and testing idempotency '''
+ set_module_args(self.set_default_args())
+ my_obj = my_module()
+ if not self.onbox:
+ my_obj.server = self.server
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ assert exc.value.args[0]['changed']
+ create_namespace.assert_called_with()
+ # to reset na_helper from remembering the previous 'changed' value
+ my_obj = my_module()
+ if not self.onbox:
+ my_obj.server = MockONTAPConnection('namespace')
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ assert not exc.value.args[0]['changed']
+
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_nvme_namespace.NetAppONTAPNVMENamespace.delete_namespace')
+ def test_successful_delete(self, delete_namespace):
+ ''' deleting namespace and testing idempotency '''
+ data = self.set_default_args()
+ data['state'] = 'absent'
+ set_module_args(data)
+ my_obj = my_module()
+ if not self.onbox:
+ my_obj.server = MockONTAPConnection('namespace')
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ assert exc.value.args[0]['changed']
+ delete_namespace.assert_called_with()
+ # to reset na_helper from remembering the previous 'changed' value
+ my_obj = my_module()
+ if not self.onbox:
+ my_obj.server = self.server
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ assert not exc.value.args[0]['changed']
+
+ def test_if_all_methods_catch_exception(self):
+ module_args = {}
+ module_args.update(self.set_default_args())
+ set_module_args(module_args)
+ my_obj = my_module()
+ if not self.onbox:
+ my_obj.server = MockONTAPConnection('quota_fail')
+ with pytest.raises(AnsibleFailJson) as exc:
+ my_obj.get_namespace()
+ assert 'Error fetching namespace info:' in exc.value.args[0]['msg']
+ with pytest.raises(AnsibleFailJson) as exc:
+ my_obj.create_namespace()
+ assert 'Error creating namespace for path' in exc.value.args[0]['msg']
+ with pytest.raises(AnsibleFailJson) as exc:
+ my_obj.delete_namespace()
+ assert 'Error deleting namespace for path' in exc.value.args[0]['msg']
diff --git a/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_nvme_subsystem.py b/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_nvme_subsystem.py
new file mode 100644
index 00000000..c056fc90
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_nvme_subsystem.py
@@ -0,0 +1,242 @@
+# (c) 2018, NetApp, Inc
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+''' unit tests ONTAP Ansible module: na_ontap_nvme_subsystem '''
+
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+import json
+import pytest
+
+from ansible_collections.netapp.ontap.tests.unit.compat import unittest
+from ansible_collections.netapp.ontap.tests.unit.compat.mock import patch
+from ansible.module_utils import basic
+from ansible.module_utils._text import to_bytes
+import ansible_collections.netapp.ontap.plugins.module_utils.netapp as netapp_utils
+
+from ansible_collections.netapp.ontap.plugins.modules.na_ontap_nvme_subsystem \
+ import NetAppONTAPNVMESubsystem as my_module
+
+if not netapp_utils.has_netapp_lib():
+ pytestmark = pytest.mark.skip('skipping as missing required netapp_lib')
+
+
+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 MockONTAPConnection(object):
+ ''' mock server connection to ONTAP host '''
+
+ def __init__(self, kind=None, parm1=None):
+ ''' save arguments '''
+ self.type = kind
+ self.parm1 = parm1
+ self.xml_in = None
+ self.xml_out = None
+
+ def invoke_successfully(self, xml, enable_tunneling): # pylint: disable=unused-argument
+ ''' mock invoke_successfully returning xml data '''
+ self.xml_in = xml
+ if self.type == 'subsystem':
+ xml = self.build_subsystem_info(self.parm1)
+ self.xml_out = xml
+ return xml
+
+ @staticmethod
+ def build_subsystem_info(vserver):
+ ''' build xml data for vserser-info '''
+ xml = netapp_utils.zapi.NaElement('xml')
+ data = {'num-records': 2,
+ 'attributes-list': [{'nvme-target-subsystem-map-info': {'path': 'abcd/vol'}},
+ {'nvme-target-subsystem-map-info': {'path': 'xyz/vol'}}]}
+ xml.translate_struct(data)
+ return xml
+
+
+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)
+ self.server = MockONTAPConnection()
+ self.onbox = False
+
+ def set_default_args(self):
+ if self.onbox:
+ hostname = '10.193.75.3'
+ username = 'admin'
+ password = 'netapp1!'
+ subsystem = 'test'
+ vserver = 'ansible'
+ ostype = 'linux'
+ paths = ['abcd/vol', 'xyz/vol']
+ else:
+ hostname = 'hostname'
+ username = 'username'
+ password = 'password'
+ subsystem = 'test'
+ vserver = 'vserver'
+ ostype = 'linux'
+ paths = ['abcd/vol', 'xyz/vol']
+ return dict({
+ 'hostname': hostname,
+ 'username': username,
+ 'password': password,
+ 'subsystem': subsystem,
+ 'ostype': ostype,
+ 'vserver': vserver,
+ 'paths': paths
+ })
+
+ def test_module_fail_when_required_args_missing(self):
+ ''' required arguments are reported as errors '''
+ with pytest.raises(AnsibleFailJson) as exc:
+ set_module_args({})
+ my_module()
+ print('Info: %s' % exc.value.args[0]['msg'])
+
+ def test_ensure_get_called(self):
+ ''' test get_subsystem() for non-existent subsystem'''
+ set_module_args(self.set_default_args())
+ my_obj = my_module()
+ my_obj.server = self.server
+ assert my_obj.get_subsystem() is None
+
+ def test_ensure_get_called_existing(self):
+ ''' test get_subsystem() for existing subsystem'''
+ set_module_args(self.set_default_args())
+ my_obj = my_module()
+ my_obj.server = MockONTAPConnection(kind='subsystem')
+ assert my_obj.get_subsystem()
+
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_nvme_subsystem.NetAppONTAPNVMESubsystem.create_subsystem')
+ def test_successful_create(self, create_subsystem):
+ ''' creating subsystem and testing idempotency '''
+ set_module_args(self.set_default_args())
+ my_obj = my_module()
+ if not self.onbox:
+ my_obj.server = self.server
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ assert exc.value.args[0]['changed']
+ create_subsystem.assert_called_with()
+ # to reset na_helper from remembering the previous 'changed' value
+ my_obj = my_module()
+ if not self.onbox:
+ my_obj.server = MockONTAPConnection('subsystem')
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ assert not exc.value.args[0]['changed']
+
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_nvme_subsystem.NetAppONTAPNVMESubsystem.delete_subsystem')
+ def test_successful_delete(self, delete_subsystem):
+ ''' deleting subsystem and testing idempotency '''
+ data = self.set_default_args()
+ data['state'] = 'absent'
+ set_module_args(data)
+ my_obj = my_module()
+ if not self.onbox:
+ my_obj.server = MockONTAPConnection('subsystem')
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ assert exc.value.args[0]['changed']
+ delete_subsystem.assert_called_with()
+ # to reset na_helper from remembering the previous 'changed' value
+ my_obj = my_module()
+ if not self.onbox:
+ my_obj.server = self.server
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ assert not exc.value.args[0]['changed']
+
+ def test_ensure_get_called(self):
+ ''' test get_subsystem_host_map() for non-existent subsystem'''
+ set_module_args(self.set_default_args())
+ my_obj = my_module()
+ my_obj.server = self.server
+ assert my_obj.get_subsystem_host_map('paths') is None
+
+ def test_ensure_get_called_existing(self):
+ ''' test get_subsystem_host_map() for existing subsystem'''
+ set_module_args(self.set_default_args())
+ my_obj = my_module()
+ my_obj.server = MockONTAPConnection(kind='subsystem')
+ assert my_obj.get_subsystem_host_map('paths')
+
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_nvme_subsystem.NetAppONTAPNVMESubsystem.add_subsystem_host_map')
+ def test_successful_add_mock(self, add_subsystem_host_map):
+ ''' adding subsystem host/map and testing idempotency '''
+ set_module_args(self.set_default_args())
+ my_obj = my_module()
+ if not self.onbox:
+ my_obj.server = self.server
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ assert exc.value.args[0]['changed']
+ add_subsystem_host_map.assert_called_with(['abcd/vol', 'xyz/vol'], 'paths')
+
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_nvme_subsystem.NetAppONTAPNVMESubsystem.remove_subsystem_host_map')
+ def test_successful_remove_mock(self, remove_subsystem_host_map):
+ ''' removing subsystem host/map and testing idempotency '''
+ data = self.set_default_args()
+ data['paths'] = ['abcd/vol']
+ set_module_args(data)
+ my_obj = my_module()
+ if not self.onbox:
+ my_obj.server = MockONTAPConnection('subsystem')
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ assert exc.value.args[0]['changed']
+ remove_subsystem_host_map.assert_called_with(['xyz/vol'], 'paths')
+
+ def test_successful_add(self):
+ ''' adding subsystem host/map and testing idempotency '''
+ set_module_args(self.set_default_args())
+ my_obj = my_module()
+ if not self.onbox:
+ my_obj.server = self.server
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ assert exc.value.args[0]['changed']
+
+ def test_successful_remove(self):
+ ''' removing subsystem host/map and testing idempotency '''
+ data = self.set_default_args()
+ data['paths'] = ['abcd/vol']
+ set_module_args(data)
+ my_obj = my_module()
+ if not self.onbox:
+ my_obj.server = MockONTAPConnection('subsystem')
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ assert exc.value.args[0]['changed']
diff --git a/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_object_store.py b/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_object_store.py
new file mode 100644
index 00000000..3317aed3
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_object_store.py
@@ -0,0 +1,300 @@
+# (c) 2019, NetApp, Inc
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+""" unit tests for Ansible module: na_ontap_object_store """
+
+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.ontap.tests.unit.compat import unittest
+from ansible_collections.netapp.ontap.tests.unit.compat.mock import patch, Mock
+import ansible_collections.netapp.ontap.plugins.module_utils.netapp as netapp_utils
+
+from ansible_collections.netapp.ontap.plugins.modules.na_ontap_object_store \
+ import NetAppOntapObjectStoreConfig as my_module # module under test
+
+if not netapp_utils.has_netapp_lib():
+ pytestmark = pytest.mark.skip('skipping as missing required netapp_lib')
+
+
+# REST API canned responses when mocking send_request
+SRR = {
+ # common responses
+ 'is_rest': (200, {}, None),
+ 'is_zapi': (400, {}, "Unreachable"),
+ 'empty_good': (200, {}, None),
+ 'end_of_sequence': (500, None, "Unexpected call to send_request"),
+ 'generic_error': (400, None, "Expected error"),
+ # module specific responses
+ 'get_uuid': (200, {'records': [{'uuid': 'ansible'}]}, None),
+ 'get_object_store': (200,
+ {'uuid': 'ansible',
+ 'name': 'ansible',
+ }, 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"""
+
+
+class AnsibleFailJson(Exception):
+ """Exception class to be raised by module.fail_json and caught by the test case"""
+
+
+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 MockONTAPConnection(object):
+ ''' mock server connection to ONTAP host '''
+
+ def __init__(self, kind=None):
+ ''' save arguments '''
+ self.type = kind
+ self.xml_in = None
+ self.xml_out = None
+
+ def invoke_successfully(self, xml, enable_tunneling): # pylint: disable=unused-argument
+ ''' mock invoke_successfully returning xml data '''
+ self.xml_in = xml
+ if self.type == 'object_store':
+ xml = self.build_object_store_info()
+ elif self.type == 'object_store_fail':
+ raise netapp_utils.zapi.NaApiError(code='TEST', message="This exception is from the unit test")
+ self.xml_out = xml
+ return xml
+
+ @staticmethod
+ def build_object_store_info():
+ ''' build xml data for object store '''
+ xml = netapp_utils.zapi.NaElement('xml')
+ data = {'attributes':
+ {'aggr-object-store-config-info':
+ {'object-store-name': 'ansible'}
+ }
+ }
+ xml.translate_struct(data)
+ print(xml.to_string())
+ return xml
+
+
+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)
+ self.server = MockONTAPConnection()
+ # whether to use a mock or a simulator
+ self.onbox = False
+
+ def set_default_args(self):
+ if self.onbox:
+ hostname = '10.10.10.10'
+ username = 'admin'
+ password = 'password'
+ name = 'ansible'
+ else:
+ hostname = 'hostname'
+ username = 'username'
+ password = 'password'
+ name = 'ansible'
+ return dict({
+ 'hostname': hostname,
+ 'username': username,
+ 'password': password,
+ 'name': name
+ })
+
+ def call_command(self, module_args, cx_type='zapi'):
+ ''' utility function to call apply '''
+ module_args.update(self.set_default_args())
+ set_module_args(module_args)
+ my_obj = my_module()
+ my_obj.asup_log_for_cserver = Mock(return_value=None)
+ if cx_type == 'zapi':
+ if not self.onbox:
+ # mock the connection
+ my_obj.server = MockONTAPConnection('object_store')
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ return exc.value.args[0]['changed']
+
+ def test_module_fail_when_required_args_missing(self):
+ ''' required arguments are reported as errors '''
+ with pytest.raises(AnsibleFailJson) as exc:
+ set_module_args({})
+ my_module()
+ print('Info: %s' % exc.value.args[0]['msg'])
+
+ @patch('ansible_collections.netapp.ontap.plugins.module_utils.netapp.OntapRestAPI.send_request')
+ def test_ensure_object_store_get_called(self, mock_request):
+ ''' fetching details of object store '''
+ mock_request.side_effect = [
+ SRR['is_zapi'],
+ SRR['end_of_sequence']
+ ]
+ set_module_args(self.set_default_args())
+ my_obj = my_module()
+ my_obj.server = self.server
+ assert my_obj.get_aggr_object_store() is not None
+
+ @patch('ansible_collections.netapp.ontap.plugins.module_utils.netapp.OntapRestAPI.send_request')
+ def test_ensure_get_called_existing(self, mock_request):
+ ''' test for existing object store'''
+ mock_request.side_effect = [
+ SRR['is_zapi'],
+ SRR['end_of_sequence']
+ ]
+ set_module_args(self.set_default_args())
+ my_obj = my_module()
+ my_obj.server = MockONTAPConnection(kind='object_store')
+ assert my_obj.get_aggr_object_store()
+
+ @patch('ansible_collections.netapp.ontap.plugins.module_utils.netapp.OntapRestAPI.send_request')
+ def test_object_store_create(self, mock_request):
+ ''' test for creating object store'''
+ mock_request.side_effect = [
+ SRR['is_zapi'],
+ SRR['end_of_sequence']
+ ]
+ module_args = {
+ 'provider_type': 'abc',
+ 'server': 'abc',
+ 'container': 'abc',
+ 'access_key': 'abc',
+ 'secret_password': 'abc'
+ }
+ module_args.update(self.set_default_args())
+ set_module_args(module_args)
+ my_obj = my_module()
+ my_obj.asup_log_for_cserver = Mock(return_value=None)
+ if not self.onbox:
+ # mock the connection
+ my_obj.server = MockONTAPConnection(kind='object_store')
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ assert not exc.value.args[0]['changed']
+
+ @patch('ansible_collections.netapp.ontap.plugins.module_utils.netapp.OntapRestAPI.send_request')
+ def test_object_store_delete(self, mock_request):
+ ''' test for deleting object store'''
+ mock_request.side_effect = [
+ SRR['is_zapi'],
+ SRR['end_of_sequence']
+ ]
+ module_args = {
+ 'state': 'absent',
+ }
+ changed = self.call_command(module_args)
+ assert changed
+
+ @patch('ansible_collections.netapp.ontap.plugins.module_utils.netapp.OntapRestAPI.send_request')
+ def test_rest_error(self, mock_request):
+ mock_request.side_effect = [
+ SRR['is_zapi'],
+ SRR['end_of_sequence']
+ ]
+ set_module_args(self.set_default_args())
+ mock_request.side_effect = [
+ SRR['is_rest'],
+ SRR['generic_error'],
+ SRR['end_of_sequence']
+ ]
+ my_obj = my_module()
+ with pytest.raises(AnsibleFailJson) as exc:
+ my_obj.apply()
+ assert exc.value.args[0]['msg'] == SRR['generic_error'][2]
+
+ @patch('ansible_collections.netapp.ontap.plugins.module_utils.netapp.OntapRestAPI.send_request')
+ def test_rest_successful_create(self, mock_request):
+ data = {
+ 'provider_type': 'abc',
+ 'server': 'abc',
+ 'container': 'abc',
+ 'access_key': 'abc',
+ 'secret_password': 'abc'
+ }
+ data.update(self.set_default_args())
+ set_module_args(data)
+ mock_request.side_effect = [
+ SRR['is_rest'],
+ SRR['get_uuid'],
+ SRR['get_object_store'],
+ SRR['empty_good'],
+ SRR['end_of_sequence']
+ ]
+ my_obj = my_module()
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ assert not exc.value.args[0]['changed']
+
+ @patch('ansible_collections.netapp.ontap.plugins.module_utils.netapp.OntapRestAPI.send_request')
+ def test_rest_successful_delete(self, mock_request):
+ data = {
+ 'state': 'absent',
+ }
+ data.update(self.set_default_args())
+ set_module_args(data)
+ mock_request.side_effect = [
+ SRR['is_rest'],
+ SRR['get_uuid'],
+ SRR['get_object_store'],
+ SRR['empty_good'],
+ SRR['end_of_sequence']
+ ]
+ my_obj = my_module()
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ assert exc.value.args[0]['changed']
+
+ @patch('ansible_collections.netapp.ontap.plugins.module_utils.netapp.OntapRestAPI.send_request')
+ def test_if_all_methods_catch_exception(self, mock_request):
+ mock_request.side_effect = [
+ SRR['is_zapi'],
+ SRR['end_of_sequence']
+ ]
+ module_args = {
+ 'provider_type': 'abc',
+ 'server': 'abc',
+ 'container': 'abc',
+ 'access_key': 'abc',
+ 'secret_password': 'abc'
+ }
+ module_args.update(self.set_default_args())
+ set_module_args(module_args)
+ my_obj = my_module()
+ if not self.onbox:
+ my_obj.server = MockONTAPConnection('object_store_fail')
+ with pytest.raises(AnsibleFailJson) as exc:
+ my_obj.get_aggr_object_store()
+ assert '' in exc.value.args[0]['msg']
+ with pytest.raises(AnsibleFailJson) as exc:
+ my_obj.create_aggr_object_store()
+ assert 'Error provisioning object store config ' in exc.value.args[0]['msg']
+ with pytest.raises(AnsibleFailJson) as exc:
+ my_obj.delete_aggr_object_store()
+ assert 'Error removing object store config ' in exc.value.args[0]['msg']
diff --git a/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_ports.py b/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_ports.py
new file mode 100644
index 00000000..04942486
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_ports.py
@@ -0,0 +1,173 @@
+# (c) 2019, NetApp, Inc
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+''' unit tests for ONTAP Ansible module: na_ontap_port'''
+
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+import json
+import pytest
+
+from ansible_collections.netapp.ontap.tests.unit.compat import unittest
+from ansible_collections.netapp.ontap.tests.unit.compat.mock import patch, Mock
+from ansible.module_utils import basic
+from ansible.module_utils._text import to_bytes
+import ansible_collections.netapp.ontap.plugins.module_utils.netapp as netapp_utils
+
+from ansible_collections.netapp.ontap.plugins.modules.na_ontap_ports \
+ import NetAppOntapPorts as port_module # module under test
+
+if not netapp_utils.has_netapp_lib():
+ pytestmark = pytest.mark.skip('skipping as missing required netapp_lib')
+
+
+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 mock_args(self, choice):
+ if choice == 'broadcast_domain':
+ return {
+ 'names': ['test_port_1', 'test_port_2'],
+ 'resource_name': 'test_domain',
+ 'resource_type': 'broadcast_domain',
+ 'hostname': 'test',
+ 'username': 'test_user',
+ 'password': 'test_pass!'
+ }
+ elif choice == 'portset':
+ return {
+ 'names': ['test_lif'],
+ 'resource_name': 'test_portset',
+ 'resource_type': 'portset',
+ 'hostname': 'test',
+ 'username': 'test_user',
+ 'password': 'test_pass!',
+ 'vserver': 'test_vserver'
+ }
+
+ def get_port_mock_object(self):
+ """
+ Helper method to return an na_ontap_port object
+ """
+ port_obj = port_module()
+ port_obj.asup_log_for_cserver = Mock(return_value=None)
+ port_obj.server = Mock()
+ port_obj.server.invoke_successfully = Mock()
+
+ return port_obj
+
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_ports.NetAppOntapPorts.add_broadcast_domain_ports')
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_ports.NetAppOntapPorts.get_broadcast_domain_ports')
+ def test_successfully_add_broadcast_domain_ports(self, get_broadcast_domain_ports, add_broadcast_domain_ports):
+ ''' Test successful add broadcast domain ports '''
+ data = self.mock_args('broadcast_domain')
+ set_module_args(data)
+ get_broadcast_domain_ports.side_effect = [
+ []
+ ]
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_port_mock_object().apply()
+ assert exc.value.args[0]['changed']
+
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_ports.NetAppOntapPorts.add_broadcast_domain_ports')
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_ports.NetAppOntapPorts.get_broadcast_domain_ports')
+ def test_add_broadcast_domain_ports_idempotency(self, get_broadcast_domain_ports, add_broadcast_domain_ports):
+ ''' Test add broadcast domain ports idempotency '''
+ data = self.mock_args('broadcast_domain')
+ set_module_args(data)
+ get_broadcast_domain_ports.side_effect = [
+ ['test_port_1', 'test_port_2']
+ ]
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_port_mock_object().apply()
+ assert not exc.value.args[0]['changed']
+
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_ports.NetAppOntapPorts.add_portset_ports')
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_ports.NetAppOntapPorts.portset_get')
+ def test_successfully_add_portset_ports(self, portset_get, add_portset_ports):
+ ''' Test successful add portset ports '''
+ data = self.mock_args('portset')
+ set_module_args(data)
+ portset_get.side_effect = [
+ []
+ ]
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_port_mock_object().apply()
+ assert exc.value.args[0]['changed']
+
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_ports.NetAppOntapPorts.add_portset_ports')
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_ports.NetAppOntapPorts.portset_get')
+ def test_add_portset_ports_idempotency(self, portset_get, add_portset_ports):
+ ''' Test add portset ports idempotency '''
+ data = self.mock_args('portset')
+ set_module_args(data)
+ portset_get.side_effect = [
+ ['test_lif']
+ ]
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_port_mock_object().apply()
+ assert not exc.value.args[0]['changed']
+
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_ports.NetAppOntapPorts.add_broadcast_domain_ports')
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_ports.NetAppOntapPorts.get_broadcast_domain_ports')
+ def test_successfully_remove_broadcast_domain_ports(self, get_broadcast_domain_ports, add_broadcast_domain_ports):
+ ''' Test successful remove broadcast domain ports '''
+ data = self.mock_args('broadcast_domain')
+ data['state'] = 'absent'
+ set_module_args(data)
+ get_broadcast_domain_ports.side_effect = [
+ ['test_port_1', 'test_port_2']
+ ]
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_port_mock_object().apply()
+ assert exc.value.args[0]['changed']
+
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_ports.NetAppOntapPorts.add_portset_ports')
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_ports.NetAppOntapPorts.portset_get')
+ def test_remove_add_portset_ports(self, portset_get, add_portset_ports):
+ ''' Test successful remove portset ports '''
+ data = self.mock_args('portset')
+ data['state'] = 'absent'
+ set_module_args(data)
+ portset_get.side_effect = [
+ ['test_lif']
+ ]
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_port_mock_object().apply()
+ assert exc.value.args[0]['changed']
diff --git a/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_portset.py b/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_portset.py
new file mode 100644
index 00000000..2efb2275
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_portset.py
@@ -0,0 +1,190 @@
+# (c) 2018, NetApp, Inc
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+''' unit tests for ONTAP Ansible module: na_ontap_portset'''
+
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+import json
+import pytest
+
+from ansible_collections.netapp.ontap.tests.unit.compat import unittest
+from ansible_collections.netapp.ontap.tests.unit.compat.mock import patch
+from ansible.module_utils import basic
+from ansible.module_utils._text import to_bytes
+import ansible_collections.netapp.ontap.plugins.module_utils.netapp as netapp_utils
+
+from ansible_collections.netapp.ontap.plugins.modules.na_ontap_portset \
+ import NetAppONTAPPortset as my_module # module under test
+
+if not netapp_utils.has_netapp_lib():
+ pytestmark = pytest.mark.skip('skipping as missing required netapp_lib')
+
+
+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 MockONTAPConnection(object):
+ ''' mock server connection to ONTAP host '''
+
+ def __init__(self, kind=None, parm1=None, parm2=None, parm3=None):
+ ''' save arguments '''
+ self.type = kind
+ self.parm1 = parm1
+ self.parm2 = parm2
+ self.parm3 = parm3
+ self.xml_in = None
+ self.xml_out = None
+
+ def invoke_successfully(self, xml, enable_tunneling): # pylint: disable=unused-argument
+ ''' mock invoke_successfully returning xml data '''
+ self.xml_in = xml
+ if self.type == 'portset':
+ xml = self.build_portset_info(self.parm1, self.parm2, self.parm3)
+ self.xml_out = xml
+ return xml
+
+ @staticmethod
+ def build_portset_info(portset, vserver, type):
+ ''' build xml data for vserser-info '''
+ xml = netapp_utils.zapi.NaElement('xml')
+ data = {'num-records': 1,
+ 'attributes-list': {'portset-info': {'portset-name': portset,
+ 'vserver': vserver, 'portset-type': type,
+ 'portset-port-total': '0'}}}
+ xml.translate_struct(data)
+ print(xml.to_string())
+ return xml
+
+
+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)
+ self.server = MockONTAPConnection()
+ self.use_vsim = False
+
+ def set_default_args(self):
+ if self.use_vsim:
+ hostname = '10.193.77.154'
+ username = 'admin'
+ password = 'netapp1!'
+ name = 'test'
+ type = 'mixed'
+ vserver = 'ansible_test'
+ ports = ['a1', 'a2']
+ else:
+ hostname = 'hostname'
+ username = 'username'
+ password = 'password'
+ name = 'name'
+ type = 'mixed'
+ vserver = 'vserver'
+ ports = ['a1', 'a2']
+ return dict({
+ 'hostname': hostname,
+ 'username': username,
+ 'password': password,
+ 'name': name,
+ 'type': type,
+ 'vserver': vserver,
+ 'ports': ports
+ })
+
+ def test_module_fail_when_required_args_missing(self):
+ ''' required arguments are reported as errors '''
+ with pytest.raises(AnsibleFailJson) as exc:
+ set_module_args({})
+ my_module()
+ print('Info: %s' % exc.value.args[0]['msg'])
+
+ def test_ensure_portset_get_called(self):
+ ''' a more interesting test '''
+ set_module_args(self.set_default_args())
+ my_obj = my_module()
+ my_obj.server = self.server
+ portset = my_obj.portset_get()
+ print('Info: test_portset_get: %s' % repr(portset))
+ assert portset is None
+
+ def test_ensure_portset_apply_called(self):
+ ''' Test successful create '''
+ module_args = {'name': 'create'}
+ module_args.update(self.set_default_args())
+ set_module_args(module_args)
+ my_obj = my_module()
+ if not self.use_vsim:
+ my_obj.server = self.server
+ portset = my_obj.portset_get()
+ print('Info: test_portset_get: %s' % repr(portset))
+ assert portset is None
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ print('Info: test_portset_apply: %s' % repr(exc.value))
+ assert exc.value.args[0]['changed']
+ if not self.use_vsim:
+ my_obj.server = MockONTAPConnection('portset', 'create', 'vserver', 'mixed')
+ portset = my_obj.portset_get()
+ print('Info: test_portset_get: %s' % repr(portset))
+ assert portset is not None
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ print('Info: test_portset_apply: %s' % repr(exc.value))
+ assert exc.value.args[0]['changed']
+
+ def test_modify_ports(self):
+ ''' Test modify_portset method '''
+ module_args = {'ports': ['l1', 'l2']}
+ module_args.update(self.set_default_args())
+ set_module_args(module_args)
+ my_obj = my_module()
+ if not self.use_vsim:
+ my_obj.server = MockONTAPConnection('portset', parm3='mixed')
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ print('Info: test_portset_apply: %s' % repr(exc.value))
+ assert exc.value.args[0]['changed']
+
+ def test_delete_portset(self):
+ ''' Test successful delete '''
+ module_args = {'state': 'absent'}
+ module_args.update(self.set_default_args())
+ set_module_args(module_args)
+ my_obj = my_module()
+ if not self.use_vsim:
+ my_obj.server = MockONTAPConnection('portset')
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ print('Info: test_portset_apply: %s' % repr(exc.value))
+ assert exc.value.args[0]['changed']
diff --git a/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_qos_adaptive_policy_group.py b/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_qos_adaptive_policy_group.py
new file mode 100644
index 00000000..b376ba8e
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_qos_adaptive_policy_group.py
@@ -0,0 +1,347 @@
+# (c) 2018, NetApp, Inc
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+''' unit test template for ONTAP Ansible module '''
+
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+import json
+import pytest
+
+from ansible_collections.netapp.ontap.tests.unit.compat import unittest
+from ansible_collections.netapp.ontap.tests.unit.compat.mock import patch, Mock
+from ansible.module_utils import basic
+from ansible.module_utils._text import to_bytes
+import ansible_collections.netapp.ontap.plugins.module_utils.netapp as netapp_utils
+
+from ansible_collections.netapp.ontap.plugins.modules.na_ontap_qos_adaptive_policy_group \
+ import NetAppOntapAdaptiveQosPolicyGroup as qos_policy_group_module # module under test
+
+if not netapp_utils.has_netapp_lib():
+ pytestmark = pytest.mark.skip('skipping as missing required netapp_lib')
+
+
+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 MockONTAPConnection(object):
+ ''' mock server connection to ONTAP host '''
+
+ def __init__(self, kind=None, data=None):
+ ''' save arguments '''
+ self.kind = kind
+ self.params = data
+ self.xml_in = None
+ self.xml_out = None
+
+ def invoke_successfully(self, xml, enable_tunneling): # pylint: disable=unused-argument
+ ''' mock invoke_successfully returning xml data '''
+ self.xml_in = xml
+ if self.kind == 'policy':
+ xml = self.build_policy_group_info(self.params)
+ if self.kind == 'error':
+ error = netapp_utils.zapi.NaApiError('test', 'error')
+ raise error
+ self.xml_out = xml
+ return xml
+
+ @staticmethod
+ def build_policy_group_info(vol_details):
+ ''' build xml data for volume-attributes '''
+ xml = netapp_utils.zapi.NaElement('xml')
+ attributes = {
+ 'num-records': 1,
+ 'attributes-list': {
+ 'qos-adaptive-policy-group-info': {
+ 'absolute-min-iops': '50IOPS',
+ 'expected-iops': '150IOPS/TB',
+ 'peak-iops': '220IOPS/TB',
+ 'peak-iops-allocation': 'used_space',
+ 'num-workloads': 0,
+ 'pgid': 6941,
+ 'policy-group': vol_details['name'],
+ 'vserver': vol_details['vserver']
+ }
+ }
+ }
+ xml.translate_struct(attributes)
+ return xml
+
+
+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)
+ self.mock_policy_group = {
+ 'name': 'policy_1',
+ 'vserver': 'policy_vserver',
+ 'absolute_min_iops': '50IOPS',
+ 'expected_iops': '150IOPS/TB',
+ 'peak_iops': '220IOPS/TB',
+ 'peak_iops_allocation': 'used_space'
+ }
+
+ def mock_args(self):
+ return {
+ 'name': self.mock_policy_group['name'],
+ 'vserver': self.mock_policy_group['vserver'],
+ 'absolute_min_iops': '50IOPS',
+ 'expected_iops': '150IOPS/TB',
+ 'peak_iops': '220IOPS/TB',
+ 'peak_iops_allocation': 'used_space',
+ 'hostname': 'test',
+ 'username': 'test_user',
+ 'password': 'test_pass!',
+ 'https': 'False'
+ }
+
+ def get_policy_group_mock_object(self, kind=None):
+ """
+ Helper method to return an na_ontap_volume object
+ :param kind: passes this param to MockONTAPConnection()
+ :return: na_ontap_volume object
+ """
+ policy_obj = qos_policy_group_module()
+ policy_obj.autosupport_log = Mock(return_value=None)
+ policy_obj.cluster = Mock()
+ policy_obj.cluster.invoke_successfully = Mock()
+ if kind is None:
+ policy_obj.server = MockONTAPConnection()
+ else:
+ policy_obj.server = MockONTAPConnection(kind=kind, data=self.mock_policy_group)
+ return policy_obj
+
+ def test_module_fail_when_required_args_missing(self):
+ ''' required arguments are reported as errors '''
+ with pytest.raises(AnsibleFailJson) as exc:
+ set_module_args({})
+ qos_policy_group_module()
+ print('Info: %s' % exc.value.args[0]['msg'])
+
+ def test_get_nonexistent_policy(self):
+ ''' Test if get_policy_group returns None for non-existent policy_group '''
+ set_module_args(self.mock_args())
+ result = self.get_policy_group_mock_object().get_policy_group()
+ assert result is None
+
+ def test_get_existing_policy_group(self):
+ ''' Test if get_policy_group returns details for existing policy_group '''
+ set_module_args(self.mock_args())
+ result = self.get_policy_group_mock_object('policy').get_policy_group()
+ assert result['name'] == self.mock_policy_group['name']
+ assert result['vserver'] == self.mock_policy_group['vserver']
+
+ def test_create_error_missing_param(self):
+ ''' Test if create throws an error if name is not specified'''
+ data = self.mock_args()
+ del data['name']
+ set_module_args(data)
+ with pytest.raises(AnsibleFailJson) as exc:
+ self.get_policy_group_mock_object('policy').create_policy_group()
+ msg = 'missing required arguments: name'
+ assert exc.value.args[0]['msg'] == msg
+
+ def test_successful_create(self):
+ ''' Test successful create '''
+ data = self.mock_args()
+ set_module_args(data)
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_policy_group_mock_object().apply()
+ assert exc.value.args[0]['changed']
+
+ def test_create_idempotency(self):
+ ''' Test create idempotency '''
+ set_module_args(self.mock_args())
+ obj = self.get_policy_group_mock_object('policy')
+ with pytest.raises(AnsibleExitJson) as exc:
+ obj.apply()
+ assert not exc.value.args[0]['changed']
+
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_qos_adaptive_policy_group.NetAppOntapAdaptiveQosPolicyGroup.get_policy_group')
+ def test_create_error(self, get_policy_group):
+ ''' Test create error '''
+ set_module_args(self.mock_args())
+ get_policy_group.side_effect = [
+ None
+ ]
+ with pytest.raises(AnsibleFailJson) as exc:
+ self.get_policy_group_mock_object('error').apply()
+ assert exc.value.args[0]['msg'] == 'Error creating adaptive qos policy group policy_1: NetApp API failed. Reason - test:error'
+
+ def test_successful_delete(self):
+ ''' Test delete existing volume '''
+ data = self.mock_args()
+ data['state'] = 'absent'
+ set_module_args(data)
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_policy_group_mock_object('policy').apply()
+ assert exc.value.args[0]['changed']
+
+ def test_delete_idempotency(self):
+ ''' Test delete idempotency '''
+ data = self.mock_args()
+ data['state'] = 'absent'
+ set_module_args(data)
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_policy_group_mock_object().apply()
+ assert not exc.value.args[0]['changed']
+
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_qos_adaptive_policy_group.NetAppOntapAdaptiveQosPolicyGroup.get_policy_group')
+ def test_delete_error(self, get_policy_group):
+ ''' Test create idempotency'''
+ data = self.mock_args()
+ data['state'] = 'absent'
+ set_module_args(data)
+ current = {
+ 'absolute_min_iops': '50IOPS',
+ 'expected_iops': '150IOPS/TB',
+ 'peak_iops': '220IOPS/TB',
+ 'peak_iops_allocation': 'used_space',
+ 'name': 'policy_1',
+ 'vserver': 'policy_vserver'
+ }
+ get_policy_group.side_effect = [
+ current
+ ]
+ with pytest.raises(AnsibleFailJson) as exc:
+ self.get_policy_group_mock_object('error').apply()
+ assert exc.value.args[0]['msg'] == 'Error deleting adaptive qos policy group policy_1: NetApp API failed. Reason - test:error'
+
+ def test_successful_modify_expected_iops(self):
+ ''' Test successful modify expected iops '''
+ data = self.mock_args()
+ data['expected_iops'] = '175IOPS'
+ set_module_args(data)
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_policy_group_mock_object('policy').apply()
+ assert exc.value.args[0]['changed']
+
+ def test_modify_expected_iops_idempotency(self):
+ ''' Test modify idempotency '''
+ data = self.mock_args()
+ set_module_args(data)
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_policy_group_mock_object('policy').apply()
+ assert not exc.value.args[0]['changed']
+
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_qos_adaptive_policy_group.NetAppOntapAdaptiveQosPolicyGroup.get_policy_group')
+ def test_modify_error(self, get_policy_group):
+ ''' Test create idempotency '''
+ data = self.mock_args()
+ data['expected_iops'] = '175IOPS'
+ set_module_args(data)
+ current = {
+ 'absolute_min_iops': '50IOPS',
+ 'expected_iops': '150IOPS/TB',
+ 'peak_iops': '220IOPS/TB',
+ 'peak_iops_allocation': 'used_space',
+ 'name': 'policy_1',
+ 'vserver': 'policy_vserver'
+ }
+ get_policy_group.side_effect = [
+ current
+ ]
+ with pytest.raises(AnsibleFailJson) as exc:
+ self.get_policy_group_mock_object('error').apply()
+ assert exc.value.args[0]['msg'] == 'Error modifying adaptive qos policy group policy_1: NetApp API failed. Reason - test:error'
+
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_qos_adaptive_policy_group.NetAppOntapAdaptiveQosPolicyGroup.get_policy_group')
+ def test_rename(self, get_policy_group):
+ ''' Test rename idempotency '''
+ data = self.mock_args()
+ data['name'] = 'policy_2'
+ data['from_name'] = 'policy_1'
+ set_module_args(data)
+ current = {
+ 'absolute_min_iops': '50IOPS',
+ 'expected_iops': '150IOPS/TB',
+ 'peak_iops': '220IOPS/TB',
+ 'peak_iops_allocation': 'used_space',
+ 'name': 'policy_1',
+ 'vserver': 'policy_vserver'
+ }
+ get_policy_group.side_effect = [
+ None,
+ current
+ ]
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_policy_group_mock_object('policy').apply()
+ assert exc.value.args[0]['changed']
+
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_qos_adaptive_policy_group.NetAppOntapAdaptiveQosPolicyGroup.get_policy_group')
+ def test_rename_idempotency(self, get_policy_group):
+ ''' Test rename idempotency '''
+ data = self.mock_args()
+ data['name'] = 'policy_1'
+ data['from_name'] = 'policy_1'
+ current = {
+ 'absolute_min_iops': '50IOPS',
+ 'expected_iops': '150IOPS/TB',
+ 'peak_iops': '220IOPS/TB',
+ 'peak_iops_allocation': 'used_space',
+ 'name': 'policy_1',
+ 'vserver': 'policy_vserver'
+ }
+ get_policy_group.side_effect = [
+ current,
+ current
+ ]
+ set_module_args(data)
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_policy_group_mock_object('policy').apply()
+ assert not exc.value.args[0]['changed']
+
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_qos_adaptive_policy_group.NetAppOntapAdaptiveQosPolicyGroup.get_policy_group')
+ def test_rename_error(self, get_policy_group):
+ ''' Test create idempotency '''
+ data = self.mock_args()
+ data['from_name'] = 'policy_1'
+ data['name'] = 'policy_2'
+ set_module_args(data)
+ current = {
+ 'absolute_min_iops': '50IOPS',
+ 'expected_iops': '150IOPS/TB',
+ 'peak_iops': '220IOPS/TB',
+ 'peak_iops_allocation': 'used_space',
+ 'is_shared': 'true',
+ 'name': 'policy_1',
+ 'vserver': 'policy_vserver'
+ }
+ get_policy_group.side_effect = [
+ None,
+ current
+ ]
+ with pytest.raises(AnsibleFailJson) as exc:
+ self.get_policy_group_mock_object('error').apply()
+ assert exc.value.args[0]['msg'] == 'Error renaming adaptive qos policy group policy_1: NetApp API failed. Reason - test:error'
diff --git a/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_qos_policy_group.py b/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_qos_policy_group.py
new file mode 100644
index 00000000..295f9070
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_qos_policy_group.py
@@ -0,0 +1,340 @@
+# (c) 2018, NetApp, Inc
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+''' unit test template for ONTAP Ansible module '''
+
+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.ontap.tests.unit.compat import unittest
+from ansible_collections.netapp.ontap.tests.unit.compat.mock import patch, Mock
+import ansible_collections.netapp.ontap.plugins.module_utils.netapp as netapp_utils
+
+from ansible_collections.netapp.ontap.plugins.modules.na_ontap_qos_policy_group \
+ import NetAppOntapQosPolicyGroup as qos_policy_group_module # module under test
+
+if not netapp_utils.has_netapp_lib():
+ pytestmark = pytest.mark.skip('skipping as missing required netapp_lib')
+
+
+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"""
+
+
+class AnsibleFailJson(Exception):
+ """Exception class to be raised by module.fail_json and caught by the test case"""
+
+
+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 MockONTAPConnection(object):
+ ''' mock server connection to ONTAP host '''
+
+ def __init__(self, kind=None, data=None):
+ ''' save arguments '''
+ self.kind = kind
+ self.params = data
+ self.xml_in = None
+ self.xml_out = None
+
+ def invoke_successfully(self, xml, enable_tunneling): # pylint: disable=unused-argument
+ ''' mock invoke_successfully returning xml data '''
+ self.xml_in = xml
+ if self.kind == 'policy':
+ xml = self.build_policy_group_info(self.params)
+ if self.kind == 'error':
+ error = netapp_utils.zapi.NaApiError('test', 'error')
+ raise error
+ self.xml_out = xml
+ return xml
+
+ @staticmethod
+ def build_policy_group_info(vol_details):
+ ''' build xml data for volume-attributes '''
+ xml = netapp_utils.zapi.NaElement('xml')
+ attributes = {
+ 'num-records': 1,
+ 'attributes-list': {
+ 'qos-policy-group-info': {
+ 'is-shared': 'true',
+ 'max-throughput': '800KB/s,800IOPS',
+ 'min-throughput': '100IOPS',
+ 'num-workloads': 0,
+ 'pgid': 8690,
+ 'policy-group': vol_details['name'],
+ 'vserver': vol_details['vserver']
+ }
+ }
+ }
+ xml.translate_struct(attributes)
+ return xml
+
+
+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)
+ self.mock_policy_group = {
+ 'name': 'policy_1',
+ 'vserver': 'policy_vserver',
+ 'max_throughput': '800KB/s,800IOPS',
+ 'min_throughput': '100IOPS'
+ }
+
+ def mock_args(self):
+ return {
+ 'name': self.mock_policy_group['name'],
+ 'vserver': self.mock_policy_group['vserver'],
+ 'max_throughput': '800KB/s,800IOPS',
+ 'min_throughput': '100IOPS',
+ 'hostname': 'test',
+ 'username': 'test_user',
+ 'password': 'test_pass!',
+ 'https': 'False'
+ }
+
+ def get_policy_group_mock_object(self, kind=None):
+ """
+ Helper method to return an na_ontap_volume object
+ :param kind: passes this param to MockONTAPConnection()
+ :return: na_ontap_volume object
+ """
+ policy_obj = qos_policy_group_module()
+ policy_obj.asup_log_for_cserver = Mock(return_value=None)
+ policy_obj.cluster = Mock()
+ policy_obj.cluster.invoke_successfully = Mock()
+ if kind is None:
+ policy_obj.server = MockONTAPConnection()
+ else:
+ policy_obj.server = MockONTAPConnection(kind=kind, data=self.mock_policy_group)
+ return policy_obj
+
+ def test_module_fail_when_required_args_missing(self):
+ ''' required arguments are reported as errors '''
+ with pytest.raises(AnsibleFailJson) as exc:
+ set_module_args({})
+ qos_policy_group_module()
+ print('Info: %s' % exc.value.args[0]['msg'])
+
+ def test_get_nonexistent_policy(self):
+ ''' Test if get_policy_group returns None for non-existent policy_group '''
+ set_module_args(self.mock_args())
+ result = self.get_policy_group_mock_object().get_policy_group()
+ assert result is None
+
+ def test_get_existing_policy_group(self):
+ ''' Test if get_policy_group returns details for existing policy_group '''
+ set_module_args(self.mock_args())
+ result = self.get_policy_group_mock_object('policy').get_policy_group()
+ assert result['name'] == self.mock_policy_group['name']
+ assert result['vserver'] == self.mock_policy_group['vserver']
+
+ def test_create_error_missing_param(self):
+ ''' Test if create throws an error if name is not specified'''
+ data = self.mock_args()
+ del data['name']
+ set_module_args(data)
+ with pytest.raises(AnsibleFailJson) as exc:
+ self.get_policy_group_mock_object('policy').create_policy_group()
+ msg = 'missing required arguments: name'
+ assert exc.value.args[0]['msg'] == msg
+
+ def test_successful_create(self):
+ ''' Test successful create '''
+ data = self.mock_args()
+ set_module_args(data)
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_policy_group_mock_object().apply()
+ assert exc.value.args[0]['changed']
+
+ def test_create_idempotency(self):
+ ''' Test create idempotency '''
+ set_module_args(self.mock_args())
+ obj = self.get_policy_group_mock_object('policy')
+ with pytest.raises(AnsibleExitJson) as exc:
+ obj.apply()
+ assert not exc.value.args[0]['changed']
+
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_qos_policy_group.NetAppOntapQosPolicyGroup.get_policy_group')
+ def test_create_error(self, get_policy_group):
+ ''' Test create error '''
+ set_module_args(self.mock_args())
+ get_policy_group.side_effect = [
+ None
+ ]
+ with pytest.raises(AnsibleFailJson) as exc:
+ self.get_policy_group_mock_object('error').apply()
+ assert exc.value.args[0]['msg'] == 'Error creating qos policy group policy_1: NetApp API failed. Reason - test:error'
+
+ def test_successful_delete(self):
+ ''' Test delete existing volume '''
+ data = self.mock_args()
+ data['state'] = 'absent'
+ set_module_args(data)
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_policy_group_mock_object('policy').apply()
+ assert exc.value.args[0]['changed']
+
+ def test_delete_idempotency(self):
+ ''' Test delete idempotency '''
+ data = self.mock_args()
+ data['state'] = 'absent'
+ set_module_args(data)
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_policy_group_mock_object().apply()
+ assert not exc.value.args[0]['changed']
+
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_qos_policy_group.NetAppOntapQosPolicyGroup.get_policy_group')
+ def test_delete_error(self, get_policy_group):
+ ''' Test create idempotency '''
+ data = self.mock_args()
+ data['state'] = 'absent'
+ set_module_args(data)
+ current = {
+ 'max_throughput': '800KB/s,800IOPS',
+ 'min_throughput': '100IOPS',
+ 'name': 'policy_1',
+ 'vserver': 'policy_vserver'
+ }
+ get_policy_group.side_effect = [
+ current
+ ]
+ with pytest.raises(AnsibleFailJson) as exc:
+ self.get_policy_group_mock_object('error').apply()
+ assert exc.value.args[0]['msg'] == 'Error deleting qos policy group policy_1: NetApp API failed. Reason - test:error'
+
+ def test_successful_modify_max_throughput(self):
+ ''' Test successful modify max throughput '''
+ data = self.mock_args()
+ data['max_throughput'] = '900KB/s,800iops'
+ set_module_args(data)
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_policy_group_mock_object('policy').apply()
+ assert exc.value.args[0]['changed']
+
+ def test_modify_max_throughput_idempotency(self):
+ ''' Test modify idempotency '''
+ data = self.mock_args()
+ set_module_args(data)
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_policy_group_mock_object('policy').apply()
+ assert not exc.value.args[0]['changed']
+
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_qos_policy_group.NetAppOntapQosPolicyGroup.get_policy_group')
+ def test_modify_error(self, get_policy_group):
+ ''' Test create idempotency '''
+ data = self.mock_args()
+ data['max_throughput'] = '900KB/s,900IOPS'
+ set_module_args(data)
+ current = {
+ 'max_throughput': '800KB/s,800IOPS',
+ 'min_throughput': '100IOPS',
+ 'name': 'policy_1',
+ 'vserver': 'policy_vserver'
+ }
+ get_policy_group.side_effect = [
+ current
+ ]
+ with pytest.raises(AnsibleFailJson) as exc:
+ self.get_policy_group_mock_object('error').apply()
+ assert exc.value.args[0]['msg'] == 'Error modifying qos policy group policy_1: NetApp API failed. Reason - test:error'
+
+ def test_modify_is_shared_error(self):
+ ''' Test create idempotency '''
+ data = self.mock_args()
+ data['max_throughput'] = '900KB/s,900IOPS'
+ data['is_shared'] = False
+ set_module_args(data)
+ with pytest.raises(AnsibleFailJson) as exc:
+ self.get_policy_group_mock_object('policy').apply()
+ assert exc.value.args[0]['msg'] == 'Error cannot modify is_shared attribute.'
+
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_qos_policy_group.NetAppOntapQosPolicyGroup.get_policy_group')
+ def test_rename(self, get_policy_group):
+ ''' Test rename idempotency '''
+ data = self.mock_args()
+ data['name'] = 'policy_2'
+ data['from_name'] = 'policy_1'
+ set_module_args(data)
+ current = {
+ 'max_throughput': '800KB/s,800IOPS',
+ 'min_throughput': '100IOPS',
+ 'name': 'policy_1',
+ 'vserver': 'policy_vserver'
+ }
+ get_policy_group.side_effect = [
+ None,
+ current
+ ]
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_policy_group_mock_object('policy').apply()
+ assert exc.value.args[0]['changed']
+
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_qos_policy_group.NetAppOntapQosPolicyGroup.get_policy_group')
+ def test_rename_idempotency(self, get_policy_group):
+ ''' Test rename idempotency '''
+ data = self.mock_args()
+ data['name'] = 'policy_1'
+ data['from_name'] = 'policy_1'
+ current = {
+ 'max_throughput': '800KB/s,800IOPS',
+ 'min_throughput': '100IOPS',
+ 'name': 'policy_1',
+ 'vserver': 'policy_vserver'
+ }
+ get_policy_group.side_effect = [
+ current,
+ current
+ ]
+ set_module_args(data)
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_policy_group_mock_object('policy').apply()
+ assert not exc.value.args[0]['changed']
+
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_qos_policy_group.NetAppOntapQosPolicyGroup.get_policy_group')
+ def test_rename_error(self, get_policy_group):
+ ''' Test create idempotency '''
+ data = self.mock_args()
+ data['from_name'] = 'policy_1'
+ data['name'] = 'policy_2'
+ set_module_args(data)
+ current = {
+ 'is_shared': 'true',
+ 'max_throughput': '800KB/s,800IOPS',
+ 'min_throughput': '100IOPS',
+ 'name': 'policy_1',
+ 'vserver': 'policy_vserver'
+ }
+ get_policy_group.side_effect = [
+ None,
+ current
+ ]
+ with pytest.raises(AnsibleFailJson) as exc:
+ self.get_policy_group_mock_object('error').apply()
+ assert exc.value.args[0]['msg'] == 'Error renaming qos policy group policy_1: NetApp API failed. Reason - test:error'
diff --git a/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_qtree.py b/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_qtree.py
new file mode 100644
index 00000000..700dd251
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_qtree.py
@@ -0,0 +1,464 @@
+''' unit tests ONTAP Ansible module: na_ontap_quotas '''
+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.ontap.tests.unit.compat import unittest
+from ansible_collections.netapp.ontap.tests.unit.compat.mock import patch
+import ansible_collections.netapp.ontap.plugins.module_utils.netapp as netapp_utils
+
+from ansible_collections.netapp.ontap.plugins.modules.na_ontap_qtree \
+ import NetAppOntapQTree as qtree_module # module under test
+
+if not netapp_utils.has_netapp_lib():
+ pytestmark = pytest.mark.skip('skipping as missing required netapp_lib')
+
+# REST API canned responses when mocking send_request
+SRR = {
+ # common responses
+ 'is_rest': (200, {}, None),
+ 'is_zapi': (400, {}, "Unreachable"),
+ 'empty_good': (200, {}, None),
+ 'end_of_sequence': (500, None, "Ooops, the UT needs one more SRR response"),
+ 'generic_error': (400, None, "Expected error"),
+ # module specific responses
+ 'qtree_record': (200,
+ {"records": [{"svm": {"uuid": "09e9fd5e-8ebd-11e9-b162-005056b39fe7",
+ "name": "ansibleSVM"},
+ "id": 1,
+ "name": "string",
+ "security_style": "unix",
+ "unix_permissions": "abc",
+ "export_policy": {"name": "ansible"},
+ "volume": {"name": "volume1",
+ "uuid": "028baa66-41bd-11e9-81d5-00a0986138f7"}}]}, 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"""
+
+
+class AnsibleFailJson(Exception):
+ """Exception class to be raised by module.fail_json and caught by the test case"""
+
+
+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 MockONTAPConnection(object):
+ ''' mock server connection to ONTAP host '''
+
+ def __init__(self, kind=None):
+ ''' save arguments '''
+ self.type = kind
+ self.xml_in = None
+ self.xml_out = None
+
+ def invoke_successfully(self, xml, enable_tunneling): # pylint: disable=unused-argument
+ ''' mock invoke_successfully returning xml data '''
+ self.xml_in = xml
+ if self.type == 'qtree':
+ xml = self.build_quota_info()
+ elif self.type == 'qtree_fail':
+ raise netapp_utils.zapi.NaApiError(code='TEST', message="This exception is from the unit test")
+ self.xml_out = xml
+ return xml
+
+ @staticmethod
+ def build_quota_info():
+ ''' build xml data for quota-entry '''
+ xml = netapp_utils.zapi.NaElement('xml')
+ data = {'num-records': 1,
+ 'attributes-list': {'qtree-info': {'export-policy': 'ansible', 'vserver': 'ansible', 'qtree': 'ansible',
+ 'oplocks': 'enabled', 'security-style': 'unix', 'mode': 'abc',
+ 'volume': 'ansible'}}}
+ xml.translate_struct(data)
+ return xml
+
+
+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)
+ self.server = MockONTAPConnection()
+ self.onbox = False
+
+ def set_default_args(self, use_rest=None):
+ if self.onbox:
+ hostname = '10.10.10.10'
+ username = 'username'
+ password = 'password'
+ name = 'ansible'
+ vserver = 'ansible'
+ flexvol_name = 'ansible'
+ export_policy = 'ansible'
+ security_style = 'unix'
+ mode = 'abc'
+ else:
+ hostname = '10.10.10.10'
+ username = 'username'
+ password = 'password'
+ name = 'ansible'
+ vserver = 'ansible'
+ flexvol_name = 'ansible'
+ export_policy = 'ansible'
+ security_style = 'unix'
+ mode = 'abc'
+
+ args = dict({
+ 'state': 'present',
+ 'hostname': hostname,
+ 'username': username,
+ 'password': password,
+ 'name': name,
+ 'vserver': vserver,
+ 'flexvol_name': flexvol_name,
+ 'export_policy': export_policy,
+ 'security_style': security_style,
+ 'unix_permissions': mode
+ })
+
+ if use_rest is not None:
+ args['use_rest'] = use_rest
+
+ return args
+
+ @staticmethod
+ def get_qtree_mock_object(cx_type='zapi', kind=None):
+ qtree_obj = qtree_module()
+ if cx_type == 'zapi':
+ if kind is None:
+ qtree_obj.server = MockONTAPConnection()
+ else:
+ qtree_obj.server = MockONTAPConnection(kind=kind)
+ return qtree_obj
+
+ def test_module_fail_when_required_args_missing(self):
+ ''' required arguments are reported as errors '''
+ with pytest.raises(AnsibleFailJson) as exc:
+ set_module_args({})
+ qtree_module()
+ print('Info: %s' % exc.value.args[0]['msg'])
+
+ def test_ensure_get_called(self):
+ ''' test get_qtree for non-existent qtree'''
+ set_module_args(self.set_default_args(use_rest='Never'))
+ print('starting')
+ my_obj = qtree_module()
+ print('use_rest:', my_obj.use_rest)
+ my_obj.server = self.server
+ assert my_obj.get_qtree is not None
+
+ def test_ensure_get_called_existing(self):
+ ''' test get_qtree for existing qtree'''
+ set_module_args(self.set_default_args(use_rest='Never'))
+ my_obj = qtree_module()
+ my_obj.server = MockONTAPConnection(kind='qtree')
+ assert my_obj.get_qtree()
+
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_qtree.NetAppOntapQTree.create_qtree')
+ def test_successful_create(self, create_qtree):
+ ''' creating qtree and testing idempotency '''
+ set_module_args(self.set_default_args(use_rest='Never'))
+ my_obj = qtree_module()
+ if not self.onbox:
+ my_obj.server = self.server
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ assert exc.value.args[0]['changed']
+ create_qtree.assert_called_with()
+ # to reset na_helper from remembering the previous 'changed' value
+ set_module_args(self.set_default_args(use_rest='Never'))
+ my_obj = qtree_module()
+ if not self.onbox:
+ my_obj.server = MockONTAPConnection('qtree')
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ assert not exc.value.args[0]['changed']
+
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_qtree.NetAppOntapQTree.delete_qtree')
+ def test_successful_delete(self, delete_qtree):
+ ''' deleting qtree and testing idempotency '''
+ data = self.set_default_args(use_rest='Never')
+ data['state'] = 'absent'
+ set_module_args(data)
+ my_obj = qtree_module()
+ if not self.onbox:
+ my_obj.server = MockONTAPConnection('qtree')
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ assert exc.value.args[0]['changed']
+ # delete_qtree.assert_called_with()
+ # to reset na_helper from remembering the previous 'changed' value
+ my_obj = qtree_module()
+ if not self.onbox:
+ my_obj.server = self.server
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ assert not exc.value.args[0]['changed']
+
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_qtree.NetAppOntapQTree.modify_qtree')
+ def test_successful_modify(self, modify_qtree):
+ ''' modifying qtree and testing idempotency '''
+ data = self.set_default_args(use_rest='Never')
+ data['export_policy'] = 'test'
+ set_module_args(data)
+ my_obj = qtree_module()
+ if not self.onbox:
+ my_obj.server = MockONTAPConnection('qtree')
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ assert exc.value.args[0]['changed']
+ # modify_qtree.assert_called_with()
+ # to reset na_helper from remembering the previous 'changed' value
+ data['export_policy'] = 'ansible'
+ set_module_args(data)
+ my_obj = qtree_module()
+ if not self.onbox:
+ my_obj.server = MockONTAPConnection('qtree')
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ assert not exc.value.args[0]['changed']
+
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_qtree.NetAppOntapQTree.get_qtree')
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_qtree.NetAppOntapQTree.rename_qtree')
+ def test_failed_rename(self, rename_qtree, get_qtree):
+ ''' creating qtree and testing idempotency '''
+ get_qtree.side_effect = [None, None]
+ data = self.set_default_args(use_rest='Never')
+ data['from_name'] = 'ansible_old'
+ set_module_args(data)
+ my_obj = qtree_module()
+ if not self.onbox:
+ my_obj.server = self.server
+ with pytest.raises(AnsibleFailJson) as exc:
+ my_obj.apply()
+ msg = 'Error renaming: qtree %s does not exist' % data['from_name']
+ assert msg in exc.value.args[0]['msg']
+
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_qtree.NetAppOntapQTree.get_qtree')
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_qtree.NetAppOntapQTree.rename_qtree')
+ def test_successful_rename(self, rename_qtree, get_qtree):
+ ''' creating qtree and testing idempotency '''
+ data = self.set_default_args(use_rest='Never')
+ data['from_name'] = 'ansible_old'
+ qtree = dict(
+ security_style=data['security_style'],
+ unix_permissions=data['unix_permissions'],
+ export_policy=data['export_policy']
+ )
+ get_qtree.side_effect = [None, qtree]
+ set_module_args(data)
+ my_obj = qtree_module()
+ if not self.onbox:
+ my_obj.server = self.server
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ assert exc.value.args[0]['changed']
+ rename_qtree.assert_called_with(qtree)
+ # Idempotency
+ get_qtree.side_effect = [qtree, 'whatever']
+ my_obj = qtree_module()
+ if not self.onbox:
+ my_obj.server = MockONTAPConnection('qtree')
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ assert not exc.value.args[0]['changed']
+
+ def test_if_all_methods_catch_exception(self):
+ data = self.set_default_args(use_rest='Never')
+ data['from_name'] = 'ansible'
+ set_module_args(data)
+ my_obj = qtree_module()
+ if not self.onbox:
+ my_obj.server = MockONTAPConnection('qtree_fail')
+ with pytest.raises(AnsibleFailJson) as exc:
+ my_obj.create_qtree()
+ assert 'Error provisioning qtree ' in exc.value.args[0]['msg']
+ with pytest.raises(AnsibleFailJson) as exc:
+ my_obj.delete_qtree(self.get_qtree_mock_object())
+ assert 'Error deleting qtree ' in exc.value.args[0]['msg']
+ with pytest.raises(AnsibleFailJson) as exc:
+ my_obj.modify_qtree(self.get_qtree_mock_object())
+ assert 'Error modifying qtree ' in exc.value.args[0]['msg']
+ with pytest.raises(AnsibleFailJson) as exc:
+ my_obj.rename_qtree(self.get_qtree_mock_object())
+ assert 'Error renaming qtree ' in exc.value.args[0]['msg']
+
+ @patch('ansible_collections.netapp.ontap.plugins.module_utils.netapp.OntapRestAPI.send_request')
+ def test_rest_error(self, mock_request):
+ data = self.set_default_args()
+ set_module_args(data)
+ mock_request.side_effect = [
+ SRR['is_rest'],
+ SRR['generic_error'],
+ SRR['end_of_sequence']
+ ]
+ with pytest.raises(AnsibleFailJson) as exc:
+ self.get_qtree_mock_object(cx_type='rest').apply()
+ assert exc.value.args[0]['msg'] == SRR['generic_error'][2]
+
+ @patch('ansible_collections.netapp.ontap.plugins.module_utils.netapp.OntapRestAPI.send_request')
+ def test_successful_create_rest(self, mock_request):
+ data = self.set_default_args()
+ set_module_args(data)
+ mock_request.side_effect = [
+ SRR['is_rest'],
+ SRR['empty_good'], # get
+ SRR['empty_good'], # post
+ SRR['end_of_sequence']
+ ]
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_qtree_mock_object(cx_type='rest').apply()
+ assert exc.value.args[0]['changed']
+
+ @patch('ansible_collections.netapp.ontap.plugins.module_utils.netapp.OntapRestAPI.send_request')
+ def test_idempotent_create_rest(self, mock_request):
+ data = self.set_default_args()
+ set_module_args(data)
+ mock_request.side_effect = [
+ SRR['is_rest'],
+ SRR['qtree_record'], # get
+ SRR['end_of_sequence']
+ ]
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_qtree_mock_object(cx_type='rest').apply()
+ assert not exc.value.args[0]['changed']
+
+ @patch('ansible_collections.netapp.ontap.plugins.module_utils.netapp.OntapRestAPI.send_request')
+ def test_successful_delete_rest(self, mock_request):
+ data = self.set_default_args()
+ data['state'] = 'absent'
+ set_module_args(data)
+ mock_request.side_effect = [
+ SRR['is_rest'],
+ SRR['qtree_record'], # get
+ SRR['empty_good'], # delete
+ SRR['end_of_sequence']
+ ]
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_qtree_mock_object(cx_type='rest').apply()
+ assert exc.value.args[0]['changed']
+
+ @patch('ansible_collections.netapp.ontap.plugins.module_utils.netapp.OntapRestAPI.send_request')
+ def test_idempotent_delete_rest(self, mock_request):
+ data = self.set_default_args()
+ data['state'] = 'absent'
+ set_module_args(data)
+ mock_request.side_effect = [
+ SRR['is_rest'],
+ SRR['empty_good'], # get
+ SRR['end_of_sequence']
+ ]
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_qtree_mock_object(cx_type='rest').apply()
+ assert not exc.value.args[0]['changed']
+
+ @patch('ansible_collections.netapp.ontap.plugins.module_utils.netapp.OntapRestAPI.send_request')
+ def test_successful_modify_rest(self, mock_request):
+ data = self.set_default_args()
+ data['state'] = 'present'
+ data['unix_permissions'] = 'abcde'
+ set_module_args(data)
+ mock_request.side_effect = [
+ SRR['is_rest'],
+ SRR['qtree_record'], # get
+ SRR['empty_good'], # patch
+ SRR['end_of_sequence']
+ ]
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_qtree_mock_object(cx_type='rest').apply()
+ assert exc.value.args[0]['changed']
+
+ @patch('ansible_collections.netapp.ontap.plugins.module_utils.netapp.OntapRestAPI.send_request')
+ def test_idempotent_modify_rest(self, mock_request):
+ data = self.set_default_args()
+ data['state'] = 'present'
+ set_module_args(data)
+ mock_request.side_effect = [
+ SRR['is_rest'],
+ SRR['qtree_record'], # get
+ SRR['end_of_sequence']
+ ]
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_qtree_mock_object(cx_type='rest').apply()
+ assert not exc.value.args[0]['changed']
+
+ @patch('ansible_collections.netapp.ontap.plugins.module_utils.netapp.OntapRestAPI.send_request')
+ def test_successful_rename_rest(self, mock_request):
+ data = self.set_default_args()
+ data['state'] = 'present'
+ data['from_name'] = 'abcde'
+ # data['unix_permissions'] = 'abcde'
+ set_module_args(data)
+ mock_request.side_effect = [
+ SRR['is_rest'],
+ SRR['empty_good'], # get (current)
+ SRR['qtree_record'], # get (from)
+ SRR['empty_good'], # patch
+ SRR['end_of_sequence']
+ ]
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_qtree_mock_object(cx_type='rest').apply()
+ assert exc.value.args[0]['changed']
+
+ @patch('ansible_collections.netapp.ontap.plugins.module_utils.netapp.OntapRestAPI.send_request')
+ def test_successful_rename_rest_idempotent(self, mock_request):
+ data = self.set_default_args()
+ data['state'] = 'present'
+ data['from_name'] = 'abcde'
+ # data['unix_permissions'] = 'abcde'
+ set_module_args(data)
+ mock_request.side_effect = [
+ SRR['is_rest'],
+ SRR['qtree_record'], # get (current exists)
+ SRR['empty_good'], # patch
+ SRR['end_of_sequence']
+ ]
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_qtree_mock_object(cx_type='rest').apply()
+ assert not exc.value.args[0]['changed']
+
+ @patch('ansible_collections.netapp.ontap.plugins.module_utils.netapp.OntapRestAPI.send_request')
+ def test_successful_rename_and_modify_rest(self, mock_request):
+ data = self.set_default_args()
+ data['state'] = 'present'
+ data['from_name'] = 'abcde'
+ data['unix_permissions'] = 'abcde'
+ set_module_args(data)
+ mock_request.side_effect = [
+ SRR['is_rest'],
+ SRR['empty_good'], # get (current)
+ SRR['qtree_record'], # get (from)
+ SRR['empty_good'], # patch (rename)
+ SRR['empty_good'], # patch (modify)
+ SRR['end_of_sequence']
+ ]
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_qtree_mock_object(cx_type='rest').apply()
+ assert exc.value.args[0]['changed']
diff --git a/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_quota_policy.py b/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_quota_policy.py
new file mode 100644
index 00000000..55f59ae9
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_quota_policy.py
@@ -0,0 +1,207 @@
+# (c) 2019, NetApp, Inc
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+''' unit tests for Ansible module: na_ontap_quota_policy '''
+
+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.ontap.tests.unit.compat import unittest
+from ansible_collections.netapp.ontap.tests.unit.compat.mock import patch, Mock
+import ansible_collections.netapp.ontap.plugins.module_utils.netapp as netapp_utils
+
+from ansible_collections.netapp.ontap.plugins.modules.na_ontap_quota_policy \
+ import NetAppOntapQuotaPolicy as quota_policy_module # module under test
+
+if not netapp_utils.has_netapp_lib():
+ pytestmark = pytest.mark.skip('skipping as missing required netapp_lib')
+
+
+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"""
+
+
+class AnsibleFailJson(Exception):
+ """Exception class to be raised by module.fail_json and caught by the test case"""
+
+
+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 MockONTAPConnection(object):
+ ''' mock server connection to ONTAP host '''
+
+ def __init__(self, kind=None, data=None):
+ ''' save arguments '''
+ self.kind = kind
+ self.params = data
+ self.xml_in = None
+ self.xml_out = None
+
+ def invoke_successfully(self, xml, enable_tunneling): # pylint: disable=unused-argument
+ ''' mock invoke_successfully returning xml data '''
+ self.xml_in = xml
+ if self.kind == 'quota':
+ xml = self.build_quota_policy_info(self.params, True)
+ if self.kind == 'quota_not_assigned':
+ xml = self.build_quota_policy_info(self.params, False)
+ elif self.kind == 'zapi_error':
+ error = netapp_utils.zapi.NaApiError('test', 'error')
+ raise error
+ self.xml_out = xml
+ return xml
+
+ @staticmethod
+ def build_quota_policy_info(params, assigned):
+ xml = netapp_utils.zapi.NaElement('xml')
+ attributes = {'num-records': 1,
+ 'attributes-list': {
+ 'quota-policy-info': {
+ 'policy-name': params['name']},
+ 'vserver-info': {
+ 'quota-policy': params['name'] if assigned else 'default'}
+ }}
+ xml.translate_struct(attributes)
+ return xml
+
+
+class TestMyModule(unittest.TestCase):
+ ''' Unit tests for na_ontap_quota_policy '''
+
+ 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)
+ self.mock_quota_policy = {
+ 'state': 'present',
+ 'vserver': 'test_vserver',
+ 'name': 'test_policy'
+ }
+
+ def mock_args(self):
+ return {
+ 'state': self.mock_quota_policy['state'],
+ 'vserver': self.mock_quota_policy['vserver'],
+ 'name': self.mock_quota_policy['name'],
+ 'hostname': 'test',
+ 'username': 'test_user',
+ 'password': 'test_pass!'
+ }
+
+ def get_quota_policy_mock_object(self, kind=None):
+ policy_obj = quota_policy_module()
+ netapp_utils.ems_log_event = Mock(return_value=None)
+ if kind is None:
+ policy_obj.server = MockONTAPConnection()
+ else:
+ policy_obj.server = MockONTAPConnection(kind=kind, data=self.mock_quota_policy)
+ return policy_obj
+
+ def test_module_fail_when_required_args_missing(self):
+ ''' required arguments are reported as errors '''
+ with pytest.raises(AnsibleFailJson) as exc:
+ set_module_args({})
+ quota_policy_module()
+ print('Info: %s' % exc.value.args[0]['msg'])
+
+ def test_successfully_create(self):
+ set_module_args(self.mock_args())
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_quota_policy_mock_object().apply()
+ assert exc.value.args[0]['changed']
+
+ def test_create_idempotency(self):
+ set_module_args(self.mock_args())
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_quota_policy_mock_object('quota').apply()
+ assert not exc.value.args[0]['changed']
+
+ def test_cannot_delete(self):
+ data = self.mock_args()
+ data['state'] = 'absent'
+ set_module_args(data)
+ with pytest.raises(AnsibleFailJson) as exc:
+ self.get_quota_policy_mock_object('quota').apply()
+ msg = 'Error policy test_policy cannot be deleted as it is assigned to the vserver test_vserver'
+ assert msg == exc.value.args[0]['msg']
+
+ def test_successfully_delete(self):
+ data = self.mock_args()
+ data['state'] = 'absent'
+ set_module_args(data)
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_quota_policy_mock_object('quota_not_assigned').apply()
+ assert exc.value.args[0]['changed']
+
+ def test_delete_idempotency(self):
+ data = self.mock_args()
+ data['state'] = 'absent'
+ set_module_args(data)
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_quota_policy_mock_object().apply()
+ assert not exc.value.args[0]['changed']
+
+ def test_successfully_assign(self):
+ data = self.mock_args()
+ set_module_args(data)
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_quota_policy_mock_object('quota_not_assigned').apply()
+ assert exc.value.args[0]['changed']
+
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_quota_policy.NetAppOntapQuotaPolicy.get_quota_policy')
+ def test_successful_rename(self, get_volume):
+ data = self.mock_args()
+ data['name'] = 'new_policy'
+ data['from_name'] = 'test_policy'
+ set_module_args(data)
+ current = {
+ 'name': 'test_policy'
+ }
+ get_volume.side_effect = [
+ None,
+ current
+ ]
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_quota_policy_mock_object('quota').apply()
+ assert exc.value.args[0]['changed']
+
+ def test_error(self):
+ data = self.mock_args()
+ set_module_args(data)
+ with pytest.raises(AnsibleFailJson) as exc:
+ self.get_quota_policy_mock_object('zapi_error').get_quota_policy()
+ assert exc.value.args[0]['msg'] == 'Error fetching quota policy test_policy: NetApp API failed. Reason - test:error'
+ with pytest.raises(AnsibleFailJson) as exc:
+ self.get_quota_policy_mock_object('zapi_error').create_quota_policy()
+ assert exc.value.args[0]['msg'] == 'Error creating quota policy test_policy: NetApp API failed. Reason - test:error'
+ with pytest.raises(AnsibleFailJson) as exc:
+ self.get_quota_policy_mock_object('zapi_error').delete_quota_policy()
+ assert exc.value.args[0]['msg'] == 'Error deleting quota policy test_policy: NetApp API failed. Reason - test:error'
+ data['name'] = 'new_policy'
+ data['from_name'] = 'test_policy'
+ set_module_args(data)
+ with pytest.raises(AnsibleFailJson) as exc:
+ self.get_quota_policy_mock_object('zapi_error').rename_quota_policy()
+ assert exc.value.args[0]['msg'] == 'Error renaming quota policy test_policy: NetApp API failed. Reason - test:error'
diff --git a/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_quotas.py b/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_quotas.py
new file mode 100644
index 00000000..134269d7
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_quotas.py
@@ -0,0 +1,243 @@
+''' unit tests ONTAP Ansible module: na_ontap_quotas '''
+
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+import json
+import pytest
+
+from ansible_collections.netapp.ontap.tests.unit.compat import unittest
+from ansible_collections.netapp.ontap.tests.unit.compat.mock import patch
+from ansible.module_utils import basic
+from ansible.module_utils._text import to_bytes
+import ansible_collections.netapp.ontap.plugins.module_utils.netapp as netapp_utils
+
+from ansible_collections.netapp.ontap.plugins.modules.na_ontap_quotas \
+ import NetAppONTAPQuotas as my_module
+
+if not netapp_utils.has_netapp_lib():
+ pytestmark = pytest.mark.skip('skipping as missing required netapp_lib')
+
+
+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 MockONTAPConnection(object):
+ ''' mock server connection to ONTAP host '''
+
+ def __init__(self, kind=None):
+ ''' save arguments '''
+ self.type = kind
+ self.xml_in = None
+ self.xml_out = None
+
+ def invoke_successfully(self, xml, enable_tunneling): # pylint: disable=unused-argument
+ ''' mock invoke_successfully returning xml data '''
+ self.xml_in = xml
+ if self.type == 'quotas':
+ xml = self.build_quota_info()
+ elif self.type == 'quota_fail':
+ raise netapp_utils.zapi.NaApiError(code='TEST', message="This exception is from the unit test")
+ self.xml_out = xml
+ return xml
+
+ @staticmethod
+ def build_quota_info():
+ ''' build xml data for quota-entry '''
+ xml = netapp_utils.zapi.NaElement('xml')
+ data = {'num-records': 1,
+ 'attributes-list': {'quota-entry': {'volume': 'ansible',
+ 'file-limit': '-', 'disk-limit': '-',
+ 'soft-file-limit': '-', 'soft-disk-limit': '-', 'threshold': '-'}},
+ 'status': 'true'}
+ xml.translate_struct(data)
+ return xml
+
+
+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)
+ self.server = MockONTAPConnection()
+ self.onbox = False
+
+ def set_default_args(self):
+ if self.onbox:
+ hostname = '10.193.75.3'
+ username = 'admin'
+ password = 'netapp1!'
+ volume = 'ansible'
+ vserver = 'ansible'
+ policy = 'ansible'
+ quota_target = '/vol/ansible'
+ type = 'user'
+ else:
+ hostname = 'hostname'
+ username = 'username'
+ password = 'password'
+ volume = 'ansible'
+ vserver = 'ansible'
+ policy = 'ansible'
+ quota_target = '/vol/ansible'
+ type = 'user'
+ return dict({
+ 'hostname': hostname,
+ 'username': username,
+ 'password': password,
+ 'volume': volume,
+ 'vserver': vserver,
+ 'policy': policy,
+ 'quota_target': quota_target,
+ 'type': type
+ })
+
+ def test_module_fail_when_required_args_missing(self):
+ ''' required arguments are reported as errors '''
+ with pytest.raises(AnsibleFailJson) as exc:
+ set_module_args({})
+ my_module()
+ print('Info: %s' % exc.value.args[0]['msg'])
+
+ def test_ensure_get_called(self):
+ ''' test get_quota for non-existent quota'''
+ set_module_args(self.set_default_args())
+ my_obj = my_module()
+ my_obj.server = self.server
+ assert my_obj.get_quotas is not None
+
+ def test_ensure_get_called_existing(self):
+ ''' test get_quota for existing quota'''
+ set_module_args(self.set_default_args())
+ my_obj = my_module()
+ my_obj.server = MockONTAPConnection(kind='quotas')
+ assert my_obj.get_quotas()
+
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_quotas.NetAppONTAPQuotas.quota_entry_set')
+ def test_successful_create(self, quota_entry_set):
+ ''' creating quota and testing idempotency '''
+ data = self.set_default_args()
+ data.update({'file_limit': '3',
+ 'disk_limit': '4',
+ 'soft_file_limit': '3',
+ 'soft_disk_limit': '4',
+ })
+ # data['file_limit'] = '3'
+ # data['disk_limit'] = '4'
+ # data['threshold'] = '4'
+ set_module_args(data)
+ my_obj = my_module()
+ if not self.onbox:
+ my_obj.server = self.server
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ assert exc.value.args[0]['changed']
+ quota_entry_set.assert_called_with()
+ # to reset na_helper from remembering the previous 'changed' value
+ set_module_args(self.set_default_args())
+ my_obj = my_module()
+ if not self.onbox:
+ my_obj.server = MockONTAPConnection('quotas')
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ assert not exc.value.args[0]['changed']
+
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_quotas.NetAppONTAPQuotas.quota_entry_delete')
+ def test_successful_delete(self, quota_entry_delete):
+ ''' deleting quota and testing idempotency '''
+ data = self.set_default_args()
+ data['state'] = 'absent'
+ set_module_args(data)
+ my_obj = my_module()
+ if not self.onbox:
+ my_obj.server = MockONTAPConnection('quotas')
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ assert exc.value.args[0]['changed']
+ quota_entry_delete.assert_called_with()
+ # to reset na_helper from remembering the previous 'changed' value
+ my_obj = my_module()
+ if not self.onbox:
+ my_obj.server = self.server
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ assert not exc.value.args[0]['changed']
+
+ def test_successful_modify(self):
+ ''' modifying quota and testing idempotency '''
+ data = self.set_default_args()
+ data['file_limit'] = '3'
+ set_module_args(data)
+ my_obj = my_module()
+ if not self.onbox:
+ my_obj.server = MockONTAPConnection('quotas')
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ assert exc.value.args[0]['changed']
+
+ def test_quota_on_off(self):
+ ''' quota set on or off '''
+ data = self.set_default_args()
+ data['set_quota_status'] = 'false'
+ set_module_args(data)
+ my_obj = my_module()
+ if not self.onbox:
+ my_obj.server = MockONTAPConnection('quotas')
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ assert not exc.value.args[0]['changed']
+
+ def test_if_all_methods_catch_exception(self):
+ module_args = {}
+ module_args.update(self.set_default_args())
+ set_module_args(module_args)
+ my_obj = my_module()
+ if not self.onbox:
+ my_obj.server = MockONTAPConnection('quota_fail')
+ with pytest.raises(AnsibleFailJson) as exc:
+ my_obj.get_quota_status()
+ assert 'Error fetching quotas status info' in exc.value.args[0]['msg']
+ with pytest.raises(AnsibleFailJson) as exc:
+ my_obj.get_quotas()
+ assert 'Error fetching quotas info' in exc.value.args[0]['msg']
+ with pytest.raises(AnsibleFailJson) as exc:
+ my_obj.quota_entry_set()
+ assert 'Error adding/modifying quota entry' in exc.value.args[0]['msg']
+ with pytest.raises(AnsibleFailJson) as exc:
+ my_obj.quota_entry_delete()
+ assert 'Error deleting quota entry' in exc.value.args[0]['msg']
+ with pytest.raises(AnsibleFailJson) as exc:
+ my_obj.quota_entry_modify(module_args)
+ assert 'Error modifying quota entry' in exc.value.args[0]['msg']
+ with pytest.raises(AnsibleFailJson) as exc:
+ my_obj.on_or_off_quota('quota-on')
+ assert 'Error setting quota-on for ansible' in exc.value.args[0]['msg']
diff --git a/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_rest_cli.py b/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_rest_cli.py
new file mode 100644
index 00000000..d145a53e
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_rest_cli.py
@@ -0,0 +1,137 @@
+# (c) 2019, NetApp, Inc
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+''' unit tests for Ansible module: na_ontap_rest_cli'''
+
+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.ontap.tests.unit.compat import unittest
+from ansible_collections.netapp.ontap.tests.unit.compat.mock import patch
+import ansible_collections.netapp.ontap.plugins.module_utils.netapp as netapp_utils
+
+from ansible_collections.netapp.ontap.plugins.modules.na_ontap_rest_cli \
+ import NetAppONTAPCommandREST as rest_cli_module # module under test
+
+if not netapp_utils.has_netapp_lib():
+ pytestmark = pytest.mark.skip('skipping as missing required netapp_lib')
+
+
+# REST API canned responses when mocking send_request
+SRR = {
+ # common responses
+ 'is_rest': (200, {}, None),
+ 'empty_good': (200, {}, None),
+ 'end_of_sequence': (500, None, "Ooops, the UT needs one more SRR response"),
+ 'generic_error': (400, None, "Expected error"),
+ # module specific response
+ 'allow': (200, {'Allow': ['GET', 'WHATEVER']}, 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"""
+
+
+class AnsibleFailJson(Exception):
+ """Exception class to be raised by module.fail_json and caught by the test case"""
+
+
+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 MockONTAPConnection(object):
+ ''' mock server connection to ONTAP host '''
+
+ def __init__(self, data=None):
+ ''' save arguments '''
+ self.params = data
+ self.xml_in = None
+ self.xml_out = None
+
+ def invoke_successfully(self, xml, enable_tunneling): # pylint: disable=unused-argument
+ ''' mock invoke_successfully returning xml data '''
+ self.xml_in = xml
+ self.xml_out = xml
+ return xml
+
+
+class TestMyModule(unittest.TestCase):
+ ''' Unit tests for na_ontap_job_schedule '''
+
+ 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 mock_args(self):
+ return {
+ 'hostname': 'test',
+ 'username': 'test_user',
+ 'password': 'test_pass!',
+ 'https': False,
+ 'command': 'volume',
+ 'verb': 'GET',
+ 'params': {'fields': 'size,percent_used'}
+ }
+
+ def get_cli_mock_object(self):
+ # For rest, mocking is achieved through side_effect
+ return rest_cli_module()
+
+ def test_module_fail_when_required_args_missing(self):
+ ''' required arguments are reported as errors '''
+ with pytest.raises(AnsibleFailJson) as exc:
+ set_module_args({})
+ rest_cli_module()
+ print('Info: %s' % exc.value.args[0]['msg'])
+
+ @patch('ansible_collections.netapp.ontap.plugins.module_utils.netapp.OntapRestAPI.send_request')
+ def test_rest_cli(self, mock_request):
+ data = dict(self.mock_args())
+ set_module_args(data)
+ mock_request.side_effect = [
+ SRR['is_rest'],
+ SRR['empty_good'],
+ SRR['end_of_sequence']
+ ]
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_cli_mock_object().apply()
+ assert exc.value.args[0]['changed']
+
+ @patch('ansible_collections.netapp.ontap.plugins.module_utils.netapp.OntapRestAPI.send_request')
+ def test_rest_cli_options(self, mock_request):
+ data = dict(self.mock_args())
+ data['verb'] = 'OPTIONS'
+ set_module_args(data)
+ mock_request.side_effect = [
+ SRR['is_rest'],
+ SRR['allow'],
+ SRR['end_of_sequence']
+ ]
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_cli_mock_object().apply()
+ assert exc.value.args[0]['changed']
+ assert 'Allow' in exc.value.args[0]['msg']
diff --git a/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_rest_info.py b/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_rest_info.py
new file mode 100644
index 00000000..a7b400f9
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_rest_info.py
@@ -0,0 +1,543 @@
+# (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 ONTAP REST APIs Ansible module: na_ontap_rest_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.ontap.tests.unit.compat import unittest
+from ansible_collections.netapp.ontap.tests.unit.compat.mock import patch
+
+from ansible_collections.netapp.ontap.plugins.modules.na_ontap_rest_info \
+ import NetAppONTAPGatherInfo as ontap_rest_info_module
+
+# REST API canned responses when mocking send_request
+SRR = {
+ # common responses
+ 'validate_ontap_version_pass': (200, {'version': 'ontap_version'}, None),
+ 'validate_ontap_version_fail': (200, None, 'API not found error'),
+ 'get_subset_info': (200,
+ {'_links': {'self': {'href': 'dummy_href'}},
+ 'num_records': 3,
+ 'records': [{'name': 'dummy_vol1'},
+ {'name': 'dummy_vol2'},
+ {'name': 'dummy_vol3'}],
+ 'version': 'ontap_version'}, None),
+ 'get_subset_info_with_next': (200,
+ {'_links': {'self': {'href': 'dummy_href'},
+ 'next': {'href': '/api/next_record_api'}},
+ 'num_records': 3,
+ 'records': [{'name': 'dummy_vol1'},
+ {'name': 'dummy_vol2'},
+ {'name': 'dummy_vol3'}],
+ 'version': 'ontap_version'}, None),
+ 'get_next_record': (200,
+ {'_links': {'self': {'href': 'dummy_href'}},
+ 'num_records': 2,
+ 'records': [{'name': 'dummy_vol1'},
+ {'name': 'dummy_vol2'}],
+ 'version': 'ontap_version'}, None),
+ 'metrocluster_post': (200,
+ {'job': {
+ 'uuid': 'fde79888-692a-11ea-80c2-005056b39fe7',
+ '_links': {
+ 'self': {
+ 'href': '/api/cluster/jobs/fde79888-692a-11ea-80c2-005056b39fe7'}}
+ }},
+ None),
+ 'metrocluster_return': (200,
+ {"_links": {
+ "self": {
+ "href": "/api/cluster/metrocluster/diagnostics"
+ }
+ }, "aggregate": {
+ "state": "ok",
+ "summary": {
+ "message": ""
+ }, "timestamp": "2020-07-22T16:42:51-07:00"
+ }}, None),
+ 'job': (200,
+ {
+ "uuid": "cca3d070-58c6-11ea-8c0c-005056826c14",
+ "description": "POST /api/cluster/metrocluster",
+ "state": "success",
+ "message": "There are not enough disks in Pool1.",
+ "code": 2432836,
+ "start_time": "2020-02-26T10:35:44-08:00",
+ "end_time": "2020-02-26T10:47:38-08:00",
+ "_links": {
+ "self": {
+ "href": "/api/cluster/jobs/cca3d070-58c6-11ea-8c0c-005056826c14"
+ }
+ }
+ }, 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"""
+
+
+class AnsibleFailJson(Exception):
+ """Exception class to be raised by module.fail_json and caught by the test case"""
+
+
+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(self):
+ return dict({
+ 'hostname': 'hostname',
+ 'username': 'username',
+ 'password': 'password',
+ 'https': True,
+ 'validate_certs': False
+ })
+
+ def set_args_run_ontap_version_check(self):
+ return dict({
+ 'hostname': 'hostname',
+ 'username': 'username',
+ 'password': 'password',
+ 'https': True,
+ 'validate_certs': False,
+ 'max_records': 1024,
+ 'gather_subset': ['volume_info']
+ })
+
+ def set_args_run_metrocluster_diag(self):
+ return dict({
+ 'hostname': 'hostname',
+ 'username': 'username',
+ 'password': 'password',
+ 'https': True,
+ 'validate_certs': False,
+ 'max_records': 1024,
+ 'gather_subset': ['cluster/metrocluster/diagnostics']
+ })
+
+ def set_args_run_ontap_gather_facts_for_vserver_info(self):
+ return dict({
+ 'hostname': 'hostname',
+ 'username': 'username',
+ 'password': 'password',
+ 'https': True,
+ 'validate_certs': False,
+ 'max_records': 1024,
+ 'gather_subset': ['vserver_info']
+ })
+
+ def set_args_run_ontap_gather_facts_for_volume_info(self):
+ return dict({
+ 'hostname': 'hostname',
+ 'username': 'username',
+ 'password': 'password',
+ 'https': True,
+ 'validate_certs': False,
+ 'max_records': 1024,
+ 'gather_subset': ['volume_info']
+ })
+
+ def set_args_run_ontap_gather_facts_for_all_subsets(self):
+ return dict({
+ 'hostname': 'hostname',
+ 'username': 'username',
+ 'password': 'password',
+ 'https': True,
+ 'validate_certs': False,
+ 'max_records': 1024,
+ 'gather_subset': ['all']
+ })
+
+ def set_args_run_ontap_gather_facts_for_all_subsets_with_fields_section_pass(self):
+ return dict({
+ 'hostname': 'hostname',
+ 'username': 'username',
+ 'password': 'password',
+ 'https': True,
+ 'validate_certs': False,
+ 'max_records': 1024,
+ 'fields': '*',
+ 'gather_subset': ['all']
+ })
+
+ def set_args_run_ontap_gather_facts_for_all_subsets_with_fields_section_fail(self):
+ return dict({
+ 'hostname': 'hostname',
+ 'username': 'username',
+ 'password': 'password',
+ 'https': True,
+ 'validate_certs': False,
+ 'max_records': 1024,
+ 'fields': ['uuid', 'name', 'node'],
+ 'gather_subset': ['all']
+ })
+
+ def set_args_run_ontap_gather_facts_for_aggregate_info_with_fields_section_pass(self):
+ return dict({
+ 'hostname': 'hostname',
+ 'username': 'username',
+ 'password': 'password',
+ 'https': True,
+ 'fields': ['uuid', 'name', 'node'],
+ 'validate_certs': False,
+ 'max_records': 1024,
+ 'gather_subset': ['aggregate_info']
+ })
+
+ def set_args_get_all_records_for_volume_info_to_check_next_api_call_functionality_pass(self):
+ return dict({
+ 'hostname': 'hostname',
+ 'username': 'username',
+ 'password': 'password',
+ 'https': True,
+ 'validate_certs': False,
+ 'max_records': 3,
+ 'gather_subset': ['volume_info']
+ })
+
+ @patch('ansible_collections.netapp.ontap.plugins.module_utils.netapp.OntapRestAPI.send_request')
+ def test_run_ontap_version_check_for_9_6_pass(self, mock_request):
+ set_module_args(self.set_args_run_ontap_version_check())
+ my_obj = ontap_rest_info_module()
+ mock_request.side_effect = [
+ SRR['validate_ontap_version_pass'],
+ SRR['get_subset_info'],
+ ]
+
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ assert exc.value.args[0]['changed']
+
+ @patch('ansible_collections.netapp.ontap.plugins.module_utils.netapp.OntapRestAPI.send_request')
+ def test_run_ontap_version_check_for_10_2_pass(self, mock_request):
+ set_module_args(self.set_args_run_ontap_version_check())
+ my_obj = ontap_rest_info_module()
+ mock_request.side_effect = [
+ SRR['validate_ontap_version_pass'],
+ SRR['get_subset_info'],
+ ]
+
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ assert exc.value.args[0]['changed']
+
+ @patch('ansible_collections.netapp.ontap.plugins.module_utils.netapp.OntapRestAPI.send_request')
+ def test_run_ontap_version_check_for_9_2_fail(self, mock_request):
+ ''' Test for Checking the ONTAP version '''
+ set_module_args(self.set_args_run_ontap_version_check())
+ my_obj = ontap_rest_info_module()
+ mock_request.side_effect = [
+ SRR['validate_ontap_version_fail'],
+ ]
+
+ with pytest.raises(AnsibleFailJson) as exc:
+ my_obj.apply()
+ assert exc.value.args[0]['msg'] == SRR['validate_ontap_version_fail'][2]
+
+ @patch('ansible_collections.netapp.ontap.plugins.module_utils.netapp.OntapRestAPI.send_request')
+ def test_run_metrocluster_pass(self, mock_request):
+ set_module_args(self.set_args_run_metrocluster_diag())
+ my_obj = ontap_rest_info_module()
+ gather_subset = ['cluster/metrocluster/diagnostics']
+ mock_request.side_effect = [
+ SRR['validate_ontap_version_pass'],
+ SRR['metrocluster_post'],
+ SRR['job'],
+ SRR['metrocluster_return']
+ ]
+
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ print('Info: test_run_metrocluster_digag_pass: %s' % repr(exc.value.args))
+ assert set(exc.value.args[0]['ontap_info']) == set(gather_subset)
+
+ @patch('ansible_collections.netapp.ontap.plugins.module_utils.netapp.OntapRestAPI.send_request')
+ def test_run_ontap_gather_facts_for_vserver_info_pass(self, mock_request):
+ set_module_args(self.set_args_run_ontap_gather_facts_for_vserver_info())
+ my_obj = ontap_rest_info_module()
+ gather_subset = ['svm/svms']
+ mock_request.side_effect = [
+ SRR['validate_ontap_version_pass'],
+ SRR['get_subset_info'],
+ ]
+
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ print('Info: test_run_ontap_gather_facts_for_vserver_info_pass: %s' % repr(exc.value.args))
+ assert set(exc.value.args[0]['ontap_info']) == set(gather_subset)
+
+ @patch('ansible_collections.netapp.ontap.plugins.module_utils.netapp.OntapRestAPI.send_request')
+ def test_run_ontap_gather_facts_for_volume_info_pass(self, mock_request):
+ set_module_args(self.set_args_run_ontap_gather_facts_for_volume_info())
+ my_obj = ontap_rest_info_module()
+ gather_subset = ['storage/volumes']
+ mock_request.side_effect = [
+ SRR['validate_ontap_version_pass'],
+ SRR['get_subset_info'],
+ ]
+
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ print('Info: test_run_ontap_gather_facts_for_volume_info_pass: %s' % repr(exc.value.args))
+ assert set(exc.value.args[0]['ontap_info']) == set(gather_subset)
+
+ # Super Important, Metrocluster doesn't call get_subset_info and has 3 api calls instead of 1!!!!
+ # The metrocluster calls need to be in the correct place. The Module return the keys in a sorted list.
+ @patch('ansible_collections.netapp.ontap.plugins.module_utils.netapp.OntapRestAPI.send_request')
+ def test_run_ontap_gather_facts_for_all_subsets_pass(self, mock_request):
+ set_module_args(self.set_args_run_ontap_gather_facts_for_all_subsets())
+ my_obj = ontap_rest_info_module()
+ gather_subset = ['application/applications', 'application/templates', 'cloud/targets', 'cluster/chassis', 'cluster/jobs',
+ 'cluster/metrocluster/diagnostics', 'cluster/metrics', 'cluster/nodes', 'cluster/peers', 'cluster/schedules',
+ 'cluster/software', 'cluster/software/download', 'cluster/software/history', 'cluster/software/packages',
+ 'name-services/dns', 'name-services/ldap', 'name-services/name-mappings', 'name-services/nis',
+ 'network/ethernet/broadcast-domains', 'network/ethernet/ports', 'network/fc/logins', 'network/fc/wwpn-aliases',
+ 'network/ip/interfaces', 'network/ip/routes', 'network/ip/service-policies', 'network/ipspaces',
+ 'protocols/cifs/home-directory/search-paths', 'protocols/cifs/services', 'protocols/cifs/shares',
+ 'protocols/san/fcp/services', 'protocols/san/igroups', 'protocols/san/iscsi/credentials',
+ 'protocols/san/iscsi/services', 'protocols/san/lun-maps', 'security/accounts', 'security/roles', 'storage/aggregates',
+ 'storage/disks', 'storage/flexcache/flexcaches', 'storage/flexcache/origins', 'storage/luns', 'storage/namespaces',
+ 'storage/ports', 'storage/qos/policies', 'storage/qtrees', 'storage/quota/reports', 'storage/quota/rules',
+ 'storage/shelves', 'storage/snapshot-policies', 'storage/volumes', 'support/autosupport', 'support/autosupport/messages',
+ 'support/ems', 'support/ems/destinations', 'support/ems/events', 'support/ems/filters', 'svm/peers', 'svm/peer-permissions',
+ 'svm/svms']
+ mock_request.side_effect = [
+ SRR['validate_ontap_version_pass'],
+ SRR['get_subset_info'],
+ SRR['get_subset_info'],
+ SRR['get_subset_info'],
+ SRR['get_subset_info'],
+ SRR['get_subset_info'],
+ SRR['get_subset_info'],
+ SRR['metrocluster_post'],
+ SRR['job'],
+ SRR['metrocluster_return'],
+ SRR['get_subset_info'],
+ SRR['get_subset_info'],
+ SRR['get_subset_info'],
+ SRR['get_subset_info'],
+ SRR['get_subset_info'],
+ SRR['get_subset_info'],
+ SRR['get_subset_info'],
+ SRR['get_subset_info'],
+ SRR['get_subset_info'],
+ SRR['get_subset_info'],
+ SRR['get_subset_info'],
+ SRR['get_subset_info'],
+ SRR['get_subset_info'],
+ SRR['get_subset_info'],
+ SRR['get_subset_info'],
+ SRR['get_subset_info'],
+ SRR['get_subset_info'],
+ SRR['get_subset_info'],
+ SRR['get_subset_info'],
+ SRR['get_subset_info'],
+ SRR['get_subset_info'],
+ SRR['get_subset_info'],
+ SRR['get_subset_info'],
+ SRR['get_subset_info'],
+ SRR['get_subset_info'],
+ SRR['get_subset_info'],
+ SRR['get_subset_info'],
+ SRR['get_subset_info'],
+ SRR['get_subset_info'],
+ SRR['get_subset_info'],
+ SRR['get_subset_info'],
+ SRR['get_subset_info'],
+ SRR['get_subset_info'],
+ SRR['get_subset_info'],
+ SRR['get_subset_info'],
+ SRR['get_subset_info'],
+ SRR['get_subset_info'],
+ SRR['get_subset_info'],
+ SRR['get_subset_info'],
+ SRR['get_subset_info'],
+ SRR['get_subset_info'],
+ SRR['get_subset_info'],
+ SRR['get_subset_info'],
+ SRR['get_subset_info'],
+ SRR['get_subset_info'],
+ SRR['get_subset_info'],
+ SRR['get_subset_info'],
+ SRR['get_subset_info'],
+ SRR['get_subset_info'],
+ SRR['get_subset_info'],
+ SRR['get_subset_info'],
+ SRR['get_subset_info'],
+ ]
+
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ print('Info: test_run_ontap_gather_facts_for_all_subsets_pass: %s' % repr(exc.value.args))
+ assert set(exc.value.args[0]['ontap_info']) == set(gather_subset)
+
+ # Super Important, Metrocluster doesn't call get_subset_info and has 3 api calls instead of 1!!!!
+ # The metrocluster calls need to be in the correct place. The Module return the keys in a sorted list.
+ @patch('ansible_collections.netapp.ontap.plugins.module_utils.netapp.OntapRestAPI.send_request')
+ def test_run_ontap_gather_facts_for_all_subsets_with_fields_section_pass(self, mock_request):
+ set_module_args(self.set_args_run_ontap_gather_facts_for_all_subsets_with_fields_section_pass())
+ my_obj = ontap_rest_info_module()
+ gather_subset = ['application/applications', 'application/templates', 'cloud/targets', 'cluster/chassis', 'cluster/jobs',
+ 'cluster/metrocluster/diagnostics', 'cluster/metrics', 'cluster/nodes', 'cluster/peers', 'cluster/schedules',
+ 'cluster/software', 'cluster/software/download', 'cluster/software/history', 'cluster/software/packages',
+ 'name-services/dns', 'name-services/ldap', 'name-services/name-mappings', 'name-services/nis',
+ 'network/ethernet/broadcast-domains', 'network/ethernet/ports', 'network/fc/logins', 'network/fc/wwpn-aliases',
+ 'network/ip/interfaces', 'network/ip/routes', 'network/ip/service-policies', 'network/ipspaces',
+ 'protocols/cifs/home-directory/search-paths', 'protocols/cifs/services', 'protocols/cifs/shares',
+ 'protocols/san/fcp/services', 'protocols/san/igroups', 'protocols/san/iscsi/credentials',
+ 'protocols/san/iscsi/services', 'protocols/san/lun-maps', 'security/accounts', 'security/roles', 'storage/aggregates',
+ 'storage/disks', 'storage/flexcache/flexcaches', 'storage/flexcache/origins', 'storage/luns', 'storage/namespaces',
+ 'storage/ports', 'storage/qos/policies', 'storage/qtrees', 'storage/quota/reports', 'storage/quota/rules',
+ 'storage/shelves', 'storage/snapshot-policies', 'storage/volumes', 'support/autosupport', 'support/autosupport/messages',
+ 'support/ems', 'support/ems/destinations', 'support/ems/events', 'support/ems/filters', 'svm/peers', 'svm/peer-permissions',
+ 'svm/svms']
+ mock_request.side_effect = [
+ SRR['validate_ontap_version_pass'],
+ SRR['get_subset_info'],
+ SRR['get_subset_info'],
+ SRR['get_subset_info'],
+ SRR['get_subset_info'],
+ SRR['get_subset_info'],
+ SRR['get_subset_info'],
+ SRR['metrocluster_post'],
+ SRR['job'],
+ SRR['metrocluster_return'],
+ SRR['get_subset_info'],
+ SRR['get_subset_info'],
+ SRR['get_subset_info'],
+ SRR['get_subset_info'],
+ SRR['get_subset_info'],
+ SRR['get_subset_info'],
+ SRR['get_subset_info'],
+ SRR['get_subset_info'],
+ SRR['get_subset_info'],
+ SRR['get_subset_info'],
+ SRR['get_subset_info'],
+ SRR['get_subset_info'],
+ SRR['get_subset_info'],
+ SRR['get_subset_info'],
+ SRR['get_subset_info'],
+ SRR['get_subset_info'],
+ SRR['get_subset_info'],
+ SRR['get_subset_info'],
+ SRR['get_subset_info'],
+ SRR['get_subset_info'],
+ SRR['get_subset_info'],
+ SRR['get_subset_info'],
+ SRR['get_subset_info'],
+ SRR['get_subset_info'],
+ SRR['get_subset_info'],
+ SRR['get_subset_info'],
+ SRR['get_subset_info'],
+ SRR['get_subset_info'],
+ SRR['get_subset_info'],
+ SRR['get_subset_info'],
+ SRR['get_subset_info'],
+ SRR['get_subset_info'],
+ SRR['get_subset_info'],
+ SRR['get_subset_info'],
+ SRR['get_subset_info'],
+ SRR['get_subset_info'],
+ SRR['get_subset_info'],
+ SRR['get_subset_info'],
+ SRR['get_subset_info'],
+ SRR['get_subset_info'],
+ SRR['get_subset_info'],
+ SRR['get_subset_info'],
+ SRR['get_subset_info'],
+ SRR['get_subset_info'],
+ SRR['get_subset_info'],
+ SRR['get_subset_info'],
+ SRR['get_subset_info'],
+ SRR['get_subset_info'],
+ SRR['get_subset_info'],
+ SRR['get_subset_info'],
+ SRR['get_subset_info'],
+ SRR['get_subset_info'],
+ ]
+
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ print('Info: test_run_ontap_gather_facts_for_all_subsets_pass: %s' % repr(exc.value.args))
+ assert set(exc.value.args[0]['ontap_info']) == set(gather_subset)
+
+ @patch('ansible_collections.netapp.ontap.plugins.module_utils.netapp.OntapRestAPI.send_request')
+ def test_run_ontap_gather_facts_for_all_subsets_with_fields_section_fail(self, mock_request):
+ set_module_args(self.set_args_run_ontap_gather_facts_for_all_subsets_with_fields_section_fail())
+ my_obj = ontap_rest_info_module()
+ error_message = "Error: fields: %s, only one subset will be allowed." \
+ % self.set_args_run_ontap_gather_facts_for_aggregate_info_with_fields_section_pass()['fields']
+ mock_request.side_effect = [
+ SRR['validate_ontap_version_pass'],
+ SRR['get_subset_info'],
+ SRR['get_subset_info'],
+ SRR['get_subset_info'],
+ ]
+
+ with pytest.raises(AnsibleFailJson) as exc:
+ my_obj.apply()
+ print('Info: test_run_ontap_gather_facts_for_all_subsets_pass: %s' % repr(exc.value.args))
+ assert exc.value.args[0]['msg'] == error_message
+
+ @patch('ansible_collections.netapp.ontap.plugins.module_utils.netapp.OntapRestAPI.send_request')
+ def test_run_ontap_gather_facts_for_aggregate_info_pass_with_fields_section_pass(self, mock_request):
+ set_module_args(self.set_args_run_ontap_gather_facts_for_aggregate_info_with_fields_section_pass())
+ my_obj = ontap_rest_info_module()
+ gather_subset = ['storage/aggregates']
+ mock_request.side_effect = [
+ SRR['validate_ontap_version_pass'],
+ SRR['get_subset_info'],
+ ]
+
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ print('Info: test_run_ontap_gather_facts_for_volume_info_pass: %s' % repr(exc.value.args))
+ assert set(exc.value.args[0]['ontap_info']) == set(gather_subset)
+
+ @patch('ansible_collections.netapp.ontap.plugins.module_utils.netapp.OntapRestAPI.send_request')
+ def test_get_all_records_for_volume_info_to_check_next_api_call_functionality_pass(self, mock_request):
+ set_module_args(self.set_args_get_all_records_for_volume_info_to_check_next_api_call_functionality_pass())
+ my_obj = ontap_rest_info_module()
+ total_records = 5
+ mock_request.side_effect = [
+ SRR['validate_ontap_version_pass'],
+ SRR['get_subset_info_with_next'],
+ SRR['get_next_record'],
+ ]
+
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ print('Info: test_get_all_records_for_volume_info_to_check_next_api_call_functionality_pass: %s' % repr(exc.value.args))
+ assert exc.value.args[0]['ontap_info']['storage/volumes']['num_records'] == total_records
diff --git a/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_security_certificates.py b/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_security_certificates.py
new file mode 100644
index 00000000..6e298145
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_security_certificates.py
@@ -0,0 +1,435 @@
+# (c) 2019, NetApp, Inc
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+""" unit tests for Ansible module: na_ontap_security_certificates """
+
+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.ontap.tests.unit.compat.mock import patch
+
+from ansible_collections.netapp.ontap.plugins.modules.na_ontap_security_certificates \
+ import NetAppOntapSecurityCertificates as my_module # module under test
+
+
+# REST API canned responses when mocking send_request
+SRR = {
+ # common responses
+ 'is_rest': (200, {}, None),
+ 'is_zapi': (400, {}, "Unreachable"),
+ 'empty_good': (200, {}, None),
+ 'end_of_sequence': (500, None, "Unexpected call to send_request"),
+ 'generic_error': (400, None, "Expected error"),
+ # module specific responses
+ 'empty_records': (200, {'records': []}, None),
+ 'get_uuid': (200, {'records': [{'uuid': 'ansible'}]}, None),
+ 'error_unexpected_name': (200, None, {'message': 'Unexpected argument "name".'})
+}
+
+NAME_ERROR = "Error calling API: security/certificates - ONTAP 9.6 and 9.7 do not support 'name'. Use 'common_name' and 'type' as a work-around."
+TYPE_ERROR = "Error calling API: security/certificates - When using 'common_name', 'type' is required."
+EXPECTED_ERROR = "Error calling API: security/certificates - Expected error"
+
+
+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"""
+
+
+class AnsibleFailJson(Exception):
+ """Exception class to be raised by module.fail_json and caught by the test case"""
+
+
+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)
+
+
+def set_default_args():
+ return dict({
+ 'hostname': 'hostname',
+ 'username': 'username',
+ 'password': 'password',
+ 'name': 'name_for_certificate'
+ })
+
+
+@patch('ansible.module_utils.basic.AnsibleModule.fail_json')
+def test_module_fail_when_required_args_missing(mock_fail):
+ ''' required arguments are reported as errors '''
+ mock_fail.side_effect = fail_json
+ set_module_args({})
+ with pytest.raises(AnsibleFailJson) as exc:
+ my_module()
+ print('Info: %s' % exc.value.args[0]['msg'])
+
+
+@patch('ansible.module_utils.basic.AnsibleModule.fail_json')
+@patch('ansible_collections.netapp.ontap.plugins.module_utils.netapp.OntapRestAPI.send_request')
+def test_ensure_get_certificate_called(mock_request, mock_fail):
+ mock_fail.side_effect = fail_json
+ mock_request.side_effect = [
+ SRR['is_rest'],
+ SRR['get_uuid'],
+ SRR['end_of_sequence']
+ ]
+ set_module_args(set_default_args())
+ my_obj = my_module()
+ assert my_obj.get_certificate() is not None
+
+
+@patch('ansible.module_utils.basic.AnsibleModule.fail_json')
+@patch('ansible_collections.netapp.ontap.plugins.module_utils.netapp.OntapRestAPI.send_request')
+def test_rest_error(mock_request, mock_fail):
+ mock_fail.side_effect = fail_json
+ mock_request.side_effect = [
+ SRR['is_rest'],
+ SRR['generic_error'],
+ SRR['end_of_sequence']
+ ]
+ set_module_args(set_default_args())
+ my_obj = my_module()
+ with pytest.raises(AnsibleFailJson) as exc:
+ my_obj.apply()
+ assert exc.value.args[0]['msg'] == EXPECTED_ERROR
+
+
+@patch('ansible.module_utils.basic.AnsibleModule.exit_json')
+@patch('ansible.module_utils.basic.AnsibleModule.fail_json')
+@patch('ansible_collections.netapp.ontap.plugins.module_utils.netapp.OntapRestAPI.send_request')
+def test_rest_create_failed(mock_request, mock_fail, mock_exit):
+ mock_exit.side_effect = exit_json
+ mock_fail.side_effect = fail_json
+ mock_request.side_effect = [
+ SRR['is_rest'],
+ SRR['empty_records'], # get certificate -> not found
+ SRR['empty_good'],
+ SRR['end_of_sequence']
+ ]
+ data = {
+ 'type': 'client_ca',
+ 'vserver': 'abc',
+ }
+ data.update(set_default_args())
+ set_module_args(data)
+ my_obj = my_module()
+ with pytest.raises(AnsibleFailJson) as exc:
+ my_obj.apply()
+ msg = 'Error creating or installing certificate: one or more of the following options are missing:'
+ assert exc.value.args[0]['msg'].startswith(msg)
+
+
+@patch('ansible.module_utils.basic.AnsibleModule.exit_json')
+@patch('ansible.module_utils.basic.AnsibleModule.fail_json')
+@patch('ansible_collections.netapp.ontap.plugins.module_utils.netapp.OntapRestAPI.send_request')
+def test_rest_successful_create(mock_request, mock_fail, mock_exit):
+ mock_exit.side_effect = exit_json
+ mock_fail.side_effect = fail_json
+ mock_request.side_effect = [
+ SRR['is_rest'],
+ SRR['empty_records'], # get certificate -> not found
+ SRR['empty_good'],
+ SRR['end_of_sequence']
+ ]
+ data = {
+ 'type': 'client_ca',
+ 'vserver': 'abc',
+ 'common_name': 'cname'
+ }
+ data.update(set_default_args())
+ set_module_args(data)
+ my_obj = my_module()
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ assert exc.value.args[0]['changed']
+
+
+@patch('ansible.module_utils.basic.AnsibleModule.exit_json')
+@patch('ansible.module_utils.basic.AnsibleModule.fail_json')
+@patch('ansible_collections.netapp.ontap.plugins.module_utils.netapp.OntapRestAPI.send_request')
+def test_rest_idempotent_create(mock_request, mock_fail, mock_exit):
+ mock_exit.side_effect = exit_json
+ mock_fail.side_effect = fail_json
+ mock_request.side_effect = [
+ SRR['is_rest'],
+ SRR['get_uuid'], # get certificate -> found
+ SRR['end_of_sequence']
+ ]
+ data = {
+ 'type': 'client_ca',
+ 'vserver': 'abc',
+ }
+ data.update(set_default_args())
+ set_module_args(data)
+ my_obj = my_module()
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ assert not exc.value.args[0]['changed']
+
+
+@patch('ansible.module_utils.basic.AnsibleModule.exit_json')
+@patch('ansible.module_utils.basic.AnsibleModule.fail_json')
+@patch('ansible_collections.netapp.ontap.plugins.module_utils.netapp.OntapRestAPI.send_request')
+def test_rest_successful_delete(mock_request, mock_fail, mock_exit):
+ mock_exit.side_effect = exit_json
+ mock_fail.side_effect = fail_json
+ mock_request.side_effect = [
+ SRR['is_rest'],
+ SRR['get_uuid'], # get certificate -> found
+ SRR['empty_good'],
+ SRR['end_of_sequence']
+ ]
+ data = {
+ 'state': 'absent',
+ }
+ data.update(set_default_args())
+ set_module_args(data)
+ my_obj = my_module()
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ assert exc.value.args[0]['changed']
+
+
+@patch('ansible.module_utils.basic.AnsibleModule.exit_json')
+@patch('ansible.module_utils.basic.AnsibleModule.fail_json')
+@patch('ansible_collections.netapp.ontap.plugins.module_utils.netapp.OntapRestAPI.send_request')
+def test_rest_idempotent_delete(mock_request, mock_fail, mock_exit):
+ mock_exit.side_effect = exit_json
+ mock_fail.side_effect = fail_json
+ mock_request.side_effect = [
+ SRR['is_rest'],
+ SRR['empty_records'], # get certificate -> not found
+ SRR['empty_good'],
+ SRR['end_of_sequence']
+ ]
+ data = {
+ 'state': 'absent',
+ }
+ data.update(set_default_args())
+ set_module_args(data)
+ my_obj = my_module()
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ assert not exc.value.args[0]['changed']
+
+
+@patch('ansible.module_utils.basic.AnsibleModule.exit_json')
+@patch('ansible.module_utils.basic.AnsibleModule.fail_json')
+@patch('ansible_collections.netapp.ontap.plugins.module_utils.netapp.OntapRestAPI.send_request')
+def test_rest_successful_sign(mock_request, mock_fail, mock_exit):
+ mock_exit.side_effect = exit_json
+ mock_fail.side_effect = fail_json
+ mock_request.side_effect = [
+ SRR['is_rest'],
+ SRR['get_uuid'], # get certificate -> found
+ SRR['empty_good'],
+ SRR['end_of_sequence']
+ ]
+ data = {
+ 'vserver': 'abc',
+ 'signing_request': 'CSR'
+ }
+ data.update(set_default_args())
+ set_module_args(data)
+ my_obj = my_module()
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ assert exc.value.args[0]['changed']
+
+
+@patch('ansible.module_utils.basic.AnsibleModule.exit_json')
+@patch('ansible.module_utils.basic.AnsibleModule.fail_json')
+@patch('ansible_collections.netapp.ontap.plugins.module_utils.netapp.OntapRestAPI.send_request')
+def test_rest_failed_sign_missing_ca(mock_request, mock_fail, mock_exit):
+ mock_exit.side_effect = exit_json
+ mock_fail.side_effect = fail_json
+ mock_request.side_effect = [
+ SRR['is_rest'],
+ SRR['empty_records'], # get certificate -> not found
+ SRR['empty_good'],
+ SRR['end_of_sequence']
+ ]
+ data = {
+ 'vserver': 'abc',
+ 'signing_request': 'CSR'
+ }
+ data.update(set_default_args())
+ set_module_args(data)
+ my_obj = my_module()
+ with pytest.raises(AnsibleFailJson) as exc:
+ my_obj.apply()
+ msg = "signing certificate with name '%s' not found on svm: %s" % (data['name'], data['vserver'])
+ assert exc.value.args[0]['msg'] == msg
+
+
+@patch('ansible.module_utils.basic.AnsibleModule.exit_json')
+@patch('ansible.module_utils.basic.AnsibleModule.fail_json')
+@patch('ansible_collections.netapp.ontap.plugins.module_utils.netapp.OntapRestAPI.send_request')
+def test_rest_failed_sign_absent(mock_request, mock_fail, mock_exit):
+ mock_exit.side_effect = exit_json
+ mock_fail.side_effect = fail_json
+ mock_request.side_effect = [
+ SRR['is_rest'],
+ SRR['get_uuid'], # get certificate -> found
+ SRR['end_of_sequence']
+ ]
+ data = {
+ 'vserver': 'abc',
+ 'signing_request': 'CSR',
+ 'state': 'absent'
+ }
+ data.update(set_default_args())
+ set_module_args(data)
+ my_obj = my_module()
+ with pytest.raises(AnsibleFailJson) as exc:
+ my_obj.apply()
+ msg = "'signing_request' is not supported with 'state' set to 'absent'"
+ assert exc.value.args[0]['msg'] == msg
+
+
+@patch('ansible.module_utils.basic.AnsibleModule.exit_json')
+@patch('ansible.module_utils.basic.AnsibleModule.fail_json')
+@patch('ansible_collections.netapp.ontap.plugins.module_utils.netapp.OntapRestAPI.send_request')
+def test_rest_failed_on_name(mock_request, mock_fail, mock_exit):
+ mock_exit.side_effect = exit_json
+ mock_fail.side_effect = fail_json
+ mock_request.side_effect = [
+ SRR['is_rest'],
+ SRR['error_unexpected_name'], # get certificate -> error
+ SRR['end_of_sequence']
+ ]
+ data = {
+ 'vserver': 'abc',
+ 'signing_request': 'CSR',
+ 'state': 'absent',
+ 'ignore_name_if_not_supported': False,
+ 'common_name': 'common_name',
+ 'type': 'root_ca'
+ }
+ data.update(set_default_args())
+ set_module_args(data)
+ my_obj = my_module()
+ with pytest.raises(AnsibleFailJson) as exc:
+ my_obj.apply()
+ assert exc.value.args[0]['msg'] == NAME_ERROR
+
+
+@patch('ansible.module_utils.basic.AnsibleModule.exit_json')
+@patch('ansible.module_utils.basic.AnsibleModule.fail_json')
+@patch('ansible_collections.netapp.ontap.plugins.module_utils.netapp.OntapRestAPI.send_request')
+def test_rest_cannot_ignore_name_error_no_common_name(mock_request, mock_fail, mock_exit):
+ mock_exit.side_effect = exit_json
+ mock_fail.side_effect = fail_json
+ mock_request.side_effect = [
+ SRR['is_rest'],
+ SRR['error_unexpected_name'], # get certificate -> error
+ SRR['end_of_sequence']
+ ]
+ data = {
+ 'vserver': 'abc',
+ 'signing_request': 'CSR',
+ 'state': 'absent',
+ }
+ data.update(set_default_args())
+ set_module_args(data)
+ my_obj = my_module()
+ with pytest.raises(AnsibleFailJson) as exc:
+ my_obj.apply()
+ assert exc.value.args[0]['msg'] == NAME_ERROR
+
+
+@patch('ansible.module_utils.basic.AnsibleModule.exit_json')
+@patch('ansible.module_utils.basic.AnsibleModule.fail_json')
+@patch('ansible_collections.netapp.ontap.plugins.module_utils.netapp.OntapRestAPI.send_request')
+def test_rest_cannot_ignore_name_error_no_type(mock_request, mock_fail, mock_exit):
+ mock_exit.side_effect = exit_json
+ mock_fail.side_effect = fail_json
+ mock_request.side_effect = [
+ SRR['is_rest'],
+ SRR['error_unexpected_name'], # get certificate -> error
+ SRR['end_of_sequence']
+ ]
+ data = {
+ 'vserver': 'abc',
+ 'signing_request': 'CSR',
+ 'state': 'absent',
+ 'common_name': 'common_name'
+ }
+ data.update(set_default_args())
+ set_module_args(data)
+ my_obj = my_module()
+ with pytest.raises(AnsibleFailJson) as exc:
+ my_obj.apply()
+ assert exc.value.args[0]['msg'] == TYPE_ERROR
+
+
+@patch('ansible.module_utils.basic.AnsibleModule.exit_json')
+@patch('ansible.module_utils.basic.AnsibleModule.fail_json')
+@patch('ansible_collections.netapp.ontap.plugins.module_utils.netapp.OntapRestAPI.send_request')
+def test_rest_ignore_name_error(mock_request, mock_fail, mock_exit):
+ mock_exit.side_effect = exit_json
+ mock_fail.side_effect = fail_json
+ mock_request.side_effect = [
+ SRR['is_rest'],
+ SRR['error_unexpected_name'], # get certificate -> error
+ SRR['get_uuid'], # get certificate -> found
+ SRR['end_of_sequence']
+ ]
+ data = {
+ 'vserver': 'abc',
+ 'signing_request': 'CSR',
+ 'state': 'absent',
+ 'common_name': 'common_name',
+ 'type': 'root_ca'
+ }
+ data.update(set_default_args())
+ set_module_args(data)
+ my_obj = my_module()
+ with pytest.raises(AnsibleFailJson) as exc:
+ my_obj.apply()
+ msg = "'signing_request' is not supported with 'state' set to 'absent'"
+ assert exc.value.args[0]['msg'] == msg
+
+
+@patch('ansible.module_utils.basic.AnsibleModule.exit_json')
+@patch('ansible.module_utils.basic.AnsibleModule.fail_json')
+@patch('ansible_collections.netapp.ontap.plugins.module_utils.netapp.OntapRestAPI.send_request')
+def test_rest_successful_create_name_error(mock_request, mock_fail, mock_exit):
+ mock_exit.side_effect = exit_json
+ mock_fail.side_effect = fail_json
+ mock_request.side_effect = [
+ SRR['is_rest'],
+ SRR['error_unexpected_name'], # get certificate -> error
+ SRR['empty_records'], # get certificate -> not found
+ SRR['empty_good'],
+ SRR['end_of_sequence']
+ ]
+ data = {
+ 'common_name': 'cname',
+ 'type': 'client_ca',
+ 'vserver': 'abc',
+ }
+ data.update(set_default_args())
+ set_module_args(data)
+ my_obj = my_module()
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ assert exc.value.args[0]['changed']
+ print(mock_request.mock_calls)
diff --git a/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_security_key_manager.py b/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_security_key_manager.py
new file mode 100644
index 00000000..3f5e6a41
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_security_key_manager.py
@@ -0,0 +1,174 @@
+# (c) 2019, NetApp, Inc
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+''' unit test template for ONTAP Ansible module '''
+
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+import json
+import pytest
+
+from ansible_collections.netapp.ontap.tests.unit.compat import unittest
+from ansible_collections.netapp.ontap.tests.unit.compat.mock import patch, Mock
+from ansible.module_utils import basic
+from ansible.module_utils._text import to_bytes
+import ansible_collections.netapp.ontap.plugins.module_utils.netapp as netapp_utils
+
+from ansible_collections.netapp.ontap.plugins.modules.na_ontap_security_key_manager \
+ import NetAppOntapSecurityKeyManager as key_manager_module # module under test
+
+if not netapp_utils.has_netapp_lib():
+ pytestmark = pytest.mark.skip('skipping as missing required netapp_lib')
+
+
+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 MockONTAPConnection(object):
+ ''' mock server connection to ONTAP host '''
+
+ def __init__(self, kind=None, data=None):
+ ''' save arguments '''
+ self.type = kind
+ self.data = data
+ self.xml_in = None
+ self.xml_out = None
+
+ def invoke_successfully(self, xml, enable_tunneling): # pylint: disable=unused-argument
+ ''' mock invoke_successfully returning xml data '''
+ self.xml_in = xml
+ if self.type == 'key_manager':
+ xml = self.build_port_info(self.data)
+ self.xml_out = xml
+ return xml
+
+ @staticmethod
+ def build_port_info(key_manager_details):
+ ''' build xml data for-key-manager-info '''
+ xml = netapp_utils.zapi.NaElement('xml')
+ attributes = {
+ 'num-records': 1,
+ 'attributes-list': {
+ 'key-manager-info': {
+ 'key-manager-ip-address': '0.0.0.0',
+ 'key-manager-server-status': 'available',
+ 'key-manager-tcp-port': '5696',
+ 'node-name': 'test_node'
+ }
+ }
+ }
+ xml.translate_struct(attributes)
+ return xml
+
+
+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)
+ self.mock_key_manager = {
+ 'node_name': 'test_node',
+ 'tcp_port': 5696,
+ 'ip_address': '0.0.0.0',
+ 'server_status': 'available'
+ }
+
+ def mock_args(self):
+ return {
+ 'node': self.mock_key_manager['node_name'],
+ 'tcp_port': self.mock_key_manager['tcp_port'],
+ 'ip_address': self.mock_key_manager['ip_address'],
+ 'hostname': 'test',
+ 'username': 'test_user',
+ 'password': 'test_pass!',
+ 'https': 'False'
+ }
+
+ def get_key_manager_mock_object(self, kind=None):
+ """
+ Helper method to return an na_ontap_security_key_manager object
+ :param kind: passes this param to MockONTAPConnection()
+ :return: na_ontap_security_key_manager object
+ """
+ obj = key_manager_module()
+ obj.asup_log_for_cserver = Mock(return_value=None)
+ obj.cluster = Mock()
+ obj.cluster.invoke_successfully = Mock()
+ if kind is None:
+ obj.cluster = MockONTAPConnection()
+ else:
+ obj.cluster = MockONTAPConnection(kind=kind, data=self.mock_key_manager)
+ return obj
+
+ def test_module_fail_when_required_args_missing(self):
+ ''' required arguments are reported as errors '''
+ with pytest.raises(AnsibleFailJson) as exc:
+ set_module_args({})
+ key_manager_module()
+ print('Info: %s' % exc.value.args[0]['msg'])
+
+ def test_get_nonexistent_key_manager(self):
+ ''' Test if get_key_manager() returns None for non-existent key manager '''
+ set_module_args(self.mock_args())
+ result = self.get_key_manager_mock_object().get_key_manager()
+ assert result is None
+
+ def test_get_existing_key_manager(self):
+ ''' Test if get_key_manager() returns details for existing key manager '''
+ set_module_args(self.mock_args())
+ result = self.get_key_manager_mock_object('key_manager').get_key_manager()
+ assert result['ip_address'] == self.mock_key_manager['ip_address']
+
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_security_key_manager.NetAppOntapSecurityKeyManager.get_key_manager')
+ def test_successfully_add_key_manager(self, get_key_manager):
+ ''' Test successfully add key manager'''
+ data = self.mock_args()
+ data['state'] = 'present'
+ set_module_args(data)
+ get_key_manager.side_effect = [
+ None
+ ]
+ obj = self.get_key_manager_mock_object('key_manager')
+ with pytest.raises(AnsibleExitJson) as exc:
+ obj.apply()
+ assert exc.value.args[0]['changed']
+
+ def test_successfully_delete_key_manager(self):
+ ''' Test successfully delete key manager'''
+ data = self.mock_args()
+ data['state'] = 'absent'
+ set_module_args(data)
+ obj = self.get_key_manager_mock_object('key_manager')
+ with pytest.raises(AnsibleExitJson) as exc:
+ obj.apply()
+ assert exc.value.args[0]['changed']
diff --git a/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_service_processor_network.py b/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_service_processor_network.py
new file mode 100644
index 00000000..ed3596b6
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_service_processor_network.py
@@ -0,0 +1,234 @@
+''' unit test template for ONTAP Ansible module '''
+
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+import json
+import pytest
+
+import time
+from ansible_collections.netapp.ontap.tests.unit.compat import unittest
+from ansible_collections.netapp.ontap.tests.unit.compat.mock import patch, Mock
+from ansible.module_utils import basic
+from ansible.module_utils._text import to_bytes
+import ansible_collections.netapp.ontap.plugins.module_utils.netapp as netapp_utils
+
+from ansible_collections.netapp.ontap.plugins.modules.na_ontap_service_processor_network \
+ import NetAppOntapServiceProcessorNetwork as sp_module # module under test
+
+if not netapp_utils.has_netapp_lib():
+ pytestmark = pytest.mark.skip('skipping as missing required netapp_lib')
+
+
+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 MockONTAPConnection(object):
+ ''' mock server connection to ONTAP host '''
+
+ def __init__(self, kind=None, data=None):
+ ''' save arguments '''
+ self.kind = kind
+ self.data = data
+ self.xml_in = None
+ self.xml_out = None
+
+ def invoke_successfully(self, xml, enable_tunneling): # pylint: disable=unused-argument
+ ''' mock invoke_successfully returning xml data '''
+ self.xml_in = xml
+ if self.kind == 'sp-enabled':
+ xml = self.build_sp_info(self.data)
+ elif self.kind == 'sp-disabled':
+ xml = self.build_sp_disabled_info(self.data)
+ else:
+ xml = self.build_info()
+ self.xml_out = xml
+ return xml
+
+ @staticmethod
+ def build_sp_info(sp):
+ ''' build xml data for vserser-info '''
+ xml = netapp_utils.zapi.NaElement('xml')
+ attributes = {
+ 'num-records': 1,
+ 'attributes-list':
+ {
+ 'service-processor-network-info':
+ {
+ 'node-name': sp['node'],
+ 'is-enabled': 'true',
+ 'address-type': sp['address_type'],
+ 'dhcp': 'v4',
+ 'gateway-ip-address': sp['gateway_ip_address'],
+ 'netmask': sp['netmask'],
+ 'ip-address': sp['ip_address'],
+ 'setup-status': 'succeeded',
+ }
+ }
+ }
+ xml.translate_struct(attributes)
+ return xml
+
+ @staticmethod
+ def build_info():
+ ''' build xml data for vserser-info '''
+ xml = netapp_utils.zapi.NaElement('xml')
+ attributes = {
+ 'num-records': 0
+ }
+ xml.translate_struct(attributes)
+ return xml
+
+ @staticmethod
+ def build_sp_disabled_info(sp):
+ ''' build xml data for vserser-info '''
+ xml = netapp_utils.zapi.NaElement('xml')
+ attributes = {
+ 'num-records': 1,
+ 'attributes-list':
+ {
+ 'service-processor-network-info':
+ {
+ 'node-name': sp['node'],
+ 'is-enabled': 'false',
+ 'address-type': sp['address_type'],
+ 'setup-status': 'not_setup',
+ }
+ }
+ }
+ xml.translate_struct(attributes)
+ return xml
+
+
+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)
+ self.server = MockONTAPConnection()
+ self.mock_sp = {
+ 'node': 'test-vsim1',
+ 'gateway_ip_address': '2.2.2.2',
+ 'address_type': 'ipv4',
+ 'ip_address': '1.1.1.1',
+ 'netmask': '255.255.248.0',
+ 'dhcp': 'v4'
+ }
+
+ def mock_args(self, enable=True):
+ data = {
+ 'node': self.mock_sp['node'],
+ 'is_enabled': enable,
+ 'address_type': self.mock_sp['address_type'],
+ 'hostname': 'host',
+ 'username': 'admin',
+ 'password': 'password',
+ }
+ if enable is True:
+ data['ip_address'] = self.mock_sp['ip_address']
+ data['gateway_ip_address'] = self.mock_sp['gateway_ip_address']
+ data['netmask'] = self.mock_sp['netmask']
+ data['dhcp'] = 'v4'
+ return data
+
+ def get_sp_mock_object(self, kind=None):
+ """
+ Helper method to return an na_ontap_volume object
+ :param kind: passes this param to MockONTAPConnection()
+ :return: na_ontap_volume object
+ """
+ sp_obj = sp_module()
+ sp_obj.autosupport_log = Mock(return_value=None)
+ if kind is None:
+ sp_obj.server = MockONTAPConnection()
+ else:
+ sp_obj.server = MockONTAPConnection(kind=kind, data=self.mock_sp)
+ return sp_obj
+
+ def test_module_fail_when_required_args_missing(self):
+ ''' required arguments are reported as errors '''
+ with pytest.raises(AnsibleFailJson) as exc:
+ set_module_args({})
+ sp_module()
+ print('Info: %s' % exc.value.args[0]['msg'])
+
+ def test_modify_error_on_disabled_sp(self):
+ ''' a more interesting test '''
+ data = self.mock_args(enable=False)
+ data['ip_address'] = self.mock_sp['ip_address']
+ set_module_args(data)
+ with pytest.raises(AnsibleFailJson) as exc:
+ self.get_sp_mock_object('sp-disabled').apply()
+ assert 'Error: Cannot modify a service processor network if it is disabled.' in \
+ exc.value.args[0]['msg']
+
+ def test_modify_sp(self):
+ ''' a more interesting test '''
+ data = self.mock_args()
+ data['ip_address'] = '3.3.3.3'
+ set_module_args(data)
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_sp_mock_object('sp-enabled').apply()
+ assert exc.value.args[0]['changed']
+
+ def test_modify_sp_wait(self):
+ ''' a more interesting test '''
+ data = self.mock_args()
+ data['ip_address'] = '3.3.3.3'
+ data['wait_for_completion'] = True
+ set_module_args(data)
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_sp_mock_object('sp-enabled').apply()
+ assert exc.value.args[0]['changed']
+
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_service_processor_network.NetAppOntapServiceProcessorNetwork.'
+ 'get_service_processor_network')
+ def test_non_existing_sp(self, get_sp):
+ set_module_args(self.mock_args())
+ get_sp.return_value = None
+ with pytest.raises(AnsibleFailJson) as exc:
+ self.get_sp_mock_object().apply()
+ assert 'Error No Service Processor for node: test-vsim1' in exc.value.args[0]['msg']
+
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_service_processor_network.NetAppOntapServiceProcessorNetwork.'
+ 'get_sp_network_status')
+ @patch('time.sleep')
+ def test_wait_on_sp_status(self, get_sp, sleep):
+ data = self.mock_args()
+ data['gateway_ip_address'] = '4.4.4.4'
+ data['wait_for_completion'] = True
+ set_module_args(data)
+ get_sp.side_effect = ['in_progress', 'done']
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_sp_mock_object('sp-enabled').apply()
+ sleep.assert_called_once_with()
+ assert exc.value.args[0]['changed']
diff --git a/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_snapmirror.py b/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_snapmirror.py
new file mode 100644
index 00000000..b568e218
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_snapmirror.py
@@ -0,0 +1,630 @@
+''' unit tests ONTAP Ansible module: na_ontap_snapmirror '''
+
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+import json
+import pytest
+
+from ansible_collections.netapp.ontap.tests.unit.compat import unittest
+from ansible_collections.netapp.ontap.tests.unit.compat.mock import patch, Mock
+from ansible.module_utils import basic
+from ansible.module_utils._text import to_bytes
+import ansible_collections.netapp.ontap.plugins.module_utils.netapp as netapp_utils
+
+from ansible_collections.netapp.ontap.plugins.modules.na_ontap_snapmirror \
+ import NetAppONTAPSnapmirror as my_module
+
+if not netapp_utils.has_netapp_lib():
+ pytestmark = pytest.mark.skip('skipping as missing required netapp_lib')
+
+
+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 MockONTAPConnection(object):
+ ''' mock server connection to ONTAP host '''
+
+ def __init__(self, kind=None, parm=None, status=None, quiesce_status='passed'):
+ ''' save arguments '''
+ self.type = kind
+ self.xml_in = None
+ self.xml_out = None
+ self.parm = parm
+ self.status = status
+ self.quiesce_status = quiesce_status
+
+ def invoke_successfully(self, xml, enable_tunneling): # pylint: disable=unused-argument
+ ''' mock invoke_successfully returning xml data '''
+ self.xml_in = xml
+ if self.type == 'snapmirror':
+ xml = self.build_snapmirror_info(self.parm, self.status, self.quiesce_status)
+ elif self.type == 'snapmirror_fail':
+ raise netapp_utils.zapi.NaApiError(code='TEST', message="This exception is from the unit test")
+ self.xml_out = xml
+ return xml
+
+ @staticmethod
+ def build_snapmirror_info(mirror_state, status, quiesce_status):
+ ''' build xml data for snapmirror-entry '''
+ xml = netapp_utils.zapi.NaElement('xml')
+ data = {'num-records': 1,
+ 'status': quiesce_status,
+ 'attributes-list': {'snapmirror-info': {'mirror-state': mirror_state, 'schedule': None,
+ 'source-location': 'ansible:ansible',
+ 'relationship-status': status, 'policy': 'ansible',
+ 'relationship-type': 'data_protection',
+ 'max-transfer-rate': 1000,
+ 'identity-preserve': 'true'},
+ 'snapmirror-destination-info': {'destination-location': 'ansible'}}}
+ xml.translate_struct(data)
+ return xml
+
+
+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)
+ self.server = MockONTAPConnection()
+ self.source_server = MockONTAPConnection()
+ self.onbox = False
+
+ def set_default_args(self):
+ if self.onbox:
+ hostname = '10.10.10.10'
+ username = 'admin'
+ password = 'password'
+ source_path = 'ansible:ansible'
+ destination_path = 'ansible:ansible'
+ policy = 'ansible'
+ source_vserver = 'ansible'
+ destination_vserver = 'ansible'
+ relationship_type = 'data_protection'
+ schedule = None
+ source_username = 'admin'
+ source_password = 'password'
+ relationship_state = 'active'
+ update = True
+ else:
+ hostname = '10.10.10.10'
+ username = 'admin'
+ password = 'password'
+ source_path = 'ansible:ansible'
+ destination_path = 'ansible:ansible'
+ policy = 'ansible'
+ source_vserver = 'ansible'
+ destination_vserver = 'ansible'
+ relationship_type = 'data_protection'
+ schedule = None
+ source_username = 'admin'
+ source_password = 'password'
+ relationship_state = 'active'
+ update = True
+ return dict({
+ 'hostname': hostname,
+ 'username': username,
+ 'password': password,
+ 'source_path': source_path,
+ 'destination_path': destination_path,
+ 'policy': policy,
+ 'source_vserver': source_vserver,
+ 'destination_vserver': destination_vserver,
+ 'relationship_type': relationship_type,
+ 'schedule': schedule,
+ 'source_username': source_username,
+ 'source_password': source_password,
+ 'relationship_state': relationship_state,
+ 'update': update
+ })
+
+ def test_module_fail_when_required_args_missing(self):
+ ''' required arguments are reported as errors '''
+ with pytest.raises(AnsibleFailJson) as exc:
+ set_module_args({})
+ my_module()
+ print('Info: %s' % exc.value.args[0]['msg'])
+
+ def test_ensure_get_called(self):
+ ''' test snapmirror_get for non-existent snapmirror'''
+ set_module_args(self.set_default_args())
+ my_obj = my_module()
+ my_obj.server = self.server
+ assert my_obj.snapmirror_get is not None
+
+ def test_ensure_get_called_existing(self):
+ ''' test snapmirror_get for existing snapmirror'''
+ set_module_args(self.set_default_args())
+ my_obj = my_module()
+ my_obj.server = MockONTAPConnection(kind='snapmirror', status='idle')
+ assert my_obj.snapmirror_get()
+
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_snapmirror.NetAppONTAPSnapmirror.snapmirror_create')
+ def test_successful_create(self, snapmirror_create):
+ ''' creating snapmirror and testing idempotency '''
+ data = self.set_default_args()
+ data['schedule'] = 'abc'
+ data['identity_preserve'] = True
+ set_module_args(data)
+ my_obj = my_module()
+ my_obj.asup_log_for_cserver = Mock(return_value=None)
+ if not self.onbox:
+ my_obj.server = self.server
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ assert exc.value.args[0]['changed']
+ snapmirror_create.assert_called_with()
+ # to reset na_helper from remembering the previous 'changed' value
+ data = self.set_default_args()
+ data['update'] = False
+ set_module_args(data)
+ my_obj = my_module()
+ my_obj.asup_log_for_cserver = Mock(return_value=None)
+ if not self.onbox:
+ my_obj.server = MockONTAPConnection('snapmirror', 'snapmirrored', status='idle')
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ assert not exc.value.args[0]['changed']
+
+ def test_failure_break(self):
+ ''' breaking snapmirror to test quiesce time-delay failure '''
+ data = self.set_default_args()
+ data['relationship_state'] = 'broken'
+ set_module_args(data)
+ my_obj = my_module()
+ my_obj.asup_log_for_cserver = Mock(return_value=None)
+ if not self.onbox:
+ my_obj.server = MockONTAPConnection('snapmirror', 'snapmirrored', status='idle', quiesce_status='InProgress')
+ with pytest.raises(AnsibleFailJson) as exc:
+ # replace time.sleep with a noop
+ with patch('time.sleep', lambda a: None):
+ my_obj.apply()
+ assert 'Taking a long time to Quiescing SnapMirror, try again later' in exc.value.args[0]['msg']
+
+ def test_successful_break(self):
+ ''' breaking snapmirror and testing idempotency '''
+ data = self.set_default_args()
+ data['relationship_state'] = 'broken'
+ set_module_args(data)
+ my_obj = my_module()
+ my_obj.asup_log_for_cserver = Mock(return_value=None)
+ if not self.onbox:
+ my_obj.server = MockONTAPConnection('snapmirror', 'snapmirrored', status='idle')
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ assert exc.value.args[0]['changed']
+ # to reset na_helper from remembering the previous 'changed' value
+ set_module_args(self.set_default_args())
+ my_obj = my_module()
+ my_obj.asup_log_for_cserver = Mock(return_value=None)
+ if not self.onbox:
+ my_obj.server = MockONTAPConnection('snapmirror', 'broken-off', status='idle')
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ assert exc.value.args[0]['changed']
+
+ def test_successful_create_without_initialize(self):
+ ''' creating snapmirror and testing idempotency '''
+ data = self.set_default_args()
+ data['schedule'] = 'abc'
+ data['identity_preserve'] = True
+ data['initialize'] = False
+ set_module_args(data)
+ my_obj = my_module()
+ my_obj.asup_log_for_cserver = Mock(return_value=None)
+ my_obj.server = MockONTAPConnection('snapmirror', 'Uninitialized', status='idle')
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ assert exc.value.args[0]['changed']
+
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_snapmirror.NetAppONTAPSnapmirror.snapmirror_create')
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_snapmirror.NetAppONTAPSnapmirror.check_elementsw_parameters')
+ def test_successful_element_ontap_create(self, check_param, snapmirror_create):
+ ''' creating ElementSW to ONTAP snapmirror '''
+ data = self.set_default_args()
+ data['schedule'] = 'abc'
+ data['connection_type'] = 'elementsw_ontap'
+ data['source_hostname'] = '10.10.10.10'
+ set_module_args(data)
+ my_obj = my_module()
+ my_obj.asup_log_for_cserver = Mock(return_value=None)
+ if not self.onbox:
+ my_obj.server = self.server
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ assert exc.value.args[0]['changed']
+ snapmirror_create.assert_called_with()
+ check_param.assert_called_with()
+
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_snapmirror.NetAppONTAPSnapmirror.snapmirror_create')
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_snapmirror.NetAppONTAPSnapmirror.check_elementsw_parameters')
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_snapmirror.NetAppONTAPSnapmirror.snapmirror_get')
+ def test_successful_ontap_element_create(self, snapmirror_get, check_param, snapmirror_create):
+ ''' creating ONTAP to ElementSW snapmirror '''
+ data = self.set_default_args()
+ data['schedule'] = 'abc'
+ data['connection_type'] = 'ontap_elementsw'
+ data['source_hostname'] = '10.10.10.10'
+ set_module_args(data)
+ my_obj = my_module()
+ my_obj.asup_log_for_cserver = Mock(return_value=None)
+ snapmirror_get.side_effect = [
+ Mock(),
+ None
+ ]
+ if not self.onbox:
+ my_obj.server = self.server
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ assert exc.value.args[0]['changed']
+ snapmirror_create.assert_called_with()
+ check_param.assert_called_with('destination')
+
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_snapmirror.NetAppONTAPSnapmirror.delete_snapmirror')
+ def test_successful_delete(self, delete_snapmirror):
+ ''' deleting snapmirror and testing idempotency '''
+ data = self.set_default_args()
+ data['state'] = 'absent'
+ data['source_hostname'] = '10.10.10.10'
+ set_module_args(data)
+ my_obj = my_module()
+ my_obj.asup_log_for_cserver = Mock(return_value=None)
+ my_obj.get_destination = Mock(return_value=True)
+ if not self.onbox:
+ my_obj.server = MockONTAPConnection('snapmirror', status='idle')
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ assert exc.value.args[0]['changed']
+ delete_snapmirror.assert_called_with(False, 'data_protection', None)
+ # to reset na_helper from remembering the previous 'changed' value
+ my_obj = my_module()
+ my_obj.asup_log_for_cserver = Mock(return_value=None)
+ if not self.onbox:
+ my_obj.server = self.server
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ assert not exc.value.args[0]['changed']
+
+ def test_successful_delete_error_check(self):
+ ''' check required parameter source cluster hostname deleting snapmirror'''
+ data = self.set_default_args()
+ data['state'] = 'absent'
+ set_module_args(data)
+ my_obj = my_module()
+ my_obj.asup_log_for_cserver = Mock(return_value=None)
+ if not self.onbox:
+ my_obj.server = MockONTAPConnection('snapmirror', status='idle')
+ with pytest.raises(AnsibleFailJson) as exc:
+ my_obj.apply()
+ assert 'Missing parameters for delete:' in exc.value.args[0]['msg']
+
+ def test_successful_delete_check_get_destination(self):
+ ''' check required parameter source cluster hostname deleting snapmirror'''
+ data = self.set_default_args()
+ data['state'] = 'absent'
+ data['source_hostname'] = '10.10.10.10'
+ set_module_args(data)
+ my_obj = my_module()
+ my_obj.asup_log_for_cserver = Mock(return_value=None)
+ if not self.onbox:
+ my_obj.server = MockONTAPConnection('snapmirror', status='idle')
+ my_obj.source_server = MockONTAPConnection('snapmirror', status='idle')
+ res = my_obj.get_destination()
+ assert res is True
+
+ def test_snapmirror_release(self):
+ data = self.set_default_args()
+ set_module_args(data)
+ my_obj = my_module()
+ my_obj.asup_log_for_cserver = Mock(return_value=None)
+ if not self.onbox:
+ my_obj.source_server = MockONTAPConnection('snapmirror', status='idle', parm='snapmirrored')
+ my_obj.snapmirror_release()
+ assert my_obj.source_server.xml_in['destination-location'] == data['destination_path']
+
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_snapmirror.NetAppONTAPSnapmirror.snapmirror_resume')
+ def test_snapmirror_resume(self, snapmirror_resume):
+ ''' resuming snapmirror '''
+ data = self.set_default_args()
+ set_module_args(data)
+ my_obj = my_module()
+ my_obj.asup_log_for_cserver = Mock(return_value=None)
+ if not self.onbox:
+ my_obj.server = MockONTAPConnection('snapmirror', status='quiesced', parm='snapmirrored')
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ assert exc.value.args[0]['changed']
+ snapmirror_resume.assert_called_with()
+
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_snapmirror.NetAppONTAPSnapmirror.snapmirror_restore')
+ def test_snapmirror_restore(self, snapmirror_restore):
+ ''' restore snapmirror '''
+ data = self.set_default_args()
+ data['relationship_type'] = 'restore'
+ set_module_args(data)
+ my_obj = my_module()
+ my_obj.asup_log_for_cserver = Mock(return_value=None)
+ if not self.onbox:
+ my_obj.server = self.server
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ assert exc.value.args[0]['changed']
+ snapmirror_restore.assert_called_with()
+
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_snapmirror.NetAppONTAPSnapmirror.snapmirror_abort')
+ def test_successful_abort(self, snapmirror_abort):
+ ''' deleting snapmirror and testing idempotency '''
+ data = self.set_default_args()
+ data['state'] = 'absent'
+ data['source_hostname'] = '10.10.10.10'
+ set_module_args(data)
+ my_obj = my_module()
+ my_obj.asup_log_for_cserver = Mock(return_value=None)
+ if not self.onbox:
+ my_obj.server = MockONTAPConnection('snapmirror', status='transferring')
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ assert exc.value.args[0]['changed']
+ snapmirror_abort.assert_called_with()
+ # to reset na_helper from remembering the previous 'changed' value
+ my_obj = my_module()
+ my_obj.asup_log_for_cserver = Mock(return_value=None)
+ if not self.onbox:
+ my_obj.server = self.server
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ assert not exc.value.args[0]['changed']
+
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_snapmirror.NetAppONTAPSnapmirror.snapmirror_modify')
+ def test_successful_modify(self, snapmirror_modify):
+ ''' modifying snapmirror and testing idempotency '''
+ data = self.set_default_args()
+ data['policy'] = 'ansible2'
+ data['schedule'] = 'abc2'
+ data['max_transfer_rate'] = 2000
+ set_module_args(data)
+ my_obj = my_module()
+ my_obj.asup_log_for_cserver = Mock(return_value=None)
+ if not self.onbox:
+ my_obj.server = MockONTAPConnection('snapmirror', status='idle')
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ assert exc.value.args[0]['changed']
+ snapmirror_modify.assert_called_with({'policy': 'ansible2', 'schedule': 'abc2', 'max_transfer_rate': 2000})
+ # to reset na_helper from remembering the previous 'changed' value
+ data = self.set_default_args()
+ data['update'] = False
+ set_module_args(data)
+ my_obj = my_module()
+ my_obj.asup_log_for_cserver = Mock(return_value=None)
+ if not self.onbox:
+ my_obj.server = MockONTAPConnection('snapmirror', status='idle', parm='snapmirrored')
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ assert not exc.value.args[0]['changed']
+
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_snapmirror.NetAppONTAPSnapmirror.snapmirror_initialize')
+ def test_successful_initialize(self, snapmirror_initialize):
+ ''' initialize snapmirror and testing idempotency '''
+ data = self.set_default_args()
+ set_module_args(data)
+ my_obj = my_module()
+ my_obj.asup_log_for_cserver = Mock(return_value=None)
+ if not self.onbox:
+ my_obj.server = MockONTAPConnection('snapmirror', status='idle', parm='uninitialized')
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ assert exc.value.args[0]['changed']
+ snapmirror_initialize.assert_called_with()
+ # to reset na_helper from remembering the previous 'changed' value
+ data = self.set_default_args()
+ data['update'] = False
+ set_module_args(data)
+ my_obj = my_module()
+ my_obj.asup_log_for_cserver = Mock(return_value=None)
+ if not self.onbox:
+ my_obj.server = MockONTAPConnection('snapmirror', status='idle', parm='snapmirrored')
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ assert not exc.value.args[0]['changed']
+
+ def test_successful_update(self):
+ ''' update snapmirror and testing idempotency '''
+ data = self.set_default_args()
+ data['policy'] = 'ansible2'
+ set_module_args(data)
+ my_obj = my_module()
+ my_obj.asup_log_for_cserver = Mock(return_value=None)
+ if not self.onbox:
+ my_obj.server = MockONTAPConnection('snapmirror', status='idle')
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ assert exc.value.args[0]['changed']
+
+ def test_elementsw_volume_exists(self):
+ ''' elementsw_volume_exists '''
+ data = self.set_default_args()
+ set_module_args(data)
+ my_obj = my_module()
+ my_obj.asup_log_for_cserver = Mock(return_value=None)
+ mock_helper = Mock()
+ mock_helper.volume_id_exists.side_effect = [1000, None]
+ if not self.onbox:
+ my_obj.server = MockONTAPConnection('snapmirror', status='idle', parm='snapmirrored')
+ my_obj.check_if_elementsw_volume_exists('10.10.10.10:/lun/1000', mock_helper)
+ with pytest.raises(AnsibleFailJson) as exc:
+ my_obj.check_if_elementsw_volume_exists('10.10.10.10:/lun/1000', mock_helper)
+ assert 'Error: Source volume does not exist in the ElementSW cluster' in exc.value.args[0]['msg']
+
+ def test_elementsw_svip_exists(self):
+ ''' svip_exists '''
+ data = self.set_default_args()
+ set_module_args(data)
+ my_obj = my_module()
+ my_obj.asup_log_for_cserver = Mock(return_value=None)
+ mock_helper = Mock()
+ mock_helper.get_cluster_info.return_value.cluster_info.svip = '10.10.10.10'
+ if not self.onbox:
+ my_obj.server = MockONTAPConnection('snapmirror', status='idle', parm='snapmirrored')
+ my_obj.validate_elementsw_svip('10.10.10.10:/lun/1000', mock_helper)
+
+ def test_elementsw_svip_exists_negative(self):
+ ''' svip_exists negative testing'''
+ data = self.set_default_args()
+ set_module_args(data)
+ my_obj = my_module()
+ my_obj.asup_log_for_cserver = Mock(return_value=None)
+ mock_helper = Mock()
+ mock_helper.get_cluster_info.return_value.cluster_info.svip = '10.10.10.10'
+ if not self.onbox:
+ my_obj.server = MockONTAPConnection('snapmirror', status='idle', parm='snapmirrored')
+ with pytest.raises(AnsibleFailJson) as exc:
+ my_obj.validate_elementsw_svip('10.10.10.11:/lun/1000', mock_helper)
+ assert 'Error: Invalid SVIP' in exc.value.args[0]['msg']
+
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_snapmirror.NetAppONTAPSnapmirror.set_element_connection')
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_snapmirror.NetAppONTAPSnapmirror.validate_elementsw_svip')
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_snapmirror.NetAppONTAPSnapmirror.check_if_elementsw_volume_exists')
+ def test_check_elementsw_params_source(self, validate_volume, validate_svip, connection):
+ ''' check elementsw parameters for source '''
+ data = self.set_default_args()
+ data['source_path'] = '10.10.10.10:/lun/1000'
+ set_module_args(data)
+ my_obj = my_module()
+ my_obj.asup_log_for_cserver = Mock(return_value=None)
+ mock_elem, mock_helper = Mock(), Mock()
+ connection.return_value = mock_helper, mock_elem
+ if not self.onbox:
+ my_obj.server = MockONTAPConnection('snapmirror', status='idle', parm='snapmirrored')
+ my_obj.check_elementsw_parameters('source')
+ connection.called_once_with('source')
+ validate_svip.called_once_with(data['source_path'], mock_elem)
+ validate_volume.called_once_with(data['source_path'], mock_helper)
+
+ def test_check_elementsw_params_negative(self):
+ ''' check elementsw parameters for source negative testing '''
+ data = self.set_default_args()
+ del data['source_path']
+ set_module_args(data)
+ my_obj = my_module()
+ my_obj.asup_log_for_cserver = Mock(return_value=None)
+ if not self.onbox:
+ my_obj.server = MockONTAPConnection('snapmirror', status='idle', parm='snapmirrored')
+ with pytest.raises(AnsibleFailJson) as exc:
+ my_obj.check_elementsw_parameters('source')
+ assert 'Error: Missing required parameter source_path' in exc.value.args[0]['msg']
+
+ def test_check_elementsw_params_invalid(self):
+ ''' check elementsw parameters for source invalid testing '''
+ data = self.set_default_args()
+ set_module_args(data)
+ my_obj = my_module()
+ my_obj.asup_log_for_cserver = Mock(return_value=None)
+ if not self.onbox:
+ my_obj.server = MockONTAPConnection('snapmirror', status='idle', parm='snapmirrored')
+ with pytest.raises(AnsibleFailJson) as exc:
+ my_obj.check_elementsw_parameters('source')
+ assert 'Error: invalid source_path' in exc.value.args[0]['msg']
+
+ def test_elementsw_source_path_format(self):
+ ''' test element_source_path_format_matches '''
+ data = self.set_default_args()
+ set_module_args(data)
+ my_obj = my_module()
+ my_obj.asup_log_for_cserver = Mock(return_value=None)
+ if not self.onbox:
+ my_obj.server = MockONTAPConnection('snapmirror', status='idle', parm='snapmirrored')
+ match = my_obj.element_source_path_format_matches('1.1.1.1:dummy')
+ assert match is None
+ match = my_obj.element_source_path_format_matches('10.10.10.10:/lun/10')
+ assert match is not None
+
+ def test_remote_volume_exists(self):
+ ''' test check_if_remote_volume_exists '''
+ data = self.set_default_args()
+ data['source_volume'] = 'test_vol'
+ data['destination_volume'] = 'test_vol2'
+ set_module_args(data)
+ my_obj = my_module()
+ my_obj.set_source_cluster_connection = Mock(return_value=None)
+ my_obj.asup_log_for_cserver = Mock(return_value=None)
+ if not self.onbox:
+ my_obj.server = MockONTAPConnection('snapmirror', status='idle', parm='snapmirrored')
+ my_obj.source_server = MockONTAPConnection('snapmirror', status='idle', parm='snapmirrored')
+ res = my_obj.check_if_remote_volume_exists()
+ assert res
+
+ def test_if_all_methods_catch_exception(self):
+ data = self.set_default_args()
+ data['source_hostname'] = '10.10.10.10'
+ data['source_volume'] = 'ansible'
+ data['destination_volume'] = 'ansible2'
+ set_module_args(data)
+ my_obj = my_module()
+ if not self.onbox:
+ my_obj.server = MockONTAPConnection('snapmirror_fail')
+ with pytest.raises(AnsibleFailJson) as exc:
+ my_obj.snapmirror_get()
+ assert 'Error fetching snapmirror info: ' in exc.value.args[0]['msg']
+ with pytest.raises(AnsibleFailJson) as exc:
+ my_obj.snapmirror_abort()
+ assert 'Error aborting SnapMirror relationship :' in exc.value.args[0]['msg']
+ with pytest.raises(AnsibleFailJson) as exc:
+ my_obj.snapmirror_quiesce = Mock(return_value=None)
+ my_obj.snapmirror_break()
+ assert 'Error breaking SnapMirror relationship :' in exc.value.args[0]['msg']
+ with pytest.raises(AnsibleFailJson) as exc:
+ my_obj.snapmirror_get = Mock(return_value={'mirror_state': 'transferring'})
+ my_obj.snapmirror_initialize()
+ assert 'Error initializing SnapMirror :' in exc.value.args[0]['msg']
+ with pytest.raises(AnsibleFailJson) as exc:
+ my_obj.snapmirror_update()
+ assert 'Error updating SnapMirror :' in exc.value.args[0]['msg']
+ with pytest.raises(AnsibleFailJson) as exc:
+ my_obj.set_source_cluster_connection = Mock(return_value=True)
+ my_obj.source_server = MockONTAPConnection('snapmirror_fail')
+ my_obj.check_if_remote_volume_exists()
+ assert 'Error fetching source volume details' in exc.value.args[0]['msg']
+ with pytest.raises(AnsibleFailJson) as exc:
+ my_obj.check_if_remote_volume_exists = Mock(return_value=True)
+ my_obj.source_server = MockONTAPConnection()
+ my_obj.snapmirror_create()
+ assert 'Error creating SnapMirror ' in exc.value.args[0]['msg']
+ with pytest.raises(AnsibleFailJson) as exc:
+ my_obj.snapmirror_quiesce = Mock(return_value=None)
+ my_obj.get_destination = Mock(return_value=None)
+ my_obj.snapmirror_break = Mock(return_value=None)
+ my_obj.delete_snapmirror(False, 'data_protection', None)
+ assert 'Error deleting SnapMirror :' in exc.value.args[0]['msg']
+ with pytest.raises(AnsibleFailJson) as exc:
+ my_obj.snapmirror_modify({'policy': 'ansible2', 'schedule': 'abc2'})
+ assert 'Error modifying SnapMirror schedule or policy :' in exc.value.args[0]['msg']
diff --git a/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_snapmirror_policy.py b/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_snapmirror_policy.py
new file mode 100644
index 00000000..2b62e4dc
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_snapmirror_policy.py
@@ -0,0 +1,717 @@
+''' unit tests ONTAP Ansible module: na_ontap_snapmirror_policy '''
+
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+import json
+import pytest
+
+from ansible_collections.netapp.ontap.tests.unit.compat import unittest
+from ansible_collections.netapp.ontap.tests.unit.compat.mock import patch, Mock
+from ansible.module_utils import basic
+from ansible.module_utils._text import to_bytes
+import ansible_collections.netapp.ontap.plugins.module_utils.netapp as netapp_utils
+
+from ansible_collections.netapp.ontap.plugins.modules.na_ontap_snapmirror_policy \
+ import NetAppOntapSnapMirrorPolicy as my_module
+
+if not netapp_utils.has_netapp_lib():
+ pytestmark = pytest.mark.skip('skipping as missing required netapp_lib')
+
+# REST API canned responses when mocking send_request
+SRR = {
+ # common responses
+ 'is_rest': (200, {}, None),
+ 'is_zapi': (400, {}, "Unreachable"),
+ 'empty_good': ({}, None),
+ 'end_of_sequence': (None, "Unexpected call to send_request"),
+ 'generic_error': (None, "Expected error"),
+ # module specific responses
+ 'get_snapmirror_policy': {'vserver': 'ansible',
+ 'policy_name': 'ansible',
+ 'uuid': 'abcdef12-3456-7890-abcd-ef1234567890',
+ 'comment': 'created by ansible',
+ 'policy_type': 'async_mirror',
+ 'snapmirror_label': [],
+ 'keep': [],
+ 'schedule': [],
+ 'prefix': []},
+ 'get_snapmirror_policy_with_rules': {'vserver': 'ansible',
+ 'policy_name': 'ansible',
+ 'uuid': 'abcdef12-3456-7890-abcd-ef1234567890',
+ 'comment': 'created by ansible',
+ 'policy_type': 'async_mirror',
+ 'snapmirror_label': ['daily', 'weekly', 'monthly'],
+ 'keep': [7, 5, 12],
+ 'schedule': ['', 'weekly', 'monthly'],
+ 'prefix': ['', 'weekly', 'monthly']}
+}
+
+
+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 MockONTAPConnection(object):
+ ''' mock server connection to ONTAP host '''
+
+ def __init__(self, kind=None, parm=None):
+ ''' save arguments '''
+ self.type = kind
+ self.xml_in = None
+ self.xml_out = None
+ self.parm = parm
+
+ def invoke_successfully(self, xml, enable_tunneling): # pylint: disable=unused-argument
+ ''' mock invoke_successfully returning xml data '''
+ self.xml_in = xml
+ if self.type == 'snapmirror_policy':
+ xml = self.build_snapmirror_policy_info(self.parm)
+ elif self.type == 'snapmirror_policy_fail':
+ raise netapp_utils.zapi.NaApiError(code='TEST', message="This exception is from the unit test")
+ self.xml_out = xml
+ return xml
+
+ @staticmethod
+ def build_snapmirror_policy_info(mirror_state):
+ ''' build xml data for snapmirror_policy-entry '''
+ xml = netapp_utils.zapi.NaElement('xml')
+ data = {'num-records': 1,
+ 'attributes-list': {'snapmirror-policy-info': {'comment': 'created by ansible',
+ 'policy-name': 'ansible',
+ 'type': 'async_mirror',
+ 'tries': '8',
+ 'transfer-priority': 'normal',
+ 'restart': 'always',
+ 'is-network-compression-enabled': False,
+ 'ignore-atime': False,
+ 'vserver-name': 'ansible'}}}
+ xml.translate_struct(data)
+ return xml
+
+
+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)
+ self.server = MockONTAPConnection()
+ self.source_server = MockONTAPConnection()
+ self.onbox = False
+
+ def set_default_args(self, use_rest=None, with_rules=False):
+ if self.onbox:
+ hostname = '10.10.10.10'
+ username = 'admin'
+ password = 'password'
+ vserver = 'ansible'
+ policy_name = 'ansible'
+ policy_type = 'async_mirror'
+ comment = 'created by ansible'
+ snapmirror_label = ['daily', 'weekly', 'monthly']
+ keep = [7, 5, 12]
+ schedule = ['', 'weekly', 'monthly']
+ prefix = ['', 'weekly', 'monthly']
+ else:
+ hostname = '10.10.10.10'
+ username = 'admin'
+ password = 'password'
+ vserver = 'ansible'
+ policy_name = 'ansible'
+ policy_type = 'async_mirror'
+ comment = 'created by ansible'
+ snapmirror_label = ['daily', 'weekly', 'monthly']
+ keep = [7, 5, 12]
+ schedule = ['', 'weekly', 'monthly']
+ prefix = ['', 'weekly', 'monthly']
+
+ args = dict({
+ 'hostname': hostname,
+ 'username': username,
+ 'password': password,
+ 'vserver': vserver,
+ 'policy_name': policy_name,
+ 'policy_type': policy_type,
+ 'comment': comment
+ })
+
+ if with_rules:
+ args['snapmirror_label'] = snapmirror_label
+ args['keep'] = keep
+ args['schedule'] = schedule
+ args['prefix'] = prefix
+
+ if use_rest is not None:
+ args['use_rest'] = use_rest
+
+ return args
+
+ def test_module_fail_when_required_args_missing(self):
+ ''' required arguments are reported as errors '''
+ with pytest.raises(AnsibleFailJson) as exc:
+ set_module_args({})
+ my_module()
+ print('Info: %s' % exc.value.args[0]['msg'])
+
+ def test_ensure_get_called(self):
+ ''' test get_snapmirror_policy for non-existent snapmirror policy'''
+ set_module_args(self.set_default_args(use_rest='Never'))
+ my_obj = my_module()
+ my_obj.server = self.server
+ assert my_obj.get_snapmirror_policy is not None
+
+ def test_ensure_get_called_existing(self):
+ ''' test get_snapmirror_policy for existing snapmirror policy'''
+ set_module_args(self.set_default_args(use_rest='Never'))
+ my_obj = my_module()
+ my_obj.server = MockONTAPConnection(kind='snapmirror_policy')
+ assert my_obj.get_snapmirror_policy()
+
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_snapmirror_policy.NetAppOntapSnapMirrorPolicy.create_snapmirror_policy')
+ def test_successful_create(self, snapmirror_create_policy):
+ ''' creating snapmirror policy without rules and testing idempotency '''
+ data = self.set_default_args(use_rest='Never')
+ set_module_args(data)
+ my_obj = my_module()
+ my_obj.asup_log_for_cserver = Mock(return_value=None)
+ if not self.onbox:
+ my_obj.server = self.server
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ assert exc.value.args[0]['changed']
+ snapmirror_create_policy.assert_called_with()
+ # to reset na_helper from remembering the previous 'changed' value
+ data = self.set_default_args(use_rest='Never')
+ set_module_args(data)
+ my_obj = my_module()
+ my_obj.asup_log_for_cserver = Mock(return_value=None)
+ if not self.onbox:
+ my_obj.server = MockONTAPConnection('snapmirror_policy')
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ assert not exc.value.args[0]['changed']
+
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_snapmirror_policy.NetAppOntapSnapMirrorPolicy.create_snapmirror_policy')
+ def test_successful_create_with_rest(self, snapmirror_create_policy):
+ ''' creating snapmirror policy without rules via REST and testing idempotency '''
+ data = self.set_default_args()
+ data['use_rest'] = 'Always'
+ set_module_args(data)
+ my_obj = my_module()
+ my_obj.asup_log_for_cserver = Mock(return_value=None)
+ my_obj.get_snapmirror_policy = Mock()
+ my_obj.get_snapmirror_policy.side_effect = [None, SRR['get_snapmirror_policy']]
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ assert exc.value.args[0]['changed']
+ snapmirror_create_policy.assert_called_with()
+ # to reset na_helper from remembering the previous 'changed' value
+ data = self.set_default_args(use_rest='Never')
+ data['use_rest'] = 'Always'
+ set_module_args(data)
+ my_obj = my_module()
+ my_obj.asup_log_for_cserver = Mock(return_value=None)
+ my_obj.get_snapmirror_policy = Mock(return_value=SRR['get_snapmirror_policy'])
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ assert not exc.value.args[0]['changed']
+
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_snapmirror_policy.NetAppOntapSnapMirrorPolicy.create_snapmirror_policy')
+ def test_successful_create_with_rules(self, snapmirror_create_policy):
+ ''' creating snapmirror policy with rules and testing idempotency '''
+ data = self.set_default_args(use_rest='Never', with_rules=True)
+ set_module_args(data)
+ my_obj = my_module()
+ my_obj.asup_log_for_cserver = Mock(return_value=None)
+ my_obj.get_snapmirror_policy = Mock(return_value=None)
+ if not self.onbox:
+ my_obj.server = self.server
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ assert exc.value.args[0]['changed']
+ snapmirror_create_policy.assert_called_with()
+ # to reset na_helper from remembering the previous 'changed' value
+ data = self.set_default_args(use_rest='Never', with_rules=True)
+ set_module_args(data)
+ my_obj = my_module()
+ my_obj.asup_log_for_cserver = Mock(return_value=None)
+ my_obj.get_snapmirror_policy = Mock(return_value=SRR['get_snapmirror_policy_with_rules'])
+ if not self.onbox:
+ my_obj.server = MockONTAPConnection('snapmirror_policy')
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ assert not exc.value.args[0]['changed']
+
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_snapmirror_policy.NetAppOntapSnapMirrorPolicy.modify_snapmirror_policy_rules')
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_snapmirror_policy.NetAppOntapSnapMirrorPolicy.create_snapmirror_policy')
+ def test_successful_create_with_rules_via_rest(self, snapmirror_create_policy, modify_snapmirror_policy_rules):
+ ''' creating snapmirror policy with rules via rest and testing idempotency '''
+ data = self.set_default_args(use_rest='Always', with_rules=True)
+ set_module_args(data)
+ my_obj = my_module()
+ my_obj.asup_log_for_cserver = Mock(return_value=None)
+ my_obj.get_snapmirror_policy = Mock()
+ my_obj.get_snapmirror_policy.side_effect = [None, SRR['get_snapmirror_policy']]
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ assert exc.value.args[0]['changed']
+ snapmirror_create_policy.assert_called_with()
+ # to reset na_helper from remembering the previous 'changed' value
+ data = self.set_default_args(use_rest='Always', with_rules=True)
+ set_module_args(data)
+ my_obj = my_module()
+ my_obj.asup_log_for_cserver = Mock(return_value=None)
+ my_obj.get_snapmirror_policy = Mock(return_value=SRR['get_snapmirror_policy_with_rules'])
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ assert not exc.value.args[0]['changed']
+
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_snapmirror_policy.NetAppOntapSnapMirrorPolicy.delete_snapmirror_policy')
+ def test_successful_delete(self, delete_snapmirror_policy):
+ ''' deleting snapmirror policy and testing idempotency '''
+ data = self.set_default_args(use_rest='Never')
+ data['state'] = 'absent'
+ set_module_args(data)
+ my_obj = my_module()
+ my_obj.asup_log_for_cserver = Mock(return_value=None)
+ if not self.onbox:
+ my_obj.server = MockONTAPConnection('snapmirror_policy')
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ assert exc.value.args[0]['changed']
+ delete_snapmirror_policy.assert_called_with(None)
+ # to reset na_helper from remembering the previous 'changed' value
+ my_obj = my_module()
+ my_obj.asup_log_for_cserver = Mock(return_value=None)
+ if not self.onbox:
+ my_obj.server = self.server
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ assert not exc.value.args[0]['changed']
+
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_snapmirror_policy.NetAppOntapSnapMirrorPolicy.delete_snapmirror_policy')
+ def test_successful_delete_with_rest(self, delete_snapmirror_policy):
+ ''' deleting snapmirror policy via REST and testing idempotency '''
+ data = self.set_default_args()
+ data['state'] = 'absent'
+ data['use_rest'] = 'Always'
+ set_module_args(data)
+ my_obj = my_module()
+ my_obj.asup_log_for_cserver = Mock(return_value=None)
+ my_obj.get_snapmirror_policy = Mock(return_value=SRR['get_snapmirror_policy'])
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ assert exc.value.args[0]['changed']
+ delete_snapmirror_policy.assert_called_with('abcdef12-3456-7890-abcd-ef1234567890')
+ my_obj = my_module()
+ my_obj.asup_log_for_cserver = Mock(return_value=None)
+ my_obj.get_snapmirror_policy = Mock(return_value=None)
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ assert not exc.value.args[0]['changed']
+
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_snapmirror_policy.NetAppOntapSnapMirrorPolicy.modify_snapmirror_policy')
+ def test_successful_modify(self, snapmirror_policy_modify):
+ ''' modifying snapmirror policy without rules and testing idempotency '''
+ data = self.set_default_args(use_rest='Never')
+ data['comment'] = 'old comment'
+ data['ignore_atime'] = True
+ data['is_network_compression_enabled'] = True
+ data['owner'] = 'cluster_admin'
+ data['restart'] = 'default'
+ data['tries'] = '7'
+ set_module_args(data)
+ my_obj = my_module()
+ my_obj.asup_log_for_cserver = Mock(return_value=None)
+ if not self.onbox:
+ my_obj.server = MockONTAPConnection('snapmirror_policy')
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ assert exc.value.args[0]['changed']
+ snapmirror_policy_modify.assert_called_with()
+ # to reset na_helper from remembering the previous 'changed' value
+ data = self.set_default_args(use_rest='Never')
+ set_module_args(data)
+ my_obj = my_module()
+ my_obj.asup_log_for_cserver = Mock(return_value=None)
+ if not self.onbox:
+ my_obj.server = MockONTAPConnection('snapmirror_policy')
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ assert not exc.value.args[0]['changed']
+
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_snapmirror_policy.NetAppOntapSnapMirrorPolicy.modify_snapmirror_policy')
+ def test_successful_modify_with_rest(self, snapmirror_policy_modify):
+ ''' modifying snapmirror policy without rules via REST and testing idempotency '''
+ data = self.set_default_args()
+ data['comment'] = 'old comment'
+ data['use_rest'] = 'Always'
+ set_module_args(data)
+ my_obj = my_module()
+ my_obj.asup_log_for_cserver = Mock(return_value=None)
+ my_obj.get_snapmirror_policy = Mock(return_value=SRR['get_snapmirror_policy'])
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ assert exc.value.args[0]['changed']
+ snapmirror_policy_modify.assert_called_with('abcdef12-3456-7890-abcd-ef1234567890', 'async_mirror')
+ # to reset na_helper from remembering the previous 'changed' value
+ data = self.set_default_args()
+ data['use_rest'] = 'Always'
+ set_module_args(data)
+ my_obj = my_module()
+ my_obj.asup_log_for_cserver = Mock(return_value=None)
+ my_obj.get_snapmirror_policy = Mock(return_value=SRR['get_snapmirror_policy'])
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ assert not exc.value.args[0]['changed']
+
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_snapmirror_policy.NetAppOntapSnapMirrorPolicy.modify_snapmirror_policy')
+ def test_successful_modify_with_rules(self, snapmirror_policy_modify):
+ ''' modifying snapmirror policy with rules and testing idempotency '''
+ data = self.set_default_args(use_rest='Never', with_rules=True)
+ set_module_args(data)
+ my_obj = my_module()
+ my_obj.asup_log_for_cserver = Mock(return_value=None)
+ my_obj.get_snapmirror_policy = Mock(return_value=SRR['get_snapmirror_policy'])
+ if not self.onbox:
+ my_obj.server = MockONTAPConnection('snapmirror_policy')
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ assert exc.value.args[0]['changed']
+ snapmirror_policy_modify.assert_called_with()
+ # to reset na_helper from remembering the previous 'changed' value
+ data = self.set_default_args(use_rest='Never', with_rules=True)
+ set_module_args(data)
+ my_obj = my_module()
+ my_obj.asup_log_for_cserver = Mock(return_value=None)
+ my_obj.get_snapmirror_policy = Mock(return_value=SRR['get_snapmirror_policy_with_rules'])
+ if not self.onbox:
+ my_obj.server = MockONTAPConnection('snapmirror_policy')
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ assert not exc.value.args[0]['changed']
+
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_snapmirror_policy.NetAppOntapSnapMirrorPolicy.modify_snapmirror_policy_rules')
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_snapmirror_policy.NetAppOntapSnapMirrorPolicy.modify_snapmirror_policy')
+ def test_successful_modify_with_rules_via_rest(self, snapmirror_policy_modify, modify_snapmirror_policy_rules):
+ ''' modifying snapmirror policy with rules via rest and testing idempotency '''
+ data = self.set_default_args(use_rest='Always', with_rules=True)
+ set_module_args(data)
+ my_obj = my_module()
+ my_obj.asup_log_for_cserver = Mock(return_value=None)
+ my_obj.get_snapmirror_policy = Mock(return_value=SRR['get_snapmirror_policy'])
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ assert exc.value.args[0]['changed']
+ snapmirror_policy_modify.assert_called_with('abcdef12-3456-7890-abcd-ef1234567890', 'async_mirror')
+ # to reset na_helper from remembering the previous 'changed' value
+ data = self.set_default_args(use_rest='Always', with_rules=True)
+ set_module_args(data)
+ my_obj = my_module()
+ my_obj.asup_log_for_cserver = Mock(return_value=None)
+ my_obj.get_snapmirror_policy = Mock(return_value=SRR['get_snapmirror_policy_with_rules'])
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ assert not exc.value.args[0]['changed']
+
+ def test_if_all_methods_catch_exception(self):
+ data = self.set_default_args(use_rest='Never')
+ set_module_args(data)
+ my_obj = my_module()
+ if not self.onbox:
+ my_obj.server = MockONTAPConnection('snapmirror_policy_fail')
+ with pytest.raises(AnsibleFailJson) as exc:
+ my_obj.get_snapmirror_policy()
+ assert 'Error getting snapmirror policy ansible:' in exc.value.args[0]['msg']
+ with pytest.raises(AnsibleFailJson) as exc:
+ my_obj.create_snapmirror_policy()
+ assert 'Error creating snapmirror policy ansible:' in exc.value.args[0]['msg']
+ with pytest.raises(AnsibleFailJson) as exc:
+ my_obj.delete_snapmirror_policy()
+ assert 'Error deleting snapmirror policy ansible:' in exc.value.args[0]['msg']
+ with pytest.raises(AnsibleFailJson) as exc:
+ my_obj.modify_snapmirror_policy()
+ assert 'Error modifying snapmirror policy ansible:' in exc.value.args[0]['msg']
+
+ def test_create_snapmirror_policy_retention_obj_for_rest(self):
+ ''' test create_snapmirror_policy_retention_obj_for_rest '''
+ data = self.set_default_args(use_rest='Never')
+ set_module_args(data)
+ my_obj = my_module()
+
+ # Test no rules
+ self.assertEqual(my_obj.create_snapmirror_policy_retention_obj_for_rest(), [])
+
+ # Test one rule
+ rules = [{'snapmirror_label': 'daily', 'keep': 7}]
+ retention_obj = [{'label': 'daily', 'count': '7'}]
+ self.assertEqual(my_obj.create_snapmirror_policy_retention_obj_for_rest(rules), retention_obj)
+
+ # Test two rules, with a prefix
+ rules = [{'snapmirror_label': 'daily', 'keep': 7},
+ {'snapmirror_label': 'weekly', 'keep': 5, 'prefix': 'weekly'}]
+ retention_obj = [{'label': 'daily', 'count': '7'},
+ {'label': 'weekly', 'count': '5', 'prefix': 'weekly'}]
+ self.assertEqual(my_obj.create_snapmirror_policy_retention_obj_for_rest(rules), retention_obj)
+
+ # Test three rules, with a prefix & schedule
+ rules = [{'snapmirror_label': 'daily', 'keep': 7},
+ {'snapmirror_label': 'weekly', 'keep': 5, 'prefix': 'weekly_sv'},
+ {'snapmirror_label': 'monthly', 'keep': 12, 'prefix': 'monthly_sv', 'schedule': 'monthly'}]
+ retention_obj = [{'label': 'daily', 'count': '7'},
+ {'label': 'weekly', 'count': '5', 'prefix': 'weekly_sv'},
+ {'label': 'monthly', 'count': '12', 'prefix': 'monthly_sv', 'creation_schedule': {'name': 'monthly'}}]
+ self.assertEqual(my_obj.create_snapmirror_policy_retention_obj_for_rest(rules), retention_obj)
+
+ def test_identify_snapmirror_policy_rules_with_schedule(self):
+ ''' test identify_snapmirror_policy_rules_with_schedule '''
+ data = self.set_default_args(use_rest='Never')
+ set_module_args(data)
+ my_obj = my_module()
+
+ # Test no rules
+ self.assertEqual(my_obj.identify_snapmirror_policy_rules_with_schedule(), ([], []))
+
+ # Test one non-schedule rule identified
+ rules = [{'snapmirror_label': 'daily', 'keep': 7}]
+ schedule_rules = []
+ non_schedule_rules = [{'snapmirror_label': 'daily', 'keep': 7}]
+ self.assertEqual(my_obj.identify_snapmirror_policy_rules_with_schedule(rules), (schedule_rules, non_schedule_rules))
+
+ # Test one schedule and two non-schedule rules identified
+ rules = [{'snapmirror_label': 'daily', 'keep': 7},
+ {'snapmirror_label': 'weekly', 'keep': 5, 'prefix': 'weekly_sv'},
+ {'snapmirror_label': 'monthly', 'keep': 12, 'prefix': 'monthly_sv', 'schedule': 'monthly'}]
+ schedule_rules = [{'snapmirror_label': 'monthly', 'keep': 12, 'prefix': 'monthly_sv', 'schedule': 'monthly'}]
+ non_schedule_rules = [{'snapmirror_label': 'daily', 'keep': 7},
+ {'snapmirror_label': 'weekly', 'keep': 5, 'prefix': 'weekly_sv'}]
+ self.assertEqual(my_obj.identify_snapmirror_policy_rules_with_schedule(rules), (schedule_rules, non_schedule_rules))
+
+ # Test three schedule & zero non-schedule rules identified
+ rules = [{'snapmirror_label': 'daily', 'keep': 7, 'schedule': 'daily'},
+ {'snapmirror_label': 'weekly', 'keep': 5, 'prefix': 'weekly_sv', 'schedule': 'weekly'},
+ {'snapmirror_label': 'monthly', 'keep': 12, 'prefix': 'monthly_sv', 'schedule': 'monthly'}]
+ schedule_rules = [{'snapmirror_label': 'daily', 'keep': 7, 'schedule': 'daily'},
+ {'snapmirror_label': 'weekly', 'keep': 5, 'prefix': 'weekly_sv', 'schedule': 'weekly'},
+ {'snapmirror_label': 'monthly', 'keep': 12, 'prefix': 'monthly_sv', 'schedule': 'monthly'}]
+ non_schedule_rules = []
+ self.assertEqual(my_obj.identify_snapmirror_policy_rules_with_schedule(rules), (schedule_rules, non_schedule_rules))
+
+ def test_identify_new_snapmirror_policy_rules(self):
+ ''' test identify_new_snapmirror_policy_rules '''
+
+ # Test with no rules in parameters. new_rules should always be [].
+ data = self.set_default_args(use_rest='Never', with_rules=False)
+ set_module_args(data)
+ my_obj = my_module()
+
+ current = None
+ new_rules = []
+ self.assertEqual(my_obj.identify_new_snapmirror_policy_rules(current), new_rules)
+
+ current = {'snapmirror_label': ['daily'], 'keep': [7], 'prefix': [''], 'schedule': ['']}
+ new_rules = []
+ self.assertEqual(my_obj.identify_new_snapmirror_policy_rules(current), new_rules)
+
+ # Test with rules in parameters.
+ data = self.set_default_args(use_rest='Never', with_rules=True)
+ set_module_args(data)
+ my_obj = my_module()
+
+ # Test three new rules identified when no rules currently exist
+ current = None
+ new_rules = [{'snapmirror_label': 'daily', 'keep': 7, 'prefix': '', 'schedule': ''},
+ {'snapmirror_label': 'weekly', 'keep': 5, 'prefix': 'weekly', 'schedule': 'weekly'},
+ {'snapmirror_label': 'monthly', 'keep': 12, 'prefix': 'monthly', 'schedule': 'monthly'}]
+ self.assertEqual(my_obj.identify_new_snapmirror_policy_rules(current), new_rules)
+
+ # Test two new rules identified and one rule already exists
+ current = {'snapmirror_label': ['daily'], 'keep': [7], 'prefix': [''], 'schedule': ['']}
+ new_rules = [{'snapmirror_label': 'weekly', 'keep': 5, 'prefix': 'weekly', 'schedule': 'weekly'},
+ {'snapmirror_label': 'monthly', 'keep': 12, 'prefix': 'monthly', 'schedule': 'monthly'}]
+ self.assertEqual(my_obj.identify_new_snapmirror_policy_rules(current), new_rules)
+
+ # Test one new rule identified and two rules already exist
+ current = {'snapmirror_label': ['daily', 'monthly'],
+ 'keep': [7, 12],
+ 'prefix': ['', 'monthly'],
+ 'schedule': ['', 'monthly']}
+ new_rules = [{'snapmirror_label': 'weekly', 'keep': 5, 'prefix': 'weekly', 'schedule': 'weekly'}]
+ self.assertEqual(my_obj.identify_new_snapmirror_policy_rules(current), new_rules)
+
+ # Test no new rules identified as all rules already exist
+ current = {'snapmirror_label': ['daily', 'monthly', 'weekly'],
+ 'keep': [7, 12, 5],
+ 'prefix': ['', 'monthly', 'weekly'],
+ 'schedule': ['', 'monthly', 'weekly']}
+ new_rules = []
+ self.assertEqual(my_obj.identify_new_snapmirror_policy_rules(current), new_rules)
+
+ def test_identify_obsolete_snapmirror_policy_rules(self):
+ ''' test identify_obsolete_snapmirror_policy_rules '''
+
+ # Test with no rules in parameters. obsolete_rules should always be [].
+ data = self.set_default_args(use_rest='Never', with_rules=False)
+ set_module_args(data)
+ my_obj = my_module()
+
+ current = None
+ obsolete_rules = []
+ self.assertEqual(my_obj.identify_obsolete_snapmirror_policy_rules(current), obsolete_rules)
+
+ current = {'snapmirror_label': ['daily'], 'keep': [7], 'prefix': [''], 'schedule': ['']}
+ obsolete_rules = []
+ self.assertEqual(my_obj.identify_obsolete_snapmirror_policy_rules(current), obsolete_rules)
+
+ # Test removing all rules. obsolete_rules should equal current.
+ data = self.set_default_args(use_rest='Never', with_rules=False)
+ data['snapmirror_label'] = []
+ set_module_args(data)
+ my_obj = my_module()
+
+ current = {'snapmirror_label': ['monthly', 'weekly', 'hourly', 'daily', 'yearly'],
+ 'keep': [12, 5, 24, 7, 7],
+ 'prefix': ['monthly', 'weekly', '', '', 'yearly'],
+ 'schedule': ['monthly', 'weekly', '', '', 'yearly']}
+ obsolete_rules = [{'snapmirror_label': 'monthly', 'keep': 12, 'prefix': 'monthly', 'schedule': 'monthly'},
+ {'snapmirror_label': 'weekly', 'keep': 5, 'prefix': 'weekly', 'schedule': 'weekly'},
+ {'snapmirror_label': 'hourly', 'keep': 24, 'prefix': '', 'schedule': ''},
+ {'snapmirror_label': 'daily', 'keep': 7, 'prefix': '', 'schedule': ''},
+ {'snapmirror_label': 'yearly', 'keep': 7, 'prefix': 'yearly', 'schedule': 'yearly'}]
+ self.assertEqual(my_obj.identify_obsolete_snapmirror_policy_rules(current), obsolete_rules)
+
+ # Test with rules in parameters.
+ data = self.set_default_args(use_rest='Never', with_rules=True)
+ set_module_args(data)
+ my_obj = my_module()
+
+ # Test no rules exist, thus no obsolete rules
+ current = None
+ obsolete_rules = []
+ self.assertEqual(my_obj.identify_obsolete_snapmirror_policy_rules(current), obsolete_rules)
+
+ # Test new rules and one obsolete rule identified
+ current = {'snapmirror_label': ['hourly'], 'keep': [24], 'prefix': [''], 'schedule': ['']}
+ obsolete_rules = [{'snapmirror_label': 'hourly', 'keep': 24, 'prefix': '', 'schedule': ''}]
+ self.assertEqual(my_obj.identify_obsolete_snapmirror_policy_rules(current), obsolete_rules)
+
+ # Test new rules, with one retained and one obsolete rule identified
+ current = {'snapmirror_label': ['hourly', 'daily'],
+ 'keep': [24, 7],
+ 'prefix': ['', ''],
+ 'schedule': ['', '']}
+ obsolete_rules = [{'snapmirror_label': 'hourly', 'keep': 24, 'prefix': '', 'schedule': ''}]
+ self.assertEqual(my_obj.identify_obsolete_snapmirror_policy_rules(current), obsolete_rules)
+
+ # Test new rules and two obsolete rules identified
+ current = {'snapmirror_label': ['monthly', 'weekly', 'hourly', 'daily', 'yearly'],
+ 'keep': [12, 5, 24, 7, 7],
+ 'prefix': ['monthly', 'weekly', '', '', 'yearly'],
+ 'schedule': ['monthly', 'weekly', '', '', 'yearly']}
+ obsolete_rules = [{'snapmirror_label': 'hourly', 'keep': 24, 'prefix': '', 'schedule': ''},
+ {'snapmirror_label': 'yearly', 'keep': 7, 'prefix': 'yearly', 'schedule': 'yearly'}]
+ self.assertEqual(my_obj.identify_obsolete_snapmirror_policy_rules(current), obsolete_rules)
+
+ def test_identify_modified_snapmirror_policy_rules(self):
+ ''' test identify_modified_snapmirror_policy_rules '''
+
+ # Test with no rules in parameters. modified_rules & unmodified_rules should always be [].
+ data = self.set_default_args(use_rest='Never', with_rules=False)
+ data.pop('snapmirror_label', None)
+ set_module_args(data)
+ my_obj = my_module()
+
+ current = None
+ modified_rules, unmodified_rules = [], []
+ self.assertEqual(my_obj.identify_modified_snapmirror_policy_rules(current), (modified_rules, unmodified_rules))
+
+ current = {'snapmirror_label': ['daily'], 'keep': [14], 'prefix': ['daily'], 'schedule': ['daily']}
+ modified_rules, unmodified_rules = [], []
+ self.assertEqual(my_obj.identify_modified_snapmirror_policy_rules(current), (modified_rules, unmodified_rules))
+
+ # Test removing all rules. modified_rules & unmodified_rules should be [].
+ data = self.set_default_args(use_rest='Never', with_rules=False)
+ data['snapmirror_label'] = []
+ set_module_args(data)
+ my_obj = my_module()
+ current = {'snapmirror_label': ['monthly', 'weekly', 'hourly', 'daily', 'yearly'],
+ 'keep': [12, 5, 24, 7, 7],
+ 'prefix': ['monthly', 'weekly', '', '', 'yearly'],
+ 'schedule': ['monthly', 'weekly', '', '', 'yearly']}
+ modified_rules, unmodified_rules = [], []
+ self.assertEqual(my_obj.identify_modified_snapmirror_policy_rules(current), (modified_rules, unmodified_rules))
+
+ # Test with rules in parameters.
+ data = self.set_default_args(use_rest='Never', with_rules=True)
+ set_module_args(data)
+ my_obj = my_module()
+
+ # Test no rules exist, thus no modified & unmodified rules
+ current = None
+ modified_rules, unmodified_rules = [], []
+ self.assertEqual(my_obj.identify_modified_snapmirror_policy_rules(current), (modified_rules, unmodified_rules))
+
+ # Test new rules don't exist, thus no modified & unmodified rules
+ current = {'snapmirror_label': ['hourly'], 'keep': [24], 'prefix': [''], 'schedule': ['']}
+ modified_rules, unmodified_rules = [], []
+ self.assertEqual(my_obj.identify_modified_snapmirror_policy_rules(current), (modified_rules, unmodified_rules))
+
+ # Test daily & monthly modified, weekly unmodified
+ current = {'snapmirror_label': ['hourly', 'daily', 'weekly', 'monthly'],
+ 'keep': [24, 14, 5, 6],
+ 'prefix': ['', 'daily', 'weekly', 'monthly'],
+ 'schedule': ['', 'daily', 'weekly', 'monthly']}
+ modified_rules = [{'snapmirror_label': 'daily', 'keep': 7, 'prefix': '', 'schedule': ''},
+ {'snapmirror_label': 'monthly', 'keep': 12, 'prefix': 'monthly', 'schedule': 'monthly'}]
+ unmodified_rules = [{'snapmirror_label': 'weekly', 'keep': 5, 'prefix': 'weekly', 'schedule': 'weekly'}]
+ self.assertEqual(my_obj.identify_modified_snapmirror_policy_rules(current), (modified_rules, unmodified_rules))
+
+ # Test all rules modified
+ current = {'snapmirror_label': ['daily', 'weekly', 'monthly'],
+ 'keep': [14, 10, 6],
+ 'prefix': ['', '', ''],
+ 'schedule': ['daily', 'weekly', 'monthly']}
+ modified_rules = [{'snapmirror_label': 'daily', 'keep': 7, 'prefix': '', 'schedule': ''},
+ {'snapmirror_label': 'weekly', 'keep': 5, 'prefix': 'weekly', 'schedule': 'weekly'},
+ {'snapmirror_label': 'monthly', 'keep': 12, 'prefix': 'monthly', 'schedule': 'monthly'}]
+ unmodified_rules = []
+ self.assertEqual(my_obj.identify_modified_snapmirror_policy_rules(current), (modified_rules, unmodified_rules))
+
+ # Test all rules unmodified
+ current = {'snapmirror_label': ['daily', 'weekly', 'monthly'],
+ 'keep': [7, 5, 12],
+ 'prefix': ['', 'weekly', 'monthly'],
+ 'schedule': ['', 'weekly', 'monthly']}
+ modified_rules = []
+ unmodified_rules = [{'snapmirror_label': 'daily', 'keep': 7, 'prefix': '', 'schedule': ''},
+ {'snapmirror_label': 'weekly', 'keep': 5, 'prefix': 'weekly', 'schedule': 'weekly'},
+ {'snapmirror_label': 'monthly', 'keep': 12, 'prefix': 'monthly', 'schedule': 'monthly'}]
+ self.assertEqual(my_obj.identify_modified_snapmirror_policy_rules(current), (modified_rules, unmodified_rules))
diff --git a/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_snapshot.py b/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_snapshot.py
new file mode 100644
index 00000000..74c87355
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_snapshot.py
@@ -0,0 +1,227 @@
+# (c) 2018, NetApp, Inc
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+''' unit tests ONTAP Ansible module: na_ontap_nvme_snapshot'''
+
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+import json
+import pytest
+
+from ansible_collections.netapp.ontap.tests.unit.compat import unittest
+from ansible_collections.netapp.ontap.tests.unit.compat.mock import patch
+from ansible.module_utils import basic
+from ansible.module_utils._text import to_bytes
+import ansible_collections.netapp.ontap.plugins.module_utils.netapp as netapp_utils
+
+from ansible_collections.netapp.ontap.plugins.modules.na_ontap_snapshot \
+ import NetAppOntapSnapshot as my_module
+
+if not netapp_utils.has_netapp_lib():
+ pytestmark = pytest.mark.skip('skipping as missing required netapp_lib')
+
+
+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 MockONTAPConnection(object):
+ ''' mock server connection to ONTAP host '''
+
+ def __init__(self, kind=None):
+ ''' save arguments '''
+ self.type = kind
+ self.xml_in = None
+ self.xml_out = None
+
+ def invoke_successfully(self, xml, enable_tunneling): # pylint: disable=unused-argument
+ ''' mock invoke_successfully returning xml data '''
+ self.xml_in = xml
+ if self.type == 'snapshot':
+ xml = self.build_snapshot_info()
+ elif self.type == 'snapshot_fail':
+ raise netapp_utils.zapi.NaApiError(code='TEST', message="This exception is from the unit test")
+ self.xml_out = xml
+ return xml
+
+ @staticmethod
+ def build_snapshot_info():
+ ''' build xml data for snapshot-info '''
+ xml = netapp_utils.zapi.NaElement('xml')
+ data = {'num-records': 1,
+ 'attributes-list': {'snapshot-info': {'comment': 'new comment',
+ 'name': 'ansible',
+ 'snapmirror-label': 'label12'}}}
+ xml.translate_struct(data)
+ return xml
+
+
+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)
+ self.server = MockONTAPConnection()
+ self.onbox = False
+
+ def set_default_args(self):
+ if self.onbox:
+ hostname = '10.193.75.3'
+ username = 'admin'
+ password = 'netapp1!'
+ vserver = 'ansible'
+ volume = 'ansible'
+ snapshot = 'ansible'
+ comment = 'new comment'
+ snapmirror_label = 'label12'
+ else:
+ hostname = 'hostname'
+ username = 'username'
+ password = 'password'
+ vserver = 'vserver'
+ volume = 'ansible'
+ snapshot = 'ansible'
+ comment = 'new comment'
+ snapmirror_label = 'label12'
+ return dict({
+ 'hostname': hostname,
+ 'username': username,
+ 'password': password,
+ 'vserver': vserver,
+ 'volume': volume,
+ 'snapshot': snapshot,
+ 'comment': comment,
+ 'snapmirror_label': snapmirror_label
+ })
+
+ def test_module_fail_when_required_args_missing(self):
+ ''' required arguments are reported as errors '''
+ with pytest.raises(AnsibleFailJson) as exc:
+ set_module_args({})
+ my_module()
+ print('Info: %s' % exc.value.args[0]['msg'])
+
+ def test_ensure_get_called(self):
+ ''' test get_snapshot() for non-existent snapshot'''
+ set_module_args(self.set_default_args())
+ my_obj = my_module()
+ my_obj.server = self.server
+ assert my_obj.get_snapshot() is None
+
+ def test_ensure_get_called_existing(self):
+ ''' test get_snapshot() for existing snapshot'''
+ set_module_args(self.set_default_args())
+ my_obj = my_module()
+ my_obj.server = MockONTAPConnection(kind='snapshot')
+ assert my_obj.get_snapshot()
+
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_snapshot.NetAppOntapSnapshot.create_snapshot')
+ def test_successful_create(self, create_snapshot):
+ ''' creating snapshot and testing idempotency '''
+ set_module_args(self.set_default_args())
+ my_obj = my_module()
+ if not self.onbox:
+ my_obj.server = self.server
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ assert exc.value.args[0]['changed']
+ create_snapshot.assert_called_with()
+ # to reset na_helper from remembering the previous 'changed' value
+ my_obj = my_module()
+ if not self.onbox:
+ my_obj.server = MockONTAPConnection('snapshot')
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ assert not exc.value.args[0]['changed']
+
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_snapshot.NetAppOntapSnapshot.modify_snapshot')
+ def test_successful_modify(self, modify_snapshot):
+ ''' modifying snapshot and testing idempotency '''
+ data = self.set_default_args()
+ data['comment'] = 'adding comment'
+ data['snapmirror_label'] = 'label22'
+ set_module_args(data)
+ my_obj = my_module()
+ if not self.onbox:
+ my_obj.server = MockONTAPConnection('snapshot')
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ assert exc.value.args[0]['changed']
+ modify_snapshot.assert_called_with()
+ # to reset na_helper from remembering the previous 'changed' value
+ data['comment'] = 'new comment'
+ data['snapmirror_label'] = 'label12'
+ set_module_args(data)
+ my_obj = my_module()
+ if not self.onbox:
+ my_obj.server = MockONTAPConnection('snapshot')
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ assert not exc.value.args[0]['changed']
+
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_snapshot.NetAppOntapSnapshot.delete_snapshot')
+ def test_successful_delete(self, delete_snapshot):
+ ''' deleting snapshot and testing idempotency '''
+ data = self.set_default_args()
+ data['state'] = 'absent'
+ set_module_args(data)
+ my_obj = my_module()
+ if not self.onbox:
+ my_obj.server = MockONTAPConnection('snapshot')
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ assert exc.value.args[0]['changed']
+ delete_snapshot.assert_called_with()
+ # to reset na_helper from remembering the previous 'changed' value
+ my_obj = my_module()
+ if not self.onbox:
+ my_obj.server = self.server
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ assert not exc.value.args[0]['changed']
+
+ def test_if_all_methods_catch_exception(self):
+ module_args = {}
+ module_args.update(self.set_default_args())
+ set_module_args(module_args)
+ my_obj = my_module()
+ if not self.onbox:
+ my_obj.server = MockONTAPConnection('snapshot_fail')
+ with pytest.raises(AnsibleFailJson) as exc:
+ my_obj.create_snapshot()
+ assert 'Error creating snapshot ansible:' in exc.value.args[0]['msg']
+ with pytest.raises(AnsibleFailJson) as exc:
+ my_obj.delete_snapshot()
+ assert 'Error deleting snapshot ansible:' in exc.value.args[0]['msg']
+ with pytest.raises(AnsibleFailJson) as exc:
+ my_obj.modify_snapshot()
+ assert 'Error modifying snapshot ansible:' in exc.value.args[0]['msg']
diff --git a/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_snapshot_policy.py b/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_snapshot_policy.py
new file mode 100644
index 00000000..57bd42dc
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_snapshot_policy.py
@@ -0,0 +1,691 @@
+# (c) 2018, NetApp, Inc
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+''' unit tests ONTAP Ansible module: na_ontap_snapshot_policy'''
+
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+import json
+import pytest
+
+from ansible_collections.netapp.ontap.tests.unit.compat import unittest
+from ansible_collections.netapp.ontap.tests.unit.compat.mock import patch, Mock
+from ansible.module_utils import basic
+from ansible.module_utils._text import to_bytes
+import ansible_collections.netapp.ontap.plugins.module_utils.netapp as netapp_utils
+
+from ansible_collections.netapp.ontap.plugins.modules.na_ontap_snapshot_policy \
+ import NetAppOntapSnapshotPolicy as my_module
+
+if not netapp_utils.has_netapp_lib():
+ pytestmark = pytest.mark.skip('skipping as missing required netapp_lib')
+
+
+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 MockONTAPConnection(object):
+ ''' mock server connection to ONTAP host '''
+
+ def __init__(self, kind=None):
+ ''' save arguments '''
+ self.type = kind
+ self.xml_in = None
+ self.xml_out = None
+
+ def invoke_successfully(self, xml, enable_tunneling): # pylint: disable=unused-argument
+ ''' mock invoke_successfully returning xml data '''
+ self.xml_in = xml
+ if self.type == 'policy':
+ xml = self.build_snapshot_policy_info()
+ elif self.type == 'snapshot_policy_info_policy_disabled':
+ xml = self.build_snapshot_policy_info_policy_disabled()
+ elif self.type == 'snapshot_policy_info_comment_modified':
+ xml = self.build_snapshot_policy_info_comment_modified()
+ elif self.type == 'snapshot_policy_info_schedules_added':
+ xml = self.build_snapshot_policy_info_schedules_added()
+ elif self.type == 'snapshot_policy_info_schedules_deleted':
+ xml = self.build_snapshot_policy_info_schedules_deleted()
+ elif self.type == 'snapshot_policy_info_modified_schedule_counts':
+ xml = self.build_snapshot_policy_info_modified_schedule_counts()
+ elif self.type == 'policy_fail':
+ raise netapp_utils.zapi.NaApiError(code='TEST', message="This exception is from the unit test")
+ self.xml_out = xml
+ return xml
+
+ def asup_log_for_cserver(self):
+ ''' mock autosupport log'''
+ return None
+
+ @staticmethod
+ def build_snapshot_policy_info():
+ ''' build xml data for snapshot-policy-info '''
+ xml = netapp_utils.zapi.NaElement('xml')
+ data = {'num-records': 1,
+ 'attributes-list': {
+ 'snapshot-policy-info': {
+ 'comment': 'new comment',
+ 'enabled': 'true',
+ 'policy': 'ansible',
+ 'snapshot-policy-schedules': {
+ 'snapshot-schedule-info': {
+ 'count': 100,
+ 'schedule': 'hourly',
+ 'prefix': 'hourly',
+ 'snapmirror-label': ''
+ }
+ },
+ 'vserver-name': 'hostname'
+ }
+ }}
+ xml.translate_struct(data)
+ return xml
+
+ @staticmethod
+ def build_snapshot_policy_info_comment_modified():
+ ''' build xml data for snapshot-policy-info '''
+ xml = netapp_utils.zapi.NaElement('xml')
+ data = {'num-records': 1,
+ 'attributes-list': {
+ 'snapshot-policy-info': {
+ 'comment': 'modified comment',
+ 'enabled': 'true',
+ 'policy': 'ansible',
+ 'snapshot-policy-schedules': {
+ 'snapshot-schedule-info': {
+ 'count': 100,
+ 'schedule': 'hourly',
+ 'prefix': 'hourly',
+ 'snapmirror-label': ''
+ }
+ },
+ 'vserver-name': 'hostname'
+ }
+ }}
+ xml.translate_struct(data)
+ return xml
+
+ @staticmethod
+ def build_snapshot_policy_info_policy_disabled():
+ ''' build xml data for snapshot-policy-info '''
+ xml = netapp_utils.zapi.NaElement('xml')
+ data = {'num-records': 1,
+ 'attributes-list': {
+ 'snapshot-policy-info': {
+ 'comment': 'new comment',
+ 'enabled': 'false',
+ 'policy': 'ansible',
+ 'snapshot-policy-schedules': {
+ 'snapshot-schedule-info': {
+ 'count': 100,
+ 'schedule': 'hourly',
+ 'prefix': 'hourly',
+ 'snapmirror-label': ''
+ }
+ },
+ 'vserver-name': 'hostname'
+ }
+ }}
+ xml.translate_struct(data)
+ return xml
+
+ @staticmethod
+ def build_snapshot_policy_info_schedules_added():
+ ''' build xml data for snapshot-policy-info '''
+ xml = netapp_utils.zapi.NaElement('xml')
+ data = {'num-records': 1,
+ 'attributes-list': {
+ 'snapshot-policy-info': {
+ 'comment': 'new comment',
+ 'enabled': 'true',
+ 'policy': 'ansible',
+ 'snapshot-policy-schedules': [
+ {
+ 'snapshot-schedule-info': {
+ 'count': 100,
+ 'schedule': 'hourly',
+ 'prefix': 'hourly',
+ 'snapmirror-label': ''
+ }
+ },
+ {
+ 'snapshot-schedule-info': {
+ 'count': 5,
+ 'schedule': 'daily',
+ 'prefix': 'daily',
+ 'snapmirror-label': 'daily'
+ }
+ },
+ {
+ 'snapshot-schedule-info': {
+ 'count': 10,
+ 'schedule': 'weekly',
+ 'prefix': 'weekly',
+ 'snapmirror-label': ''
+ }
+ }
+ ],
+ 'vserver-name': 'hostname'
+ }
+ }}
+ xml.translate_struct(data)
+ return xml
+
+ @staticmethod
+ def build_snapshot_policy_info_schedules_deleted():
+ ''' build xml data for snapshot-policy-info '''
+ xml = netapp_utils.zapi.NaElement('xml')
+ data = {'num-records': 1,
+ 'attributes-list': {
+ 'snapshot-policy-info': {
+ 'comment': 'new comment',
+ 'enabled': 'true',
+ 'policy': 'ansible',
+ 'snapshot-policy-schedules': [
+ {
+ 'snapshot-schedule-info': {
+ 'schedule': 'daily',
+ 'prefix': 'daily',
+ 'count': 5,
+ 'snapmirror-label': 'daily'
+ }
+ }
+ ],
+ 'vserver-name': 'hostname'
+ }
+ }}
+ xml.translate_struct(data)
+ return xml
+
+ @staticmethod
+ def build_snapshot_policy_info_modified_schedule_counts():
+ ''' build xml data for snapshot-policy-info '''
+ xml = netapp_utils.zapi.NaElement('xml')
+ data = {'num-records': 1,
+ 'attributes-list': {
+ 'snapshot-policy-info': {
+ 'comment': 'new comment',
+ 'enabled': 'true',
+ 'policy': 'ansible',
+ 'snapshot-policy-schedules': [
+ {
+ 'snapshot-schedule-info': {
+ 'count': 10,
+ 'schedule': 'hourly',
+ 'prefix': 'hourly',
+ 'snapmirror-label': ''
+ }
+ },
+ {
+ 'snapshot-schedule-info': {
+ 'count': 50,
+ 'schedule': 'daily',
+ 'prefix': 'daily',
+ 'snapmirror-label': 'daily'
+ }
+ },
+ {
+ 'snapshot-schedule-info': {
+ 'count': 100,
+ 'schedule': 'weekly',
+ 'prefix': 'weekly',
+ 'snapmirror-label': ''
+ }
+ }
+ ],
+ 'vserver-name': 'hostname'
+ }
+ }}
+ xml.translate_struct(data)
+ return xml
+
+
+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)
+ self.server = MockONTAPConnection()
+ self.onbox = False
+
+ def set_default_args(self):
+ if self.onbox:
+ hostname = '10.10.10.10'
+ username = 'admin'
+ password = '1234'
+ name = 'ansible'
+ enabled = True
+ count = 100
+ schedule = 'hourly'
+ prefix = 'hourly'
+ comment = 'new comment'
+ else:
+ hostname = 'hostname'
+ username = 'username'
+ password = 'password'
+ name = 'ansible'
+ enabled = True
+ count = 100
+ schedule = 'hourly'
+ prefix = 'hourly'
+ comment = 'new comment'
+ return dict({
+ 'hostname': hostname,
+ 'username': username,
+ 'password': password,
+ 'name': name,
+ 'enabled': enabled,
+ 'count': count,
+ 'schedule': schedule,
+ 'prefix': prefix,
+ 'comment': comment
+ })
+
+ def set_default_current(self):
+ default_args = self.set_default_args()
+ return dict({
+ 'name': default_args['name'],
+ 'enabled': default_args['enabled'],
+ 'count': [default_args['count']],
+ 'schedule': [default_args['schedule']],
+ 'snapmirror_label': [''],
+ 'prefix': [default_args['prefix']],
+ 'comment': default_args['comment'],
+ 'vserver': default_args['hostname']
+ })
+
+ def test_module_fail_when_required_args_missing(self):
+ ''' required arguments are reported as errors '''
+ with pytest.raises(AnsibleFailJson) as exc:
+ set_module_args({})
+ my_module()
+ print('Info: %s' % exc.value.args[0]['msg'])
+
+ def test_ensure_get_called(self):
+ ''' test get_snapshot_policy() for non-existent snapshot policy'''
+ set_module_args(self.set_default_args())
+ my_obj = my_module()
+ my_obj.server = self.server
+ assert my_obj.get_snapshot_policy() is None
+
+ def test_ensure_get_called_existing(self):
+ ''' test get_snapshot_policy() for existing snapshot policy'''
+ set_module_args(self.set_default_args())
+ my_obj = my_module()
+ my_obj.server = MockONTAPConnection(kind='policy')
+ assert my_obj.get_snapshot_policy()
+
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_snapshot_policy.NetAppOntapSnapshotPolicy.create_snapshot_policy')
+ def test_successful_create(self, create_snapshot):
+ ''' creating snapshot policy and testing idempotency '''
+ set_module_args(self.set_default_args())
+ my_obj = my_module()
+ my_obj.asup_log_for_cserver = Mock(return_value=None)
+ if not self.onbox:
+ my_obj.server = self.server
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ assert exc.value.args[0]['changed']
+ create_snapshot.assert_called_with()
+ # to reset na_helper from remembering the previous 'changed' value
+ my_obj = my_module()
+ my_obj.asup_log_for_cserver = Mock(return_value=None)
+ if not self.onbox:
+ my_obj.server = MockONTAPConnection('policy')
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ assert not exc.value.args[0]['changed']
+
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_snapshot_policy.NetAppOntapSnapshotPolicy.modify_snapshot_policy')
+ def test_successful_modify_comment(self, modify_snapshot):
+ ''' modifying snapshot policy comment and testing idempotency '''
+ data = self.set_default_args()
+ data['comment'] = 'modified comment'
+ set_module_args(data)
+ my_obj = my_module()
+ my_obj.asup_log_for_cserver = Mock(return_value=None)
+ if not self.onbox:
+ my_obj.server = MockONTAPConnection('policy')
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ assert exc.value.args[0]['changed']
+ current = self.set_default_current()
+ modify_snapshot.assert_called_with(current)
+ # to reset na_helper from remembering the previous 'changed' value
+ my_obj = my_module()
+ my_obj.asup_log_for_cserver = Mock(return_value=None)
+ if not self.onbox:
+ my_obj.server = MockONTAPConnection('snapshot_policy_info_comment_modified')
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ assert not exc.value.args[0]['changed']
+
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_snapshot_policy.NetAppOntapSnapshotPolicy.modify_snapshot_policy')
+ def test_successful_disable_policy(self, modify_snapshot):
+ ''' disabling snapshot policy and testing idempotency '''
+ data = self.set_default_args()
+ data['enabled'] = False
+ set_module_args(data)
+ my_obj = my_module()
+ my_obj.asup_log_for_cserver = Mock(return_value=None)
+ if not self.onbox:
+ my_obj.server = MockONTAPConnection('policy')
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ assert exc.value.args[0]['changed']
+ current = self.set_default_current()
+ modify_snapshot.assert_called_with(current)
+ # to reset na_helper from remembering the previous 'changed' value
+ my_obj = my_module()
+ my_obj.asup_log_for_cserver = Mock(return_value=None)
+ if not self.onbox:
+ my_obj.server = MockONTAPConnection('snapshot_policy_info_policy_disabled')
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ assert not exc.value.args[0]['changed']
+
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_snapshot_policy.NetAppOntapSnapshotPolicy.modify_snapshot_policy')
+ def test_successful_enable_policy(self, modify_snapshot):
+ ''' enabling snapshot policy and testing idempotency '''
+ data = self.set_default_args()
+ data['enabled'] = True
+ set_module_args(data)
+ my_obj = my_module()
+ my_obj.asup_log_for_cserver = Mock(return_value=None)
+ if not self.onbox:
+ my_obj.server = MockONTAPConnection('snapshot_policy_info_policy_disabled')
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ assert exc.value.args[0]['changed']
+ current = self.set_default_current()
+ current['enabled'] = False
+ modify_snapshot.assert_called_with(current)
+ # to reset na_helper from remembering the previous 'changed' value
+ my_obj = my_module()
+ my_obj.asup_log_for_cserver = Mock(return_value=None)
+ if not self.onbox:
+ my_obj.server = MockONTAPConnection('policy')
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ assert not exc.value.args[0]['changed']
+
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_snapshot_policy.NetAppOntapSnapshotPolicy.modify_snapshot_policy')
+ def test_successful_modify_schedules_add(self, modify_snapshot):
+ ''' adding snapshot policy schedules and testing idempotency '''
+ data = self.set_default_args()
+ data['schedule'] = ['hourly', 'daily', 'weekly']
+ data['prefix'] = ['hourly', 'daily', 'weekly']
+ data['count'] = [100, 5, 10]
+ data['snapmirror_label'] = ['', 'daily', '']
+ set_module_args(data)
+ my_obj = my_module()
+ my_obj.asup_log_for_cserver = Mock(return_value=None)
+ if not self.onbox:
+ my_obj.server = MockONTAPConnection('policy')
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ assert exc.value.args[0]['changed']
+ current = self.set_default_current()
+ modify_snapshot.assert_called_with(current)
+ # to reset na_helper from remembering the previous 'changed' value
+ my_obj = my_module()
+ my_obj.asup_log_for_cserver = Mock(return_value=None)
+ if not self.onbox:
+ my_obj.server = MockONTAPConnection('snapshot_policy_info_schedules_added')
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ assert not exc.value.args[0]['changed']
+
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_snapshot_policy.NetAppOntapSnapshotPolicy.modify_snapshot_policy')
+ def test_successful_modify_schedules_delete(self, modify_snapshot):
+ ''' deleting snapshot policy schedules and testing idempotency '''
+ data = self.set_default_args()
+ data['schedule'] = ['daily']
+ data['prefix'] = ['daily']
+ data['count'] = [5]
+ set_module_args(data)
+ my_obj = my_module()
+ my_obj.asup_log_for_cserver = Mock(return_value=None)
+ if not self.onbox:
+ my_obj.server = MockONTAPConnection('policy')
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ assert exc.value.args[0]['changed']
+ current = self.set_default_current()
+ modify_snapshot.assert_called_with(current)
+ # to reset na_helper from remembering the previous 'changed' value
+ my_obj = my_module()
+ my_obj.asup_log_for_cserver = Mock(return_value=None)
+ if not self.onbox:
+ my_obj.server = MockONTAPConnection('snapshot_policy_info_schedules_deleted')
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ assert not exc.value.args[0]['changed']
+
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_snapshot_policy.NetAppOntapSnapshotPolicy.modify_snapshot_policy')
+ def test_successful_modify_schedules(self, modify_snapshot):
+ ''' modifying snapshot policy schedule counts and testing idempotency '''
+ data = self.set_default_args()
+ data['schedule'] = ['hourly', 'daily', 'weekly']
+ data['count'] = [10, 50, 100]
+ data['prefix'] = ['hourly', 'daily', 'weekly']
+ set_module_args(data)
+ my_obj = my_module()
+ my_obj.asup_log_for_cserver = Mock(return_value=None)
+ if not self.onbox:
+ my_obj.server = MockONTAPConnection('policy')
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ assert exc.value.args[0]['changed']
+ current = self.set_default_current()
+ modify_snapshot.assert_called_with(current)
+ # to reset na_helper from remembering the previous 'changed' value
+ my_obj = my_module()
+ my_obj.asup_log_for_cserver = Mock(return_value=None)
+ if not self.onbox:
+ my_obj.server = MockONTAPConnection('snapshot_policy_info_modified_schedule_counts')
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ assert not exc.value.args[0]['changed']
+
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_snapshot_policy.NetAppOntapSnapshotPolicy.delete_snapshot_policy')
+ def test_successful_delete(self, delete_snapshot):
+ ''' deleting snapshot policy and testing idempotency '''
+ data = self.set_default_args()
+ data['state'] = 'absent'
+ set_module_args(data)
+ my_obj = my_module()
+ my_obj.asup_log_for_cserver = Mock(return_value=None)
+ if not self.onbox:
+ my_obj.server = MockONTAPConnection('policy')
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ assert exc.value.args[0]['changed']
+ delete_snapshot.assert_called_with()
+ # to reset na_helper from remembering the previous 'changed' value
+ my_obj = my_module()
+ my_obj.asup_log_for_cserver = Mock(return_value=None)
+ if not self.onbox:
+ my_obj.server = self.server
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ assert not exc.value.args[0]['changed']
+
+ def test_valid_schedule_count(self):
+ ''' validate when schedule has same number of elements '''
+ data = self.set_default_args()
+ data['schedule'] = ['hourly', 'daily', 'weekly', 'monthly', '5min']
+ data['prefix'] = ['hourly', 'daily', 'weekly', 'monthly', '5min']
+ data['count'] = [1, 2, 3, 4, 5]
+ set_module_args(data)
+ my_obj = my_module()
+ my_obj.asup_log_for_cserver = Mock(return_value=None)
+ if not self.onbox:
+ my_obj.server = self.server
+ my_obj.create_snapshot_policy()
+ create_xml = my_obj.server.xml_in
+ assert data['count'][2] == int(create_xml['count3'])
+ assert data['schedule'][4] == create_xml['schedule5']
+
+ def test_valid_schedule_count_with_snapmirror_labels(self):
+ ''' validate when schedule has same number of elements with snapmirror labels '''
+ data = self.set_default_args()
+ data['schedule'] = ['hourly', 'daily', 'weekly', 'monthly', '5min']
+ data['prefix'] = ['hourly', 'daily', 'weekly', 'monthly', '5min']
+ data['count'] = [1, 2, 3, 4, 5]
+ data['snapmirror_label'] = ['hourly', 'daily', 'weekly', 'monthly', '5min']
+ set_module_args(data)
+ my_obj = my_module()
+ my_obj.asup_log_for_cserver = Mock(return_value=None)
+ if not self.onbox:
+ my_obj.server = self.server
+ my_obj.create_snapshot_policy()
+ create_xml = my_obj.server.xml_in
+ assert data['count'][2] == int(create_xml['count3'])
+ assert data['schedule'][4] == create_xml['schedule5']
+ assert data['snapmirror_label'][3] == create_xml['snapmirror-label4']
+
+ def test_invalid_params(self):
+ ''' validate error when schedule does not have same number of elements '''
+ data = self.set_default_args()
+ data['schedule'] = ['s1', 's2']
+ data['prefix'] = ['s1', 's2']
+ data['count'] = [1, 2, 3]
+ set_module_args(data)
+ my_obj = my_module()
+ my_obj.asup_log_for_cserver = Mock(return_value=None)
+ if not self.onbox:
+ my_obj.server = self.server
+ with pytest.raises(AnsibleFailJson) as exc:
+ my_obj.create_snapshot_policy()
+ msg = 'Error: A Snapshot policy must have at least 1 ' \
+ 'schedule and can have up to a maximum of 5 schedules, with a count ' \
+ 'representing the maximum number of Snapshot copies for each schedule'
+ assert exc.value.args[0]['msg'] == msg
+
+ def test_invalid_schedule_count(self):
+ ''' validate error when schedule has more than 5 elements '''
+ data = self.set_default_args()
+ data['schedule'] = ['s1', 's2', 's3', 's4', 's5', 's6']
+ data['count'] = [1, 2, 3, 4, 5, 6]
+ set_module_args(data)
+ my_obj = my_module()
+ my_obj.asup_log_for_cserver = Mock(return_value=None)
+ if not self.onbox:
+ my_obj.server = self.server
+ with pytest.raises(AnsibleFailJson) as exc:
+ my_obj.create_snapshot_policy()
+ msg = 'Error: A Snapshot policy must have at least 1 ' \
+ 'schedule and can have up to a maximum of 5 schedules, with a count ' \
+ 'representing the maximum number of Snapshot copies for each schedule'
+ assert exc.value.args[0]['msg'] == msg
+
+ def test_invalid_schedule_count_less_than_one(self):
+ ''' validate error when schedule has less than 1 element '''
+ data = self.set_default_args()
+ data['schedule'] = []
+ data['count'] = []
+ set_module_args(data)
+ my_obj = my_module()
+ my_obj.asup_log_for_cserver = Mock(return_value=None)
+ if not self.onbox:
+ my_obj.server = self.server
+ with pytest.raises(AnsibleFailJson) as exc:
+ my_obj.create_snapshot_policy()
+ msg = 'Error: A Snapshot policy must have at least 1 ' \
+ 'schedule and can have up to a maximum of 5 schedules, with a count ' \
+ 'representing the maximum number of Snapshot copies for each schedule'
+ assert exc.value.args[0]['msg'] == msg
+
+ def test_invalid_schedule_count_is_none(self):
+ ''' validate error when schedule is None '''
+ data = self.set_default_args()
+ data['schedule'] = None
+ data['count'] = None
+ set_module_args(data)
+ my_obj = my_module()
+ my_obj.asup_log_for_cserver = Mock(return_value=None)
+ if not self.onbox:
+ my_obj.server = self.server
+ with pytest.raises(AnsibleFailJson) as exc:
+ my_obj.create_snapshot_policy()
+ msg = 'Error: A Snapshot policy must have at least 1 ' \
+ 'schedule and can have up to a maximum of 5 schedules, with a count ' \
+ 'representing the maximum number of Snapshot copies for each schedule'
+ assert exc.value.args[0]['msg'] == msg
+
+ def test_invalid_schedule_count_with_snapmirror_labels(self):
+ ''' validate error when schedule with snapmirror labels does not have same number of elements '''
+ data = self.set_default_args()
+ data['schedule'] = ['s1', 's2', 's3']
+ data['count'] = [1, 2, 3]
+ data['snapmirror_label'] = ['sm1', 'sm2']
+ set_module_args(data)
+ my_obj = my_module()
+ my_obj.asup_log_for_cserver = Mock(return_value=None)
+ if not self.onbox:
+ my_obj.server = self.server
+ with pytest.raises(AnsibleFailJson) as exc:
+ my_obj.create_snapshot_policy()
+ msg = 'Error: Each Snapshot Policy schedule must have an accompanying SnapMirror Label'
+ assert exc.value.args[0]['msg'] == msg
+
+ def test_invalid_schedule_count_with_prefixes(self):
+ ''' validate error when schedule with prefixes does not have same number of elements '''
+ data = self.set_default_args()
+ data['schedule'] = ['s1', 's2', 's3']
+ data['count'] = [1, 2, 3]
+ data['prefix'] = ['s1', 's2']
+ set_module_args(data)
+ my_obj = my_module()
+ my_obj.asup_log_for_cserver = Mock(return_value=None)
+ if not self.onbox:
+ my_obj.server = self.server
+ with pytest.raises(AnsibleFailJson) as exc:
+ my_obj.create_snapshot_policy()
+ msg = 'Error: Each Snapshot Policy schedule must have an accompanying prefix'
+ assert exc.value.args[0]['msg'] == msg
+
+ def test_if_all_methods_catch_exception(self):
+ module_args = {}
+ module_args.update(self.set_default_args())
+ set_module_args(module_args)
+ my_obj = my_module()
+ if not self.onbox:
+ my_obj.server = MockONTAPConnection('policy_fail')
+ with pytest.raises(AnsibleFailJson) as exc:
+ my_obj.create_snapshot_policy()
+ assert 'Error creating snapshot policy ansible:' in exc.value.args[0]['msg']
+ with pytest.raises(AnsibleFailJson) as exc:
+ my_obj.delete_snapshot_policy()
+ assert 'Error deleting snapshot policy ansible:' in exc.value.args[0]['msg']
diff --git a/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_snmp_traphosts.py b/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_snmp_traphosts.py
new file mode 100644
index 00000000..18092fe0
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_snmp_traphosts.py
@@ -0,0 +1,153 @@
+# (c) 2020, NetApp, Inc
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+""" unit tests for Ansible module: na_ontap_snmp_traphosts """
+
+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.ontap.tests.unit.compat import unittest
+from ansible_collections.netapp.ontap.tests.unit.compat.mock import patch
+
+from ansible_collections.netapp.ontap.plugins.modules.na_ontap_snmp_traphosts \
+ import NetAppONTAPSnmpTraphosts as traphost_module # module under test
+
+# REST API canned responses when mocking send_request
+
+SRR = {
+ # common responses
+ 'is_rest': (200, {}, None),
+ 'is_zapi': (400, {}, "Unreachable"),
+ 'empty_good': (200, {}, None),
+ 'end_of_sequence': (500, None, "Unexpected call to send_request"),
+ 'generic_error': (400, None, "Expected error"),
+ # module specific responses
+ 'no_record': (200, {"records": {}}, None),
+ 'get_snmp_traphosts': (
+ 200,
+ {"records": [{
+ "host": "0.0.0.0",
+ "ip_address": "0.0.0.0"
+ }]
+ }, 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"""
+
+
+class AnsibleFailJson(Exception):
+ """Exception class to be raised by module.fail_json and caught by the test case"""
+
+
+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):
+ """ Unit tests for na_ontap_wwpn_alias """
+
+ 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)
+ self.mock_alias = {
+ 'ip_address': '0.0.0.0',
+ }
+
+ def mock_args(self):
+ return {
+ 'ip_address': self.mock_alias['ip_address'],
+ 'hostname': 'test_host',
+ 'username': 'test_user',
+ 'password': 'test_pass!'
+ }
+
+ def get_alias_mock_object(self):
+ alias_obj = traphost_module()
+ return alias_obj
+
+ @patch('ansible_collections.netapp.ontap.plugins.module_utils.netapp.OntapRestAPI.send_request')
+ def test_rest_successful_create(self, mock_request):
+ """Test successful rest create"""
+ data = self.mock_args()
+ set_module_args(data)
+ mock_request.side_effect = [
+ SRR['is_rest'],
+ SRR['no_record'],
+ SRR['empty_good'],
+ SRR['end_of_sequence']
+ ]
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_alias_mock_object().apply()
+ assert exc.value.args[0]['changed']
+
+ @patch('ansible_collections.netapp.ontap.plugins.module_utils.netapp.OntapRestAPI.send_request')
+ def test_rest_create_idempotency(self, mock_request):
+ """Test rest create idempotency"""
+ data = self.mock_args()
+ set_module_args(data)
+ mock_request.side_effect = [
+ SRR['is_rest'],
+ SRR['get_snmp_traphosts'],
+ SRR['empty_good'],
+ SRR['end_of_sequence']
+ ]
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_alias_mock_object().apply()
+ assert not exc.value.args[0]['changed']
+
+ @patch('ansible_collections.netapp.ontap.plugins.module_utils.netapp.OntapRestAPI.send_request')
+ def test_rest_successful_delete(self, mock_request):
+ """Test successful rest delete"""
+ data = self.mock_args()
+ data['state'] = 'absent'
+ set_module_args(data)
+ mock_request.side_effect = [
+ SRR['is_rest'],
+ SRR['get_snmp_traphosts'],
+ SRR['empty_good'],
+ SRR['end_of_sequence']
+ ]
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_alias_mock_object().apply()
+ assert exc.value.args[0]['changed']
+
+ @patch('ansible_collections.netapp.ontap.plugins.module_utils.netapp.OntapRestAPI.send_request')
+ def test_rest_delete_idempotency(self, mock_request):
+ """Test successful rest delete"""
+ data = self.mock_args()
+ data['state'] = 'absent'
+ set_module_args(data)
+ mock_request.side_effect = [
+ SRR['is_rest'],
+ SRR['no_record'],
+ SRR['empty_good'],
+ SRR['end_of_sequence']
+ ]
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_alias_mock_object().apply()
+ assert not exc.value.args[0]['changed']
diff --git a/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_software_update.py b/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_software_update.py
new file mode 100644
index 00000000..a771b276
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_software_update.py
@@ -0,0 +1,190 @@
+# (c) 2018, NetApp, Inc
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+''' unit tests ONTAP Ansible module: na_ontap_software_update '''
+
+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.ontap.tests.unit.compat import unittest
+from ansible_collections.netapp.ontap.tests.unit.compat.mock import patch, Mock
+import ansible_collections.netapp.ontap.plugins.module_utils.netapp as netapp_utils
+
+from ansible_collections.netapp.ontap.plugins.modules.na_ontap_software_update \
+ import NetAppONTAPSoftwareUpdate as my_module # module under test
+
+if not netapp_utils.has_netapp_lib():
+ pytestmark = pytest.mark.skip('skipping as missing required netapp_lib')
+
+
+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"""
+
+
+class AnsibleFailJson(Exception):
+ """Exception class to be raised by module.fail_json and caught by the test case"""
+
+
+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 MockONTAPConnection(object):
+ ''' mock server connection to ONTAP host '''
+
+ def __init__(self, kind=None, parm1=None, parm2=None):
+ ''' save arguments '''
+ self.type = kind
+ self.parm1 = parm1
+ self.parm2 = parm2
+ self.xml_in = None
+ self.xml_out = None
+
+ def invoke_successfully(self, xml, enable_tunneling): # pylint: disable=unused-argument
+ ''' mock invoke_successfully returning xml data '''
+ self.xml_in = xml
+ print(xml.to_string())
+ if xml.to_string().startswith(b'<cluster-image-get><node-id>'):
+ xml = self.build_image_info()
+ elif self.type == 'software_update':
+ xml = self.build_software_update_info(self.parm1, self.parm2)
+ self.xml_out = xml
+ return xml
+
+ def autosupport_log(self):
+ ''' mock autosupport log'''
+ return None
+
+ @staticmethod
+ def build_image_info():
+ ''' build xml data for software-update-info '''
+ xml = netapp_utils.zapi.NaElement('xml')
+ data = {
+ 'attributes': {'cluster-image-info': {'node-id': 'node4test',
+ 'current-version': 'Fattire__9.3.0'}},
+ }
+ xml.translate_struct(data)
+ print(xml.to_string())
+ return xml
+
+ @staticmethod
+ def build_software_update_info(status, node):
+ ''' build xml data for software-update-info '''
+ xml = netapp_utils.zapi.NaElement('xml')
+ data = {
+ 'num-records': 1,
+ 'attributes-list': {'cluster-image-info': {'node-id': node}},
+ 'progress-status': status,
+ 'attributes': {'ndu-progress-info': {'overall-status': 'completed',
+ 'completed-node-count': '0'}},
+ }
+ xml.translate_struct(data)
+ print(xml.to_string())
+ return xml
+
+
+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)
+ self.server = MockONTAPConnection()
+ self.use_vsim = False
+
+ def set_default_args(self):
+ if self.use_vsim:
+ hostname = '10.10.10.10'
+ username = 'admin'
+ password = 'admin'
+ node = 'vsim1'
+ package_version = 'Fattire__9.3.0'
+ package_url = 'abc.com'
+ stabilize_minutes = 10
+ else:
+ hostname = 'hostname'
+ username = 'username'
+ password = 'password'
+ node = 'abc'
+ package_version = 'Fattire__9.3.0'
+ package_url = 'abc.com'
+ stabilize_minutes = 10
+ return dict({
+ 'hostname': hostname,
+ 'username': username,
+ 'password': password,
+ 'nodes': node,
+ 'package_version': package_version,
+ 'package_url': package_url,
+ 'https': 'true',
+ 'stabilize_minutes': stabilize_minutes
+ })
+
+ def test_module_fail_when_required_args_missing(self):
+ ''' required arguments are reported as errors '''
+ with pytest.raises(AnsibleFailJson) as exc:
+ set_module_args({})
+ my_module()
+ print('Info: %s' % exc.value.args[0]['msg'])
+
+ def test_ensure_image_get_called(self):
+ ''' a more interesting test '''
+ set_module_args(self.set_default_args())
+ my_obj = my_module()
+ my_obj.server = self.server
+ cluster_image_get = my_obj.cluster_image_get()
+ print('Info: test_software_update_get: %s' % repr(cluster_image_get))
+ assert cluster_image_get == list()
+
+ def test_ensure_apply_for_update_called_idempotent(self):
+ ''' updating software and checking idempotency '''
+ module_args = {}
+ module_args.update(self.set_default_args())
+ set_module_args(module_args)
+ my_obj = my_module()
+ my_obj.autosupport_log = Mock(return_value=None)
+ if not self.use_vsim:
+ my_obj.server = self.server
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ print('Info: test_software_update_apply: %s' % repr(exc.value))
+ assert not exc.value.args[0]['changed']
+
+ def test_ensure_apply_for_update_called(self):
+ ''' updating software and checking idempotency '''
+ module_args = {}
+ module_args.update(self.set_default_args())
+ module_args.update({'package_version': 'PlinyTheElder'})
+ set_module_args(module_args)
+ my_obj = my_module()
+ my_obj.autosupport_log = Mock(return_value=None)
+ if not self.use_vsim:
+ my_obj.server = MockONTAPConnection('software_update', 'async_pkg_get_phase_complete', 'abc')
+ with pytest.raises(AnsibleExitJson) as exc:
+ # replace time.sleep with a noop
+ with patch('time.sleep', lambda a: None):
+ my_obj.apply()
+ print('Info: test_software_update_apply: %s' % repr(exc.value))
+ assert exc.value.args[0]['changed']
diff --git a/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_svm.py b/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_svm.py
new file mode 100644
index 00000000..9ea6785a
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_svm.py
@@ -0,0 +1,430 @@
+# (c) 2018, NetApp, Inc
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+''' unit test template for ONTAP Ansible module '''
+
+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.ontap.tests.unit.compat import unittest
+from ansible_collections.netapp.ontap.tests.unit.compat.mock import patch, Mock
+import ansible_collections.netapp.ontap.plugins.module_utils.netapp as netapp_utils
+
+from ansible_collections.netapp.ontap.plugins.modules.na_ontap_svm \
+ import NetAppOntapSVM as svm_module # module under test
+
+if not netapp_utils.has_netapp_lib():
+ pytestmark = pytest.mark.skip('skipping as missing required netapp_lib')
+
+# REST API canned responses when mocking send_request
+SRR = {
+ # common responses
+ 'is_rest': (200, {}, None),
+ 'is_zapi': (400, {}, "Unreachable"),
+ 'empty_good': (200, {}, None),
+ 'end_of_sequence': (500, None, "Unexpected call to send_request"),
+ 'generic_error': (400, None, "Expected error"),
+ # module specific responses
+ 'svm_record': (200,
+ {'records': [{"uuid": "09e9fd5e-8ebd-11e9-b162-005056b39fe7",
+ "name": "test_svm",
+ "subtype": "default",
+ "language": "c.utf_8",
+ "aggregates": [{"name": "aggr_1",
+ "uuid": "850dd65b-8811-4611-ac8c-6f6240475ff9"},
+ {"name": "aggr_2",
+ "uuid": "850dd65b-8811-4611-ac8c-6f6240475ff9"}],
+ "comment": "new comment",
+ "ipspace": {"name": "ansible_ipspace",
+ "uuid": "2b760d31-8dfd-11e9-b162-005056b39fe7"},
+ "snapshot_policy": {"uuid": "3b611707-8dfd-11e9-b162-005056b39fe7",
+ "name": "old_snapshot_policy"},
+ "nfs": {"enabled": True},
+ "cifs": {"enabled": False},
+ "iscsi": {"enabled": False},
+ "fcp": {"enabled": False},
+ "nvme": {"enabled": 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"""
+
+
+class AnsibleFailJson(Exception):
+ """Exception class to be raised by module.fail_json and caught by the test case"""
+
+
+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 MockONTAPConnection(object):
+ ''' mock server connection to ONTAP host '''
+
+ def __init__(self, kind=None, data=None):
+ ''' save arguments '''
+ self.type = kind
+ self.params = data
+ self.xml_in = None
+ self.xml_out = None
+
+ def invoke_successfully(self, xml, enable_tunneling): # pylint: disable=unused-argument
+ ''' mock invoke_successfully returning xml data '''
+ self.xml_in = xml
+ if self.type == 'vserver':
+ xml = self.build_vserver_info(self.params)
+ self.xml_out = xml
+ return xml
+
+ @staticmethod
+ def build_vserver_info(vserver):
+ ''' build xml data for vserser-info '''
+ xml = netapp_utils.zapi.NaElement('xml')
+ data = {'num-records': 1, 'attributes-list': {'vserver-info': {
+ 'vserver-name': vserver['name'],
+ 'ipspace': vserver['ipspace'],
+ 'root-volume': vserver['root_volume'],
+ 'root-volume-aggregate': vserver['root_volume_aggregate'],
+ 'language': vserver['language'],
+ 'comment': vserver['comment'],
+ 'snapshot-policy': vserver['snapshot_policy'],
+ 'vserver-subtype': vserver['subtype'],
+ 'allowed-protocols': [{'protocol': 'nfs'}, {'protocol': 'cifs'}],
+ 'aggr-list': [{'aggr-name': 'aggr_1'}, {'aggr-name': 'aggr_2'}],
+ }}}
+ xml.translate_struct(data)
+ return xml
+
+
+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)
+ self.server = MockONTAPConnection()
+ self.mock_vserver = {
+ 'name': 'test_svm',
+ 'root_volume': 'ansible_vol',
+ 'root_volume_aggregate': 'ansible_aggr',
+ 'aggr_list': 'aggr_1,aggr_2',
+ 'ipspace': 'ansible_ipspace',
+ 'subtype': 'default',
+ 'language': 'c.utf_8',
+ 'snapshot_policy': 'old_snapshot_policy',
+ 'comment': 'new comment'
+ }
+
+ def mock_args(self, rest=False):
+ if rest:
+ return {'name': self.mock_vserver['name'],
+ 'aggr_list': self.mock_vserver['aggr_list'],
+ 'ipspace': self.mock_vserver['ipspace'],
+ 'comment': self.mock_vserver['comment'],
+ 'subtype': 'default',
+ 'hostname': 'test',
+ 'username': 'test_user',
+ 'password': 'test_pass!'}
+ else:
+ return {
+ 'name': self.mock_vserver['name'],
+ 'root_volume': self.mock_vserver['root_volume'],
+ 'root_volume_aggregate': self.mock_vserver['root_volume_aggregate'],
+ 'aggr_list': self.mock_vserver['aggr_list'],
+ 'ipspace': self.mock_vserver['ipspace'],
+ 'comment': self.mock_vserver['comment'],
+ 'subtype': 'default',
+ 'hostname': 'test',
+ 'username': 'test_user',
+ 'password': 'test_pass!'
+ }
+
+ def get_vserver_mock_object(self, kind=None, data=None, cx_type='zapi'):
+ """
+ Helper method to return an na_ontap_volume object
+ :param kind: passes this param to MockONTAPConnection()
+ :param data: passes this param to MockONTAPConnection()
+ :return: na_ontap_volume object
+ """
+ vserver_obj = svm_module()
+ if cx_type == 'zapi':
+ vserver_obj.asup_log_for_cserver = Mock(return_value=None)
+ vserver_obj.cluster = Mock()
+ vserver_obj.cluster.invoke_successfully = Mock()
+ if kind is None:
+ vserver_obj.server = MockONTAPConnection()
+ else:
+ if data is None:
+ vserver_obj.server = MockONTAPConnection(kind='vserver', data=self.mock_vserver)
+ else:
+ vserver_obj.server = MockONTAPConnection(kind='vserver', data=data)
+ return vserver_obj
+
+ def test_module_fail_when_required_args_missing(self):
+ ''' required arguments are reported as errors '''
+ with pytest.raises(AnsibleFailJson) as exc:
+ set_module_args({})
+ svm_module()
+ print('Info: %s' % exc.value.args[0]['msg'])
+
+ def test_get_nonexistent_vserver(self):
+ ''' test if get_vserver() throws an error if vserver is not specified '''
+ data = self.mock_args()
+ set_module_args(data)
+ result = self.get_vserver_mock_object().get_vserver()
+ assert result is None
+
+ def test_create_error_missing_name(self):
+ ''' Test if create throws an error if name is not specified'''
+ data = self.mock_args()
+ del data['name']
+ set_module_args(data)
+ with pytest.raises(AnsibleFailJson) as exc:
+ self.get_vserver_mock_object('vserver').create_vserver()
+ msg = 'missing required arguments: name'
+ assert exc.value.args[0]['msg'] == msg
+
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_svm.NetAppOntapSVM.create_vserver')
+ def test_successful_create(self, create_vserver):
+ '''Test successful create'''
+ data = self.mock_args()
+ set_module_args(data)
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_vserver_mock_object().apply()
+ assert exc.value.args[0]['changed']
+ create_vserver.assert_called_with()
+
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_svm.NetAppOntapSVM.create_vserver')
+ def test_create_idempotency(self, create_vserver):
+ '''Test successful create'''
+ data = self.mock_args()
+ set_module_args(data)
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_vserver_mock_object('vserver').apply()
+ assert not exc.value.args[0]['changed']
+ create_vserver.assert_not_called()
+
+ def test_successful_delete(self):
+ '''Test successful delete'''
+ data = self.mock_args()
+ data['state'] = 'absent'
+ set_module_args(data)
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_vserver_mock_object('vserver').apply()
+ assert exc.value.args[0]['changed']
+
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_svm.NetAppOntapSVM.delete_vserver')
+ def test_delete_idempotency(self, delete_vserver):
+ '''Test delete idempotency'''
+ data = self.mock_args()
+ data['state'] = 'absent'
+ set_module_args(data)
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_vserver_mock_object().apply()
+ assert not exc.value.args[0]['changed']
+ delete_vserver.assert_not_called()
+
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_svm.NetAppOntapSVM.get_vserver')
+ def test_successful_rename(self, get_vserver):
+ '''Test successful rename'''
+ data = self.mock_args()
+ data['from_name'] = 'test_svm'
+ data['name'] = 'test_new_svm'
+ set_module_args(data)
+ current = {
+ 'name': 'test_svm',
+ 'root_volume': 'ansible_vol',
+ 'root_volume_aggregate': 'ansible_aggr',
+ 'ipspace': 'ansible_ipspace',
+ 'subtype': 'default',
+ 'language': 'c.utf_8'
+ }
+ get_vserver.side_effect = [
+ None,
+ current
+ ]
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_vserver_mock_object().apply()
+ assert exc.value.args[0]['changed']
+
+ def test_successful_modify_language(self):
+ '''Test successful modify language'''
+ data = self.mock_args()
+ data['language'] = 'c'
+ set_module_args(data)
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_vserver_mock_object('vserver').apply()
+ assert exc.value.args[0]['changed']
+
+ def test_successful_modify_snapshot_policy(self):
+ '''Test successful modify language'''
+ data = self.mock_args()
+ data['snapshot_policy'] = 'new_snapshot_policy'
+ set_module_args(data)
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_vserver_mock_object('vserver').apply()
+ assert exc.value.args[0]['changed']
+
+ def test_successful_modify_allowed_protocols(self):
+ '''Test successful modify allowed protocols'''
+ data = self.mock_args()
+ data['allowed_protocols'] = 'protocol_1,protocol_2'
+ set_module_args(data)
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_vserver_mock_object('vserver').apply()
+ assert exc.value.args[0]['changed']
+
+ def test_successful_modify_aggr_list(self):
+ '''Test successful modify aggr-list'''
+ data = self.mock_args()
+ data['aggr_list'] = 'aggr_3,aggr_4'
+ set_module_args(data)
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_vserver_mock_object('vserver').apply()
+ assert exc.value.args[0]['changed']
+
+ @patch('ansible_collections.netapp.ontap.plugins.module_utils.netapp.OntapRestAPI.send_request')
+ def test_rest_error(self, mock_request):
+ data = self.mock_args(rest=True)
+ set_module_args(data)
+ mock_request.side_effect = [
+ SRR['is_rest'],
+ SRR['generic_error'],
+ SRR['end_of_sequence']
+ ]
+ with pytest.raises(AnsibleFailJson) as exc:
+ self.get_vserver_mock_object(cx_type='rest').apply()
+ assert exc.value.args[0]['msg'] == SRR['generic_error'][2]
+
+ @patch('ansible_collections.netapp.ontap.plugins.module_utils.netapp.OntapRestAPI.send_request')
+ def test_rest_error_unsupported_parm(self, mock_request):
+ data = self.mock_args(rest=True)
+ data['use_rest'] = 'Always'
+ data['root_volume'] = 'not_supported_by_rest'
+ set_module_args(data)
+ mock_request.side_effect = [
+ SRR['is_rest'],
+ SRR['end_of_sequence']
+ ]
+ with pytest.raises(AnsibleFailJson) as exc:
+ self.get_vserver_mock_object(cx_type='rest').apply()
+ assert exc.value.args[0]['msg'] == "REST API currently does not support 'root_volume'"
+
+ @patch('ansible_collections.netapp.ontap.plugins.module_utils.netapp.OntapRestAPI.send_request')
+ def test_rest_successfully_create(self, mock_request):
+ data = self.mock_args(rest=True)
+ data['state'] = 'present'
+ set_module_args(data)
+ mock_request.side_effect = [
+ SRR['is_rest'],
+ SRR['empty_good'], # get
+ SRR['empty_good'], # post
+ SRR['end_of_sequence']
+ ]
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_vserver_mock_object(cx_type='rest').apply()
+ assert exc.value.args[0]['changed']
+
+ @patch('ansible_collections.netapp.ontap.plugins.module_utils.netapp.OntapRestAPI.send_request')
+ def test_rest_create_idempotency(self, mock_request):
+ data = self.mock_args(rest=True)
+ data['state'] = 'present'
+ set_module_args(data)
+ mock_request.side_effect = [
+ SRR['is_rest'],
+ SRR['svm_record'], # get
+ SRR['end_of_sequence']
+ ]
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_vserver_mock_object(cx_type='rest').apply()
+ assert not exc.value.args[0]['changed']
+
+ @patch('ansible_collections.netapp.ontap.plugins.module_utils.netapp.OntapRestAPI.send_request')
+ def test_rest_successful_delete(self, mock_request):
+ '''Test successful delete'''
+ data = self.mock_args(rest=True)
+ data['state'] = 'absent'
+ set_module_args(data)
+ mock_request.side_effect = [
+ SRR['is_rest'],
+ SRR['svm_record'], # get
+ SRR['empty_good'], # delete
+ SRR['end_of_sequence']
+ ]
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_vserver_mock_object(cx_type='rest').apply()
+ assert exc.value.args[0]['changed']
+
+ @patch('ansible_collections.netapp.ontap.plugins.module_utils.netapp.OntapRestAPI.send_request')
+ def test_rest_delete_idempotency(self, mock_request):
+ '''Test delete idempotency'''
+ data = self.mock_args(rest=True)
+ data['state'] = 'absent'
+ set_module_args(data)
+ mock_request.side_effect = [
+ SRR['is_rest'],
+ SRR['empty_good'], # get
+ SRR['end_of_sequence']
+ ]
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_vserver_mock_object(cx_type='rest').apply()
+ assert not exc.value.args[0]['changed']
+
+ @patch('ansible_collections.netapp.ontap.plugins.module_utils.netapp.OntapRestAPI.send_request')
+ def test_rest_successful_rename(self, mock_request):
+ '''Test successful rename'''
+ data = self.mock_args(rest=True)
+ data['from_name'] = 'test_svm'
+ data['name'] = 'test_new_svm'
+ set_module_args(data)
+ mock_request.side_effect = [
+ SRR['is_rest'],
+ SRR['svm_record'], # get
+ SRR['svm_record'], # get
+ SRR['empty_good'], # patch
+ SRR['end_of_sequence']
+ ]
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_vserver_mock_object(cx_type='rest').apply()
+ assert exc.value.args[0]['changed']
+
+ @patch('ansible_collections.netapp.ontap.plugins.module_utils.netapp.OntapRestAPI.send_request')
+ def test_rest_successful_modify_language(self, mock_request):
+ '''Test successful modify language'''
+ data = self.mock_args(rest=True)
+ data['language'] = 'c'
+ set_module_args(data)
+ mock_request.side_effect = [
+ SRR['is_rest'],
+ SRR['svm_record'], # get
+ SRR['svm_record'], # get
+ SRR['empty_good'], # patch
+ SRR['end_of_sequence']
+ ]
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_vserver_mock_object(cx_type='rest').apply()
+ assert exc.value.args[0]['changed']
diff --git a/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_template.py b/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_template.py
new file mode 100644
index 00000000..60648253
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_template.py
@@ -0,0 +1,121 @@
+# (c) 2018, NetApp, Inc
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+''' unit test template for ONTAP Ansible module '''
+
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+import json
+import pytest
+
+from ansible_collections.netapp.ontap.tests.unit.compat import unittest
+from ansible_collections.netapp.ontap.tests.unit.compat.mock import patch
+from ansible.module_utils import basic
+from ansible.module_utils._text import to_bytes
+import ansible_collections.netapp.ontap.plugins.module_utils.netapp as netapp_utils
+
+from ansible_collections.netapp.ontap.plugins.modules.na_ontap_cg_snapshot \
+ import NetAppONTAPCGSnapshot as my_module # module under test
+
+if not netapp_utils.has_netapp_lib():
+ pytestmark = pytest.mark.skip('skipping as missing required netapp_lib')
+
+
+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 MockONTAPConnection(object):
+ ''' mock server connection to ONTAP host '''
+
+ def __init__(self, kind=None, parm1=None):
+ ''' save arguments '''
+ self.type = kind
+ self.parm1 = parm1
+ self.xml_in = None
+ self.xml_out = None
+
+ def invoke_successfully(self, xml, enable_tunneling): # pylint: disable=unused-argument
+ ''' mock invoke_successfully returning xml data '''
+ self.xml_in = xml
+ if self.type == 'vserver':
+ xml = self.build_vserver_info(self.parm1)
+ self.xml_out = xml
+ return xml
+
+ @staticmethod
+ def build_vserver_info(vserver):
+ ''' build xml data for vserser-info '''
+ xml = netapp_utils.zapi.NaElement('xml')
+ attributes = netapp_utils.zapi.NaElement('attributes-list')
+ attributes.add_node_with_children('vserver-info',
+ **{'vserver-name': vserver})
+ xml.add_child_elem(attributes)
+ # print(xml.to_string())
+ return xml
+
+
+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)
+ self.server = MockONTAPConnection()
+
+ def test_module_fail_when_required_args_missing(self):
+ ''' required arguments are reported as errors '''
+ with pytest.raises(AnsibleFailJson) as exc:
+ set_module_args({})
+ my_module()
+ print('Info: %s' % exc.value.args[0]['msg'])
+
+ def test_ensure_command_called(self):
+ ''' a more interesting test '''
+# TODO: change argument names/values
+ set_module_args({
+ 'vserver': 'vserver',
+ 'volumes': 'volumes',
+ 'snapshot': 'snapshot',
+ 'hostname': 'hostname',
+ 'username': 'username',
+ 'password': 'password',
+ })
+ my_obj = my_module()
+ my_obj.server = self.server
+ with pytest.raises(AnsibleFailJson) as exc:
+ # It may not be a good idea to start with apply
+ # More atomic methods can be easier to mock
+ # Hint: start with get methods, as they are called first
+ my_obj.apply()
+# TODO: change message, and maybe test contents
+ msg = 'Error fetching CG ID for CG commit snapshot'
+ assert exc.value.args[0]['msg'] == msg
diff --git a/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_ucadapter.py b/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_ucadapter.py
new file mode 100644
index 00000000..6b8459a2
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_ucadapter.py
@@ -0,0 +1,176 @@
+# (c) 2018, NetApp, Inc
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+''' unit tests ONTAP Ansible module: na_ontap_ucadapter '''
+
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+import json
+import pytest
+
+from ansible_collections.netapp.ontap.tests.unit.compat import unittest
+from ansible_collections.netapp.ontap.tests.unit.compat.mock import patch, Mock
+from ansible.module_utils import basic
+from ansible.module_utils._text import to_bytes
+import ansible_collections.netapp.ontap.plugins.module_utils.netapp as netapp_utils
+
+from ansible_collections.netapp.ontap.plugins.modules.na_ontap_ucadapter \
+ import NetAppOntapadapter as ucadapter_module # module under test
+
+if not netapp_utils.has_netapp_lib():
+ pytestmark = pytest.mark.skip('skipping as missing required netapp_lib')
+
+
+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 MockONTAPConnection(object):
+ ''' mock server connection to ONTAP host '''
+
+ def __init__(self, kind=None, data=None):
+ ''' save arguments '''
+ self.type = kind
+ self.parm1 = data
+ self.xml_in = None
+ self.xml_out = None
+
+ def invoke_successfully(self, xml, enable_tunneling): # pylint: disable=unused-argument
+ ''' mock invoke_successfully returning xml data '''
+ self.xml_in = xml
+ if self.type == 'ucadapter':
+ xml = self.build_ucadapter_info(self.parm1)
+ self.xml_out = xml
+ return xml
+
+ def autosupport_log(self):
+ ''' mock autosupport log'''
+ return None
+
+ @staticmethod
+ def build_ucadapter_info(params):
+ ''' build xml data for ucadapter_info '''
+ xml = netapp_utils.zapi.NaElement('xml')
+ data = {'attributes': {'uc-adapter-info': {
+ 'mode': 'fc',
+ 'pending-mode': 'abc',
+ 'type': 'target',
+ 'pending-type': 'intitiator',
+ 'status': params['status'],
+ }}}
+ xml.translate_struct(data)
+ print(xml.to_string())
+ return xml
+
+
+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)
+ self.server = MockONTAPConnection()
+ self.use_vsim = False
+ self.mock_ucadapter = {
+ 'mode': 'fc',
+ 'pending-mode': 'fc',
+ 'type': 'target',
+ 'pending-type': 'intitiator',
+ 'status': 'up',
+ }
+
+ def set_default_args(self):
+ args = (dict({
+ 'hostname': '10.0.0.0',
+ 'username': 'user',
+ 'password': 'pass',
+ 'node_name': 'node1',
+ 'adapter_name': '0f',
+ 'mode': self.mock_ucadapter['mode'],
+ 'type': self.mock_ucadapter['type']
+ }))
+ return args
+
+ def get_ucadapter_mock_object(self, kind=None, data=None):
+ """
+ Helper method to return an na_ontap_unix_user object
+ :param kind: passes this param to MockONTAPConnection()
+ :return: na_ontap_unix_user object
+ """
+ obj = ucadapter_module()
+ obj.autosupport_log = Mock(return_value=None)
+ params = self.mock_ucadapter
+ if data is not None:
+ for k, v in data.items():
+ params[k] = v
+ obj.server = MockONTAPConnection(kind=kind, data=params)
+ return obj
+
+ def test_module_fail_when_required_args_missing(self):
+ ''' required arguments are reported as errors '''
+ with pytest.raises(AnsibleFailJson) as exc:
+ set_module_args({})
+ ucadapter_module()
+ print('Info: %s' % exc.value.args[0]['msg'])
+
+ def test_ensure_ucadapter_get_called(self):
+ ''' fetching ucadapter details '''
+ set_module_args(self.set_default_args())
+ get_adapter = self.get_ucadapter_mock_object().get_adapter()
+ print('Info: test_ucadapter_get: %s' % repr(get_adapter))
+ assert get_adapter is None
+
+ def test_change_mode_from_cna_to_fc(self):
+ ''' configuring ucadaptor and checking idempotency '''
+ module_args = {}
+ module_args.update(self.set_default_args())
+ set_module_args(module_args)
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_ucadapter_mock_object().apply()
+ assert not exc.value.args[0]['changed']
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_ucadapter_mock_object('ucadapter', {'mode': 'cna', 'pending-mode': 'cna'}).apply()
+ assert exc.value.args[0]['changed']
+
+ module_args['type'] = 'intitiator'
+ set_module_args(module_args)
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_ucadapter_mock_object('ucadapter', {'mode': 'cna', 'pending-mode': 'cna'}).apply()
+ assert exc.value.args[0]['changed']
+
+ def test_change_mode_from_fc_to_cna(self):
+ module_args = self.set_default_args()
+ module_args['mode'] = 'cna'
+ del module_args['type']
+ set_module_args(module_args)
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_ucadapter_mock_object('ucadapter').apply()
+ assert exc.value.args[0]['changed']
diff --git a/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_unix_group.py b/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_unix_group.py
new file mode 100644
index 00000000..016e951b
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_unix_group.py
@@ -0,0 +1,289 @@
+# (c) 2018, NetApp, Inc
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+''' unit test template for ONTAP Ansible module '''
+
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+import json
+import pytest
+
+from ansible_collections.netapp.ontap.tests.unit.compat import unittest
+from ansible_collections.netapp.ontap.tests.unit.compat.mock import patch, Mock
+from ansible.module_utils import basic
+from ansible.module_utils._text import to_bytes
+import ansible_collections.netapp.ontap.plugins.module_utils.netapp as netapp_utils
+
+from ansible_collections.netapp.ontap.plugins.modules.na_ontap_unix_group \
+ import NetAppOntapUnixGroup as group_module # module under test
+
+if not netapp_utils.has_netapp_lib():
+ pytestmark = pytest.mark.skip('skipping as missing required netapp_lib')
+
+
+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 MockONTAPConnection(object):
+ ''' mock server connection to ONTAP host '''
+
+ def __init__(self, kind=None, data=None):
+ ''' save arguments '''
+ self.kind = kind
+ self.params = data
+ self.xml_in = None
+ self.xml_out = None
+
+ def invoke_successfully(self, xml, enable_tunneling): # pylint: disable=unused-argument
+ ''' mock invoke_successfully returning xml data '''
+ self.xml_in = xml
+ if self.kind == 'group':
+ xml = self.build_group_info(self.params)
+ elif self.kind == 'group-fail':
+ raise netapp_utils.zapi.NaApiError(code='TEST', message="This exception is from the unit test")
+ self.xml_out = xml
+ return xml
+
+ @staticmethod
+ def build_group_info(data):
+ ''' build xml data for vserser-info '''
+ xml = netapp_utils.zapi.NaElement('xml')
+ attributes = \
+ {'attributes-list': {'unix-group-info': {'group-name': data['name'],
+ 'group-id': data['id']}},
+ 'num-records': 1}
+ xml.translate_struct(attributes)
+ return xml
+
+
+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)
+ self.server = MockONTAPConnection()
+ self.mock_group = {
+ 'name': 'test',
+ 'id': '11',
+ 'vserver': 'something',
+ }
+
+ def mock_args(self):
+ return {
+ 'name': self.mock_group['name'],
+ 'id': self.mock_group['id'],
+ 'vserver': self.mock_group['vserver'],
+ 'hostname': 'test',
+ 'username': 'test_user',
+ 'password': 'test_pass!'
+ }
+
+ def get_group_mock_object(self, kind=None, data=None):
+ """
+ Helper method to return an na_ontap_unix_group object
+ :param kind: passes this param to MockONTAPConnection()
+ :return: na_ontap_unix_group object
+ """
+ obj = group_module()
+ obj.autosupport_log = Mock(return_value=None)
+ if data is None:
+ data = self.mock_group
+ obj.server = MockONTAPConnection(kind=kind, data=data)
+ return obj
+
+ def test_module_fail_when_required_args_missing(self):
+ ''' required arguments are reported as errors '''
+ with pytest.raises(AnsibleFailJson) as exc:
+ set_module_args({})
+ group_module()
+
+ def test_get_nonexistent_group(self):
+ ''' Test if get_unix_group returns None for non-existent group '''
+ set_module_args(self.mock_args())
+ result = self.get_group_mock_object().get_unix_group()
+ assert result is None
+
+ def test_get_existing_group(self):
+ ''' Test if get_unix_group returns details for existing group '''
+ set_module_args(self.mock_args())
+ result = self.get_group_mock_object('group').get_unix_group()
+ assert result['name'] == self.mock_group['name']
+
+ def test_get_xml(self):
+ set_module_args(self.mock_args())
+ obj = self.get_group_mock_object('group')
+ result = obj.get_unix_group()
+ assert obj.server.xml_in['query']
+ assert obj.server.xml_in['query']['unix-group-info']
+ group_info = obj.server.xml_in['query']['unix-group-info']
+ assert group_info['group-name'] == self.mock_group['name']
+ assert group_info['vserver'] == self.mock_group['vserver']
+
+ def test_create_error_missing_params(self):
+ data = self.mock_args()
+ del data['id']
+ set_module_args(data)
+ with pytest.raises(AnsibleFailJson) as exc:
+ self.get_group_mock_object('group').create_unix_group()
+ assert 'Error: Missing a required parameter for create: (id)' == exc.value.args[0]['msg']
+
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_unix_group.NetAppOntapUnixGroup.create_unix_group')
+ def test_create_called(self, create_group):
+ set_module_args(self.mock_args())
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_group_mock_object().apply()
+ assert exc.value.args[0]['changed']
+ create_group.assert_called_with()
+
+ def test_create_xml(self):
+ '''Test create ZAPI element'''
+ set_module_args(self.mock_args())
+ create = self.get_group_mock_object()
+ with pytest.raises(AnsibleExitJson) as exc:
+ create.apply()
+ mock_key = {
+ 'group-name': 'name',
+ 'group-id': 'id',
+ }
+ for key in ['group-name', 'group-id']:
+ assert create.server.xml_in[key] == self.mock_group[mock_key[key]]
+
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_unix_group.NetAppOntapUnixGroup.modify_unix_group')
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_unix_group.NetAppOntapUnixGroup.delete_unix_group')
+ def test_delete_called(self, delete_group, modify_group):
+ ''' Test delete existing group '''
+ data = self.mock_args()
+ data['state'] = 'absent'
+ set_module_args(data)
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_group_mock_object('group').apply()
+ assert exc.value.args[0]['changed']
+ delete_group.assert_called_with()
+ assert modify_group.call_count == 0
+
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_unix_group.NetAppOntapUnixGroup.get_unix_group')
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_unix_group.NetAppOntapUnixGroup.modify_unix_group')
+ def test_modify_called(self, modify_group, get_group):
+ ''' Test modify group group_id '''
+ data = self.mock_args()
+ data['id'] = 20
+ set_module_args(data)
+ get_group.return_value = {'id': 10}
+ obj = self.get_group_mock_object('group')
+ with pytest.raises(AnsibleExitJson) as exc:
+ obj.apply()
+ get_group.assert_called_with()
+ modify_group.assert_called_with({'id': 20})
+
+ def test_modify_only_id(self):
+ ''' Test modify group id '''
+ set_module_args(self.mock_args())
+ modify = self.get_group_mock_object('group')
+ modify.modify_unix_group({'id': 123})
+ print(modify.server.xml_in.to_string())
+ assert modify.server.xml_in['group-id'] == '123'
+ with pytest.raises(KeyError):
+ modify.server.xml_in['id']
+
+ def test_modify_xml(self):
+ ''' Test modify group full_name '''
+ set_module_args(self.mock_args())
+ modify = self.get_group_mock_object('group')
+ modify.modify_unix_group({'id': 25})
+ assert modify.server.xml_in['group-name'] == self.mock_group['name']
+ assert modify.server.xml_in['group-id'] == '25'
+
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_unix_group.NetAppOntapUnixGroup.create_unix_group')
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_unix_group.NetAppOntapUnixGroup.delete_unix_group')
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_unix_group.NetAppOntapUnixGroup.modify_unix_group')
+ def test_do_nothing(self, modify, delete, create):
+ ''' changed is False and none of the opetaion methods are called'''
+ data = self.mock_args()
+ data['state'] = 'absent'
+ set_module_args(data)
+ obj = self.get_group_mock_object()
+ with pytest.raises(AnsibleExitJson) as exc:
+ obj.apply()
+ create.assert_not_called()
+ delete.assert_not_called()
+ modify.assert_not_called()
+
+ def test_get_exception(self):
+ set_module_args(self.mock_args())
+ with pytest.raises(AnsibleFailJson) as exc:
+ self.get_group_mock_object('group-fail').get_unix_group()
+ assert 'Error getting UNIX group' in exc.value.args[0]['msg']
+
+ def test_create_exception(self):
+ set_module_args(self.mock_args())
+ with pytest.raises(AnsibleFailJson) as exc:
+ self.get_group_mock_object('group-fail').create_unix_group()
+ assert 'Error creating UNIX group' in exc.value.args[0]['msg']
+
+ def test_modify_exception(self):
+ set_module_args(self.mock_args())
+ with pytest.raises(AnsibleFailJson) as exc:
+ self.get_group_mock_object('group-fail').modify_unix_group({'id': '123'})
+ assert 'Error modifying UNIX group' in exc.value.args[0]['msg']
+
+ def test_delete_exception(self):
+ set_module_args(self.mock_args())
+ with pytest.raises(AnsibleFailJson) as exc:
+ self.get_group_mock_object('group-fail').delete_unix_group()
+ assert 'Error removing UNIX group' in exc.value.args[0]['msg']
+
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_unix_group.NetAppOntapUnixGroup.get_unix_group')
+ def test_add_user_exception(self, get_unix_group):
+ data = self.mock_args()
+ data['users'] = 'test_user'
+ set_module_args(data)
+ get_unix_group.side_effect = [
+ {'users': []}
+ ]
+ with pytest.raises(AnsibleFailJson) as exc:
+ self.get_group_mock_object('group-fail').modify_users_in_group()
+ print(exc.value.args[0]['msg'])
+ assert 'Error adding user' in exc.value.args[0]['msg']
+
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_unix_group.NetAppOntapUnixGroup.get_unix_group')
+ def test_delete_user_exception(self, get_unix_group):
+ data = self.mock_args()
+ data['users'] = ''
+ set_module_args(data)
+ get_unix_group.side_effect = [
+ {'users': ['test_user']}
+ ]
+ with pytest.raises(AnsibleFailJson) as exc:
+ self.get_group_mock_object('group-fail').modify_users_in_group()
+ print(exc.value.args[0]['msg'])
+ assert 'Error deleting user' in exc.value.args[0]['msg']
diff --git a/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_unix_user.py b/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_unix_user.py
new file mode 100644
index 00000000..3b9e5bce
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_unix_user.py
@@ -0,0 +1,283 @@
+# (c) 2018, NetApp, Inc
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+''' unit test template for ONTAP Ansible module '''
+
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+import json
+import pytest
+
+from ansible_collections.netapp.ontap.tests.unit.compat import unittest
+from ansible_collections.netapp.ontap.tests.unit.compat.mock import patch, Mock
+from ansible.module_utils import basic
+from ansible.module_utils._text import to_bytes
+import ansible_collections.netapp.ontap.plugins.module_utils.netapp as netapp_utils
+
+from ansible_collections.netapp.ontap.plugins.modules.na_ontap_unix_user \
+ import NetAppOntapUnixUser as user_module # module under test
+
+if not netapp_utils.has_netapp_lib():
+ pytestmark = pytest.mark.skip('skipping as missing required netapp_lib')
+
+
+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 MockONTAPConnection(object):
+ ''' mock server connection to ONTAP host '''
+
+ def __init__(self, kind=None, data=None):
+ ''' save arguments '''
+ self.kind = kind
+ self.params = data
+ self.xml_in = None
+ self.xml_out = None
+
+ def invoke_successfully(self, xml, enable_tunneling): # pylint: disable=unused-argument
+ ''' mock invoke_successfully returning xml data '''
+ self.xml_in = xml
+ if self.kind == 'user':
+ xml = self.build_user_info(self.params)
+ elif self.kind == 'user-fail':
+ raise netapp_utils.zapi.NaApiError(code='TEST', message="This exception is from the unit test")
+ self.xml_out = xml
+ return xml
+
+ @staticmethod
+ def build_user_info(data):
+ ''' build xml data for vserser-info '''
+ xml = netapp_utils.zapi.NaElement('xml')
+ attributes = \
+ {'attributes-list': {'unix-user-info': {'user-id': data['id'],
+ 'group-id': data['group_id'], 'full-name': data['full_name']}},
+ 'num-records': 1}
+ xml.translate_struct(attributes)
+ return xml
+
+
+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)
+ self.server = MockONTAPConnection()
+ self.mock_user = {
+ 'name': 'test',
+ 'id': '11',
+ 'group_id': '12',
+ 'vserver': 'something',
+ 'full_name': 'Test User'
+ }
+
+ def mock_args(self):
+ return {
+ 'name': self.mock_user['name'],
+ 'group_id': self.mock_user['group_id'],
+ 'id': self.mock_user['id'],
+ 'vserver': self.mock_user['vserver'],
+ 'full_name': self.mock_user['full_name'],
+ 'hostname': 'test',
+ 'username': 'test_user',
+ 'password': 'test_pass!'
+ }
+
+ def get_user_mock_object(self, kind=None, data=None):
+ """
+ Helper method to return an na_ontap_unix_user object
+ :param kind: passes this param to MockONTAPConnection()
+ :return: na_ontap_unix_user object
+ """
+ obj = user_module()
+ obj.autosupport_log = Mock(return_value=None)
+ if data is None:
+ data = self.mock_user
+ obj.server = MockONTAPConnection(kind=kind, data=data)
+ return obj
+
+ def test_module_fail_when_required_args_missing(self):
+ ''' required arguments are reported as errors '''
+ with pytest.raises(AnsibleFailJson) as exc:
+ set_module_args({})
+ user_module()
+
+ def test_get_nonexistent_user(self):
+ ''' Test if get_unix_user returns None for non-existent user '''
+ set_module_args(self.mock_args())
+ result = self.get_user_mock_object().get_unix_user()
+ assert result is None
+
+ def test_get_existing_user(self):
+ ''' Test if get_unix_user returns details for existing user '''
+ set_module_args(self.mock_args())
+ result = self.get_user_mock_object('user').get_unix_user()
+ assert result['full_name'] == self.mock_user['full_name']
+
+ def test_get_xml(self):
+ set_module_args(self.mock_args())
+ obj = self.get_user_mock_object('user')
+ result = obj.get_unix_user()
+ assert obj.server.xml_in['query']
+ assert obj.server.xml_in['query']['unix-user-info']
+ user_info = obj.server.xml_in['query']['unix-user-info']
+ assert user_info['user-name'] == self.mock_user['name']
+ assert user_info['vserver'] == self.mock_user['vserver']
+
+ def test_create_error_missing_params(self):
+ data = self.mock_args()
+ del data['group_id']
+ set_module_args(data)
+ with pytest.raises(AnsibleFailJson) as exc:
+ self.get_user_mock_object('user').create_unix_user()
+ assert 'Error: Missing one or more required parameters for create: (group_id, id)' == exc.value.args[0]['msg']
+
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_unix_user.NetAppOntapUnixUser.create_unix_user')
+ def test_create_called(self, create_user):
+ set_module_args(self.mock_args())
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_user_mock_object().apply()
+ assert exc.value.args[0]['changed']
+ create_user.assert_called_with()
+
+ def test_create_xml(self):
+ '''Test create ZAPI element'''
+ set_module_args(self.mock_args())
+ create = self.get_user_mock_object()
+ with pytest.raises(AnsibleExitJson) as exc:
+ create.apply()
+ mock_key = {
+ 'user-name': 'name',
+ 'group-id': 'group_id',
+ 'user-id': 'id',
+ 'full-name': 'full_name'
+ }
+ for key in ['user-name', 'user-id', 'group-id', 'full-name']:
+ assert create.server.xml_in[key] == self.mock_user[mock_key[key]]
+
+ def test_create_wihtout_full_name(self):
+ '''Test create ZAPI element'''
+ data = self.mock_args()
+ del data['full_name']
+ set_module_args(data)
+ create = self.get_user_mock_object()
+ with pytest.raises(AnsibleExitJson) as exc:
+ create.apply()
+ with pytest.raises(KeyError):
+ create.server.xml_in['full-name']
+
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_unix_user.NetAppOntapUnixUser.modify_unix_user')
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_unix_user.NetAppOntapUnixUser.delete_unix_user')
+ def test_delete_called(self, delete_user, modify_user):
+ ''' Test delete existing user '''
+ data = self.mock_args()
+ data['state'] = 'absent'
+ set_module_args(data)
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_user_mock_object('user').apply()
+ assert exc.value.args[0]['changed']
+ delete_user.assert_called_with()
+ assert modify_user.call_count == 0
+
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_unix_user.NetAppOntapUnixUser.get_unix_user')
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_unix_user.NetAppOntapUnixUser.modify_unix_user')
+ def test_modify_called(self, modify_user, get_user):
+ ''' Test modify user group_id '''
+ data = self.mock_args()
+ data['group_id'] = 20
+ set_module_args(data)
+ get_user.return_value = {'group_id': 10}
+ obj = self.get_user_mock_object('user')
+ with pytest.raises(AnsibleExitJson) as exc:
+ obj.apply()
+ get_user.assert_called_with()
+ modify_user.assert_called_with({'group_id': 20})
+
+ def test_modify_only_id(self):
+ ''' Test modify user id '''
+ set_module_args(self.mock_args())
+ modify = self.get_user_mock_object('user')
+ modify.modify_unix_user({'id': 123})
+ assert modify.server.xml_in['user-id'] == '123'
+ with pytest.raises(KeyError):
+ modify.server.xml_in['group-id']
+ with pytest.raises(KeyError):
+ modify.server.xml_in['full-name']
+
+ def test_modify_xml(self):
+ ''' Test modify user full_name '''
+ set_module_args(self.mock_args())
+ modify = self.get_user_mock_object('user')
+ modify.modify_unix_user({'full_name': 'New Name',
+ 'group_id': '25'})
+ assert modify.server.xml_in['user-name'] == self.mock_user['name']
+ assert modify.server.xml_in['full-name'] == 'New Name'
+ assert modify.server.xml_in['group-id'] == '25'
+
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_unix_user.NetAppOntapUnixUser.create_unix_user')
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_unix_user.NetAppOntapUnixUser.delete_unix_user')
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_unix_user.NetAppOntapUnixUser.modify_unix_user')
+ def test_do_nothing(self, modify, delete, create):
+ ''' changed is False and none of the opetaion methods are called'''
+ data = self.mock_args()
+ data['state'] = 'absent'
+ set_module_args(data)
+ obj = self.get_user_mock_object()
+ with pytest.raises(AnsibleExitJson) as exc:
+ obj.apply()
+ create.assert_not_called()
+ delete.assert_not_called()
+ modify.assert_not_called()
+
+ def test_get_exception(self):
+ set_module_args(self.mock_args())
+ with pytest.raises(AnsibleFailJson) as exc:
+ self.get_user_mock_object('user-fail').get_unix_user()
+ assert 'Error getting UNIX user' in exc.value.args[0]['msg']
+
+ def test_create_exception(self):
+ set_module_args(self.mock_args())
+ with pytest.raises(AnsibleFailJson) as exc:
+ self.get_user_mock_object('user-fail').create_unix_user()
+ assert 'Error creating UNIX user' in exc.value.args[0]['msg']
+
+ def test_modify_exception(self):
+ set_module_args(self.mock_args())
+ with pytest.raises(AnsibleFailJson) as exc:
+ self.get_user_mock_object('user-fail').modify_unix_user({'id': '123'})
+ assert 'Error modifying UNIX user' in exc.value.args[0]['msg']
+
+ def test_delete_exception(self):
+ set_module_args(self.mock_args())
+ with pytest.raises(AnsibleFailJson) as exc:
+ self.get_user_mock_object('user-fail').delete_unix_user()
+ assert 'Error removing UNIX user' in exc.value.args[0]['msg']
diff --git a/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_user.py b/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_user.py
new file mode 100644
index 00000000..a43ac6f5
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_user.py
@@ -0,0 +1,505 @@
+# (c) 2018, NetApp, Inc
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+''' unit tests ONTAP Ansible module: na_ontap_user '''
+
+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.ontap.tests.unit.compat import unittest
+from ansible_collections.netapp.ontap.tests.unit.compat.mock import patch
+import ansible_collections.netapp.ontap.plugins.module_utils.netapp as netapp_utils
+
+from ansible_collections.netapp.ontap.plugins.modules.na_ontap_user \
+ import NetAppOntapUser as my_module # module under test
+
+if not netapp_utils.has_netapp_lib():
+ pytestmark = pytest.mark.skip('skipping as missing required netapp_lib')
+
+# REST API canned responses when mocking send_request
+SRR = {
+ # common responses
+ 'is_rest': (200, {}, None),
+ 'is_zapi': (400, {}, "Unreachable"),
+ 'empty_good': (200, {}, None),
+ 'end_of_sequence': (500, None, "Ooops, the UT needs one more SRR response"),
+ 'generic_error': (400, None, "Expected error"),
+ 'get_uuid': (200, {'owner': {'uuid': 'ansible'}}, None),
+ 'get_user_rest': (200,
+ {'num_records': 1,
+ 'records': [{'owner': {'uuid': 'ansible_vserver'},
+ 'name': 'abcd'}]}, None),
+ 'get_user_details_rest': (200,
+ {'role': {'name': 'vsadmin'},
+ 'applications': [{'application': 'http'}],
+ 'locked': 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"""
+
+
+class AnsibleFailJson(Exception):
+ """Exception class to be raised by module.fail_json and caught by the test case"""
+
+
+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)
+
+
+def set_default_args_rest():
+ return dict({
+ 'hostname': 'hostname',
+ 'username': 'username',
+ 'password': 'password',
+ 'name': 'user_name',
+ 'vserver': 'vserver',
+ 'applications': 'http',
+ 'authentication_method': 'password',
+ 'role_name': 'vsadmin',
+ 'lock_user': 'True',
+ })
+
+
+class MockONTAPConnection(object):
+ ''' mock server connection to ONTAP host '''
+
+ def __init__(self, kind=None, parm1=None, parm2=None):
+ ''' save arguments '''
+ self.type = kind
+ self.parm1 = parm1
+ self.parm2 = parm2
+ self.xml_in = None
+ self.xml_out = None
+
+ def invoke_successfully(self, xml, enable_tunneling): # pylint: disable=unused-argument
+ ''' mock invoke_successfully returning xml data '''
+ self.xml_in = xml
+ if self.type == 'user':
+ xml = self.build_user_info(self.parm1, self.parm2)
+ elif self.type == 'user_fail':
+ raise netapp_utils.zapi.NaApiError(code='TEST', message="This exception is from the unit test")
+ self.xml_out = xml
+ return xml
+
+ @staticmethod
+ def set_vserver(vserver):
+ '''mock set vserver'''
+
+ @staticmethod
+ def build_user_info(locked, role_name):
+ ''' build xml data for user-info '''
+ xml = netapp_utils.zapi.NaElement('xml')
+ data = {'num-records': 1,
+ 'attributes-list': {'security-login-account-info': {'is-locked': locked, 'role-name': role_name}}}
+
+ xml.translate_struct(data)
+ print(xml.to_string())
+ return xml
+
+
+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)
+ self.server = MockONTAPConnection()
+ self.onbox = False
+
+ def set_default_args(self, rest=False):
+ if self.onbox:
+ hostname = '10.10.10.10'
+ username = 'username'
+ password = 'password'
+ user_name = 'test'
+ vserver = 'ansible_test'
+ application = 'console'
+ authentication_method = 'password'
+ else:
+ hostname = 'hostname'
+ username = 'username'
+ password = 'password'
+ user_name = 'name'
+ vserver = 'vserver'
+ application = 'console'
+ authentication_method = 'password'
+ if rest:
+ use_rest = 'auto'
+ else:
+ use_rest = 'never'
+
+ return dict({
+ 'hostname': hostname,
+ 'username': username,
+ 'password': password,
+ 'use_rest': use_rest,
+ 'name': user_name,
+ 'vserver': vserver,
+ 'applications': application,
+ 'authentication_method': authentication_method
+ })
+
+ def test_module_fail_when_required_args_missing(self):
+ ''' required arguments are reported as errors '''
+ with pytest.raises(AnsibleFailJson) as exc:
+ set_module_args({})
+ my_module()
+ print('Info: %s' % exc.value.args[0]['msg'])
+
+ def test_ensure_user_get_called(self):
+ ''' a more interesting test '''
+ module_args = {}
+ module_args.update(self.set_default_args())
+ module_args.update({'role_name': 'test'})
+ set_module_args(module_args)
+ my_obj = my_module()
+ my_obj.server = self.server
+ user_info = my_obj.get_user()
+ print('Info: test_user_get: %s' % repr(user_info))
+ assert user_info is None
+
+ def test_ensure_user_apply_called(self):
+ ''' creating user and checking idempotency '''
+ module_args = {}
+ module_args.update(self.set_default_args())
+ module_args.update({'name': 'create'})
+ module_args.update({'role_name': 'test'})
+ set_module_args(module_args)
+ my_obj = my_module()
+ if not self.onbox:
+ my_obj.server = self.server
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ print('Info: test_user_apply: %s' % repr(exc.value))
+ assert exc.value.args[0]['changed']
+ if not self.onbox:
+ my_obj.server = MockONTAPConnection('user', 'false')
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ print('Info: test_user_apply: %s' % repr(exc.value))
+ assert exc.value.args[0]['changed']
+
+ def test_ensure_user_apply_for_delete_called(self):
+ ''' deleting user and checking idempotency '''
+ module_args = {}
+ module_args.update(self.set_default_args())
+ module_args.update({'name': 'create'})
+ module_args.update({'role_name': 'test'})
+ set_module_args(module_args)
+ my_obj = my_module()
+ if not self.onbox:
+ my_obj.server = MockONTAPConnection('user', 'false', 'test')
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ print('Info: test_user_apply: %s' % repr(exc.value))
+ assert not exc.value.args[0]['changed']
+ module_args.update({'state': 'absent'})
+ set_module_args(module_args)
+ my_obj = my_module()
+ if not self.onbox:
+ my_obj.server = MockONTAPConnection('user', 'false', 'test')
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ print('Info: test_user_delete: %s' % repr(exc.value))
+ assert exc.value.args[0]['changed']
+
+ def test_ensure_user_lock_called(self):
+ ''' changing user_lock to True and checking idempotency'''
+ module_args = {}
+ module_args.update(self.set_default_args())
+ module_args.update({'name': 'create'})
+ module_args.update({'role_name': 'test'})
+ module_args.update({'lock_user': 'false'})
+ set_module_args(module_args)
+ my_obj = my_module()
+ if not self.onbox:
+ my_obj.server = MockONTAPConnection('user', 'false', 'test')
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ print('Info: test_user_apply: %s' % repr(exc.value))
+ assert not exc.value.args[0]['changed']
+ module_args.update({'lock_user': 'true'})
+ set_module_args(module_args)
+ my_obj = my_module()
+ if not self.onbox:
+ my_obj.server = MockONTAPConnection('user', 'false')
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ print('Info: test_user_lock: %s' % repr(exc.value))
+ assert exc.value.args[0]['changed']
+
+ def test_ensure_user_unlock_called(self):
+ ''' changing user_lock to False and checking idempotency'''
+ module_args = {}
+ module_args.update(self.set_default_args())
+ module_args.update({'name': 'create'})
+ module_args.update({'role_name': 'test'})
+ module_args.update({'lock_user': 'false'})
+ set_module_args(module_args)
+ my_obj = my_module()
+ if not self.onbox:
+ my_obj.server = MockONTAPConnection('user', 'false', 'test')
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ print('Info: test_user_apply: %s' % repr(exc.value))
+ assert not exc.value.args[0]['changed']
+ module_args.update({'lock_user': 'false'})
+ set_module_args(module_args)
+ my_obj = my_module()
+ if not self.onbox:
+ my_obj.server = MockONTAPConnection('user', 'true', 'test')
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ print('Info: test_user_unlock: %s' % repr(exc.value))
+ assert exc.value.args[0]['changed']
+
+ def test_ensure_user_set_password_called(self):
+ ''' set password '''
+ module_args = {}
+ module_args.update(self.set_default_args())
+ module_args.update({'name': 'create'})
+ module_args.update({'role_name': 'test'})
+ module_args.update({'set_password': '123456'})
+ set_module_args(module_args)
+ my_obj = my_module()
+ if not self.onbox:
+ my_obj.server = MockONTAPConnection('user', 'true')
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ print('Info: test_user_apply: %s' % repr(exc.value))
+ assert exc.value.args[0]['changed']
+
+ def test_ensure_user_role_update_called(self):
+ ''' set password '''
+ module_args = {}
+ module_args.update(self.set_default_args())
+ module_args.update({'name': 'create'})
+ module_args.update({'role_name': 'test123'})
+ module_args.update({'set_password': '123456'})
+ set_module_args(module_args)
+ my_obj = my_module()
+ if not self.onbox:
+ my_obj.server = MockONTAPConnection('user', 'true')
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ print('Info: test_user_apply: %s' % repr(exc.value))
+ assert exc.value.args[0]['changed']
+
+ def test_ensure_user_role_update_additional_application_called(self):
+ ''' set password '''
+ module_args = {}
+ module_args.update(self.set_default_args())
+ module_args.update({'name': 'create'})
+ module_args.update({'role_name': 'test123'})
+ module_args.update({'application': 'http'})
+ module_args.update({'set_password': '123456'})
+ set_module_args(module_args)
+ my_obj = my_module()
+ if not self.onbox:
+ my_obj.server = MockONTAPConnection('user', 'true')
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ print('Info: test_user_apply: %s' % repr(exc.value))
+ assert exc.value.args[0]['changed']
+
+ def test_if_all_methods_catch_exception(self):
+ data = self.set_default_args()
+ data.update({'role_name': 'test'})
+ set_module_args(data)
+ my_obj = my_module()
+ if not self.onbox:
+ my_obj.server = MockONTAPConnection('user_fail')
+ with pytest.raises(AnsibleFailJson) as exc:
+ my_obj.get_user()
+ assert 'Error getting user ' in exc.value.args[0]['msg']
+ with pytest.raises(AnsibleFailJson) as exc:
+ my_obj.create_user(data['applications'])
+ assert 'Error creating user ' in exc.value.args[0]['msg']
+ with pytest.raises(AnsibleFailJson) as exc:
+ my_obj.lock_given_user()
+ assert 'Error locking user ' in exc.value.args[0]['msg']
+ with pytest.raises(AnsibleFailJson) as exc:
+ my_obj.unlock_given_user()
+ assert 'Error unlocking user ' in exc.value.args[0]['msg']
+ with pytest.raises(AnsibleFailJson) as exc:
+ my_obj.delete_user(data['applications'])
+ assert 'Error removing user ' in exc.value.args[0]['msg']
+ with pytest.raises(AnsibleFailJson) as exc:
+ my_obj.change_password()
+ assert 'Error setting password for user ' in exc.value.args[0]['msg']
+ with pytest.raises(AnsibleFailJson) as exc:
+ my_obj.modify_user(data['applications'])
+ assert 'Error modifying user ' in exc.value.args[0]['msg']
+
+ @patch('ansible_collections.netapp.ontap.plugins.module_utils.netapp.OntapRestAPI.send_request')
+ def test_rest_error_applications_snmp(self, mock_request):
+ data = self.set_default_args(rest=True)
+ data.update({'applications': 'snmp'})
+ data.update({'name': 'create'})
+ data.update({'role_name': 'test123'})
+ data.update({'set_password': '123456'})
+ set_module_args(data)
+ mock_request.side_effect = [
+ SRR['is_rest'],
+ SRR['end_of_sequence']
+ ]
+ with pytest.raises(AnsibleFailJson) as exc:
+ my_module()
+ assert exc.value.args[0]['msg'] == "Snmp as application is not supported in REST."
+
+
+@patch('ansible.module_utils.basic.AnsibleModule.fail_json')
+@patch('ansible_collections.netapp.ontap.plugins.module_utils.netapp.OntapRestAPI.send_request')
+def test_ensure_user_get_rest_called(mock_request, mock_fail):
+ mock_fail.side_effect = fail_json
+ mock_request.side_effect = [
+ SRR['is_rest'],
+ SRR['get_user_rest'],
+ SRR['end_of_sequence']
+ ]
+ set_module_args(set_default_args_rest())
+ my_obj = my_module()
+ assert my_obj.get_user_rest() is not None
+
+
+@patch('ansible.module_utils.basic.AnsibleModule.exit_json')
+@patch('ansible.module_utils.basic.AnsibleModule.fail_json')
+@patch('ansible_collections.netapp.ontap.plugins.module_utils.netapp.OntapRestAPI.send_request')
+def test_ensure_create_user_rest_called(mock_request, mock_fail, mock_exit):
+ mock_fail.side_effect = fail_json
+ mock_exit.side_effect = exit_json
+ mock_request.side_effect = [
+ SRR['is_rest'],
+ SRR['get_user_rest'],
+ SRR['get_user_details_rest'],
+ SRR['get_user_rest'],
+ ]
+ set_module_args(set_default_args_rest())
+ my_obj = my_module()
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ assert exc.value.args[0]['changed']
+
+
+@patch('ansible.module_utils.basic.AnsibleModule.exit_json')
+@patch('ansible.module_utils.basic.AnsibleModule.fail_json')
+@patch('ansible_collections.netapp.ontap.plugins.module_utils.netapp.OntapRestAPI.send_request')
+def test_ensure_delete_user_rest_called(mock_request, mock_fail, mock_exit):
+ mock_fail.side_effect = fail_json
+ mock_exit.side_effect = exit_json
+ mock_request.side_effect = [
+ SRR['is_rest'],
+ SRR['get_user_rest'],
+ SRR['get_user_details_rest'],
+ SRR['get_user_rest'],
+ SRR['empty_good'],
+ SRR['end_of_sequence']
+ ]
+ data = {
+ 'state': 'absent',
+ }
+ data.update(set_default_args_rest())
+ set_module_args(data)
+ my_obj = my_module()
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ assert exc.value.args[0]['changed']
+
+
+@patch('ansible.module_utils.basic.AnsibleModule.exit_json')
+@patch('ansible.module_utils.basic.AnsibleModule.fail_json')
+@patch('ansible_collections.netapp.ontap.plugins.module_utils.netapp.OntapRestAPI.send_request')
+def test_ensure_modify_user_rest_called(mock_request, mock_fail, mock_exit):
+ mock_fail.side_effect = fail_json
+ mock_exit.side_effect = exit_json
+ mock_request.side_effect = [
+ SRR['is_rest'],
+ SRR['get_user_rest'],
+ SRR['get_user_details_rest'],
+ SRR['get_user_rest'],
+ SRR['empty_good'],
+ SRR['end_of_sequence']
+ ]
+ data = {
+ 'application': 'ssh',
+ }
+ data.update(set_default_args_rest())
+ set_module_args(data)
+ my_obj = my_module()
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ assert exc.value.args[0]['changed']
+
+
+@patch('ansible.module_utils.basic.AnsibleModule.exit_json')
+@patch('ansible.module_utils.basic.AnsibleModule.fail_json')
+@patch('ansible_collections.netapp.ontap.plugins.module_utils.netapp.OntapRestAPI.send_request')
+def test_ensure_lock_unlock_user_rest_called(mock_request, mock_fail, mock_exit):
+ mock_fail.side_effect = fail_json
+ mock_exit.side_effect = exit_json
+ mock_request.side_effect = [
+ SRR['is_rest'],
+ SRR['get_user_rest'],
+ SRR['get_user_details_rest'],
+ SRR['get_user_rest'],
+ SRR['empty_good'],
+ SRR['end_of_sequence']
+ ]
+ data = {
+ 'lock_user': 'newvalue',
+ }
+ data.update(set_default_args_rest())
+ set_module_args(data)
+ my_obj = my_module()
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ assert exc.value.args[0]['changed']
+
+
+@patch('ansible.module_utils.basic.AnsibleModule.exit_json')
+@patch('ansible.module_utils.basic.AnsibleModule.fail_json')
+@patch('ansible_collections.netapp.ontap.plugins.module_utils.netapp.OntapRestAPI.send_request')
+def test_ensure_change_password_user_rest_called(mock_request, mock_fail, mock_exit):
+ mock_fail.side_effect = fail_json
+ mock_exit.side_effect = exit_json
+ mock_request.side_effect = [
+ SRR['is_rest'],
+ SRR['get_user_rest'],
+ SRR['get_user_details_rest'],
+ SRR['get_user_rest'],
+ SRR['empty_good'],
+ SRR['end_of_sequence']
+ ]
+ data = {
+ 'password': 'newvalue',
+ }
+ data.update(set_default_args_rest())
+ set_module_args(data)
+ my_obj = my_module()
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ assert exc.value.args[0]['changed']
diff --git a/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_user_role.py b/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_user_role.py
new file mode 100644
index 00000000..f8b3ce82
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_user_role.py
@@ -0,0 +1,239 @@
+# (c) 2018, NetApp, Inc
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+''' unit test template for ONTAP Ansible module '''
+
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+import json
+import pytest
+
+from ansible_collections.netapp.ontap.tests.unit.compat import unittest
+from ansible_collections.netapp.ontap.tests.unit.compat.mock import patch, Mock
+from ansible.module_utils import basic
+from ansible.module_utils._text import to_bytes
+import ansible_collections.netapp.ontap.plugins.module_utils.netapp as netapp_utils
+
+from ansible_collections.netapp.ontap.plugins.modules.na_ontap_user_role \
+ import NetAppOntapUserRole as role_module # module under test
+
+if not netapp_utils.has_netapp_lib():
+ pytestmark = pytest.mark.skip('skipping as missing required netapp_lib')
+
+
+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 MockONTAPConnection(object):
+ ''' mock server connection to ONTAP host '''
+
+ def __init__(self, kind=None, data=None):
+ ''' save arguments '''
+ self.kind = kind
+ self.params = data
+ self.xml_in = None
+ self.xml_out = None
+
+ def invoke_successfully(self, xml, enable_tunneling): # pylint: disable=unused-argument
+ ''' mock invoke_successfully returning xml data '''
+ self.xml_in = xml
+ if self.kind == 'role':
+ xml = self.build_role_info(self.params)
+ if self.kind == 'error':
+ error = netapp_utils.zapi.NaApiError('test', 'error')
+ raise error
+ self.xml_out = xml
+ return xml
+
+ @staticmethod
+ def build_role_info(vol_details):
+ ''' build xml data for role-attributes '''
+ xml = netapp_utils.zapi.NaElement('xml')
+ attributes = {
+ 'num-records': 1,
+ 'attributes-list': {
+ 'security-login-role-info': {
+ 'access-level': 'all',
+ 'command-directory-name': 'volume',
+ 'role-name': 'testrole',
+ 'role-query': 'show',
+ 'vserver': 'ansible'
+ }
+ }
+ }
+ xml.translate_struct(attributes)
+ return xml
+
+
+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)
+ self.mock_role = {
+ 'name': 'testrole',
+ 'access_level': 'all',
+ 'command_directory_name': 'volume',
+ 'vserver': 'ansible'
+ }
+
+ def mock_args(self):
+ return {
+ 'name': self.mock_role['name'],
+ 'vserver': self.mock_role['vserver'],
+ 'command_directory_name': self.mock_role['command_directory_name'],
+ 'hostname': 'test',
+ 'username': 'test_user',
+ 'password': 'test_pass!',
+ 'https': 'False'
+ }
+
+ def get_role_mock_object(self, kind=None):
+ """
+ Helper method to return an na_ontap_user_role object
+ :param kind: passes this param to MockONTAPConnection()
+ :return: na_ontap_user_role object
+ """
+ role_obj = role_module()
+ role_obj.asup_log_for_cserver = Mock(return_value=None)
+ role_obj.cluster = Mock()
+ role_obj.cluster.invoke_successfully = Mock()
+ if kind is None:
+ role_obj.server = MockONTAPConnection()
+ else:
+ role_obj.server = MockONTAPConnection(kind=kind, data=self.mock_role)
+ return role_obj
+
+ def test_module_fail_when_required_args_missing(self):
+ ''' required arguments are reported as errors '''
+ with pytest.raises(AnsibleFailJson) as exc:
+ set_module_args({})
+ role_module()
+ print('Info: %s' % exc.value.args[0]['msg'])
+
+ def test_get_nonexistent_policy(self):
+ ''' Test if get_role returns None for non-existent role '''
+ set_module_args(self.mock_args())
+ result = self.get_role_mock_object().get_role()
+ assert result is None
+
+ def test_get_existing_role(self):
+ ''' Test if get_role returns details for existing role '''
+ set_module_args(self.mock_args())
+ result = self.get_role_mock_object('role').get_role()
+ assert result['name'] == self.mock_role['name']
+
+ def test_successful_create(self):
+ ''' Test successful create '''
+ data = self.mock_args()
+ set_module_args(data)
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_role_mock_object().apply()
+ assert exc.value.args[0]['changed']
+
+ def test_create_idempotency(self):
+ ''' Test create idempotency '''
+ data = self.mock_args()
+ data['query'] = 'show'
+ set_module_args(data)
+ obj = self.get_role_mock_object('role')
+ with pytest.raises(AnsibleExitJson) as exc:
+ obj.apply()
+ assert not exc.value.args[0]['changed']
+
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_user_role.NetAppOntapUserRole.get_role')
+ def test_create_error(self, get_role):
+ ''' Test create error '''
+ set_module_args(self.mock_args())
+ get_role.side_effect = [
+ None
+ ]
+ with pytest.raises(AnsibleFailJson) as exc:
+ self.get_role_mock_object('error').apply()
+ assert exc.value.args[0]['msg'] == 'Error creating role testrole: NetApp API failed. Reason - test:error'
+
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_user_role.NetAppOntapUserRole.get_role')
+ def test_successful_modify(self, get_role):
+ ''' Test successful modify '''
+ data = self.mock_args()
+ data['query'] = 'show'
+ set_module_args(data)
+ current = self.mock_role
+ current['query'] = 'show-space'
+ get_role.side_effect = [
+ current
+ ]
+ obj = self.get_role_mock_object()
+ with pytest.raises(AnsibleExitJson) as exc:
+ obj.apply()
+ assert exc.value.args[0]['changed']
+
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_user_role.NetAppOntapUserRole.get_role')
+ def test_modify_idempotency(self, get_role):
+ ''' Test modify idempotency '''
+ data = self.mock_args()
+ data['query'] = 'show'
+ set_module_args(data)
+ current = self.mock_role
+ current['query'] = 'show'
+ get_role.side_effect = [
+ current
+ ]
+ obj = self.get_role_mock_object()
+ with pytest.raises(AnsibleExitJson) as exc:
+ obj.apply()
+ assert not exc.value.args[0]['changed']
+
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_user_role.NetAppOntapUserRole.get_role')
+ def test_modify_error(self, get_role):
+ ''' Test modify error '''
+ data = self.mock_args()
+ data['query'] = 'show'
+ set_module_args(data)
+ current = self.mock_role
+ current['query'] = 'show-space'
+ get_role.side_effect = [
+ current
+ ]
+ with pytest.raises(AnsibleFailJson) as exc:
+ self.get_role_mock_object('error').apply()
+ assert exc.value.args[0]['msg'] == 'Error modifying role testrole: NetApp API failed. Reason - test:error'
+
+ def test_successful_delete(self):
+ ''' Test delete existing role '''
+ data = self.mock_args()
+ data['state'] = 'absent'
+ set_module_args(data)
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_role_mock_object('role').apply()
+ assert exc.value.args[0]['changed']
diff --git a/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_volume.py b/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_volume.py
new file mode 100644
index 00000000..48c34856
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_volume.py
@@ -0,0 +1,1183 @@
+# (c) 2018, NetApp, Inc
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+''' unit test template for ONTAP Ansible module '''
+
+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.ontap.tests.unit.compat import unittest
+from ansible_collections.netapp.ontap.tests.unit.compat.mock import patch, Mock
+import ansible_collections.netapp.ontap.plugins.module_utils.netapp as netapp_utils
+
+from ansible_collections.netapp.ontap.plugins.modules.na_ontap_volume \
+ import NetAppOntapVolume as vol_module # module under test
+
+if not netapp_utils.has_netapp_lib():
+ pytestmark = pytest.mark.skip('skipping as missing required netapp_lib')
+
+
+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"""
+
+
+class AnsibleFailJson(Exception):
+ """Exception class to be raised by module.fail_json and caught by the test case"""
+
+
+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 MockONTAPConnection(object):
+ ''' mock server connection to ONTAP host '''
+
+ def __init__(self, kind=None, data=None, job_error=None):
+ ''' save arguments '''
+ self.kind = kind
+ self.params = data
+ self.xml_in = None
+ self.xml_out = None
+ self.job_error = job_error
+
+ def invoke_successfully(self, xml, enable_tunneling): # pylint: disable=unused-argument
+ ''' mock invoke_successfully returning xml data '''
+ self.xml_in = xml
+ # print("request: ", xml.to_string())
+ request = xml.to_string().decode('utf-8')
+ print(request)
+ if request.startswith('<sis-get-iter>'):
+ return self.build_sis_info()
+ if isinstance(self.kind, list):
+ kind = self.kind.pop(0)
+ if len(self.kind) == 0:
+ # loop over last element
+ self.kind = kind
+ else:
+ kind = self.kind
+
+ if kind == 'volume':
+ xml = self.build_volume_info(self.params)
+ elif kind == 'job_info':
+ xml = self.build_job_info(self.job_error)
+ elif kind == 'error_modify':
+ xml = self.build_modify_error()
+ elif kind == 'failure_modify_async':
+ xml = self.build_failure_modify_async()
+ elif kind == 'missing_element_modify_async':
+ xml = self.build_missing_element_modify_async()
+ elif kind == 'success_modify_async':
+ xml = self.build_success_modify_async()
+ elif kind == 'zapi_error':
+ error = netapp_utils.zapi.NaApiError('test', 'error')
+ raise error
+ self.xml_out = xml
+ # print(xml.to_string())
+ return xml
+
+ @staticmethod
+ def build_volume_info(vol_details):
+ ''' build xml data for volume-attributes '''
+ xml = netapp_utils.zapi.NaElement('xml')
+ attributes = {
+ 'num-records': 1,
+ 'attributes-list': {
+ 'volume-attributes': {
+ 'volume-id-attributes': {
+ 'containing-aggregate-name': vol_details['aggregate'],
+ 'junction-path': vol_details['junction_path'],
+ 'style-extended': 'flexvol'
+ },
+ 'volume-language-attributes': {
+ 'language-code': 'en'
+ },
+ 'volume-export-attributes': {
+ 'policy': 'default'
+ },
+ 'volume-performance-attributes': {
+ 'is-atime-update-enabled': 'true'
+ },
+ 'volume-state-attributes': {
+ 'state': "online",
+ 'is-nvfail-enabled': 'true'
+ },
+ 'volume-space-attributes': {
+ 'space-guarantee': 'none',
+ 'size': vol_details['size'],
+ 'percentage-snapshot-reserve': vol_details['percent_snapshot_space'],
+ 'space-slo': 'thick'
+ },
+ 'volume-snapshot-attributes': {
+ 'snapshot-policy': vol_details['snapshot_policy']
+ },
+ 'volume-comp-aggr-attributes': {
+ 'tiering-policy': 'snapshot-only'
+ },
+ 'volume-security-attributes': {
+ 'style': 'unix',
+ 'volume-security-unix-attributes': {
+ 'permissions': vol_details['unix_permissions'],
+ 'group-id': vol_details['group_id'],
+ 'user-id': vol_details['user_id']
+ }
+ },
+ 'volume-vserver-dr-protection-attributes': {
+ 'vserver-dr-protection': vol_details['vserver_dr_protection'],
+ },
+ 'volume-qos-attributes': {
+ 'policy-group-name': vol_details['qos_policy_group'],
+ 'adaptive-policy-group-name': vol_details['qos_adaptive_policy_group']
+ },
+ 'volume-snapshot-autodelete-attributes': {
+ 'commitment': 'try'
+ }
+ }
+ }
+ }
+ xml.translate_struct(attributes)
+ return xml
+
+ @staticmethod
+ def build_flex_group_info(vol_details):
+ ''' build xml data for flexGroup volume-attributes '''
+ xml = netapp_utils.zapi.NaElement('xml')
+ attributes = {
+ 'num-records': 1,
+ 'attributes-list': {
+ 'volume-attributes': {
+ 'volume-id-attributes': {
+ 'aggr-list': vol_details['aggregate'],
+ 'junction-path': vol_details['junction_path'],
+ 'style-extended': 'flexgroup'
+ },
+ 'volume-language-attributes': {
+ 'language-code': 'en'
+ },
+ 'volume-export-attributes': {
+ 'policy': 'default'
+ },
+ 'volume-performance-attributes': {
+ 'is-atime-update-enabled': 'true'
+ },
+ 'volume-state-attributes': {
+ 'state': "online"
+ },
+ 'volume-space-attributes': {
+ 'space-guarantee': 'none',
+ 'size': vol_details['size']
+ },
+ 'volume-snapshot-attributes': {
+ 'snapshot-policy': vol_details['snapshot_policy']
+ },
+ 'volume-security-attributes': {
+ 'volume-security-unix-attributes': {
+ 'permissions': vol_details['unix_permissions']
+ }
+ }
+ }
+ }
+ }
+ xml.translate_struct(attributes)
+ return xml
+
+ @staticmethod
+ def build_job_info(error):
+ ''' build xml data for a job '''
+ xml = netapp_utils.zapi.NaElement('xml')
+ attributes = netapp_utils.zapi.NaElement('attributes')
+ if error is None:
+ state = 'success'
+ elif error == 'time_out':
+ state = 'running'
+ elif error == 'failure':
+ state = 'failure'
+ else:
+ state = 'other'
+ attributes.add_node_with_children('job-info', **{
+ 'job-state': state,
+ 'job-progress': 'dummy',
+ 'job-completion': error,
+ })
+ xml.add_child_elem(attributes)
+ xml.add_new_child('result-status', 'in_progress')
+ xml.add_new_child('result-jobid', '1234')
+ return xml
+
+ @staticmethod
+ def build_modify_error():
+ ''' build xml data for modify error '''
+ xml = netapp_utils.zapi.NaElement('xml')
+ attributes = netapp_utils.zapi.NaElement('failure-list')
+ info_list_obj = netapp_utils.zapi.NaElement('volume-modify-iter-info')
+ info_list_obj.add_new_child('error-message', 'modify error message')
+ attributes.add_child_elem(info_list_obj)
+ xml.add_child_elem(attributes)
+ return xml
+
+ @staticmethod
+ def build_success_modify_async():
+ ''' build xml data for success modify async '''
+ xml = netapp_utils.zapi.NaElement('xml')
+ attributes = netapp_utils.zapi.NaElement('success-list')
+ info_list_obj = netapp_utils.zapi.NaElement('volume-modify-iter-async-info')
+ info_list_obj.add_new_child('status', 'in_progress')
+ info_list_obj.add_new_child('jobid', '1234')
+ attributes.add_child_elem(info_list_obj)
+ xml.add_child_elem(attributes)
+ return xml
+
+ @staticmethod
+ def build_missing_element_modify_async():
+ ''' build xml data for success modify async '''
+ xml = netapp_utils.zapi.NaElement('xml')
+ attributes = netapp_utils.zapi.NaElement('success-list')
+ info_list_obj = netapp_utils.zapi.NaElement('volume-modify-iter-info') # no async!
+ info_list_obj.add_new_child('status', 'in_progress')
+ info_list_obj.add_new_child('jobid', '1234')
+ attributes.add_child_elem(info_list_obj)
+ xml.add_child_elem(attributes)
+ return xml
+
+ @staticmethod
+ def build_failure_modify_async():
+ ''' build xml data for failure modify async '''
+ xml = netapp_utils.zapi.NaElement('xml')
+ attributes = netapp_utils.zapi.NaElement('failure-list')
+ info_list_obj = netapp_utils.zapi.NaElement('volume-modify-iter-async-info')
+ info_list_obj.add_new_child('status', 'failed')
+ info_list_obj.add_new_child('jobid', '1234')
+ info_list_obj.add_new_child('error-message', 'modify error message')
+ attributes.add_child_elem(info_list_obj)
+ xml.add_child_elem(attributes)
+ return xml
+
+ @staticmethod
+ def build_sis_info():
+ ''' build xml data for sis config '''
+ xml = netapp_utils.zapi.NaElement('xml')
+ attributes = {
+ 'num-records': 1,
+ 'attributes-list': {
+ 'sis-status-info': {
+ 'policy': 'testme'
+ }
+ }
+ }
+ xml.translate_struct(attributes)
+ return xml
+
+
+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)
+ self.mock_vol = {
+ 'name': 'test_vol',
+ 'aggregate': 'test_aggr',
+ 'junction_path': '/test',
+ 'vserver': 'test_vserver',
+ 'size': 20971520,
+ 'unix_permissions': '755',
+ 'user_id': 100,
+ 'group_id': 1000,
+ 'snapshot_policy': 'default',
+ 'qos_policy_group': 'performance',
+ 'qos_adaptive_policy_group': 'performance',
+ 'percent_snapshot_space': 60,
+ 'language': 'en',
+ 'vserver_dr_protection': 'unprotected'
+ }
+
+ def mock_args(self, tag=None):
+ args = {
+ 'hostname': 'test',
+ 'username': 'test_user',
+ 'password': 'test_pass!',
+ 'name': self.mock_vol['name'],
+ 'vserver': self.mock_vol['vserver'],
+ 'policy': 'default',
+ 'language': self.mock_vol['language'],
+ 'is_online': True,
+ 'unix_permissions': '---rwxr-xr-x',
+ 'user_id': 100,
+ 'group_id': 1000,
+ 'snapshot_policy': 'default',
+ 'qos_policy_group': 'performance',
+ 'qos_adaptive_policy_group': 'performance',
+ 'size': 20,
+ 'size_unit': 'mb',
+ 'junction_path': '/test',
+ 'percent_snapshot_space': 60,
+ 'type': 'type',
+ 'nvfail_enabled': True,
+ 'space_slo': 'thick'
+ }
+ if tag is None:
+ args['aggregate_name'] = self.mock_vol['aggregate']
+ return args
+
+ elif tag == 'flexGroup_manual':
+ args['aggr_list'] = 'aggr_0,aggr_1'
+ args['aggr_list_multiplier'] = 2
+ return args
+
+ elif tag == 'flexGroup_auto':
+ args['auto_provision_as'] = 'flexgroup'
+ return args
+
+ def get_volume_mock_object(self, kind=None, job_error=None):
+ """
+ Helper method to return an na_ontap_volume object
+ :param kind: passes this param to MockONTAPConnection().
+ :param job_error: error message when getting job status.
+ :return: na_ontap_volume object
+ """
+ vol_obj = vol_module()
+ vol_obj.ems_log_event = Mock(return_value=None)
+ vol_obj.get_efficiency_policy = Mock(return_value='test_efficiency')
+ vol_obj.volume_style = None
+ if kind is None:
+ vol_obj.server = MockONTAPConnection()
+ elif kind == 'job_info':
+ vol_obj.server = MockONTAPConnection(kind='job_info', data=self.mock_vol, job_error=job_error)
+ vol_obj.cluster = MockONTAPConnection(kind='job_info', data=self.mock_vol, job_error=job_error)
+ else:
+ vol_obj.server = MockONTAPConnection(kind=kind, data=self.mock_vol)
+ vol_obj.cluster = MockONTAPConnection(kind=kind, data=self.mock_vol)
+
+ return vol_obj
+
+ def test_module_fail_when_required_args_missing(self):
+ ''' required arguments are reported as errors '''
+ with pytest.raises(AnsibleFailJson) as exc:
+ set_module_args({})
+ vol_module()
+ print('Info: %s' % exc.value.args[0]['msg'])
+
+ def test_get_nonexistent_volume(self):
+ ''' Test if get_volume returns None for non-existent volume '''
+ set_module_args(self.mock_args())
+ result = self.get_volume_mock_object().get_volume()
+ assert result is None
+
+ def test_get_existing_volume(self):
+ ''' Test if get_volume returns details for existing volume '''
+ set_module_args(self.mock_args())
+ result = self.get_volume_mock_object('volume').get_volume()
+ assert result['name'] == self.mock_vol['name']
+ assert result['size'] == self.mock_vol['size']
+
+ def test_create_error_missing_param(self):
+ ''' Test if create throws an error if aggregate_name is not specified'''
+ data = self.mock_args()
+ del data['aggregate_name']
+ set_module_args(data)
+ with pytest.raises(AnsibleFailJson) as exc:
+ self.get_volume_mock_object('volume').create_volume()
+ msg = 'Error provisioning volume test_vol: aggregate_name is required'
+ assert exc.value.args[0]['msg'] == msg
+
+ def test_successful_create(self):
+ ''' Test successful create '''
+ data = self.mock_args()
+ data['size'] = 20
+ data['encrypt'] = True
+ set_module_args(data)
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_volume_mock_object().apply()
+ assert exc.value.args[0]['changed']
+
+ def test_create_idempotency(self):
+ ''' Test create idempotency '''
+ set_module_args(self.mock_args())
+ obj = self.get_volume_mock_object('volume')
+ with pytest.raises(AnsibleExitJson) as exc:
+ obj.apply()
+ assert not exc.value.args[0]['changed']
+
+ def test_successful_delete(self):
+ ''' Test delete existing volume '''
+ data = self.mock_args()
+ data['state'] = 'absent'
+ set_module_args(data)
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_volume_mock_object('volume').apply()
+ assert exc.value.args[0]['changed']
+
+ def test_delete_idempotency(self):
+ ''' Test delete idempotency '''
+ data = self.mock_args()
+ data['state'] = 'absent'
+ set_module_args(data)
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_volume_mock_object().apply()
+ assert not exc.value.args[0]['changed']
+
+ def test_successful_modify_size(self):
+ ''' Test successful modify size '''
+ data = self.mock_args()
+ data['size'] = 200
+ set_module_args(data)
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_volume_mock_object('volume').apply()
+ assert exc.value.args[0]['changed']
+
+ def test_modify_idempotency(self):
+ ''' Test modify idempotency '''
+ data = self.mock_args()
+ set_module_args(data)
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_volume_mock_object('volume').apply()
+ assert not exc.value.args[0]['changed']
+
+ def test_modify_error(self):
+ ''' Test modify idempotency '''
+ data = self.mock_args()
+ set_module_args(data)
+ with pytest.raises(AnsibleFailJson) as exc:
+ self.get_volume_mock_object('error_modify').volume_modify_attributes(dict())
+ assert exc.value.args[0]['msg'] == 'Error modifying volume test_vol: modify error message'
+
+ def test_mount_volume(self):
+ ''' Test mount volume '''
+ data = self.mock_args()
+ data['junction_path'] = "/test123"
+ set_module_args(data)
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_volume_mock_object('volume').apply()
+ assert exc.value.args[0]['changed']
+
+ def test_unmount_volume(self):
+ ''' Test unmount volume '''
+ data = self.mock_args()
+ data['junction_path'] = ""
+ set_module_args(data)
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_volume_mock_object('volume').apply()
+ assert exc.value.args[0]['changed']
+
+ def test_successful_modify_space(self):
+ ''' Test successful modify space '''
+ data = self.mock_args()
+ del data['space_slo']
+ data['space_guarantee'] = 'volume'
+ set_module_args(data)
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_volume_mock_object('volume').apply()
+ assert exc.value.args[0]['changed']
+
+ def test_successful_modify_unix_permissions(self):
+ ''' Test successful modify unix_permissions '''
+ data = self.mock_args()
+ data['unix_permissions'] = '---rw-r-xr-x'
+ set_module_args(data)
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_volume_mock_object('volume').apply()
+ assert exc.value.args[0]['changed']
+
+ def test_successful_modify_snapshot_policy(self):
+ ''' Test successful modify snapshot_policy '''
+ data = self.mock_args()
+ data['snapshot_policy'] = 'default-1weekly'
+ set_module_args(data)
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_volume_mock_object('volume').apply()
+ assert exc.value.args[0]['changed']
+
+ def test_successful_modify_efficiency_policy(self):
+ ''' Test successful modify efficiency_policy '''
+ data = self.mock_args()
+ data['efficiency_policy'] = 'test'
+ set_module_args(data)
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_volume_mock_object('volume').apply()
+ assert exc.value.args[0]['changed']
+
+ def test_successful_modify_percent_snapshot_space(self):
+ ''' Test successful modify percent_snapshot_space '''
+ data = self.mock_args()
+ data['percent_snapshot_space'] = '90'
+ set_module_args(data)
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_volume_mock_object('volume').apply()
+ assert exc.value.args[0]['changed']
+
+ def test_successful_modify_qos_policy_group(self):
+ ''' Test successful modify qos_policy_group '''
+ data = self.mock_args()
+ data['qos_policy_group'] = 'extreme'
+ set_module_args(data)
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_volume_mock_object('volume').apply()
+ assert exc.value.args[0]['changed']
+
+ def test_successful_modify_qos_adaptive_policy_group(self):
+ ''' Test successful modify qos_adaptive_policy_group '''
+ data = self.mock_args()
+ data['qos_adaptive_policy_group'] = 'extreme'
+ set_module_args(data)
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_volume_mock_object('volume').apply()
+ assert exc.value.args[0]['changed']
+
+ def test_successful_move(self):
+ ''' Test successful modify aggregate '''
+ data = self.mock_args()
+ data['aggregate_name'] = 'different_aggr'
+ set_module_args(data)
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_volume_mock_object('volume').apply()
+ assert exc.value.args[0]['changed']
+
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_volume.NetAppOntapVolume.get_volume')
+ def test_successful_rename(self, get_volume):
+ ''' Test successful rename volume '''
+ data = self.mock_args()
+ data['from_name'] = self.mock_vol['name']
+ data['name'] = 'new_name'
+ set_module_args(data)
+ current = {
+ 'hostname': 'test',
+ 'username': 'test_user',
+ 'password': 'test_pass!',
+ 'name': self.mock_vol['name'],
+ 'vserver': self.mock_vol['vserver'],
+ }
+ get_volume.side_effect = [
+ None,
+ current
+ ]
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_volume_mock_object().apply()
+ assert exc.value.args[0]['changed']
+
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_volume.NetAppOntapVolume.get_volume')
+ def test_successful_rename_async(self, get_volume):
+ ''' Test successful rename volume '''
+ data = self.mock_args()
+ data['from_name'] = self.mock_vol['name']
+ data['name'] = 'new_name'
+ set_module_args(data)
+ current = {
+ 'hostname': 'test',
+ 'username': 'test_user',
+ 'password': 'test_pass!',
+ 'name': self.mock_vol['name'],
+ 'vserver': self.mock_vol['vserver'],
+ 'is_infinite': True
+ }
+ get_volume.side_effect = [
+ None,
+ current
+ ]
+ obj = self.get_volume_mock_object('job_info')
+ with pytest.raises(AnsibleExitJson) as exc:
+ obj.apply()
+ assert exc.value.args[0]['changed']
+
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_volume.NetAppOntapVolume.change_volume_state')
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_volume.NetAppOntapVolume.volume_mount')
+ def test_modify_helper(self, mount_volume, change_state):
+ data = self.mock_args()
+ set_module_args(data)
+ modify = {
+ 'is_online': False,
+ 'junction_path': 'something'
+ }
+ obj = self.get_volume_mock_object('volume')
+ obj.modify_volume(modify)
+ change_state.assert_called_with()
+ mount_volume.assert_called_with()
+
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_volume.NetAppOntapVolume.get_volume')
+ def test_compare_chmod_value_true_1(self, get_volume):
+ data = self.mock_args()
+ data['unix_permissions'] = '------------'
+ set_module_args(data)
+ current = {
+ 'unix_permissions': '0'
+ }
+ get_volume.side_effect = [
+ current
+ ]
+ obj = self.get_volume_mock_object()
+ assert obj.compare_chmod_value(current)
+
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_volume.NetAppOntapVolume.get_volume')
+ def test_compare_chmod_value_true_2(self, get_volume):
+ data = self.mock_args()
+ data['unix_permissions'] = '---rwxrwxrwx'
+ set_module_args(data)
+ current = {
+ 'unix_permissions': '777'
+ }
+ get_volume.side_effect = [
+ current
+ ]
+ obj = self.get_volume_mock_object()
+ assert obj.compare_chmod_value(current)
+
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_volume.NetAppOntapVolume.get_volume')
+ def test_compare_chmod_value_true_3(self, get_volume):
+ data = self.mock_args()
+ data['unix_permissions'] = '---rwxr-xr-x'
+ set_module_args(data)
+ current = {
+ 'unix_permissions': '755'
+ }
+ get_volume.side_effect = [
+ current
+ ]
+ obj = self.get_volume_mock_object()
+ assert obj.compare_chmod_value(current)
+
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_volume.NetAppOntapVolume.get_volume')
+ def test_compare_chmod_value_true_4(self, get_volume):
+ data = self.mock_args()
+ data['unix_permissions'] = '755'
+ set_module_args(data)
+ current = {
+ 'unix_permissions': '755'
+ }
+ get_volume.side_effect = [
+ current
+ ]
+ obj = self.get_volume_mock_object()
+ assert obj.compare_chmod_value(current)
+
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_volume.NetAppOntapVolume.get_volume')
+ def test_compare_chmod_value_false_1(self, get_volume):
+ data = self.mock_args()
+ data['unix_permissions'] = '---rwxrwxrwx'
+ set_module_args(data)
+ current = {
+ 'unix_permissions': '0'
+ }
+ get_volume.side_effect = [
+ current
+ ]
+ obj = self.get_volume_mock_object()
+ assert not obj.compare_chmod_value(current)
+
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_volume.NetAppOntapVolume.get_volume')
+ def test_compare_chmod_value_false_2(self, get_volume):
+ data = self.mock_args()
+ data['unix_permissions'] = '---rwxrwxrwx'
+ set_module_args(data)
+ current = None
+ get_volume.side_effect = [
+ current
+ ]
+ obj = self.get_volume_mock_object()
+ assert not obj.compare_chmod_value(current)
+
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_volume.NetAppOntapVolume.get_volume')
+ def test_compare_chmod_value_invalid_input_1(self, get_volume):
+ data = self.mock_args()
+ data['unix_permissions'] = '---xwrxwrxwr'
+ set_module_args(data)
+ current = {
+ 'unix_permissions': '777'
+ }
+ get_volume.side_effect = [
+ current
+ ]
+ obj = self.get_volume_mock_object()
+ assert not obj.compare_chmod_value(current)
+
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_volume.NetAppOntapVolume.get_volume')
+ def test_compare_chmod_value_invalid_input_2(self, get_volume):
+ data = self.mock_args()
+ data['unix_permissions'] = '---rwx-wx--a'
+ set_module_args(data)
+ current = {
+ 'unix_permissions': '0'
+ }
+ get_volume.side_effect = [
+ current
+ ]
+ obj = self.get_volume_mock_object()
+ assert not obj.compare_chmod_value(current)
+
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_volume.NetAppOntapVolume.get_volume')
+ def test_compare_chmod_value_invalid_input_3(self, get_volume):
+ data = self.mock_args()
+ data['unix_permissions'] = '---'
+ set_module_args(data)
+ current = {
+ 'unix_permissions': '0'
+ }
+ get_volume.side_effect = [
+ current
+ ]
+ obj = self.get_volume_mock_object()
+ assert not obj.compare_chmod_value(current)
+
+ def test_successful_create_flex_group_manually(self):
+ ''' Test successful create flexGroup manually '''
+ data = self.mock_args('flexGroup_manual')
+ data['time_out'] = 20
+ set_module_args(data)
+ obj = self.get_volume_mock_object('job_info')
+ with pytest.raises(AnsibleExitJson) as exc:
+ obj.apply()
+ assert exc.value.args[0]['changed']
+
+ def test_successful_create_flex_group_auto_provision(self):
+ ''' Test successful create flexGroup auto provision '''
+ data = self.mock_args('flexGroup_auto')
+ data['time_out'] = 20
+ set_module_args(data)
+ obj = self.get_volume_mock_object('job_info')
+ with pytest.raises(AnsibleExitJson) as exc:
+ obj.apply()
+ assert exc.value.args[0]['changed']
+
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_volume.NetAppOntapVolume.get_volume')
+ def test_successful_delete_flex_group(self, get_volume):
+ ''' Test successful delete flexGroup '''
+ data = self.mock_args('flexGroup_manual')
+ data['state'] = 'absent'
+ set_module_args(data)
+ current = {
+ 'hostname': 'test',
+ 'username': 'test_user',
+ 'password': 'test_pass!',
+ 'name': self.mock_vol['name'],
+ 'vserver': self.mock_vol['vserver'],
+ 'style_extended': 'flexgroup',
+ 'unix_permissions': '755',
+ 'is_online': True
+ }
+ get_volume.side_effect = [
+ current
+ ]
+ obj = self.get_volume_mock_object('job_info')
+ with pytest.raises(AnsibleExitJson) as exc:
+ obj.apply()
+ assert exc.value.args[0]['changed']
+
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_volume.NetAppOntapVolume.get_volume')
+ def test_successful_resize_flex_group(self, get_volume):
+ ''' Test successful reszie flexGroup '''
+ data = self.mock_args('flexGroup_manual')
+ data['size'] = 400
+ data['size_unit'] = 'mb'
+ set_module_args(data)
+ current = {
+ 'hostname': 'test',
+ 'username': 'test_user',
+ 'password': 'test_pass!',
+ 'name': self.mock_vol['name'],
+ 'vserver': self.mock_vol['vserver'],
+ 'style_extended': 'flexgroup',
+ 'size': 20971520,
+ 'unix_permissions': '755',
+ 'uuid': '1234'
+ }
+ get_volume.side_effect = [
+ current
+ ]
+ obj = self.get_volume_mock_object('job_info')
+ with pytest.raises(AnsibleExitJson) as exc:
+ obj.apply()
+ assert exc.value.args[0]['changed']
+
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_volume.NetAppOntapVolume.check_job_status')
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_volume.NetAppOntapVolume.get_volume')
+ def test_successful_modify_unix_permissions_flex_group(self, get_volume, check_job_status):
+ ''' Test successful modify unix permissions flexGroup '''
+ data = self.mock_args('flexGroup_manual')
+ data['time_out'] = 20
+ data['unix_permissions'] = '---rw-r-xr-x'
+ set_module_args(data)
+ current = {
+ 'hostname': 'test',
+ 'username': 'test_user',
+ 'password': 'test_pass!',
+ 'name': self.mock_vol['name'],
+ 'vserver': self.mock_vol['vserver'],
+ 'style_extended': 'flexgroup',
+ 'unix_permissions': '777',
+ 'uuid': '1234'
+ }
+ get_volume.side_effect = [
+ current
+ ]
+ check_job_status.side_effect = [
+ None
+ ]
+ obj = self.get_volume_mock_object('success_modify_async')
+ with pytest.raises(AnsibleExitJson) as exc:
+ obj.apply()
+ print(exc)
+ assert exc.value.args[0]['changed']
+
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_volume.NetAppOntapVolume.get_volume')
+ def test_successful_modify_unix_permissions_flex_group_0_time_out(self, get_volume):
+ ''' Test successful modify unix permissions flexGroup '''
+ data = self.mock_args('flexGroup_manual')
+ data['time_out'] = 0
+ data['unix_permissions'] = '---rw-r-xr-x'
+ set_module_args(data)
+ current = {
+ 'hostname': 'test',
+ 'username': 'test_user',
+ 'password': 'test_pass!',
+ 'name': self.mock_vol['name'],
+ 'vserver': self.mock_vol['vserver'],
+ 'style_extended': 'flexgroup',
+ 'unix_permissions': '777',
+ 'uuid': '1234'
+ }
+ get_volume.side_effect = [
+ current
+ ]
+ obj = self.get_volume_mock_object('success_modify_async')
+ with pytest.raises(AnsibleExitJson) as exc:
+ obj.apply()
+ assert exc.value.args[0]['changed']
+
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_volume.NetAppOntapVolume.get_volume')
+ def test_successful_modify_unix_permissions_flex_group_0_missing_result(self, get_volume):
+ ''' Test successful modify unix permissions flexGroup '''
+ data = self.mock_args('flexGroup_manual')
+ data['time_out'] = 0
+ data['unix_permissions'] = '---rw-r-xr-x'
+ set_module_args(data)
+ current = {
+ 'hostname': 'test',
+ 'username': 'test_user',
+ 'password': 'test_pass!',
+ 'name': self.mock_vol['name'],
+ 'vserver': self.mock_vol['vserver'],
+ 'style_extended': 'flexgroup',
+ 'unix_permissions': '777',
+ 'uuid': '1234'
+ }
+ get_volume.side_effect = [
+ current
+ ]
+ obj = self.get_volume_mock_object('missing_element_modify_async')
+ with pytest.raises(AnsibleFailJson) as exc:
+ obj.apply()
+ msg = "Unexpected error when modifying volume: result is:"
+ assert exc.value.args[0]['msg'].startswith(msg)
+
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_volume.NetAppOntapVolume.check_job_status')
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_volume.NetAppOntapVolume.get_volume')
+ def test_error_modify_unix_permissions_flex_group(self, get_volume, check_job_status):
+ ''' Test error modify unix permissions flexGroup '''
+ data = self.mock_args('flexGroup_manual')
+ data['time_out'] = 20
+ data['unix_permissions'] = '---rw-r-xr-x'
+ set_module_args(data)
+ current = {
+ 'hostname': 'test',
+ 'username': 'test_user',
+ 'password': 'test_pass!',
+ 'name': self.mock_vol['name'],
+ 'vserver': self.mock_vol['vserver'],
+ 'style_extended': 'flexgroup',
+ 'unix_permissions': '777',
+ 'uuid': '1234'
+ }
+ get_volume.side_effect = [
+ current
+ ]
+ check_job_status.side_effect = ['error']
+ obj = self.get_volume_mock_object('success_modify_async')
+ with pytest.raises(AnsibleFailJson) as exc:
+ obj.apply()
+ assert exc.value.args[0]['msg'] == 'Error when modify volume: error'
+
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_volume.NetAppOntapVolume.get_volume')
+ def test_failure_modify_unix_permissions_flex_group(self, get_volume):
+ ''' Test failure modify unix permissions flexGroup '''
+ data = self.mock_args('flexGroup_manual')
+ data['unix_permissions'] = '---rw-r-xr-x'
+ data['time_out'] = 20
+ set_module_args(data)
+ current = {
+ 'hostname': 'test',
+ 'username': 'test_user',
+ 'password': 'test_pass!',
+ 'name': self.mock_vol['name'],
+ 'vserver': self.mock_vol['vserver'],
+ 'style_extended': 'flexvol',
+ 'unix_permissions': '777',
+ 'uuid': '1234'
+ }
+ get_volume.side_effect = [
+ current
+ ]
+ obj = self.get_volume_mock_object('failure_modify_async')
+ with pytest.raises(AnsibleFailJson) as exc:
+ obj.apply()
+ assert exc.value.args[0]['msg'] == 'Error modifying volume test_vol: modify error message'
+
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_volume.NetAppOntapVolume.get_volume')
+ def test_successful_offline_state_flex_group(self, get_volume):
+ ''' Test successful offline flexGroup state '''
+ data = self.mock_args('flexGroup_manual')
+ data['is_online'] = False
+ set_module_args(data)
+ current = {
+ 'hostname': 'test',
+ 'username': 'test_user',
+ 'password': 'test_pass!',
+ 'name': self.mock_vol['name'],
+ 'vserver': self.mock_vol['vserver'],
+ 'style_extended': 'flexgroup',
+ 'is_online': True,
+ 'junction_path': 'anything',
+ 'unix_permissions': '755',
+ 'uuid': '1234'
+ }
+ get_volume.side_effect = [
+ current
+ ]
+ obj = self.get_volume_mock_object('job_info')
+ with pytest.raises(AnsibleExitJson) as exc:
+ obj.apply()
+ assert exc.value.args[0]['changed']
+
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_volume.NetAppOntapVolume.get_volume')
+ def test_successful_online_state_flex_group(self, get_volume):
+ ''' Test successful online flexGroup state '''
+ data = self.mock_args('flexGroup_manual')
+ set_module_args(data)
+ current = {
+ 'hostname': 'test',
+ 'username': 'test_user',
+ 'password': 'test_pass!',
+ 'name': self.mock_vol['name'],
+ 'vserver': self.mock_vol['vserver'],
+ 'style_extended': 'flexgroup',
+ 'is_online': False,
+ 'junction_path': 'anything',
+ 'unix_permissions': '755',
+ 'uuid': '1234'
+ }
+ get_volume.side_effect = [
+ current
+ ]
+ online = 'job_info' # not correct, but works
+ job = 'job_info'
+ success = 'success_modify_async'
+ mount = 'job_info' # not correct, but works
+ kind = [online, job, job, success, mount, job, job]
+ obj = self.get_volume_mock_object(kind)
+ with pytest.raises(AnsibleExitJson) as exc:
+ obj.apply()
+ assert exc.value.args[0]['changed']
+
+ def test_check_job_status_error(self):
+ ''' Test check job status error '''
+ data = self.mock_args('flexGroup_manual')
+ data['time_out'] = 0
+ set_module_args(data)
+ obj = self.get_volume_mock_object('job_info', job_error='failure')
+ result = obj.check_job_status('123')
+ assert result == 'failure'
+
+ def test_check_job_status_time_out_is_0(self):
+ ''' Test check job status time out is 0'''
+ data = self.mock_args('flexGroup_manual')
+ data['time_out'] = 0
+ set_module_args(data)
+ obj = self.get_volume_mock_object('job_info', job_error='time_out')
+ result = obj.check_job_status('123')
+ assert result == 'job completion exceeded expected timer of: 0 seconds'
+
+ def test_check_job_status_unexpected(self):
+ ''' Test check job status unexpected state '''
+ data = self.mock_args('flexGroup_manual')
+ data['time_out'] = 20
+ set_module_args(data)
+ obj = self.get_volume_mock_object('job_info', job_error='other')
+ with pytest.raises(AnsibleFailJson) as exc:
+ obj.check_job_status('123')
+ assert exc.value.args[0]['failed']
+
+ def test_error_set_efficiency_policy(self):
+ data = self.mock_args()
+ data['efficiency_policy'] = 'test_policy'
+ set_module_args(data)
+ obj = self.get_volume_mock_object('zapi_error')
+ with pytest.raises(AnsibleFailJson) as exc:
+ obj.set_efficiency_config()
+ assert exc.value.args[0]['msg'] == 'Error enable efficiency on volume test_vol: NetApp API failed. Reason - test:error'
+
+ def test_error_set_efficiency_policy_async(self):
+ data = self.mock_args()
+ data['efficiency_policy'] = 'test_policy'
+ set_module_args(data)
+ obj = self.get_volume_mock_object('zapi_error')
+ with pytest.raises(AnsibleFailJson) as exc:
+ obj.set_efficiency_config_async()
+ assert exc.value.args[0]['msg'] == 'Error enable efficiency on volume test_vol: NetApp API failed. Reason - test:error'
+
+ def test_successful_modify_tiering_policy(self):
+ ''' Test successful modify tiering policy '''
+ data = self.mock_args()
+ data['tiering_policy'] = 'auto'
+ set_module_args(data)
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_volume_mock_object('volume').apply()
+ assert exc.value.args[0]['changed']
+
+ def test_successful_modify_vserver_dr_protection(self):
+ ''' Test successful modify vserver_dr_protection '''
+ data = self.mock_args()
+ data['vserver_dr_protection'] = 'protected'
+ set_module_args(data)
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_volume_mock_object('volume').apply()
+ assert exc.value.args[0]['changed']
+
+ def test_successful_group_id(self):
+ ''' Test successful modify group_id '''
+ data = self.mock_args()
+ data['group_id'] = 1001
+ set_module_args(data)
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_volume_mock_object('volume').apply()
+ assert exc.value.args[0]['changed']
+
+ def test_successful_user_id(self):
+ ''' Test successful modify user_id '''
+ data = self.mock_args()
+ data['user_id'] = 101
+ set_module_args(data)
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_volume_mock_object('volume').apply()
+ assert exc.value.args[0]['changed']
+
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_volume.NetAppOntapVolume.get_volume')
+ def test_successful_modify_snapshot_auto_delete(self, get_volume):
+ ''' Test successful modify unix permissions flexGroup '''
+ data = {
+ 'snapshot_auto_delete': {'delete_order': 'oldest_first', 'destroy_list': 'lun_clone,vol_clone',
+ 'target_free_space': 20, 'prefix': 'test', 'commitment': 'try',
+ 'state': 'on', 'trigger': 'snap_reserve', 'defer_delete': 'scheduled'},
+ 'hostname': 'test',
+ 'username': 'test_user',
+ 'password': 'test_pass!',
+ 'name': 'test_vol',
+ 'vserver': 'test_vserver',
+
+ }
+ set_module_args(data)
+ current = {
+ 'name': self.mock_vol['name'],
+ 'vserver': self.mock_vol['vserver'],
+ 'snapshot_auto_delete': {'delete_order': 'newest_first', 'destroy_list': 'lun_clone,vol_clone',
+ 'target_free_space': 30, 'prefix': 'test', 'commitment': 'try',
+ 'state': 'on', 'trigger': 'snap_reserve', 'defer_delete': 'scheduled'},
+ 'uuid': '1234'
+ }
+ get_volume.side_effect = [
+ current
+ ]
+ obj = self.get_volume_mock_object('volume')
+ with pytest.raises(AnsibleExitJson) as exc:
+ obj.apply()
+ assert exc.value.args[0]['changed']
+
+ def test_error_modify_snapshot_auto_delete(self):
+ data = {
+ 'snapshot_auto_delete': {'delete_order': 'oldest_first', 'destroy_list': 'lun_clone,vol_clone',
+ 'target_free_space': 20, 'prefix': 'test', 'commitment': 'try',
+ 'state': 'on', 'trigger': 'snap_reserve', 'defer_delete': 'scheduled'},
+ 'hostname': 'test',
+ 'username': 'test_user',
+ 'password': 'test_pass!',
+ 'name': 'test_vol',
+ 'vserver': 'test_vserver',
+
+ }
+ set_module_args(data)
+ with pytest.raises(AnsibleFailJson) as exc:
+ self.get_volume_mock_object('zapi_error').set_snapshot_auto_delete()
+ assert exc.value.args[0]['msg'] == 'Error setting snapshot auto delete options for volume test_vol: NetApp API failed. Reason - test:error'
+
+ def test_successful_volume_rehost(self):
+ data = {
+ 'hostname': 'test',
+ 'username': 'test_user',
+ 'password': 'test_pass!',
+ 'name': 'test_vol',
+ 'vserver': 'dest_vserver',
+ 'from_vserver': 'source_vserver'
+ }
+ set_module_args(data)
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_volume_mock_object('volume').apply()
+ assert exc.value.args[0]['changed']
+
+ def test_error_volume_rehost(self):
+ data = {
+ 'hostname': 'test',
+ 'username': 'test_user',
+ 'password': 'test_pass!',
+ 'name': 'test_vol',
+ 'vserver': 'dest_vserver',
+ 'from_vserver': 'source_vserver'
+ }
+ set_module_args(data)
+ with pytest.raises(AnsibleFailJson) as exc:
+ self.get_volume_mock_object('zapi_error').rehost_volume()
+ assert exc.value.args[0]['msg'] == 'Error rehosting volume test_vol: NetApp API failed. Reason - test:error'
+
+ def test_successful_volume_restore(self):
+ data = {
+ 'hostname': 'test',
+ 'username': 'test_user',
+ 'password': 'test_pass!',
+ 'name': 'test_vol',
+ 'vserver': 'test_vserver',
+ 'snapshot_restore': 'snapshot_copy'
+ }
+ set_module_args(data)
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_volume_mock_object('volume').apply()
+ assert exc.value.args[0]['changed']
+
+ def test_error_volume_restore(self):
+ data = {
+ 'hostname': 'test',
+ 'username': 'test_user',
+ 'password': 'test_pass!',
+ 'name': 'test_vol',
+ 'vserver': 'test_vserver',
+ 'snapshot_restore': 'snapshot_copy'
+ }
+ set_module_args(data)
+ with pytest.raises(AnsibleFailJson) as exc:
+ self.get_volume_mock_object('zapi_error').snapshot_restore_volume()
+ assert exc.value.args[0]['msg'] == 'Error restoring volume test_vol: NetApp API failed. Reason - test:error'
diff --git a/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_volume_autosize.py b/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_volume_autosize.py
new file mode 100644
index 00000000..653fddc5
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_volume_autosize.py
@@ -0,0 +1,243 @@
+# (c) 2019, NetApp, Inc
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+''' unit tests for Ansible module: na_ontap_volume_autosize '''
+
+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.ontap.tests.unit.compat import unittest
+from ansible_collections.netapp.ontap.tests.unit.compat.mock import patch
+import ansible_collections.netapp.ontap.plugins.module_utils.netapp as netapp_utils
+
+from ansible_collections.netapp.ontap.plugins.modules.na_ontap_volume_autosize \
+ import NetAppOntapVolumeAutosize as autosize_module # module under test
+
+if not netapp_utils.has_netapp_lib():
+ pytestmark = pytest.mark.skip('skipping as missing required netapp_lib')
+
+
+# REST API canned responses when mocking send_request
+SRR = {
+ # common responses
+ 'is_rest': (200, {}, None),
+ 'is_zapi': (400, {}, "Unreachable"),
+ 'empty_good': (200, {}, None),
+ 'end_of_sequence': (500, None, "Unexpected call to send_request"),
+ 'generic_error': (400, None, "Expected error"),
+ # module specific responses
+ 'get_uuid': (200, {'records': [{'uuid': 'testuuid'}]}, None),
+ 'get_autosize': (200,
+ {'uuid': 'testuuid',
+ 'name': 'testname',
+ 'autosize': {"maximum": 10737418240,
+ "minimum": 22020096,
+ "grow_threshold": 99,
+ "shrink_threshold": 40,
+ "mode": "grow"
+ }
+ }, 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"""
+
+
+class AnsibleFailJson(Exception):
+ """Exception class to be raised by module.fail_json and caught by the test case"""
+
+
+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 MockONTAPConnection(object):
+ ''' mock server connection to ONTAP host '''
+
+ def __init__(self, kind=None, data=None):
+ ''' save arguments '''
+ self.kind = kind
+ self.params = data
+ self.xml_in = None
+ self.xml_out = None
+
+ def invoke_successfully(self, xml, enable_tunneling): # pylint: disable=unused-argument
+ ''' mock invoke_successfully returning xml data '''
+ self.xml_in = xml
+ if self.kind == 'autosize':
+ xml = self.build_autosize_info(self.params)
+ self.xml_out = xml
+ return xml
+
+ @staticmethod
+ def build_autosize_info(autosize_details):
+ xml = netapp_utils.zapi.NaElement('xml')
+ attributes = {
+ 'grow-threshold-percent': autosize_details['grow_threshold_percent'],
+ 'maximum-size': '10485760',
+ 'minimum-size': '21504',
+ 'increment_size': '10240',
+ 'mode': autosize_details['mode'],
+ 'shrink-threshold-percent': autosize_details['shrink_threshold_percent']
+ }
+ xml.translate_struct(attributes)
+ return xml
+
+
+class TestMyModule(unittest.TestCase):
+ ''' Unit tests for na_ontap_job_schedule '''
+
+ 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)
+ self.mock_autosize = {
+ 'grow_threshold_percent': 99,
+ 'maximum_size': '10g',
+ 'minimum_size': '21m',
+ 'increment_size': '10m',
+ 'mode': 'grow',
+ 'shrink_threshold_percent': 40,
+ 'vserver': 'test_vserver',
+ 'volume': 'test_volume'
+ }
+
+ def mock_args(self, rest=False):
+ if rest:
+ return {
+ 'vserver': self.mock_autosize['vserver'],
+ 'volume': self.mock_autosize['volume'],
+ 'grow_threshold_percent': self.mock_autosize['grow_threshold_percent'],
+ 'maximum_size': self.mock_autosize['maximum_size'],
+ 'minimum_size': self.mock_autosize['minimum_size'],
+ 'mode': self.mock_autosize['mode'],
+ 'shrink_threshold_percent': self.mock_autosize['shrink_threshold_percent'],
+ 'hostname': 'test',
+ 'username': 'test_user',
+ 'password': 'test_pass!'
+ }
+ else:
+ return {
+ 'vserver': self.mock_autosize['vserver'],
+ 'volume': self.mock_autosize['volume'],
+ 'grow_threshold_percent': self.mock_autosize['grow_threshold_percent'],
+ 'maximum_size': self.mock_autosize['maximum_size'],
+ 'minimum_size': self.mock_autosize['minimum_size'],
+ 'increment_size': self.mock_autosize['increment_size'],
+ 'mode': self.mock_autosize['mode'],
+ 'shrink_threshold_percent': self.mock_autosize['shrink_threshold_percent'],
+ 'hostname': 'test',
+ 'username': 'test_user',
+ 'password': 'test_pass!',
+ 'use_rest': 'never'
+ }
+
+ def get_autosize_mock_object(self, cx_type='zapi', kind=None):
+ autosize_obj = autosize_module()
+ if cx_type == 'zapi':
+ if kind is None:
+ autosize_obj.server = MockONTAPConnection()
+ elif kind == 'autosize':
+ autosize_obj.server = MockONTAPConnection(kind='autosize', data=self.mock_autosize)
+ return autosize_obj
+
+ def test_module_fail_when_required_args_missing(self):
+ ''' required arguments are reported as errors '''
+ with pytest.raises(AnsibleFailJson) as exc:
+ set_module_args({})
+ autosize_module()
+ print('Info: %s' % exc.value.args[0]['msg'])
+
+ def test_idempotent_modify(self):
+ set_module_args(self.mock_args())
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_autosize_mock_object('zapi', 'autosize').apply()
+ assert not exc.value.args[0]['changed']
+
+ def test_successful_modify(self):
+ data = self.mock_args()
+ data['maximum_size'] = '11g'
+ set_module_args(data)
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_autosize_mock_object('zapi', 'autosize').apply()
+ assert exc.value.args[0]['changed']
+
+ def test_successful_reset(self):
+ data = {}
+ data['reset'] = True
+ data['hostname'] = 'test'
+ data['username'] = 'test_user'
+ data['password'] = 'test_pass!'
+ data['volume'] = 'test_vol'
+ data['vserver'] = 'test_vserver'
+ data['use_rest'] = 'never'
+ set_module_args(data)
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_autosize_mock_object('zapi', 'autosize').apply()
+ assert exc.value.args[0]['changed']
+
+ @patch('ansible_collections.netapp.ontap.plugins.module_utils.netapp.OntapRestAPI.send_request')
+ def test_rest_error(self, mock_request):
+ data = self.mock_args(rest=True)
+ set_module_args(data)
+ mock_request.side_effect = [
+ SRR['is_rest'],
+ SRR['generic_error'],
+ SRR['end_of_sequence']
+ ]
+ with pytest.raises(AnsibleFailJson) as exc:
+ self.get_autosize_mock_object(cx_type='rest').apply()
+ assert exc.value.args[0]['msg'] == SRR['generic_error'][2]
+
+ @patch('ansible_collections.netapp.ontap.plugins.module_utils.netapp.OntapRestAPI.send_request')
+ def test_rest_successful_modify(self, mock_request):
+ data = self.mock_args(rest=True)
+ data['maximum_size'] = '11g'
+ set_module_args(data)
+ mock_request.side_effect = [
+ SRR['is_rest'],
+ SRR['get_uuid'],
+ SRR['get_autosize'],
+ SRR['empty_good'],
+ SRR['end_of_sequence']
+ ]
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_autosize_mock_object(cx_type='rest').apply()
+ assert exc.value.args[0]['changed']
+
+ @patch('ansible_collections.netapp.ontap.plugins.module_utils.netapp.OntapRestAPI.send_request')
+ def test_rest_idempotent_modify(self, mock_request):
+ data = self.mock_args(rest=True)
+ set_module_args(data)
+ mock_request.side_effect = [
+ SRR['is_rest'],
+ SRR['get_uuid'],
+ SRR['get_autosize'],
+ SRR['empty_good'],
+ SRR['end_of_sequence']
+ ]
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_autosize_mock_object(cx_type='rest').apply()
+ assert not exc.value.args[0]['changed']
diff --git a/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_volume_clone.py b/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_volume_clone.py
new file mode 100644
index 00000000..eb78e3fa
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_volume_clone.py
@@ -0,0 +1,257 @@
+# (c) 2018, NetApp, Inc
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+''' unit tests ONTAP Ansible module: na_ontap_volume_clone'''
+
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+import json
+import pytest
+
+from ansible_collections.netapp.ontap.tests.unit.compat import unittest
+from ansible_collections.netapp.ontap.tests.unit.compat.mock import patch
+from ansible.module_utils import basic
+from ansible.module_utils._text import to_bytes
+import ansible_collections.netapp.ontap.plugins.module_utils.netapp as netapp_utils
+
+from ansible_collections.netapp.ontap.plugins.modules.na_ontap_volume_clone \
+ import NetAppONTAPVolumeClone as my_module
+
+if not netapp_utils.has_netapp_lib():
+ pytestmark = pytest.mark.skip('skipping as missing required netapp_lib')
+
+
+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 MockONTAPConnection(object):
+ ''' mock server connection to ONTAP host '''
+
+ def __init__(self, kind=None):
+ ''' save arguments '''
+ self.type = kind
+ self.xml_in = None
+ self.xml_out = None
+
+ def invoke_successfully(self, xml, enable_tunneling): # pylint: disable=unused-argument
+ ''' mock invoke_successfully returning xml data '''
+ self.xml_in = xml
+ if self.type == 'volume_clone':
+ xml = self.build_volume_clone_info()
+ elif self.type == 'volume_clone_split_in_progress':
+ xml = self.build_volume_clone_info_split_in_progress()
+ elif self.type == 'volume_clone_fail':
+ raise netapp_utils.zapi.NaApiError(code='TEST', message="This exception is from the unit test")
+ self.xml_out = xml
+ return xml
+
+ @staticmethod
+ def build_volume_clone_info():
+ ''' build xml data for volume-clone-info '''
+ xml = netapp_utils.zapi.NaElement('xml')
+ data = {'attributes': {'volume-clone-info': {'volume': 'ansible',
+ 'parent-volume': 'ansible'}}}
+ xml.translate_struct(data)
+ return xml
+
+ @staticmethod
+ def build_volume_clone_info_split_in_progress():
+ ''' build xml data for volume-clone-info whilst split in progress '''
+ xml = netapp_utils.zapi.NaElement('xml')
+ data = {'attributes': {'volume-clone-info': {'volume': 'ansible',
+ 'parent-volume': 'ansible',
+ 'block-percentage-complete': 20,
+ 'blocks-scanned': 56676,
+ 'blocks-updated': 54588}}}
+ xml.translate_struct(data)
+ return xml
+
+
+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)
+ self.vserver = MockONTAPConnection()
+ self.onbox = False
+
+ def set_default_args(self):
+ if self.onbox:
+ hostname = '10.10.10.10'
+ username = 'username'
+ password = 'password'
+ vserver = 'ansible'
+ volume = 'ansible'
+ parent_volume = 'ansible'
+ split = None
+ else:
+ hostname = '10.10.10.10'
+ username = 'username'
+ password = 'password'
+ vserver = 'ansible'
+ volume = 'ansible'
+ parent_volume = 'ansible'
+ split = None
+ return dict({
+ 'hostname': hostname,
+ 'username': username,
+ 'password': password,
+ 'vserver': vserver,
+ 'volume': volume,
+ 'parent_volume': parent_volume,
+ 'split': split
+ })
+
+ def set_default_current(self):
+ return dict({
+ 'split': False
+ })
+
+ def test_module_fail_when_required_args_missing(self):
+ ''' test required arguments are reported as errors '''
+ with pytest.raises(AnsibleFailJson) as exc:
+ set_module_args({})
+ my_module()
+ print('Info: %s' % exc.value.args[0]['msg'])
+
+ def test_ensure_get_called(self):
+ ''' test get_volume_clone() for non-existent volume clone'''
+ set_module_args(self.set_default_args())
+ my_obj = my_module()
+ my_obj.vserver = self.vserver
+ assert my_obj.get_volume_clone() is None
+
+ def test_ensure_get_called_existing(self):
+ ''' test get_volume_clone() for existing volume clone'''
+ set_module_args(self.set_default_args())
+ my_obj = my_module()
+ my_obj.vserver = MockONTAPConnection(kind='volume_clone')
+ current = self.set_default_current()
+ assert my_obj.get_volume_clone() == current
+
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_volume_clone.NetAppONTAPVolumeClone.create_volume_clone')
+ def test_successful_create(self, create_volume_clone):
+ ''' test creating volume_clone without split and testing idempotency '''
+ module_args = {
+ 'parent_vserver': 'abc',
+ 'parent_snapshot': 'abc',
+ 'volume_type': 'dp',
+ 'qos_policy_group_name': 'abc',
+ 'junction_path': 'abc',
+ 'uid': '1',
+ 'gid': '1'
+ }
+ module_args.update(self.set_default_args())
+ set_module_args(module_args)
+ my_obj = my_module()
+ if not self.onbox:
+ my_obj.vserver = self.vserver
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ assert exc.value.args[0]['changed']
+ create_volume_clone.assert_called_with()
+ # to reset na_helper from remembering the previous 'changed' value
+ my_obj = my_module()
+ if not self.onbox:
+ my_obj.vserver = MockONTAPConnection('volume_clone')
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ assert not exc.value.args[0]['changed']
+
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_volume_clone.NetAppONTAPVolumeClone.create_volume_clone')
+ def test_successful_create_with_split(self, create_volume_clone):
+ ''' test creating volume_clone with split and testing idempotency '''
+ module_args = {
+ 'parent_snapshot': 'abc',
+ 'parent_vserver': 'abc',
+ 'volume_type': 'dp',
+ 'qos_policy_group_name': 'abc',
+ 'junction_path': 'abc',
+ 'uid': '1',
+ 'gid': '1'
+ }
+ module_args.update(self.set_default_args())
+ module_args['split'] = True
+ set_module_args(module_args)
+ my_obj = my_module()
+ if not self.onbox:
+ my_obj.vserver = self.vserver
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ assert exc.value.args[0]['changed']
+ create_volume_clone.assert_called_with()
+ # to reset na_helper from remembering the previous 'changed' value
+ my_obj = my_module()
+ if not self.onbox:
+ my_obj.vserver = MockONTAPConnection('volume_clone_split_in_progress')
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ assert not exc.value.args[0]['changed']
+
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_volume_clone.NetAppONTAPVolumeClone.create_volume_clone')
+ def test_successful_create_with_split_in_progress(self, create_volume_clone):
+ ''' test creating volume_clone with split and split already in progress '''
+ module_args = {
+ 'parent_snapshot': 'abc',
+ 'parent_vserver': 'abc',
+ 'volume_type': 'dp',
+ 'qos_policy_group_name': 'abc',
+ 'junction_path': 'abc',
+ 'uid': '1',
+ 'gid': '1'
+ }
+ module_args.update(self.set_default_args())
+ module_args['split'] = True
+ set_module_args(module_args)
+ my_obj = my_module()
+ if not self.onbox:
+ my_obj.vserver = MockONTAPConnection('volume_clone_split_in_progress')
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_obj.apply()
+ assert not exc.value.args[0]['changed']
+
+ def test_if_all_methods_catch_exception(self):
+ ''' test if all methods catch exception '''
+ module_args = {}
+ module_args.update(self.set_default_args())
+ set_module_args(module_args)
+ my_obj = my_module()
+ if not self.onbox:
+ my_obj.vserver = MockONTAPConnection('volume_clone_fail')
+ my_obj.create_server = my_obj.vserver
+ with pytest.raises(AnsibleFailJson) as exc:
+ my_obj.get_volume_clone()
+ assert 'Error fetching volume clone information ' in exc.value.args[0]['msg']
+ with pytest.raises(AnsibleFailJson) as exc:
+ my_obj.create_volume_clone()
+ assert 'Error creating volume clone: ' in exc.value.args[0]['msg']
diff --git a/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_volume_rest.py b/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_volume_rest.py
new file mode 100644
index 00000000..fe5016e3
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_volume_rest.py
@@ -0,0 +1,333 @@
+# (c) 2020, NetApp, Inc
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+''' unit test template for ONTAP Ansible module '''
+
+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.ontap.tests.unit.compat import unittest
+from ansible_collections.netapp.ontap.tests.unit.compat.mock import patch, Mock
+import ansible_collections.netapp.ontap.plugins.module_utils.netapp as netapp_utils
+
+from ansible_collections.netapp.ontap.plugins.modules.na_ontap_volume \
+ import NetAppOntapVolume as volume_module # module under test
+
+# needed for get and modify/delete as they still use ZAPI
+if not netapp_utils.has_netapp_lib():
+ pytestmark = pytest.mark.skip('skipping as missing required netapp_lib')
+
+# REST API canned responses when mocking send_request
+SRR = {
+ # common responses
+ 'is_rest': (200, {}, None),
+ 'is_zapi': (400, {}, "Unreachable"),
+ 'empty_good': (200, {}, None),
+ 'end_of_sequence': (500, None, "Unexpected call to send_request"),
+ 'generic_error': (400, None, "Expected error"),
+ # module specific responses
+ 'svm_record': (200,
+ {'records': [{"uuid": "09e9fd5e-8ebd-11e9-b162-005056b39fe7",
+ "name": "test_svm",
+ "subtype": "default",
+ "language": "c.utf_8",
+ "aggregates": [{"name": "aggr_1",
+ "uuid": "850dd65b-8811-4611-ac8c-6f6240475ff9"},
+ {"name": "aggr_2",
+ "uuid": "850dd65b-8811-4611-ac8c-6f6240475ff9"}],
+ "comment": "new comment",
+ "ipspace": {"name": "ansible_ipspace",
+ "uuid": "2b760d31-8dfd-11e9-b162-005056b39fe7"},
+ "snapshot_policy": {"uuid": "3b611707-8dfd-11e9-b162-005056b39fe7",
+ "name": "old_snapshot_policy"},
+ "nfs": {"enabled": True},
+ "cifs": {"enabled": False},
+ "iscsi": {"enabled": False},
+ "fcp": {"enabled": False},
+ "nvme": {"enabled": 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"""
+
+
+class AnsibleFailJson(Exception):
+ """Exception class to be raised by module.fail_json and caught by the test case"""
+
+
+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 MockONTAPConnection(object):
+ ''' mock server connection to ONTAP host '''
+
+ def __init__(self, kind=None, data=None, get_volume=None):
+ ''' save arguments '''
+ self.type = kind
+ self.params = data
+ self.xml_in = None
+ self.xml_out = None
+ self.get_volume = get_volume
+ self.zapis = list()
+
+ def invoke_successfully(self, xml, enable_tunneling): # pylint: disable=unused-argument
+ ''' mock invoke_successfully returning xml data '''
+ self.xml_in = xml
+ zapi = xml.get_name()
+ self.zapis.append(zapi)
+ request = xml.to_string().decode('utf-8')
+ if self.type == 'error':
+ raise OSError('unexpected call to %s' % self.params)
+ print('request:', request)
+ if request.startswith('<volume-get-iter>'):
+ what = None
+ if self.get_volume:
+ what = self.get_volume.pop(0)
+ if what is None:
+ xml = self.build_empty_response()
+ else:
+ xml = self.build_get_response(what)
+ self.xml_out = xml
+ print('response:', xml.to_string())
+ return xml
+
+ @staticmethod
+ def build_response(data):
+ ''' build xml data for vserser-info '''
+ xml = netapp_utils.zapi.NaElement('xml')
+ xml.translate_struct(data)
+ return xml
+
+ def build_empty_response(self):
+ data = {'num-records': '0'}
+ return self.build_response(data)
+
+ def build_get_response(self, name):
+ ''' build xml data for vserser-info '''
+ if name is None:
+ return self.build_empty_response()
+ data = {'num-records': 1,
+ 'attributes-list': [{
+ 'volume-attributes': {
+ 'volume-id-attributes': {
+ 'name': name,
+ 'instance-uuid': '123'
+ },
+ 'volume-performance-attributes': {
+ 'is-atime-update-enabled': 'true'
+ },
+ 'volume-security-attributes': {
+ 'volume-security-unix-attributes': {
+ 'permissions': 777
+ }
+ },
+ 'volume-snapshot-attributes': {
+ 'snapshot-policy': 'default'
+ },
+ 'volume-snapshot-autodelete-attributes': {
+ 'is-autodelete-enabled': 'true'
+ },
+ 'volume-space-attributes': {
+ 'size': 10737418240 # 10 GB
+ },
+ 'volume-state-attributes': {
+ 'state': 'online'
+ },
+ }
+ }]}
+ return self.build_response(data)
+
+
+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)
+ # self.server = MockONTAPConnection()
+ self.mock_vserver = {
+ 'name': 'test_svm',
+ 'root_volume': 'ansible_vol',
+ 'root_volume_aggregate': 'ansible_aggr',
+ 'aggr_list': 'aggr_1,aggr_2',
+ 'ipspace': 'ansible_ipspace',
+ 'subtype': 'default',
+ 'language': 'c.utf_8',
+ 'snapshot_policy': 'old_snapshot_policy',
+ 'comment': 'new comment'
+ }
+
+ @staticmethod
+ def mock_args():
+ return {'name': 'test_volume',
+ 'vserver': 'ansibleSVM',
+ 'nas_application_template': dict(
+ tiering=None
+ ),
+ # 'aggregate_name': 'whatever', # not used for create when using REST application/applications
+ 'size': 10,
+ 'size_unit': 'gb',
+ 'hostname': 'test',
+ 'username': 'test_user',
+ 'password': 'test_pass!'}
+
+ def get_volume_mock_object(self, **kwargs):
+ volume_obj = volume_module()
+ netapp_utils.ems_log_event = Mock(return_value=None)
+ volume_obj.server = MockONTAPConnection(**kwargs)
+ volume_obj.cluster = MockONTAPConnection(kind='error', data='cluster ZAPI.')
+ return volume_obj
+
+ def test_module_fail_when_required_args_missing(self):
+ ''' required arguments are reported as errors '''
+ with pytest.raises(AnsibleFailJson) as exc:
+ set_module_args({})
+ volume_module()
+ print('Info: %s' % exc.value.args[0]['msg'])
+
+ @patch('ansible_collections.netapp.ontap.plugins.module_utils.netapp.OntapRestAPI.send_request')
+ def test_fail_if_aggr_is_set(self, mock_request):
+ data = dict(self.mock_args())
+ data['aggregate_name'] = 'should_fail'
+ set_module_args(data)
+ mock_request.side_effect = [
+ SRR['end_of_sequence']
+ ]
+ with pytest.raises(AnsibleFailJson) as exc:
+ self.get_volume_mock_object().apply()
+ error = 'Conflict: aggregate_name is not supported when application template is enabled. Found: aggregate_name: should_fail'
+ assert exc.value.args[0]['msg'] == error
+
+ @patch('ansible_collections.netapp.ontap.plugins.module_utils.netapp.OntapRestAPI.send_request')
+ def test_missing_size(self, mock_request):
+ data = dict(self.mock_args())
+ data.pop('size')
+ set_module_args(data)
+ mock_request.side_effect = [
+ SRR['end_of_sequence']
+ ]
+ with pytest.raises(AnsibleFailJson) as exc:
+ self.get_volume_mock_object().apply()
+ error = 'Error: "size" is required to create nas application.'
+ assert exc.value.args[0]['msg'] == error
+
+ @patch('ansible_collections.netapp.ontap.plugins.module_utils.netapp.OntapRestAPI.send_request')
+ def test_mismatched_tiering_policies(self, mock_request):
+ data = dict(self.mock_args())
+ data['tiering_policy'] = 'none'
+ data['nas_application_template'] = dict(
+ tiering=dict(policy='auto')
+ )
+ set_module_args(data)
+ mock_request.side_effect = [
+ SRR['end_of_sequence']
+ ]
+ with pytest.raises(AnsibleFailJson) as exc:
+ self.get_volume_mock_object().apply()
+ error = 'Conflict: if tiering_policy and nas_application_template tiering policy are both set, they must match.'
+ error += ' Found "none" and "auto".'
+ assert exc.value.args[0]['msg'] == error
+
+ @patch('ansible_collections.netapp.ontap.plugins.module_utils.netapp.OntapRestAPI.send_request')
+ def test_rest_error(self, mock_request):
+ data = dict(self.mock_args())
+ set_module_args(data)
+ mock_request.side_effect = [
+ SRR['generic_error'], # POST application/applications
+ SRR['end_of_sequence']
+ ]
+ with pytest.raises(AnsibleFailJson) as exc:
+ self.get_volume_mock_object().apply()
+ assert exc.value.args[0]['msg'] == 'Error: calling: /application/applications: got %s' % SRR['generic_error'][2]
+
+ @patch('ansible_collections.netapp.ontap.plugins.module_utils.netapp.OntapRestAPI.send_request')
+ def test_rest_successfully_created(self, mock_request):
+ data = dict(self.mock_args())
+ set_module_args(data)
+ mock_request.side_effect = [
+ SRR['empty_good'], # POST application/applications
+ SRR['end_of_sequence']
+ ]
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_volume_mock_object().apply()
+ assert exc.value.args[0]['changed']
+
+ @patch('ansible_collections.netapp.ontap.plugins.module_utils.netapp.OntapRestAPI.send_request')
+ def test_rest_create_idempotency(self, mock_request):
+ data = dict(self.mock_args())
+ set_module_args(data)
+ mock_request.side_effect = [
+ SRR['end_of_sequence']
+ ]
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_volume_mock_object(get_volume=['test']).apply()
+ assert not exc.value.args[0]['changed']
+
+ @patch('ansible_collections.netapp.ontap.plugins.module_utils.netapp.OntapRestAPI.send_request')
+ def test_rest_successfully_created_with_modify(self, mock_request):
+ ''' since language is not supported in application, the module is expected to:
+ 1. create the volume using application REST API
+ 2. immediately modify the volume to update options which not available in the nas template.
+ '''
+ data = dict(self.mock_args())
+ data['language'] = 'fr' # TODO: apparently language is not supported for modify
+ data['unix_permissions'] = '---rw-rx-r--'
+ set_module_args(data)
+ mock_request.side_effect = [
+ SRR['empty_good'], # POST application/applications
+ SRR['end_of_sequence']
+ ]
+ my_volume = self.get_volume_mock_object(get_volume=[None, 'test'])
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_volume.apply()
+ assert exc.value.args[0]['changed']
+ print(exc.value.args[0])
+ assert 'unix_permissions' in exc.value.args[0]['modify_after_create']
+ assert 'language' not in exc.value.args[0]['modify_after_create'] # eh!
+ assert 'volume-modify-iter' in my_volume.server.zapis
+
+ @patch('ansible_collections.netapp.ontap.plugins.module_utils.netapp.OntapRestAPI.send_request')
+ def test_rest_successfully_resized(self, mock_request):
+ ''' make sure resize if using RESP API if sizing_method is present
+ '''
+ data = dict(self.mock_args())
+ data['sizing_method'] = 'add_new_resources'
+ data['size'] = 20737418240
+ set_module_args(data)
+ mock_request.side_effect = [
+ SRR['empty_good'], # PATCH application/applications
+ SRR['end_of_sequence']
+ ]
+ my_volume = self.get_volume_mock_object(get_volume=['test'])
+ with pytest.raises(AnsibleExitJson) as exc:
+ my_volume.apply()
+ assert exc.value.args[0]['changed']
+ print(exc.value.args[0])
+ assert 'volume-size' not in my_volume.server.zapis
+ print(mock_request.call_args)
+ mock_request.assert_called_with('PATCH', '/storage/volumes/123', {'sizing_method': 'add_new_resources'}, json={'size': 22266633286068469760})
diff --git a/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_volume_snaplock.py b/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_volume_snaplock.py
new file mode 100644
index 00000000..45ec4b40
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_volume_snaplock.py
@@ -0,0 +1,166 @@
+# (c) 2020, NetApp, Inc
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+""" unit tests for Ansible module: na_ontap_volume_snaplock """
+
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+import json
+import pytest
+
+from ansible_collections.netapp.ontap.tests.unit.compat import unittest
+from ansible_collections.netapp.ontap.tests.unit.compat.mock import patch, Mock
+from ansible.module_utils import basic
+from ansible.module_utils._text import to_bytes
+import ansible_collections.netapp.ontap.plugins.module_utils.netapp as netapp_utils
+
+from ansible_collections.netapp.ontap.plugins.modules.na_ontap_volume_snaplock \
+ import NetAppOntapVolumeSnaplock as snaplock_module # module under test
+
+if not netapp_utils.has_netapp_lib():
+ pytestmark = pytest.mark.skip('skipping as missing required netapp_lib')
+
+
+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 MockONTAPConnection(object):
+ ''' mock server connection to ONTAP host '''
+
+ def __init__(self, kind=None, data=None):
+ ''' save arguments '''
+ self.type = kind
+ self.params = data
+ self.xml_in = None
+ self.xml_out = None
+
+ def invoke_successfully(self, xml, enable_tunneling): # pylint: disable=unused-argument
+ ''' mock invoke_successfully returning xml data '''
+ self.xml_in = xml
+ if self.type == 'snaplock':
+ xml = self.build_snaplock_info(self.params)
+ elif self.type == 'zapi_error':
+ error = netapp_utils.zapi.NaApiError('test', 'error')
+ raise error
+ self.xml_out = xml
+ return xml
+
+ @staticmethod
+ def build_snaplock_info(data):
+ ''' build xml data for vserser-info '''
+ xml = netapp_utils.zapi.NaElement('xml')
+ attributes = {'snaplock-attrs': {
+ 'snaplock-attrs-info': {
+ 'autocommit-period': data['autocommit_period'],
+ 'default-retention-period': data['default_retention_period'],
+ 'maximum-retention-period': data['maximum_retention_period'],
+ 'minimum-retention-period': data['minimum_retention_period'],
+ 'is-volume-append-mode-enabled': data['is_volume_append_mode_enabled']
+ }
+ }}
+ xml.translate_struct(attributes)
+ return xml
+
+
+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)
+ self.mock_snaplock = {
+ 'autocommit_period': '10days',
+ 'default_retention_period': '1years',
+ 'maximum_retention_period': '2years',
+ 'minimum_retention_period': '6months',
+ 'is_volume_append_mode_enabled': 'false'
+ }
+
+ def mock_args(self):
+ return {
+ 'name': 'test_volume',
+ 'autocommit_period': self.mock_snaplock['autocommit_period'],
+ 'default_retention_period': self.mock_snaplock['default_retention_period'],
+ 'maximum_retention_period': self.mock_snaplock['maximum_retention_period'],
+ 'minimum_retention_period': self.mock_snaplock['minimum_retention_period'],
+ 'hostname': 'hostname',
+ 'username': 'username',
+ 'password': 'password',
+ 'vserver': 'test_vserver'
+ }
+
+ def get_snaplock_mock_object(self, kind=None):
+ """
+ Helper method to return an na_ontap_volume_snaplock object
+ :param kind: passes this param to MockONTAPConnection()
+ :return: na_ontap_volume_snaplock object
+ """
+ snaplock_obj = snaplock_module()
+ netapp_utils.ems_log_event = Mock(return_value=None)
+ if kind is None:
+ snaplock_obj.server = MockONTAPConnection()
+ else:
+ snaplock_obj.server = MockONTAPConnection(kind=kind, data=self.mock_snaplock)
+ return snaplock_obj
+
+ def test_module_fail_when_required_args_missing(self):
+ ''' required arguments are reported as errors '''
+ with pytest.raises(AnsibleFailJson) as exc:
+ set_module_args({})
+ snaplock_module()
+ print('Info: %s' % exc.value.args[0]['msg'])
+
+ def test_get_existing_snaplock(self):
+ set_module_args(self.mock_args())
+ result = self.get_snaplock_mock_object(kind='snaplock').get_volume_snaplock_attrs()
+ assert result['autocommit_period'] == self.mock_snaplock['autocommit_period']
+ assert result['default_retention_period'] == self.mock_snaplock['default_retention_period']
+ assert result['is_volume_append_mode_enabled'] is False
+ assert result['maximum_retention_period'] == self.mock_snaplock['maximum_retention_period']
+
+ def test_modify_snaplock(self):
+ data = self.mock_args()
+ data['maximum_retention_period'] = '5years'
+ set_module_args(data)
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_snaplock_mock_object('snaplock').apply()
+ assert exc.value.args[0]['changed']
+
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_volume_snaplock.NetAppOntapVolumeSnaplock.get_volume_snaplock_attrs')
+ def test_modify_snaplock_error(self, get_volume_snaplock_attrs):
+ data = self.mock_args()
+ data['maximum_retention_period'] = '5years'
+ set_module_args(data)
+ get_volume_snaplock_attrs.side_effect = [self.mock_snaplock]
+ with pytest.raises(AnsibleFailJson) as exc:
+ self.get_snaplock_mock_object('zapi_error').apply()
+ assert exc.value.args[0]['msg'] == 'Error setting snaplock attributes for volume test_volume : NetApp API failed. Reason - test:error'
diff --git a/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_vscan.py b/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_vscan.py
new file mode 100644
index 00000000..6ea6893c
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_vscan.py
@@ -0,0 +1,234 @@
+# (c) 2018, NetApp, Inc
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+''' unit tests for Ansible module: na_ontap_vscan'''
+
+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.ontap.tests.unit.compat import unittest
+from ansible_collections.netapp.ontap.tests.unit.compat.mock import patch
+import ansible_collections.netapp.ontap.plugins.module_utils.netapp as netapp_utils
+
+from ansible_collections.netapp.ontap.plugins.modules.na_ontap_vscan \
+ import NetAppOntapVscan as vscan_module # module under test
+
+if not netapp_utils.has_netapp_lib():
+ pytestmark = pytest.mark.skip('skipping as missing required netapp_lib')
+HAS_NETAPP_ZAPI_MSG = "pip install netapp_lib is required"
+
+
+# REST API canned responses when mocking send_request
+SRR = {
+ # common responses
+ 'is_rest': (200, {}, None),
+ 'is_zapi': (400, {}, "Unreachable"),
+ 'empty_good': (200, {}, None),
+ 'end_of_sequence': (500, None, "Ooops, the UT needs one more SRR response"),
+ 'generic_error': (400, None, "Expected error"),
+ # module specific responses
+ 'enabled': (200, {'records': [{'enabled': True, 'svm': {'uuid': 'testuuid'}}]}, None),
+ 'disabled': (200, {'records': [{'enabled': False, 'svm': {'uuid': 'testuuid'}}]}, 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"""
+
+
+class AnsibleFailJson(Exception):
+ """Exception class to be raised by module.fail_json and caught by the test case"""
+
+
+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 MockONTAPConnection(object):
+ ''' mock server connection to ONTAP host '''
+
+ def __init__(self, kind=None, data=None):
+ ''' save arguments '''
+ self.kind = kind
+ self.params = data
+ self.xml_in = None
+ self.xml_out = None
+
+ def invoke_successfully(self, xml, enable_tunneling): # pylint: disable=unused-argument
+ ''' mock invoke_successfully returning xml data '''
+ self.xml_in = xml
+ if self.kind == 'enable':
+ xml = self.build_vscan_status_info(self.params)
+ self.xml_out = xml
+ return xml
+
+ @staticmethod
+ def build_vscan_status_info(status):
+ xml = netapp_utils.zapi.NaElement('xml')
+ attributes = {'num-records': 1,
+ 'attributes-list': {'vscan-status-info': {'is-vscan-enabled': status}}}
+ xml.translate_struct(attributes)
+ return xml
+
+
+class TestMyModule(unittest.TestCase):
+ ''' Unit tests for na_ontap_job_schedule '''
+
+ 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 mock_args(self):
+ return {
+ 'enable': False,
+ 'vserver': 'vserver',
+ 'hostname': 'test',
+ 'username': 'test_user',
+ 'password': 'test_pass!'
+ }
+
+ def get_vscan_mock_object(self, cx_type='zapi', kind=None, status=None):
+ vscan_obj = vscan_module()
+ if cx_type == 'zapi':
+ if kind is None:
+ vscan_obj.server = MockONTAPConnection()
+ else:
+ vscan_obj.server = MockONTAPConnection(kind=kind, data=status)
+ # For rest, mocking is achieved through side_effect
+ return vscan_obj
+
+ def test_module_fail_when_required_args_missing(self):
+ ''' required arguments are reported as errors '''
+ with pytest.raises(AnsibleFailJson) as exc:
+ set_module_args({})
+ vscan_module()
+ print('Info: %s' % exc.value.args[0]['msg'])
+
+ def test_successfully_enable(self):
+ data = self.mock_args()
+ data['enable'] = True
+ data['use_rest'] = 'never'
+ set_module_args(data)
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_vscan_mock_object('zapi', 'enable', 'false').apply()
+ assert exc.value.args[0]['changed']
+
+ def test_idempotently_enable(self):
+ data = self.mock_args()
+ data['enable'] = True
+ data['use_rest'] = 'never'
+ set_module_args(data)
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_vscan_mock_object('zapi', 'enable', 'true').apply()
+ assert not exc.value.args[0]['changed']
+
+ def test_successfully_disable(self):
+ data = self.mock_args()
+ data['enable'] = False
+ data['use_rest'] = 'never'
+ set_module_args(data)
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_vscan_mock_object('zapi', 'enable', 'true').apply()
+ assert exc.value.args[0]['changed']
+
+ def test_idempotently_disable(self):
+ data = self.mock_args()
+ data['enable'] = False
+ data['use_rest'] = 'never'
+ set_module_args(data)
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_vscan_mock_object('zapi', 'enable', 'false').apply()
+ assert not exc.value.args[0]['changed']
+
+ @patch('ansible_collections.netapp.ontap.plugins.module_utils.netapp.OntapRestAPI.send_request')
+ def test_rest_error(self, mock_request):
+ data = self.mock_args()
+ set_module_args(data)
+ mock_request.side_effect = [
+ SRR['is_rest'],
+ SRR['generic_error'],
+ SRR['end_of_sequence']
+ ]
+ with pytest.raises(AnsibleFailJson) as exc:
+ self.get_vscan_mock_object(cx_type='rest').apply()
+ assert exc.value.args[0]['msg'] == SRR['generic_error'][2]
+
+ @patch('ansible_collections.netapp.ontap.plugins.module_utils.netapp.OntapRestAPI.send_request')
+ def test_rest_successly_enable(self, mock_request):
+ data = self.mock_args()
+ data['enable'] = True
+ set_module_args(data)
+ mock_request.side_effect = [
+ SRR['is_rest'],
+ SRR['disabled'],
+ SRR['empty_good'],
+ SRR['end_of_sequence']
+ ]
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_vscan_mock_object(cx_type='rest').apply()
+ assert exc.value.args[0]['changed']
+
+ @patch('ansible_collections.netapp.ontap.plugins.module_utils.netapp.OntapRestAPI.send_request')
+ def test_rest_idempotently_enable(self, mock_request):
+ data = self.mock_args()
+ data['enable'] = True
+ set_module_args(data)
+ mock_request.side_effect = [
+ SRR['is_rest'],
+ SRR['enabled'],
+ SRR['end_of_sequence']
+ ]
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_vscan_mock_object(cx_type='rest').apply()
+ assert not exc.value.args[0]['changed']
+
+ @patch('ansible_collections.netapp.ontap.plugins.module_utils.netapp.OntapRestAPI.send_request')
+ def test_rest_successly_disable(self, mock_request):
+ data = self.mock_args()
+ data['enable'] = False
+ set_module_args(data)
+ mock_request.side_effect = [
+ SRR['is_rest'],
+ SRR['enabled'],
+ SRR['empty_good'],
+ SRR['end_of_sequence']
+ ]
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_vscan_mock_object(cx_type='rest').apply()
+ assert exc.value.args[0]['changed']
+
+ @patch('ansible_collections.netapp.ontap.plugins.module_utils.netapp.OntapRestAPI.send_request')
+ def test_rest_idempotently_disable(self, mock_request):
+ data = self.mock_args()
+ data['enable'] = False
+ set_module_args(data)
+ mock_request.side_effect = [
+ SRR['is_rest'],
+ SRR['disabled'],
+ SRR['end_of_sequence']
+ ]
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_vscan_mock_object(cx_type='rest').apply()
+ assert not exc.value.args[0]['changed']
diff --git a/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_vscan_on_access_policy.py b/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_vscan_on_access_policy.py
new file mode 100644
index 00000000..c595e73e
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_vscan_on_access_policy.py
@@ -0,0 +1,159 @@
+# (c) 2018, NetApp, Inc
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+''' unit tests for Ansible module: na_ontap_vscan_scanner_pool '''
+
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+import json
+import pytest
+
+from ansible_collections.netapp.ontap.tests.unit.compat import unittest
+from ansible_collections.netapp.ontap.tests.unit.compat.mock import patch, Mock
+from ansible.module_utils import basic
+from ansible.module_utils._text import to_bytes
+import ansible_collections.netapp.ontap.plugins.module_utils.netapp as netapp_utils
+
+from ansible_collections.netapp.ontap.plugins.modules.na_ontap_vscan_on_access_policy \
+ import NetAppOntapVscanOnAccessPolicy as policy_module # module under test
+
+if not netapp_utils.has_netapp_lib():
+ pytestmark = pytest.mark.skip('skipping as missing required netapp_lib')
+HAS_NETAPP_ZAPI_MSG = "pip install netapp_lib is required"
+
+
+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 MockONTAPConnection(object):
+ ''' mock server connection to ONTAP host '''
+
+ def __init__(self, kind=None, data=None):
+ ''' save arguments '''
+ self.kind = kind
+ self.params = data
+ self.xml_in = None
+ self.xml_out = None
+
+ def invoke_successfully(self, xml, enable_tunneling): # pylint: disable=unused-argument
+ ''' mock invoke_successfully returning xml data '''
+ self.xml_in = xml
+ if self.kind == 'policy':
+ xml = self.build_access_policy_info(self.params)
+ self.xml_out = xml
+ return xml
+
+ @staticmethod
+ def build_access_policy_info(policy_details):
+ xml = netapp_utils.zapi.NaElement('xml')
+ attributes = {'num-records': 1,
+ 'attributes-list': {'vscan-on-access-policy-info': {'policy-name': policy_details['policy_name']}}}
+ xml.translate_struct(attributes)
+ return xml
+
+
+class TestMyModule(unittest.TestCase):
+ ''' Unit tests for na_ontap_job_schedule '''
+
+ 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)
+ self.mock_access_policy = {
+ 'state': 'present',
+ 'vserver': 'test_vserver',
+ 'policy_name': 'test_carchi',
+ 'max_file_size': 2147483648 + 1 # 2GB + 1
+ }
+
+ def mock_args(self):
+ return {
+ 'state': self.mock_access_policy['state'],
+ 'vserver': self.mock_access_policy['vserver'],
+ 'policy_name': self.mock_access_policy['policy_name'],
+ 'max_file_size': self.mock_access_policy['max_file_size'],
+ 'hostname': 'test',
+ 'username': 'test_user',
+ 'password': 'test_pass!'
+ }
+
+ def get_policy_mock_object(self, kind=None):
+ policy_obj = policy_module()
+ if kind is None:
+ policy_obj.server = MockONTAPConnection()
+ else:
+ policy_obj.server = MockONTAPConnection(kind='policy', data=self.mock_access_policy)
+ return policy_obj
+
+ def test_module_fail_when_required_args_missing(self):
+ ''' required arguments are reported as errors '''
+ with pytest.raises(AnsibleFailJson) as exc:
+ set_module_args({})
+ policy_module()
+ print('Info: %s' % exc.value.args[0]['msg'])
+
+ def test_get_nonexistent_policy(self):
+ set_module_args(self.mock_args())
+ result = self.get_policy_mock_object().exists_access_policy()
+ assert not result
+
+ def test_get_existing_scanner(self):
+ set_module_args(self.mock_args())
+ result = self.get_policy_mock_object('policy').exists_access_policy()
+ assert result
+
+ def test_successfully_create(self):
+ set_module_args(self.mock_args())
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_policy_mock_object().apply()
+ assert exc.value.args[0]['changed']
+
+ def test_create_idempotency(self):
+ set_module_args(self.mock_args())
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_policy_mock_object('policy').apply()
+ assert exc.value.args[0]['changed']
+
+ def test_successfully_delete(self):
+ data = self.mock_args()
+ data['state'] = 'absent'
+ set_module_args(data)
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_policy_mock_object('policy').apply()
+ assert exc.value.args[0]['changed']
+
+ def test_delete_idempotency(self):
+ data = self.mock_args()
+ data['state'] = 'absent'
+ set_module_args(data)
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_policy_mock_object().apply()
+ assert not exc.value.args[0]['changed']
diff --git a/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_vscan_on_demand_task.py b/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_vscan_on_demand_task.py
new file mode 100644
index 00000000..a39e3732
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_vscan_on_demand_task.py
@@ -0,0 +1,168 @@
+''' unit tests for Ansible module: na_ontap_vscan_on_demand_task '''
+
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+import json
+import pytest
+
+from ansible_collections.netapp.ontap.tests.unit.compat import unittest
+from ansible_collections.netapp.ontap.tests.unit.compat.mock import patch, Mock
+from ansible.module_utils import basic
+from ansible.module_utils._text import to_bytes
+import ansible_collections.netapp.ontap.plugins.module_utils.netapp as netapp_utils
+
+from ansible_collections.netapp.ontap.plugins.modules.na_ontap_vscan_on_demand_task \
+ import NetAppOntapVscanOnDemandTask as onDemand_module # module under test
+
+if not netapp_utils.has_netapp_lib():
+ pytestmark = pytest.mark.skip('skipping as missing required netapp_lib')
+
+
+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 MockONTAPConnection(object):
+ ''' mock server connection to ONTAP host '''
+
+ def __init__(self, kind=None, data=None):
+ ''' save arguments '''
+ self.kind = kind
+ self.params = data
+ self.xml_in = None
+ self.xml_out = None
+
+ def invoke_successfully(self, xml, enable_tunneling): # pylint: disable=unused-argument
+ ''' mock invoke_successfully returning xml data '''
+ self.xml_in = xml
+ if self.kind == 'task':
+ xml = self.build_onDemand_pool_info(self.params)
+ self.xml_out = xml
+ return xml
+
+ @staticmethod
+ def build_onDemand_pool_info(onDemand_details):
+ xml = netapp_utils.zapi.NaElement('xml')
+ attributes = {
+ 'num-records': 1,
+ 'attributes-list': {
+ 'vscan-on-demand-task-info': {
+ 'task-name': onDemand_details['task_name'],
+ 'report-directory': onDemand_details['report_directory'],
+ 'scan-paths': {
+ 'string': onDemand_details['scan_paths']
+ }
+ }
+ }
+ }
+ xml.translate_struct(attributes)
+ return xml
+
+
+class TestMyModule(unittest.TestCase):
+ ''' Unit tests for na_ontap_job_schedule '''
+
+ 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)
+ self.mock_onDemand = {
+ 'state': 'present',
+ 'vserver': 'test_vserver',
+ 'report_directory': '/',
+ 'task_name': '/',
+ 'scan_paths': '/'
+ }
+
+ def mock_args(self):
+ return {
+ 'state': self.mock_onDemand['state'],
+ 'vserver': self.mock_onDemand['vserver'],
+ 'report_directory': self.mock_onDemand['report_directory'],
+ 'task_name': self.mock_onDemand['task_name'],
+ 'scan_paths': self.mock_onDemand['scan_paths'],
+ 'hostname': 'test',
+ 'username': 'test_user',
+ 'password': 'test_pass!'
+ }
+
+ def get_demand_mock_object(self, kind=None):
+ scanner_obj = onDemand_module()
+ scanner_obj.asup_log_for_cserver = Mock(return_value=None)
+ if kind is None:
+ scanner_obj.server = MockONTAPConnection()
+ else:
+ scanner_obj.server = MockONTAPConnection(kind='task', data=self.mock_onDemand)
+ return scanner_obj
+
+ def test_module_fail_when_required_args_missing(self):
+ ''' required arguments are reported as errors '''
+ with pytest.raises(AnsibleFailJson) as exc:
+ set_module_args({})
+ onDemand_module()
+ print('Info: %s' % exc.value.args[0]['msg'])
+
+ def test_get_nonexistent_demand_task(self):
+ set_module_args(self.mock_args())
+ result = self.get_demand_mock_object().get_demand_task()
+ assert not result
+
+ def test_get_existing_demand_task(self):
+ set_module_args(self.mock_args())
+ result = self.get_demand_mock_object('task').get_demand_task()
+ assert result
+
+ def test_successfully_create(self):
+ set_module_args(self.mock_args())
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_demand_mock_object().apply()
+ assert exc.value.args[0]['changed']
+
+ def test_create_idempotency(self):
+ set_module_args(self.mock_args())
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_demand_mock_object('task').apply()
+ assert not exc.value.args[0]['changed']
+
+ def test_successfully_delete(self):
+ data = self.mock_args()
+ data['state'] = 'absent'
+ set_module_args(data)
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_demand_mock_object('task').apply()
+ assert exc.value.args[0]['changed']
+
+ def test_delete_idempotency(self):
+ data = self.mock_args()
+ data['state'] = 'absent'
+ set_module_args(data)
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_demand_mock_object().apply()
+ assert not exc.value.args[0]['changed']
diff --git a/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_vscan_scanner_pool.py b/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_vscan_scanner_pool.py
new file mode 100644
index 00000000..a1aae605
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_vscan_scanner_pool.py
@@ -0,0 +1,188 @@
+# (c) 2018, NetApp, Inc
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+''' unit tests for Ansible module: na_ontap_vscan_scanner_pool '''
+
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+import json
+import pytest
+
+from ansible_collections.netapp.ontap.tests.unit.compat import unittest
+from ansible_collections.netapp.ontap.tests.unit.compat.mock import patch, Mock
+from ansible.module_utils import basic
+from ansible.module_utils._text import to_bytes
+import ansible_collections.netapp.ontap.plugins.module_utils.netapp as netapp_utils
+
+from ansible_collections.netapp.ontap.plugins.modules.na_ontap_vscan_scanner_pool \
+ import NetAppOntapVscanScannerPool as scanner_module # module under test
+
+if not netapp_utils.has_netapp_lib():
+ pytestmark = pytest.mark.skip('skipping as missing required netapp_lib')
+
+
+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 MockONTAPConnection(object):
+ ''' mock server connection to ONTAP host '''
+
+ def __init__(self, kind=None, data=None):
+ ''' save arguments '''
+ self.kind = kind
+ self.params = data
+ self.xml_in = None
+ self.xml_out = None
+
+ def invoke_successfully(self, xml, enable_tunneling): # pylint: disable=unused-argument
+ ''' mock invoke_successfully returning xml data '''
+ self.xml_in = xml
+ if self.kind == 'scanner':
+ xml = self.build_scanner_pool_info(self.params)
+ self.xml_out = xml
+ return xml
+
+ @staticmethod
+ def build_scanner_pool_info(sanner_details):
+ xml = netapp_utils.zapi.NaElement('xml')
+ attributes = {
+ 'num-records': 1,
+ 'attributes-list': {
+ 'vscan-scanner-pool-info': {
+ 'scanner-pool': sanner_details['scanner_pool'],
+ 'scanner-policy': sanner_details['scanner_policy'],
+ 'hostnames': [
+ {'hostname': sanner_details['hostnames'][0]},
+ {'hostname': sanner_details['hostnames'][1]}
+ ],
+ 'privileged-users': [
+ {"privileged-user": sanner_details['privileged_users'][0]},
+ {"privileged-user": sanner_details['privileged_users'][1]}
+ ]
+ }
+ }
+ }
+ xml.translate_struct(attributes)
+ return xml
+
+
+class TestMyModule(unittest.TestCase):
+ ''' Unit tests for na_ontap_job_schedule '''
+
+ 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)
+ self.mock_scanner = {
+ 'state': 'present',
+ 'scanner_pool': 'test_pool',
+ 'vserver': 'test_vserver',
+ 'hostnames': ['host1', 'host2'],
+ 'privileged_users': ['domain\\admin', 'domain\\carchi8py'],
+ 'scanner_policy': 'primary'
+ }
+
+ def mock_args(self):
+ return {
+ 'state': self.mock_scanner['state'],
+ 'scanner_pool': self.mock_scanner['scanner_pool'],
+ 'vserver': self.mock_scanner['vserver'],
+ 'hostnames': self.mock_scanner['hostnames'],
+ 'privileged_users': self.mock_scanner['privileged_users'],
+ 'hostname': 'test',
+ 'username': 'test_user',
+ 'password': 'test_pass!',
+ 'scanner_policy': self.mock_scanner['scanner_policy']
+ }
+
+ def get_scanner_mock_object(self, kind=None):
+ scanner_obj = scanner_module()
+ scanner_obj.asup_log_for_cserver = Mock(return_value=None)
+ if kind is None:
+ scanner_obj.server = MockONTAPConnection()
+ else:
+ scanner_obj.server = MockONTAPConnection(kind='scanner', data=self.mock_scanner)
+ return scanner_obj
+
+ def test_module_fail_when_required_args_missing(self):
+ ''' required arguments are reported as errors '''
+ with pytest.raises(AnsibleFailJson) as exc:
+ set_module_args({})
+ scanner_module()
+ print('Info: %s' % exc.value.args[0]['msg'])
+
+ def test_get_nonexistent_scanner(self):
+ ''' Test if get_scanner_pool returns None for non-existent job '''
+ set_module_args(self.mock_args())
+ result = self.get_scanner_mock_object().get_scanner_pool()
+ assert not result
+
+ def test_get_existing_scanner(self):
+ ''' Test if get_scanner_pool returns None for non-existent job '''
+ set_module_args(self.mock_args())
+ result = self.get_scanner_mock_object('scanner').get_scanner_pool()
+ assert result
+
+ def test_successfully_create(self):
+ set_module_args(self.mock_args())
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_scanner_mock_object().apply()
+ assert exc.value.args[0]['changed']
+
+ def test_create_idempotency(self):
+ set_module_args(self.mock_args())
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_scanner_mock_object('scanner').apply()
+ assert not exc.value.args[0]['changed']
+
+ def test_successfully_delete(self):
+ data = self.mock_args()
+ data['state'] = 'absent'
+ set_module_args(data)
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_scanner_mock_object('scanner').apply()
+ assert exc.value.args[0]['changed']
+
+ def test_delete_idempotency(self):
+ data = self.mock_args()
+ data['state'] = 'absent'
+ set_module_args(data)
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_scanner_mock_object().apply()
+ assert not exc.value.args[0]['changed']
+
+ def test_successfully_modify(self):
+ data = self.mock_args()
+ data['hostnames'] = "host1"
+ set_module_args(data)
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_scanner_mock_object('scanner').apply()
+ assert exc.value.args[0]['changed']
diff --git a/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_vserver_cifs_security.py b/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_vserver_cifs_security.py
new file mode 100644
index 00000000..ccc48b24
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_vserver_cifs_security.py
@@ -0,0 +1,166 @@
+# (c) 2019, NetApp, Inc
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+''' unit test template for ONTAP Ansible module '''
+
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+import json
+import pytest
+
+from ansible_collections.netapp.ontap.tests.unit.compat import unittest
+from ansible_collections.netapp.ontap.tests.unit.compat.mock import patch, Mock
+from ansible.module_utils import basic
+from ansible.module_utils._text import to_bytes
+import ansible_collections.netapp.ontap.plugins.module_utils.netapp as netapp_utils
+
+from ansible_collections.netapp.ontap.plugins.modules.na_ontap_vserver_cifs_security \
+ import NetAppONTAPCifsSecurity as cifs_security_module # module under test
+
+if not netapp_utils.has_netapp_lib():
+ pytestmark = pytest.mark.skip('skipping as missing required netapp_lib')
+
+
+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 MockONTAPConnection(object):
+ ''' mock server connection to ONTAP host '''
+
+ def __init__(self, kind=None, data=None):
+ ''' save arguments '''
+ self.type = kind
+ self.data = data
+ self.xml_in = None
+ self.xml_out = None
+
+ def invoke_successfully(self, xml, enable_tunneling): # pylint: disable=unused-argument
+ ''' mock invoke_successfully returning xml data '''
+ self.xml_in = xml
+ if self.type == 'cifs_security':
+ xml = self.build_security_info(self.data)
+ if self.type == 'error':
+ error = netapp_utils.zapi.NaApiError('test', 'error')
+ raise error
+ self.xml_out = xml
+ return xml
+
+ @staticmethod
+ def build_security_info(cifs_security_details):
+ ''' build xml data for cifs-security '''
+ xml = netapp_utils.zapi.NaElement('xml')
+ attributes = {
+ 'num-records': 1,
+ 'attributes-list': {
+ 'cifs-security': {
+ 'is-aes-encryption-enabled': str(cifs_security_details['is_aes_encryption_enabled']).lower(),
+ 'lm-compatibility-level': cifs_security_details['lm_compatibility_level'],
+ 'kerberos-clock-skew': str(cifs_security_details['kerberos_clock_skew'])
+ }
+ }
+ }
+ xml.translate_struct(attributes)
+ return xml
+
+
+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)
+ self.mock_cifs_security = {
+ 'is_aes_encryption_enabled': True,
+ 'lm_compatibility_level': 'krb',
+ 'kerberos_clock_skew': 10
+ }
+
+ def mock_args(self):
+ return {
+ 'is_aes_encryption_enabled': self.mock_cifs_security['is_aes_encryption_enabled'],
+ 'lm_compatibility_level': self.mock_cifs_security['lm_compatibility_level'],
+ 'kerberos_clock_skew': self.mock_cifs_security['kerberos_clock_skew'],
+ 'vserver': 'ansible',
+ 'hostname': 'test',
+ 'username': 'test_user',
+ 'password': 'test_pass!',
+ 'https': 'False'
+ }
+
+ def get_cifs_security_mock_object(self, kind=None):
+ """
+ Helper method to return an na_ontap_vserver_cifs_security object
+ :param kind: passes this param to MockONTAPConnection()
+ :return: na_ontap_vserver_cifs_security object
+ """
+ obj = cifs_security_module()
+ obj.asup_log_for_cserver = Mock(return_value=None)
+ obj.server = Mock()
+ obj.server.invoke_successfully = Mock()
+ if kind is None:
+ obj.server = MockONTAPConnection()
+ else:
+ obj.server = MockONTAPConnection(kind=kind, data=self.mock_cifs_security)
+ return obj
+
+ def test_successful_modify_int_option(self):
+ ''' Test successful modify kerberos_clock_skew '''
+ data = self.mock_args()
+ data['kerberos_clock_skew'] = 15
+ set_module_args(data)
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_cifs_security_mock_object('cifs_security').apply()
+ assert exc.value.args[0]['changed']
+
+ def test_successful_modify_bool_option(self):
+ ''' Test successful modify is_aes_encryption_enabled '''
+ data = self.mock_args()
+ data['is_aes_encryption_enabled'] = False
+ set_module_args(data)
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_cifs_security_mock_object('cifs_security').apply()
+ assert exc.value.args[0]['changed']
+
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_vserver_cifs_security.NetAppONTAPCifsSecurity.cifs_security_get_iter')
+ def test_modify_error(self, get_cifs_security):
+ ''' Test modify error '''
+ data = self.mock_args()
+ set_module_args(data)
+ current = {
+ 'is_aes_encryption_enabled': False
+ }
+ get_cifs_security.side_effect = [
+ current
+ ]
+ with pytest.raises(AnsibleFailJson) as exc:
+ self.get_cifs_security_mock_object('error').apply()
+ assert exc.value.args[0]['msg'] == 'Error modifying cifs security on ansible: NetApp API failed. Reason - test:error'
diff --git a/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_vserver_peer.py b/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_vserver_peer.py
new file mode 100644
index 00000000..01523aac
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_vserver_peer.py
@@ -0,0 +1,250 @@
+# (c) 2018, NetApp, Inc
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+''' unit test template for ONTAP Ansible module '''
+
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+
+import json
+import pytest
+
+from ansible_collections.netapp.ontap.tests.unit.compat import unittest
+from ansible_collections.netapp.ontap.tests.unit.compat.mock import patch, Mock
+from ansible.module_utils import basic
+from ansible.module_utils._text import to_bytes
+import ansible_collections.netapp.ontap.plugins.module_utils.netapp as netapp_utils
+
+from ansible_collections.netapp.ontap.plugins.modules.na_ontap_vserver_peer \
+ import NetAppONTAPVserverPeer as vserver_peer # module under test
+
+if not netapp_utils.has_netapp_lib():
+ pytestmark = pytest.mark.skip('skipping as missing required netapp_lib')
+
+
+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 MockONTAPConnection(object):
+ ''' mock server connection to ONTAP host '''
+
+ def __init__(self, kind=None, data=None):
+ ''' save arguments '''
+ self.kind = kind
+ self.data = data
+ self.xml_in = None
+ self.xml_out = None
+
+ def invoke_successfully(self, xml, enable_tunneling): # pylint: disable=unused-argument
+ ''' mock invoke_successfully returning xml data '''
+ self.xml_in = xml
+ if self.kind == 'vserver_peer':
+ xml = self.build_vserver_peer_info(self.data)
+ if self.kind == 'cluster':
+ xml = self.build_cluster_info(self.data)
+ self.xml_out = xml
+ return xml
+
+ @staticmethod
+ def build_vserver_peer_info(vserver):
+ ''' build xml data for vserser-peer-info '''
+ xml = netapp_utils.zapi.NaElement('xml')
+ attributes = {
+ 'num-records': 1,
+ 'attributes-list': {
+ 'vserver-peer-info': {
+ 'peer-vserver': vserver['peer_vserver'],
+ 'vserver': vserver['vserver'],
+ 'peer-state': 'peered'
+ }
+ }
+ }
+ xml.translate_struct(attributes)
+ print(xml.to_string())
+ return xml
+
+ @staticmethod
+ def build_cluster_info(vserver):
+ ''' build xml data for cluster-identity-get '''
+ xml = netapp_utils.zapi.NaElement('xml')
+ attributes = {
+ 'attributes': {
+ 'cluster-identity-info': {
+ 'cluster-name': vserver['peer_cluster']
+ }
+ }
+ }
+ xml.translate_struct(attributes)
+ print(xml.to_string())
+ return xml
+
+
+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)
+ self.mock_vserver_peer = {
+ 'vserver': 'test',
+ 'peer_vserver': 'test_peer',
+ 'peer_cluster': 'test_cluster_peer',
+ 'applications': ['snapmirror'],
+ 'hostname': 'hostname',
+ 'dest_hostname': 'hostname',
+ 'username': 'username',
+ 'password': 'password',
+
+ }
+
+ def get_vserver_peer_mock_object(self, kind=None):
+ """
+ Helper method to return an na_ontap_vserver_peer object
+ :param kind: passes this param to MockONTAPConnection()
+ :return: na_ontap_vserver_peer object
+ """
+ vserver_peer_obj = vserver_peer()
+ vserver_peer_obj.asup_log_for_cserver = Mock(return_value=None)
+ if kind is None:
+ vserver_peer_obj.server = MockONTAPConnection()
+ vserver_peer_obj.dest_server = MockONTAPConnection()
+ else:
+ vserver_peer_obj.server = MockONTAPConnection(kind=kind, data=self.mock_vserver_peer)
+ vserver_peer_obj.dest_server = MockONTAPConnection(kind=kind, data=self.mock_vserver_peer)
+ return vserver_peer_obj
+
+ def test_module_fail_when_required_args_missing(self):
+ ''' required arguments are reported as errors '''
+ with pytest.raises(AnsibleFailJson) as exc:
+ set_module_args({})
+ vserver_peer()
+ print('Info: %s' % exc.value.args[0]['msg'])
+
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_vserver_peer.NetAppONTAPVserverPeer.vserver_peer_get')
+ def test_successful_create(self, vserver_peer_get):
+ ''' Test successful create '''
+ data = self.mock_vserver_peer
+ data['dest_hostname'] = 'test_destination'
+ set_module_args(self.mock_vserver_peer)
+ vserver_peer_get.return_value = None
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_vserver_peer_mock_object().apply()
+ assert exc.value.args[0]['changed']
+
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_vserver_peer.NetAppONTAPVserverPeer.vserver_peer_get')
+ def test_create_idempotency(self, vserver_peer_get):
+ ''' Test create idempotency '''
+ data = self.mock_vserver_peer
+ data['dest_hostname'] = 'test_destination'
+ set_module_args(self.mock_vserver_peer)
+ current = {
+ 'vserver': 'test',
+ 'peer_vserver': self.mock_vserver_peer['peer_vserver'],
+ 'peer_cluster': self.mock_vserver_peer['peer_cluster']
+ }
+ vserver_peer_get.return_value = current
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_vserver_peer_mock_object('vserver_peer').apply()
+ assert not exc.value.args[0]['changed']
+
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_vserver_peer.NetAppONTAPVserverPeer.vserver_peer_get')
+ def test_successful_delete(self, vserver_peer_get):
+ ''' Test successful delete peer '''
+ data = self.mock_vserver_peer
+ data['state'] = 'absent'
+ set_module_args(data)
+ vserver_peer_get.return_value = Mock()
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_vserver_peer_mock_object('vserver_peer').apply()
+ assert exc.value.args[0]['changed']
+
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_vserver_peer.NetAppONTAPVserverPeer.vserver_peer_get')
+ def test_delete_idempotency(self, vserver_peer_get):
+ ''' Test delete idempotency '''
+ data = self.mock_vserver_peer
+ data['state'] = 'absent'
+ set_module_args(data)
+ vserver_peer_get.return_value = None
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_vserver_peer_mock_object().apply()
+ assert not exc.value.args[0]['changed']
+
+ def test_helper_vserver_peer_get_iter(self):
+ ''' Test vserver_peer_get_iter method '''
+ set_module_args(self.mock_vserver_peer)
+ obj = self.get_vserver_peer_mock_object('vserver_peer')
+ result = obj.vserver_peer_get_iter()
+ print(result.to_string(pretty=True))
+ assert result['query'] is not None
+ assert result['query']['vserver-peer-info'] is not None
+ info = result['query']['vserver-peer-info']
+ assert info['vserver'] == self.mock_vserver_peer['vserver']
+ assert info['peer-vserver'] == self.mock_vserver_peer['peer_vserver']
+
+ def test_get_packet(self):
+ ''' Test vserver_peer_get method '''
+ set_module_args(self.mock_vserver_peer)
+ obj = self.get_vserver_peer_mock_object('vserver_peer')
+ result = obj.vserver_peer_get()
+ assert 'vserver' in result.keys()
+ assert 'peer_vserver' in result.keys()
+ assert 'peer_state' in result.keys()
+
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_vserver_peer.NetAppONTAPVserverPeer.vserver_peer_get')
+ def test_error_on_missing_params_create(self, vserver_peer_get):
+ ''' Test error thrown from vserver_peer_create '''
+ data = self.mock_vserver_peer
+ del data['applications']
+ set_module_args(data)
+ vserver_peer_get.return_value = None
+ with pytest.raises(AnsibleFailJson) as exc:
+ self.get_vserver_peer_mock_object().apply()
+ assert exc.value.args[0]['msg'] == "applications parameter is missing"
+
+ @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_vserver_peer.NetAppONTAPVserverPeer.get_peer_cluster_name')
+ def test_get_peer_cluster_called(self, cluster_peer_get):
+ ''' Test get_peer_cluster_name called if peer_cluster is missing '''
+ data = self.mock_vserver_peer
+ del data['peer_cluster']
+ set_module_args(data)
+ cluster_peer_get.return_value = 'something'
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_vserver_peer_mock_object().apply()
+ assert cluster_peer_get.call_count == 1
+
+ def test_get_peer_cluster_packet(self):
+ ''' Test get_peer_cluster_name xml packet '''
+ data = self.mock_vserver_peer
+ set_module_args(data)
+ obj = self.get_vserver_peer_mock_object('cluster')
+ result = obj.get_peer_cluster_name()
+ assert result == data['peer_cluster']
diff --git a/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_wwpn_alias.py b/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_wwpn_alias.py
new file mode 100644
index 00000000..3dc513ff
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_wwpn_alias.py
@@ -0,0 +1,224 @@
+# (c) 2020, NetApp, Inc
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+''' unit tests for Ansible module: na_ontap_wwpn_alias '''
+
+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.ontap.tests.unit.compat import unittest
+from ansible_collections.netapp.ontap.tests.unit.compat.mock import patch
+
+from ansible_collections.netapp.ontap.plugins.modules.na_ontap_wwpn_alias \
+ import NetAppOntapWwpnAlias as alias_module # module under test
+
+# REST API canned responses when mocking send_request
+SRR = {
+ # common responses
+ 'is_rest': (200, {}, None),
+ 'is_zapi': (400, {}, "Unreachable"),
+ 'empty_good': (200, {}, None),
+ 'end_of_sequence': (500, None, "Unexpected call to send_request"),
+ 'generic_error': (400, None, "Expected error"),
+ # module specific responses
+ 'get_alias': (
+ 200,
+ {"records": [{
+ "svm": {
+ "uuid": "uuid",
+ "name": "svm"},
+ "alias": "host1",
+ "wwpn": "01:02:03:04:0a:0b:0c:0d"}],
+ "num_records": 1}, None),
+ 'get_svm_uuid': (
+ 200,
+ {"records": [{
+ "uuid": "test_uuid"
+ }]}, None),
+ "no_record": (
+ 200,
+ {"num_records": 0},
+ 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"""
+
+
+class AnsibleFailJson(Exception):
+ """Exception class to be raised by module.fail_json and caught by the test case"""
+
+
+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):
+ ''' Unit tests for na_ontap_wwpn_alias '''
+
+ 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)
+ self.mock_alias = {
+ 'name': 'host1',
+ 'vserver': 'test_vserver'
+ }
+
+ def mock_args(self):
+ return {
+ 'vserver': self.mock_alias['vserver'],
+ 'name': self.mock_alias['name'],
+ "wwpn": "01:02:03:04:0a:0b:0c:0d",
+ 'hostname': 'test_host',
+ 'username': 'test_user',
+ 'password': 'test_pass!'
+ }
+
+ def get_alias_mock_object(self):
+ alias_obj = alias_module()
+ return alias_obj
+
+ @patch('ansible_collections.netapp.ontap.plugins.module_utils.netapp.OntapRestAPI.send_request')
+ def test_rest_successful_create(self, mock_request):
+ '''Test successful rest create'''
+ data = self.mock_args()
+ set_module_args(data)
+ mock_request.side_effect = [
+ SRR['is_rest'],
+ SRR['get_svm_uuid'],
+ SRR['no_record'],
+ SRR['empty_good'],
+ SRR['end_of_sequence']
+ ]
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_alias_mock_object().apply()
+ assert exc.value.args[0]['changed']
+
+ @patch('ansible_collections.netapp.ontap.plugins.module_utils.netapp.OntapRestAPI.send_request')
+ def test_rest_create_idempotency(self, mock_request):
+ '''Test rest create idempotency'''
+ data = self.mock_args()
+ set_module_args(data)
+ mock_request.side_effect = [
+ SRR['is_rest'],
+ SRR['get_svm_uuid'],
+ SRR['get_alias'],
+ SRR['no_record'],
+ SRR['empty_good'],
+ SRR['end_of_sequence']
+ ]
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_alias_mock_object().apply()
+ assert not exc.value.args[0]['changed']
+
+ @patch('ansible_collections.netapp.ontap.plugins.module_utils.netapp.OntapRestAPI.send_request')
+ def test_rest_create_error(self, mock_request):
+ '''Test rest create error'''
+ data = self.mock_args()
+ set_module_args(data)
+ mock_request.side_effect = [
+ SRR['is_rest'],
+ SRR['get_svm_uuid'],
+ SRR['no_record'],
+ SRR['generic_error'],
+ SRR['empty_good'],
+ SRR['end_of_sequence']
+ ]
+ with pytest.raises(AnsibleFailJson) as exc:
+ self.get_alias_mock_object().apply()
+ assert exc.value.args[0]['msg'] == "Error on creating wwpn alias: Expected error."
+
+ @patch('ansible_collections.netapp.ontap.plugins.module_utils.netapp.OntapRestAPI.send_request')
+ def test_rest_modify(self, mock_request):
+ '''Test rest modify error'''
+ data = self.mock_args()
+ data['wwpn'] = "01:02:03:04:0a:0b:0c:0e"
+ set_module_args(data)
+ mock_request.side_effect = [
+ SRR['is_rest'],
+ SRR['get_svm_uuid'],
+ SRR['get_alias'],
+ SRR['empty_good'],
+ SRR['empty_good'],
+ SRR['end_of_sequence']
+ ]
+ with pytest.raises(AnsibleExitJson) as exc:
+ self.get_alias_mock_object().apply()
+ assert exc.value.args[0]['changed']
+
+ @patch('ansible_collections.netapp.ontap.plugins.module_utils.netapp.OntapRestAPI.send_request')
+ def test_rest_modify_error_delete(self, mock_request):
+ '''Test rest modify error'''
+ data = self.mock_args()
+ data['wwpn'] = "01:02:03:04:0a:0b:0c:0e"
+ set_module_args(data)
+ mock_request.side_effect = [
+ SRR['is_rest'],
+ SRR['get_svm_uuid'],
+ SRR['get_alias'],
+ SRR['generic_error'],
+ SRR['empty_good'],
+ SRR['end_of_sequence']
+ ]
+ with pytest.raises(AnsibleFailJson) as exc:
+ self.get_alias_mock_object().apply()
+ assert exc.value.args[0]['msg'] == "Error on modifying wwpn alias when trying to delete alias: Expected error."
+
+ @patch('ansible_collections.netapp.ontap.plugins.module_utils.netapp.OntapRestAPI.send_request')
+ def test_rest_modify_error_create(self, mock_request):
+ '''Test rest modify error'''
+ data = self.mock_args()
+ data['wwpn'] = "01:02:03:04:0a:0b:0c:0e"
+ set_module_args(data)
+ mock_request.side_effect = [
+ SRR['is_rest'],
+ SRR['get_svm_uuid'],
+ SRR['get_alias'],
+ SRR['empty_good'],
+ SRR['generic_error'],
+ SRR['end_of_sequence']
+ ]
+ with pytest.raises(AnsibleFailJson) as exc:
+ self.get_alias_mock_object().apply()
+ assert exc.value.args[0]['msg'] == "Error on modifying wwpn alias when trying to re-create alias: Expected error."
+
+ @patch('ansible_collections.netapp.ontap.plugins.module_utils.netapp.OntapRestAPI.send_request')
+ def test_rest_delete_error(self, mock_request):
+ '''Test rest delete error'''
+ data = self.mock_args()
+ data['state'] = 'absent'
+ set_module_args(data)
+ mock_request.side_effect = [
+ SRR['is_rest'],
+ SRR['get_svm_uuid'],
+ SRR['get_alias'],
+ SRR['generic_error'],
+ SRR['empty_good'],
+ SRR['end_of_sequence']
+ ]
+ with pytest.raises(AnsibleFailJson) as exc:
+ self.get_alias_mock_object().apply()
+ assert exc.value.args[0]['msg'] == "Error on deleting wwpn alias: Expected error."
diff --git a/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/requirements.txt b/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/requirements.txt
new file mode 100644
index 00000000..c7897ee3
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/netapp/ontap/tests/unit/requirements.txt
@@ -0,0 +1,3 @@
+netapp-lib
+solidfire-sdk-python
+unittest2 ; python_version < '2.7'