# coding=utf-8 # (c) 2018, NetApp Inc. # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) from mock import MagicMock from ansible.modules.storage.netapp.netapp_e_mgmt_interface import MgmtInterface from units.modules.utils import AnsibleExitJson, AnsibleFailJson, ModuleTestCase, set_module_args __metaclass__ = type import mock from units.compat.mock import PropertyMock class MgmtInterfaceTest(ModuleTestCase): REQUIRED_PARAMS = { 'api_username': 'rw', 'api_password': 'password', 'api_url': 'http://localhost', 'ssid': '1', } TEST_DATA = [ { "controllerRef": "070000000000000000000001", "controllerSlot": 1, "interfaceName": "wan0", "interfaceRef": "2800070000000000000000000001000000000000", "channel": 1, "alias": "creG1g-AP-a", "ipv4Enabled": True, "ipv4Address": "10.1.1.10", "linkStatus": "up", "ipv4SubnetMask": "255.255.255.0", "ipv4AddressConfigMethod": "configStatic", "ipv4GatewayAddress": "10.1.1.1", "ipv6Enabled": False, "physicalLocation": { "slot": 0, }, "dnsProperties": { "acquisitionProperties": { "dnsAcquisitionType": "stat", "dnsServers": [ { "addressType": "ipv4", "ipv4Address": "10.1.0.250", }, { "addressType": "ipv4", "ipv4Address": "10.10.0.20", } ] }, "dhcpAcquiredDnsServers": [] }, "ntpProperties": { "acquisitionProperties": { "ntpAcquisitionType": "disabled", "ntpServers": None }, "dhcpAcquiredNtpServers": [] }, }, { "controllerRef": "070000000000000000000001", "controllerSlot": 1, "interfaceName": "wan1", "interfaceRef": "2800070000000000000000000001000000000000", "channel": 2, "alias": "creG1g-AP-a", "ipv4Enabled": True, "ipv4Address": "0.0.0.0", "ipv4SubnetMask": "0.0.0.0", "ipv4AddressConfigMethod": "configDhcp", "ipv4GatewayAddress": "10.1.1.1", "ipv6Enabled": False, "physicalLocation": { "slot": 1, }, "dnsProperties": { "acquisitionProperties": { "dnsAcquisitionType": "stat", "dnsServers": [ { "addressType": "ipv4", "ipv4Address": "10.1.0.250", "ipv6Address": None }, { "addressType": "ipv4", "ipv4Address": "10.10.0.20", "ipv6Address": None } ] }, "dhcpAcquiredDnsServers": [] }, "ntpProperties": { "acquisitionProperties": { "ntpAcquisitionType": "disabled", "ntpServers": None }, "dhcpAcquiredNtpServers": [] }, }, { "controllerRef": "070000000000000000000002", "controllerSlot": 2, "interfaceName": "wan0", "interfaceRef": "2800070000000000000000000001000000000000", "channel": 1, "alias": "creG1g-AP-b", "ipv4Enabled": True, "ipv4Address": "0.0.0.0", "ipv4SubnetMask": "0.0.0.0", "ipv4AddressConfigMethod": "configDhcp", "ipv4GatewayAddress": "10.1.1.1", "ipv6Enabled": False, "physicalLocation": { "slot": 0, }, "dnsProperties": { "acquisitionProperties": { "dnsAcquisitionType": "stat", "dnsServers": [ { "addressType": "ipv4", "ipv4Address": "10.1.0.250", "ipv6Address": None } ] }, "dhcpAcquiredDnsServers": [] }, "ntpProperties": { "acquisitionProperties": { "ntpAcquisitionType": "stat", "ntpServers": [ { "addrType": "ipvx", "domainName": None, "ipvxAddress": { "addressType": "ipv4", "ipv4Address": "10.13.1.5", "ipv6Address": None } }, { "addrType": "ipvx", "domainName": None, "ipvxAddress": { "addressType": "ipv4", "ipv4Address": "10.15.1.8", "ipv6Address": None } } ] }, "dhcpAcquiredNtpServers": [] }, }, { "controllerRef": "070000000000000000000002", "controllerSlot": 2, "interfaceName": "wan1", "interfaceRef": "2801070000000000000000000001000000000000", "channel": 2, "alias": "creG1g-AP-b", "ipv4Enabled": True, "ipv4Address": "0.0.0.0", "ipv4SubnetMask": "0.0.0.0", "ipv4AddressConfigMethod": "configDhcp", "ipv4GatewayAddress": "10.1.1.1", "ipv6Enabled": False, "physicalLocation": { "slot": 1, }, "dnsProperties": { "acquisitionProperties": { "dnsAcquisitionType": "stat", "dnsServers": [ { "addressType": "ipv4", "ipv4Address": "10.19.1.2", "ipv6Address": None } ] }, "dhcpAcquiredDnsServers": [] }, "ntpProperties": { "acquisitionProperties": { "ntpAcquisitionType": "stat", "ntpServers": [ { "addrType": "ipvx", "domainName": None, "ipvxAddress": { "addressType": "ipv4", "ipv4Address": "10.13.1.5", "ipv6Address": None } }, { "addrType": "ipvx", "domainName": None, "ipvxAddress": { "addressType": "ipv4", "ipv4Address": "10.15.1.18", "ipv6Address": None } } ] }, "dhcpAcquiredNtpServers": [] }, }, ] REQ_FUNC = 'ansible.modules.storage.netapp.netapp_e_mgmt_interface.request' def _set_args(self, args=None): module_args = self.REQUIRED_PARAMS.copy() if args is not None: module_args.update(args) set_module_args(module_args) def test_controller_property_pass(self): """Verify dictionary return from controller property.""" initial = { "state": "enable", "controller": "A", "channel": "1", "address": "192.168.1.1", "subnet_mask": "255.255.255.1", "config_method": "static"} controller_request = [ {"physicalLocation": {"slot": 2}, "controllerRef": "070000000000000000000002", "networkSettings": {"remoteAccessEnabled": True}}, {"physicalLocation": {"slot": 1}, "controllerRef": "070000000000000000000001", "networkSettings": {"remoteAccessEnabled": False}}] expected = { 'A': {'controllerRef': '070000000000000000000001', 'controllerSlot': 1, 'ssh': False}, 'B': {'controllerRef': '070000000000000000000002', 'controllerSlot': 2, 'ssh': True}} self._set_args(initial) mgmt_interface = MgmtInterface() with mock.patch(self.REQ_FUNC, return_value=(200, controller_request)): response = mgmt_interface.controllers self.assertTrue(response == expected) def test_controller_property_fail(self): """Verify controllers endpoint request failure causes AnsibleFailJson exception.""" initial = { "state": "enable", "controller": "A", "channel": "1", "address": "192.168.1.1", "subnet_mask": "255.255.255.1", "config_method": "static"} controller_request = [ {"physicalLocation": {"slot": 2}, "controllerRef": "070000000000000000000002", "networkSettings": {"remoteAccessEnabled": True}}, {"physicalLocation": {"slot": 1}, "controllerRef": "070000000000000000000001", "networkSettings": {"remoteAccessEnabled": False}}] expected = { 'A': {'controllerRef': '070000000000000000000001', 'controllerSlot': 1, 'ssh': False}, 'B': {'controllerRef': '070000000000000000000002', 'controllerSlot': 2, 'ssh': True}} self._set_args(initial) mgmt_interface = MgmtInterface() with self.assertRaisesRegexp(AnsibleFailJson, r"Failed to retrieve the controller settings."): with mock.patch(self.REQ_FUNC, return_value=Exception): response = mgmt_interface.controllers def test_interface_property_match_pass(self): """Verify return value from interface property.""" initial = { "state": "enable", "controller": "A", "channel": "1", "address": "192.168.1.1", "subnet_mask": "255.255.255.0", "config_method": "static"} controller_request = [ {"physicalLocation": {"slot": 2}, "controllerRef": "070000000000000000000002", "networkSettings": {"remoteAccessEnabled": True}}, {"physicalLocation": {"slot": 1}, "controllerRef": "070000000000000000000001", "networkSettings": {"remoteAccessEnabled": False}}] expected = { "dns_servers": [{"ipv4Address": "10.1.0.250", "addressType": "ipv4"}, {"ipv4Address": "10.10.0.20", "addressType": "ipv4"}], "subnet_mask": "255.255.255.0", "link_status": "up", "ntp_servers": None, "ntp_config_method": "disabled", "controllerRef": "070000000000000000000001", "config_method": "configStatic", "enabled": True, "gateway": "10.1.1.1", "alias": "creG1g-AP-a", "controllerSlot": 1, "dns_config_method": "stat", "id": "2800070000000000000000000001000000000000", "address": "10.1.1.10", "ipv6Enabled": False, "channel": 1} self._set_args(initial) mgmt_interface = MgmtInterface() with mock.patch(self.REQ_FUNC, side_effect=[(200, self.TEST_DATA), (200, controller_request)]): iface = mgmt_interface.interface self.assertTrue(iface == expected) def test_interface_property_request_exception_fail(self): """Verify ethernet-interfaces endpoint request failure results in AnsibleFailJson exception.""" initial = { "state": "enable", "controller": "A", "channel": "1", "address": "192.168.1.1", "subnet_mask": "255.255.255.1", "config_method": "static"} controller_request = [ {"physicalLocation": {"slot": 2}, "controllerRef": "070000000000000000000002", "networkSettings": {"remoteAccessEnabled": True}}, {"physicalLocation": {"slot": 1}, "controllerRef": "070000000000000000000001", "networkSettings": {"remoteAccessEnabled": False}}] self._set_args(initial) mgmt_interface = MgmtInterface() with self.assertRaisesRegexp(AnsibleFailJson, r"Failed to retrieve defined management interfaces."): with mock.patch(self.REQ_FUNC, side_effect=[Exception, (200, controller_request)]): iface = mgmt_interface.interface def test_interface_property_no_match_fail(self): """Verify return value from interface property.""" initial = { "state": "enable", "controller": "A", "name": "wrong_name", "address": "192.168.1.1", "subnet_mask": "255.255.255.1", "config_method": "static"} controller_request = [ {"physicalLocation": {"slot": 2}, "controllerRef": "070000000000000000000002", "networkSettings": {"remoteAccessEnabled": True}}, {"physicalLocation": {"slot": 1}, "controllerRef": "070000000000000000000001", "networkSettings": {"remoteAccessEnabled": False}}] expected = { "dns_servers": [{"ipv4Address": "10.1.0.20", "addressType": "ipv4"}, {"ipv4Address": "10.1.0.50", "addressType": "ipv4"}], "subnet_mask": "255.255.255.0", "ntp_servers": None, "ntp_config_method": "disabled", "controllerRef": "070000000000000000000001", "config_method": "configStatic", "enabled": True, "gateway": "10.1.1.1", "alias": "creG1g-AP-a", "controllerSlot": 1, "dns_config_method": "stat", "id": "2800070000000000000000000001000000000000", "address": "10.1.1.111", "ipv6Enabled": False, "channel": 1} self._set_args(initial) mgmt_interface = MgmtInterface() with self.assertRaisesRegexp(AnsibleFailJson, r"We could not find an interface matching"): with mock.patch(self.REQ_FUNC, side_effect=[(200, self.TEST_DATA), (200, controller_request)]): iface = mgmt_interface.interface def test_get_enable_interface_settings_enabled_pass(self): """Validate get_enable_interface_settings updates properly.""" initial = { "state": "enable", "controller": "A", "name": "wrong_name", "address": "192.168.1.1", "subnet_mask": "255.255.255.1", "config_method": "static"} iface = {"enabled": False} expected_iface = {} self._set_args(initial) mgmt_interface = MgmtInterface() update, expected_iface, body = mgmt_interface.get_enable_interface_settings(iface, expected_iface, False, {}) self.assertTrue(update and expected_iface["enabled"] and body["ipv4Enabled"]) def test_get_enable_interface_settings_disabled_pass(self): """Validate get_enable_interface_settings updates properly.""" initial = { "state": "disable", "controller": "A", "name": "wan0", "address": "192.168.1.1", "subnet_mask": "255.255.255.1", "config_method": "static"} iface = {"enabled": True} expected_iface = {} self._set_args(initial) mgmt_interface = MgmtInterface() update, expected_iface, body = mgmt_interface.get_enable_interface_settings(iface, expected_iface, False, {}) self.assertTrue(update and not expected_iface["enabled"] and not body["ipv4Enabled"]) def test_update_array_interface_ssh_pass(self): """Verify get_interface_settings gives the right static configuration response.""" initial = { "state": "enable", "controller": "A", "name": "wan0", "address": "192.168.1.1", "subnet_mask": "255.255.255.1", "config_method": "static", "ssh": True} iface = {"dns_servers": [{"ipv4Address": "10.1.0.20", "addressType": "ipv4"}, {"ipv4Address": "10.1.0.50", "addressType": "ipv4"}], "subnet_mask": "255.255.255.0", "link_status": "up", "ntp_servers": None, "ntp_config_method": "disabled", "controllerRef": "070000000000000000000001", "config_method": "configStatic", "enabled": True, "gateway": "10.1.1.1", "alias": "creG1g-AP-a", "controllerSlot": 1, "dns_config_method": "stat", "id": "2800070000000000000000000001000000000000", "address": "10.1.1.111", "ipv6Enabled": False, "channel": 1} settings = {"controllerRef": "070000000000000000000001", "ssh": False} self._set_args(initial) mgmt_interface = MgmtInterface() with mock.patch(self.REQ_FUNC, return_value=(200, None)): update = mgmt_interface.update_array(settings, iface) self.assertTrue(update) def test_update_array_dns_static_ntp_disable_pass(self): """Verify get_interface_settings gives the right static configuration response.""" initial = { "controller": "A", "name": "wan0", "dns_config_method": "static", "dns_address": "192.168.1.1", "dns_address_backup": "192.168.1.100", "ntp_config_method": "disable"} iface = {"dns_servers": [{"ipv4Address": "10.1.0.20", "addressType": "ipv4"}, {"ipv4Address": "10.1.0.50", "addressType": "ipv4"}], "subnet_mask": "255.255.255.0", "link_status": "up", "ntp_servers": None, "ntp_config_method": "disabled", "controllerRef": "070000000000000000000001", "config_method": "configStatic", "enabled": True, "gateway": "10.1.1.1", "alias": "creG1g-AP-a", "controllerSlot": 1, "dns_config_method": "configDhcp", "id": "2800070000000000000000000001000000000000", "address": "10.1.1.111", "ipv6Enabled": False, "channel": 1} settings = {"controllerRef": "070000000000000000000001", "ssh": False} self._set_args(initial) mgmt_interface = MgmtInterface() with mock.patch(self.REQ_FUNC, return_value=(200, None)): update = mgmt_interface.update_array(settings, iface) self.assertTrue(update) def test_update_array_dns_dhcp_ntp_static_pass(self): """Verify get_interface_settings gives the right static configuration response.""" initial = { "controller": "A", "name": "wan0", "ntp_config_method": "static", "ntp_address": "192.168.1.1", "ntp_address_backup": "192.168.1.100", "dns_config_method": "dhcp"} iface = {"dns_servers": [{"ipv4Address": "10.1.0.20", "addressType": "ipv4"}, {"ipv4Address": "10.1.0.50", "addressType": "ipv4"}], "subnet_mask": "255.255.255.0", "link_status": "up", "ntp_servers": None, "ntp_config_method": "disabled", "controllerRef": "070000000000000000000001", "config_method": "configStatic", "enabled": True, "gateway": "10.1.1.1", "alias": "creG1g-AP-a", "controllerSlot": 1, "dns_config_method": "configStatic", "id": "2800070000000000000000000001000000000000", "address": "10.1.1.111", "ipv6Enabled": False, "channel": 1} settings = {"controllerRef": "070000000000000000000001", "ssh": False} self._set_args(initial) mgmt_interface = MgmtInterface() with mock.patch(self.REQ_FUNC, return_value=(200, None)): update = mgmt_interface.update_array(settings, iface) self.assertTrue(update) def test_update_array_dns_dhcp_ntp_static_no_change_pass(self): """Verify get_interface_settings gives the right static configuration response.""" initial = { "controller": "A", "name": "wan0", "ntp_config_method": "dhcp", "dns_config_method": "dhcp"} iface = {"dns_servers": [{"ipv4Address": "10.1.0.20", "addressType": "ipv4"}, {"ipv4Address": "10.1.0.50", "addressType": "ipv4"}], "subnet_mask": "255.255.255.0", "ntp_servers": None, "ntp_config_method": "dhcp", "controllerRef": "070000000000000000000001", "config_method": "static", "enabled": True, "gateway": "10.1.1.1", "alias": "creG1g-AP-a", "controllerSlot": 1, "dns_config_method": "dhcp", "id": "2800070000000000000000000001000000000000", "address": "10.1.1.11", "ipv6Enabled": False, "channel": 1} settings = {"controllerRef": "070000000000000000000001", "ssh": False} self._set_args(initial) mgmt_interface = MgmtInterface() with mock.patch(self.REQ_FUNC, return_value=(200, None)): update = mgmt_interface.update_array(settings, iface) self.assertFalse(update) def test_update_array_ipv4_ipv6_disabled_fail(self): """Verify exception is thrown when both ipv4 and ipv6 would be disabled at the same time.""" initial = { "state": "disable", "controller": "A", "name": "wan0", "address": "192.168.1.1", "subnet_mask": "255.255.255.1", "config_method": "static", "ssh": True} iface = {"dns_servers": [{"ipv4Address": "10.1.0.20", "addressType": "ipv4"}, {"ipv4Address": "10.1.0.50", "addressType": "ipv4"}], "subnet_mask": "255.255.255.0", "ntp_servers": None, "ntp_config_method": "disabled", "controllerRef": "070000000000000000000001", "config_method": "configStatic", "enabled": True, "gateway": "10.1.1.1", "alias": "creG1g-AP-a", "controllerSlot": 1, "dns_config_method": "stat", "id": "2800070000000000000000000001000000000000", "address": "10.1.1.11", "ipv6Enabled": False, "channel": 1} settings = {"controllerRef": "070000000000000000000001", "ssh": False} self._set_args(initial) mgmt_interface = MgmtInterface() with self.assertRaisesRegexp(AnsibleFailJson, r"This storage-system already has IPv6 connectivity disabled."): with mock.patch(self.REQ_FUNC, return_value=(422, dict(ipv4Enabled=False, retcode="4", errorMessage=""))): mgmt_interface.update_array(settings, iface) def test_update_array_request_error_fail(self): """Verify exception is thrown when request results in an error.""" initial = { "state": "disable", "controller": "A", "name": "wan0", "address": "192.168.1.1", "subnet_mask": "255.255.255.1", "config_method": "static", "ssh": True} iface = {"dns_servers": [{"ipv4Address": "10.1.0.20", "addressType": "ipv4"}, {"ipv4Address": "10.1.0.50", "addressType": "ipv4"}], "subnet_mask": "255.255.255.0", "ntp_servers": None, "ntp_config_method": "disabled", "controllerRef": "070000000000000000000001", "config_method": "configStatic", "enabled": True, "gateway": "10.1.1.1", "alias": "creG1g-AP-a", "controllerSlot": 1, "dns_config_method": "stat", "id": "2800070000000000000000000001000000000000", "address": "10.1.1.111", "ipv6Enabled": False, "channel": 1} settings = {"controllerRef": "070000000000000000000001", "ssh": False} self._set_args(initial) mgmt_interface = MgmtInterface() with self.assertRaisesRegexp(AnsibleFailJson, r"We failed to configure the management interface."): with mock.patch(self.REQ_FUNC, return_value=(300, dict(ipv4Enabled=False, retcode="4", errorMessage=""))): mgmt_interface.update_array(settings, iface) def test_update_pass(self): """Validate update method completes.""" initial = { "state": "enable", "controller": "A", "channel": "1", "address": "192.168.1.1", "subnet_mask": "255.255.255.1", "config_method": "static", "ssh": "yes"} controller_request = [ {"physicalLocation": {"slot": 2}, "controllerRef": "070000000000000000000002", "networkSettings": {"remoteAccessEnabled": True}}, {"physicalLocation": {"slot": 1}, "controllerRef": "070000000000000000000001", "networkSettings": {"remoteAccessEnabled": False}}] expected = { "dns_servers": [{"ipv4Address": "10.1.0.20", "addressType": "ipv4"}, {"ipv4Address": "10.1.0.50", "addressType": "ipv4"}], "subnet_mask": "255.255.255.0", "ntp_servers": None, "ntp_config_method": "disabled", "controllerRef": "070000000000000000000001", "config_method": "configStatic", "enabled": True, "gateway": "10.1.1.1", "alias": "creG1g-AP-a", "controllerSlot": 1, "dns_config_method": "stat", "id": "2800070000000000000000000001000000000000", "address": "10.1.1.111", "ipv6Enabled": False, "channel": 1} self._set_args(initial) mgmt_interface = MgmtInterface() with self.assertRaisesRegexp(AnsibleExitJson, r"The interface settings have been updated."): with mock.patch(self.REQ_FUNC, side_effect=[(200, None), (200, controller_request), (200, self.TEST_DATA), (200, controller_request), (200, self.TEST_DATA)]): mgmt_interface.update()