1# (c) 2019, NetApp, Inc 2# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) 3 4''' unit tests for Ansible module: na_ontap_volume_autosize ''' 5 6from __future__ import (absolute_import, division, print_function) 7__metaclass__ = type 8import json 9import pytest 10 11from ansible.module_utils import basic 12from ansible.module_utils._text import to_bytes 13from ansible_collections.netapp.ontap.tests.unit.compat import unittest 14from ansible_collections.netapp.ontap.tests.unit.compat.mock import patch 15import ansible_collections.netapp.ontap.plugins.module_utils.netapp as netapp_utils 16 17from ansible_collections.netapp.ontap.plugins.modules.na_ontap_volume_autosize \ 18 import NetAppOntapVolumeAutosize as autosize_module # module under test 19 20if not netapp_utils.has_netapp_lib(): 21 pytestmark = pytest.mark.skip('skipping as missing required netapp_lib') 22 23 24# REST API canned responses when mocking send_request 25SRR = { 26 # common responses 27 'is_rest': (200, {}, None), 28 'is_zapi': (400, {}, "Unreachable"), 29 'empty_good': (200, {}, None), 30 'end_of_sequence': (500, None, "Unexpected call to send_request"), 31 'generic_error': (400, None, "Expected error"), 32 # module specific responses 33 'get_uuid': (200, {'records': [{'uuid': 'testuuid'}]}, None), 34 'get_autosize': (200, 35 {'uuid': 'testuuid', 36 'name': 'testname', 37 'autosize': {"maximum": 10737418240, 38 "minimum": 22020096, 39 "grow_threshold": 99, 40 "shrink_threshold": 40, 41 "mode": "grow" 42 } 43 }, None) 44} 45 46 47def set_module_args(args): 48 """prepare arguments so that they will be picked up during module creation""" 49 args = json.dumps({'ANSIBLE_MODULE_ARGS': args}) 50 basic._ANSIBLE_ARGS = to_bytes(args) # pylint: disable=protected-access 51 52 53class AnsibleExitJson(Exception): 54 """Exception class to be raised by module.exit_json and caught by the test case""" 55 56 57class AnsibleFailJson(Exception): 58 """Exception class to be raised by module.fail_json and caught by the test case""" 59 60 61def exit_json(*args, **kwargs): # pylint: disable=unused-argument 62 """function to patch over exit_json; package return data into an exception""" 63 if 'changed' not in kwargs: 64 kwargs['changed'] = False 65 raise AnsibleExitJson(kwargs) 66 67 68def fail_json(*args, **kwargs): # pylint: disable=unused-argument 69 """function to patch over fail_json; package return data into an exception""" 70 kwargs['failed'] = True 71 raise AnsibleFailJson(kwargs) 72 73 74class MockONTAPConnection(object): 75 ''' mock server connection to ONTAP host ''' 76 77 def __init__(self, kind=None, data=None): 78 ''' save arguments ''' 79 self.kind = kind 80 self.params = data 81 self.xml_in = None 82 self.xml_out = None 83 84 def invoke_successfully(self, xml, enable_tunneling): # pylint: disable=unused-argument 85 ''' mock invoke_successfully returning xml data ''' 86 self.xml_in = xml 87 if self.kind == 'autosize': 88 xml = self.build_autosize_info(self.params) 89 self.xml_out = xml 90 return xml 91 92 @staticmethod 93 def build_autosize_info(autosize_details): 94 xml = netapp_utils.zapi.NaElement('xml') 95 attributes = { 96 'grow-threshold-percent': autosize_details['grow_threshold_percent'], 97 'maximum-size': '10485760', 98 'minimum-size': '21504', 99 'increment_size': '10240', 100 'mode': autosize_details['mode'], 101 'shrink-threshold-percent': autosize_details['shrink_threshold_percent'] 102 } 103 xml.translate_struct(attributes) 104 return xml 105 106 107class TestMyModule(unittest.TestCase): 108 ''' Unit tests for na_ontap_job_schedule ''' 109 110 def setUp(self): 111 self.mock_module_helper = patch.multiple(basic.AnsibleModule, 112 exit_json=exit_json, 113 fail_json=fail_json) 114 self.mock_module_helper.start() 115 self.addCleanup(self.mock_module_helper.stop) 116 self.mock_autosize = { 117 'grow_threshold_percent': 99, 118 'maximum_size': '10g', 119 'minimum_size': '21m', 120 'increment_size': '10m', 121 'mode': 'grow', 122 'shrink_threshold_percent': 40, 123 'vserver': 'test_vserver', 124 'volume': 'test_volume' 125 } 126 127 def mock_args(self, rest=False): 128 if rest: 129 return { 130 'vserver': self.mock_autosize['vserver'], 131 'volume': self.mock_autosize['volume'], 132 'grow_threshold_percent': self.mock_autosize['grow_threshold_percent'], 133 'maximum_size': self.mock_autosize['maximum_size'], 134 'minimum_size': self.mock_autosize['minimum_size'], 135 'mode': self.mock_autosize['mode'], 136 'shrink_threshold_percent': self.mock_autosize['shrink_threshold_percent'], 137 'hostname': 'test', 138 'username': 'test_user', 139 'password': 'test_pass!' 140 } 141 else: 142 return { 143 'vserver': self.mock_autosize['vserver'], 144 'volume': self.mock_autosize['volume'], 145 'grow_threshold_percent': self.mock_autosize['grow_threshold_percent'], 146 'maximum_size': self.mock_autosize['maximum_size'], 147 'minimum_size': self.mock_autosize['minimum_size'], 148 'increment_size': self.mock_autosize['increment_size'], 149 'mode': self.mock_autosize['mode'], 150 'shrink_threshold_percent': self.mock_autosize['shrink_threshold_percent'], 151 'hostname': 'test', 152 'username': 'test_user', 153 'password': 'test_pass!', 154 'use_rest': 'never' 155 } 156 157 def get_autosize_mock_object(self, cx_type='zapi', kind=None): 158 autosize_obj = autosize_module() 159 if cx_type == 'zapi': 160 if kind is None: 161 autosize_obj.server = MockONTAPConnection() 162 elif kind == 'autosize': 163 autosize_obj.server = MockONTAPConnection(kind='autosize', data=self.mock_autosize) 164 return autosize_obj 165 166 def test_module_fail_when_required_args_missing(self): 167 ''' required arguments are reported as errors ''' 168 with pytest.raises(AnsibleFailJson) as exc: 169 set_module_args({}) 170 autosize_module() 171 print('Info: %s' % exc.value.args[0]['msg']) 172 173 def test_idempotent_modify(self): 174 set_module_args(self.mock_args()) 175 with pytest.raises(AnsibleExitJson) as exc: 176 self.get_autosize_mock_object('zapi', 'autosize').apply() 177 assert not exc.value.args[0]['changed'] 178 179 def test_successful_modify(self): 180 data = self.mock_args() 181 data['maximum_size'] = '11g' 182 set_module_args(data) 183 with pytest.raises(AnsibleExitJson) as exc: 184 self.get_autosize_mock_object('zapi', 'autosize').apply() 185 assert exc.value.args[0]['changed'] 186 187 def test_successful_reset(self): 188 data = {} 189 data['reset'] = True 190 data['hostname'] = 'test' 191 data['username'] = 'test_user' 192 data['password'] = 'test_pass!' 193 data['volume'] = 'test_vol' 194 data['vserver'] = 'test_vserver' 195 data['use_rest'] = 'never' 196 set_module_args(data) 197 with pytest.raises(AnsibleExitJson) as exc: 198 self.get_autosize_mock_object('zapi', 'autosize').apply() 199 assert exc.value.args[0]['changed'] 200 201 @patch('ansible_collections.netapp.ontap.plugins.module_utils.netapp.OntapRestAPI.send_request') 202 def test_rest_error(self, mock_request): 203 data = self.mock_args(rest=True) 204 set_module_args(data) 205 mock_request.side_effect = [ 206 SRR['is_rest'], 207 SRR['generic_error'], 208 SRR['end_of_sequence'] 209 ] 210 with pytest.raises(AnsibleFailJson) as exc: 211 self.get_autosize_mock_object(cx_type='rest').apply() 212 assert exc.value.args[0]['msg'] == SRR['generic_error'][2] 213 214 @patch('ansible_collections.netapp.ontap.plugins.module_utils.netapp.OntapRestAPI.send_request') 215 def test_rest_successful_modify(self, mock_request): 216 data = self.mock_args(rest=True) 217 data['maximum_size'] = '11g' 218 set_module_args(data) 219 mock_request.side_effect = [ 220 SRR['is_rest'], 221 SRR['get_uuid'], 222 SRR['get_autosize'], 223 SRR['empty_good'], 224 SRR['end_of_sequence'] 225 ] 226 with pytest.raises(AnsibleExitJson) as exc: 227 self.get_autosize_mock_object(cx_type='rest').apply() 228 assert exc.value.args[0]['changed'] 229 230 @patch('ansible_collections.netapp.ontap.plugins.module_utils.netapp.OntapRestAPI.send_request') 231 def test_rest_idempotent_modify(self, mock_request): 232 data = self.mock_args(rest=True) 233 set_module_args(data) 234 mock_request.side_effect = [ 235 SRR['is_rest'], 236 SRR['get_uuid'], 237 SRR['get_autosize'], 238 SRR['empty_good'], 239 SRR['end_of_sequence'] 240 ] 241 with pytest.raises(AnsibleExitJson) as exc: 242 self.get_autosize_mock_object(cx_type='rest').apply() 243 assert not exc.value.args[0]['changed'] 244