mirror of
https://github.com/ansible-collections/community.general.git
synced 2024-09-14 20:13:21 +02:00
Define netapp_e_mgmt_interface storage module. (#42519)
* Define netapp_e_mgmt_interface storage module. The netapp_e_mgmt_interface module provides management configuration for e-series storage array interface points including remote ssh access, NTP and DNS services. This patch also includes integration and unit tests. * Fix netapp_e_mgmt_interface ssh option documentation and name/channel exclusivity.
This commit is contained in:
parent
26545ecac9
commit
d31e25acbd
2 changed files with 1392 additions and 0 deletions
708
lib/ansible/modules/storage/netapp/netapp_e_mgmt_interface.py
Normal file
708
lib/ansible/modules/storage/netapp/netapp_e_mgmt_interface.py
Normal file
|
@ -0,0 +1,708 @@
|
|||
#!/usr/bin/python
|
||||
|
||||
# (c) 2018, NetApp, Inc
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
from __future__ import absolute_import, division, print_function
|
||||
|
||||
__metaclass__ = type
|
||||
|
||||
ANSIBLE_METADATA = {'metadata_version': '1.1',
|
||||
'status': ['preview'],
|
||||
'supported_by': 'community'}
|
||||
|
||||
DOCUMENTATION = """
|
||||
---
|
||||
module: netapp_e_mgmt_interface
|
||||
short_description: NetApp E-Series management interface configuration
|
||||
description:
|
||||
- Configure the E-Series management interfaces
|
||||
version_added: '2.7'
|
||||
author:
|
||||
- Michael Price (@lmprice)
|
||||
- Nathan Swartz (@ndswartz)
|
||||
extends_documentation_fragment:
|
||||
- netapp.eseries
|
||||
options:
|
||||
state:
|
||||
description:
|
||||
- Enable or disable IPv4 network interface configuration.
|
||||
- Either IPv4 or IPv6 must be enabled otherwise error will occur.
|
||||
- Only required when enabling or disabling IPv4 network interface
|
||||
choices:
|
||||
- enable
|
||||
- disable
|
||||
required: no
|
||||
aliases:
|
||||
- enable_interface
|
||||
controller:
|
||||
description:
|
||||
- The controller that owns the port you want to configure.
|
||||
- Controller names are represented alphabetically, with the first controller as A,
|
||||
the second as B, and so on.
|
||||
- Current hardware models have either 1 or 2 available controllers, but that is not a guaranteed hard
|
||||
limitation and could change in the future.
|
||||
required: yes
|
||||
choices:
|
||||
- A
|
||||
- B
|
||||
name:
|
||||
description:
|
||||
- The port to modify the configuration for.
|
||||
- The list of choices is not necessarily comprehensive. It depends on the number of ports
|
||||
that are present in the system.
|
||||
- The name represents the port number (typically from left to right on the controller),
|
||||
beginning with a value of 1.
|
||||
- Mutually exclusive with I(channel).
|
||||
aliases:
|
||||
- port
|
||||
- iface
|
||||
channel:
|
||||
description:
|
||||
- The port to modify the configuration for.
|
||||
- The channel represents the port number (typically from left to right on the controller),
|
||||
beginning with a value of 1.
|
||||
- Mutually exclusive with I(name).
|
||||
address:
|
||||
description:
|
||||
- The IPv4 address to assign to the interface.
|
||||
- Should be specified in xx.xx.xx.xx form.
|
||||
- Mutually exclusive with I(config_method=dhcp)
|
||||
required: no
|
||||
subnet_mask:
|
||||
description:
|
||||
- The subnet mask to utilize for the interface.
|
||||
- Should be specified in xx.xx.xx.xx form.
|
||||
- Mutually exclusive with I(config_method=dhcp)
|
||||
required: no
|
||||
gateway:
|
||||
description:
|
||||
- The IPv4 gateway address to utilize for the interface.
|
||||
- Should be specified in xx.xx.xx.xx form.
|
||||
- Mutually exclusive with I(config_method=dhcp)
|
||||
required: no
|
||||
config_method:
|
||||
description:
|
||||
- The configuration method type to use for network interface ports.
|
||||
- dhcp is mutually exclusive with I(address), I(subnet_mask), and I(gateway).
|
||||
choices:
|
||||
- dhcp
|
||||
- static
|
||||
required: no
|
||||
dns_config_method:
|
||||
description:
|
||||
- The configuration method type to use for DNS services.
|
||||
- dhcp is mutually exclusive with I(dns_address), and I(dns_address_backup).
|
||||
choices:
|
||||
- dhcp
|
||||
- static
|
||||
required: no
|
||||
dns_address:
|
||||
description:
|
||||
- Primary IPv4 DNS server address
|
||||
required: no
|
||||
dns_address_backup:
|
||||
description:
|
||||
- Backup IPv4 DNS server address
|
||||
- Queried when primary DNS server fails
|
||||
required: no
|
||||
ntp_config_method:
|
||||
description:
|
||||
- The configuration method type to use for NTP services.
|
||||
- disable is mutually exclusive with I(ntp_address) and I(ntp_address_backup).
|
||||
- dhcp is mutually exclusive with I(ntp_address) and I(ntp_address_backup).
|
||||
choices:
|
||||
- disable
|
||||
- dhcp
|
||||
- static
|
||||
required: no
|
||||
ntp_address:
|
||||
description:
|
||||
- Primary IPv4 NTP server address
|
||||
required: no
|
||||
ntp_address_backup:
|
||||
description:
|
||||
- Backup IPv4 NTP server address
|
||||
- Queried when primary NTP server fails
|
||||
required: no
|
||||
ssh:
|
||||
type: bool
|
||||
description:
|
||||
- Enable ssh access to the controller for debug purposes.
|
||||
- This is a controller-level setting.
|
||||
- rlogin/telnet will be enabled for ancient equipment where ssh is not available.
|
||||
required: no
|
||||
log_path:
|
||||
description:
|
||||
- A local path to a file to be used for debug logging
|
||||
required: no
|
||||
notes:
|
||||
- Check mode is supported.
|
||||
- The interface settings are applied synchronously, but changes to the interface itself (receiving a new IP address
|
||||
via dhcp, etc), can take seconds or minutes longer to take effect.
|
||||
- "Known issue: Changes specifically to down ports will result in a failure. However, this may not be the case in up
|
||||
coming NetApp E-Series firmware releases (released after firmware version 11.40.2)."
|
||||
"""
|
||||
|
||||
EXAMPLES = """
|
||||
- name: Configure the first port on the A controller with a static IPv4 address
|
||||
netapp_e_mgmt_interface:
|
||||
name: "1"
|
||||
controller: "A"
|
||||
config_method: static
|
||||
address: "192.168.1.100"
|
||||
subnet_mask: "255.255.255.0"
|
||||
gateway: "192.168.1.1"
|
||||
ssid: "1"
|
||||
api_url: "10.1.1.1:8443"
|
||||
api_username: "admin"
|
||||
api_password: "myPass"
|
||||
|
||||
- name: Disable ipv4 connectivity for the second port on the B controller
|
||||
netapp_e_mgmt_interface:
|
||||
name: "2"
|
||||
controller: "B"
|
||||
enable_interface: no
|
||||
ssid: "{{ ssid }}"
|
||||
api_url: "{{ netapp_api_url }}"
|
||||
api_username: "{{ netapp_api_username }}"
|
||||
api_password: "{{ netapp_api_password }}"
|
||||
|
||||
- name: Enable ssh access for ports one and two on controller A
|
||||
netapp_e_mgmt_interface:
|
||||
name: "{{ item }}"
|
||||
controller: "A"
|
||||
ssh: yes
|
||||
ssid: "{{ ssid }}"
|
||||
api_url: "{{ netapp_api_url }}"
|
||||
api_username: "{{ netapp_api_username }}"
|
||||
api_password: "{{ netapp_api_password }}"
|
||||
loop:
|
||||
- 1
|
||||
- 2
|
||||
|
||||
- name: Configure static DNS settings for the first port on controller A
|
||||
netapp_e_mgmt_interface:
|
||||
name: "1"
|
||||
controller: "A"
|
||||
dns_config_method: static
|
||||
dns_address: "192.168.1.100"
|
||||
dns_address_backup: "192.168.1.1"
|
||||
ssid: "{{ ssid }}"
|
||||
api_url: "{{ netapp_api_url }}"
|
||||
api_username: "{{ netapp_api_username }}"
|
||||
api_password: "{{ netapp_api_password }}"
|
||||
|
||||
- name: Configure static NTP settings for ports one and two on controller B
|
||||
netapp_e_mgmt_interface:
|
||||
name: "{{ item }}"
|
||||
controller: "B"
|
||||
ntp_config_method: static
|
||||
ntp_address: "129.100.1.100"
|
||||
ntp_address_backup: "127.100.1.1"
|
||||
ssid: "{{ ssid }}"
|
||||
api_url: "{{ netapp_api_url }}"
|
||||
api_username: "{{ netapp_api_username }}"
|
||||
api_password: "{{ netapp_api_password }}"
|
||||
loop:
|
||||
- 1
|
||||
- 2
|
||||
"""
|
||||
|
||||
RETURN = """
|
||||
msg:
|
||||
description: Success message
|
||||
returned: on success
|
||||
type: string
|
||||
sample: The interface settings have been updated.
|
||||
enabled:
|
||||
description:
|
||||
- Indicates whether IPv4 connectivity has been enabled or disabled.
|
||||
- This does not necessarily indicate connectivity. If dhcp was enabled absent a dhcp server, for instance,
|
||||
it is unlikely that the configuration will actually be valid.
|
||||
returned: on success
|
||||
sample: True
|
||||
type: bool
|
||||
"""
|
||||
import json
|
||||
import logging
|
||||
from pprint import pformat, pprint
|
||||
import time
|
||||
import socket
|
||||
|
||||
try:
|
||||
import urlparse
|
||||
except ImportError:
|
||||
import urllib.parse as urlparse
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible.module_utils.netapp import request, eseries_host_argument_spec
|
||||
from ansible.module_utils._text import to_native
|
||||
|
||||
HEADERS = {
|
||||
"Content-Type": "application/json",
|
||||
"Accept": "application/json",
|
||||
}
|
||||
|
||||
|
||||
class MgmtInterface(object):
|
||||
MAX_RETRIES = 15
|
||||
|
||||
def __init__(self):
|
||||
argument_spec = eseries_host_argument_spec()
|
||||
argument_spec.update(dict(
|
||||
state=dict(type="str", choices=["enable", "disable"],
|
||||
aliases=["enable_interface"], required=False),
|
||||
controller=dict(type="str", required=True, choices=["A", "B"]),
|
||||
name=dict(type="str", aliases=["port", "iface"]),
|
||||
channel=dict(type="int"),
|
||||
address=dict(type="str", required=False),
|
||||
subnet_mask=dict(type="str", required=False),
|
||||
gateway=dict(type="str", required=False),
|
||||
config_method=dict(type="str", required=False, choices=["dhcp", "static"]),
|
||||
dns_config_method=dict(type="str", required=False, choices=["dhcp", "static"]),
|
||||
dns_address=dict(type="str", required=False),
|
||||
dns_address_backup=dict(type="str", required=False),
|
||||
ntp_config_method=dict(type="str", required=False, choices=["disable", "dhcp", "static"]),
|
||||
ntp_address=dict(type="str", required=False),
|
||||
ntp_address_backup=dict(type="str", required=False),
|
||||
ssh=dict(type="bool", required=False),
|
||||
log_path=dict(type="str", required=False),
|
||||
))
|
||||
|
||||
required_if = [
|
||||
["state", "enable", ["config_method"]],
|
||||
["config_method", "static", ["address", "subnet_mask"]],
|
||||
["dns_config_method", "static", ["dns_address"]],
|
||||
["ntp_config_method", "static", ["ntp_address"]],
|
||||
]
|
||||
|
||||
mutually_exclusive = [
|
||||
["name", "channel"],
|
||||
]
|
||||
|
||||
self.module = AnsibleModule(argument_spec=argument_spec,
|
||||
supports_check_mode=True,
|
||||
required_if=required_if,
|
||||
mutually_exclusive=mutually_exclusive)
|
||||
args = self.module.params
|
||||
|
||||
self.controller = args["controller"]
|
||||
self.name = args["name"]
|
||||
self.channel = args["channel"]
|
||||
|
||||
self.config_method = args["config_method"]
|
||||
self.address = args["address"]
|
||||
self.subnet_mask = args["subnet_mask"]
|
||||
self.gateway = args["gateway"]
|
||||
self.enable_interface = None if args["state"] is None else args["state"] == "enable"
|
||||
|
||||
self.dns_config_method = args["dns_config_method"]
|
||||
self.dns_address = args["dns_address"]
|
||||
self.dns_address_backup = args["dns_address_backup"]
|
||||
|
||||
self.ntp_config_method = args["ntp_config_method"]
|
||||
self.ntp_address = args["ntp_address"]
|
||||
self.ntp_address_backup = args["ntp_address_backup"]
|
||||
|
||||
self.ssh = args["ssh"]
|
||||
|
||||
self.ssid = args["ssid"]
|
||||
self.url = args["api_url"]
|
||||
self.creds = dict(url_password=args["api_password"],
|
||||
validate_certs=args["validate_certs"],
|
||||
url_username=args["api_username"], )
|
||||
|
||||
self.retries = 0
|
||||
|
||||
self.check_mode = self.module.check_mode
|
||||
self.post_body = dict()
|
||||
|
||||
log_path = args["log_path"]
|
||||
|
||||
# logging setup
|
||||
self._logger = logging.getLogger(self.__class__.__name__)
|
||||
|
||||
if log_path:
|
||||
logging.basicConfig(
|
||||
level=logging.DEBUG, filename=log_path, filemode='w',
|
||||
format='%(relativeCreated)dms %(levelname)s %(module)s.%(funcName)s:%(lineno)d\n %(message)s')
|
||||
|
||||
if not self.url.endswith('/'):
|
||||
self.url += '/'
|
||||
|
||||
@property
|
||||
def controllers(self):
|
||||
"""Retrieve a mapping of controller labels to their references
|
||||
{
|
||||
'A': '070000000000000000000001',
|
||||
'B': '070000000000000000000002',
|
||||
}
|
||||
:return: the controllers defined on the system
|
||||
"""
|
||||
try:
|
||||
(rc, controllers) = request(self.url + 'storage-systems/%s/controllers'
|
||||
% self.ssid, headers=HEADERS, **self.creds)
|
||||
except Exception as err:
|
||||
controllers = list()
|
||||
self.module.fail_json(
|
||||
msg="Failed to retrieve the controller settings. Array Id [%s]. Error [%s]."
|
||||
% (self.ssid, to_native(err)))
|
||||
|
||||
controllers.sort(key=lambda c: c['physicalLocation']['slot'])
|
||||
|
||||
controllers_dict = dict()
|
||||
i = ord('A')
|
||||
for controller in controllers:
|
||||
label = chr(i)
|
||||
settings = dict(controllerSlot=controller['physicalLocation']['slot'],
|
||||
controllerRef=controller['controllerRef'],
|
||||
ssh=controller['networkSettings']['remoteAccessEnabled'])
|
||||
controllers_dict[label] = settings
|
||||
i += 1
|
||||
|
||||
return controllers_dict
|
||||
|
||||
@property
|
||||
def interface(self):
|
||||
net_interfaces = list()
|
||||
try:
|
||||
(rc, net_interfaces) = request(self.url + 'storage-systems/%s/configuration/ethernet-interfaces'
|
||||
% self.ssid, headers=HEADERS, **self.creds)
|
||||
except Exception as err:
|
||||
self.module.fail_json(
|
||||
msg="Failed to retrieve defined management interfaces. Array Id [%s]. Error [%s]."
|
||||
% (self.ssid, to_native(err)))
|
||||
|
||||
controllers = self.controllers
|
||||
controller = controllers[self.controller]
|
||||
|
||||
net_interfaces = [iface for iface in net_interfaces if iface["controllerRef"] == controller["controllerRef"]]
|
||||
|
||||
# Find the correct interface
|
||||
iface = None
|
||||
for net in net_interfaces:
|
||||
if self.name:
|
||||
if net["alias"] == self.name or net["interfaceName"] == self.name:
|
||||
iface = net
|
||||
break
|
||||
elif self.channel:
|
||||
if net["channel"] == self.channel:
|
||||
iface = net
|
||||
break
|
||||
|
||||
if iface is None:
|
||||
identifier = self.name if self.name is not None else self.channel
|
||||
self.module.fail_json(msg="We could not find an interface matching [%s] on Array=[%s]."
|
||||
% (identifier, self.ssid))
|
||||
|
||||
return dict(alias=iface["alias"],
|
||||
channel=iface["channel"],
|
||||
link_status=iface["linkStatus"],
|
||||
enabled=iface["ipv4Enabled"],
|
||||
address=iface["ipv4Address"],
|
||||
gateway=iface["ipv4GatewayAddress"],
|
||||
subnet_mask=iface["ipv4SubnetMask"],
|
||||
dns_config_method=iface["dnsProperties"]["acquisitionProperties"]["dnsAcquisitionType"],
|
||||
dns_servers=iface["dnsProperties"]["acquisitionProperties"]["dnsServers"],
|
||||
ntp_config_method=iface["ntpProperties"]["acquisitionProperties"]["ntpAcquisitionType"],
|
||||
ntp_servers=iface["ntpProperties"]["acquisitionProperties"]["ntpServers"],
|
||||
config_method=iface["ipv4AddressConfigMethod"],
|
||||
controllerRef=iface["controllerRef"],
|
||||
controllerSlot=iface["controllerSlot"],
|
||||
ipv6Enabled=iface["ipv6Enabled"],
|
||||
id=iface["interfaceRef"], )
|
||||
|
||||
def get_enable_interface_settings(self, iface, expected_iface, update, body):
|
||||
"""Enable or disable the IPv4 network interface."""
|
||||
if self.enable_interface:
|
||||
if not iface["enabled"]:
|
||||
update = True
|
||||
body["ipv4Enabled"] = True
|
||||
else:
|
||||
if iface["enabled"]:
|
||||
update = True
|
||||
body["ipv4Enabled"] = False
|
||||
|
||||
expected_iface["enabled"] = body["ipv4Enabled"]
|
||||
return update, expected_iface, body
|
||||
|
||||
def get_interface_settings(self, iface, expected_iface, update, body):
|
||||
"""Update network interface settings."""
|
||||
|
||||
if self.config_method == "dhcp":
|
||||
if iface["config_method"] != "configDhcp":
|
||||
update = True
|
||||
body["ipv4AddressConfigMethod"] = "configDhcp"
|
||||
|
||||
else:
|
||||
if iface["config_method"] != "configStatic":
|
||||
update = True
|
||||
body["ipv4AddressConfigMethod"] = "configStatic"
|
||||
|
||||
if iface["address"] != self.address:
|
||||
update = True
|
||||
body["ipv4Address"] = self.address
|
||||
|
||||
if iface["subnet_mask"] != self.subnet_mask:
|
||||
update = True
|
||||
body["ipv4SubnetMask"] = self.subnet_mask
|
||||
|
||||
if self.gateway and iface["gateway"] != self.gateway:
|
||||
update = True
|
||||
body["ipv4GatewayAddress"] = self.gateway
|
||||
|
||||
expected_iface["address"] = body["ipv4Address"]
|
||||
expected_iface["subnet_mask"] = body["ipv4SubnetMask"]
|
||||
expected_iface["gateway"] = body["ipv4GatewayAddress"]
|
||||
|
||||
expected_iface["config_method"] = body["ipv4AddressConfigMethod"]
|
||||
|
||||
return update, expected_iface, body
|
||||
|
||||
def get_dns_server_settings(self, iface, expected_iface, update, body):
|
||||
"""Add DNS server information to the request body."""
|
||||
if self.dns_config_method == "dhcp":
|
||||
if iface["dns_config_method"] != "dhcp":
|
||||
update = True
|
||||
body["dnsAcquisitionDescriptor"] = dict(dnsAcquisitionType="dhcp")
|
||||
|
||||
elif self.dns_config_method == "static":
|
||||
dns_servers = [dict(addressType="ipv4", ipv4Address=self.dns_address)]
|
||||
if self.dns_address_backup:
|
||||
dns_servers.append(dict(addressType="ipv4", ipv4Address=self.dns_address_backup))
|
||||
|
||||
body["dnsAcquisitionDescriptor"] = dict(dnsAcquisitionType="stat", dnsServers=dns_servers)
|
||||
|
||||
if (iface["dns_config_method"] != "stat" or
|
||||
len(iface["dns_servers"]) != len(dns_servers) or
|
||||
(len(iface["dns_servers"]) == 2 and
|
||||
(iface["dns_servers"][0]["ipv4Address"] != self.dns_address or
|
||||
iface["dns_servers"][1]["ipv4Address"] != self.dns_address_backup)) or
|
||||
(len(iface["dns_servers"]) == 1 and
|
||||
iface["dns_servers"][0]["ipv4Address"] != self.dns_address)):
|
||||
update = True
|
||||
|
||||
expected_iface["dns_servers"] = dns_servers
|
||||
|
||||
expected_iface["dns_config_method"] = body["dnsAcquisitionDescriptor"]["dnsAcquisitionType"]
|
||||
return update, expected_iface, body
|
||||
|
||||
def get_ntp_server_settings(self, iface, expected_iface, update, body):
|
||||
"""Add NTP server information to the request body."""
|
||||
if self.ntp_config_method == "disable":
|
||||
if iface["ntp_config_method"] != "disabled":
|
||||
update = True
|
||||
body["ntpAcquisitionDescriptor"] = dict(ntpAcquisitionType="disabled")
|
||||
|
||||
elif self.ntp_config_method == "dhcp":
|
||||
if iface["ntp_config_method"] != "dhcp":
|
||||
update = True
|
||||
body["ntpAcquisitionDescriptor"] = dict(ntpAcquisitionType="dhcp")
|
||||
|
||||
elif self.ntp_config_method == "static":
|
||||
ntp_servers = [dict(addrType="ipvx", ipvxAddress=dict(addressType="ipv4", ipv4Address=self.ntp_address))]
|
||||
if self.ntp_address_backup:
|
||||
ntp_servers.append(dict(addrType="ipvx",
|
||||
ipvxAddress=dict(addressType="ipv4", ipv4Address=self.ntp_address_backup)))
|
||||
|
||||
body["ntpAcquisitionDescriptor"] = dict(ntpAcquisitionType="stat", ntpServers=ntp_servers)
|
||||
|
||||
if (iface["ntp_config_method"] != "stat" or
|
||||
len(iface["ntp_servers"]) != len(ntp_servers) or
|
||||
((len(iface["ntp_servers"]) == 2 and
|
||||
(iface["ntp_servers"][0]["ipvxAddress"]["ipv4Address"] != self.ntp_address or
|
||||
iface["ntp_servers"][1]["ipvxAddress"]["ipv4Address"] != self.ntp_address_backup)) or
|
||||
(len(iface["ntp_servers"]) == 1 and
|
||||
iface["ntp_servers"][0]["ipvxAddress"]["ipv4Address"] != self.ntp_address))):
|
||||
update = True
|
||||
|
||||
expected_iface["ntp_servers"] = ntp_servers
|
||||
|
||||
expected_iface["ntp_config_method"] = body["ntpAcquisitionDescriptor"]["ntpAcquisitionType"]
|
||||
return update, expected_iface, body
|
||||
|
||||
def get_remote_ssh_settings(self, settings, update, body):
|
||||
"""Configure network interface ports for remote ssh access."""
|
||||
if self.ssh != settings["ssh"]:
|
||||
update = True
|
||||
|
||||
body["enableRemoteAccess"] = self.ssh
|
||||
return update, body
|
||||
|
||||
def update_array(self, settings, iface):
|
||||
"""Update controller with new interface, dns service, ntp service and/or remote ssh access information.
|
||||
|
||||
:returns: whether information passed will modify the controller's current state
|
||||
:rtype: bool
|
||||
"""
|
||||
update = False
|
||||
body = dict(controllerRef=settings['controllerRef'],
|
||||
interfaceRef=iface['id'])
|
||||
expected_iface = iface.copy()
|
||||
|
||||
# Check if api url is using the effected management interface to change itself
|
||||
update_used_matching_address = False
|
||||
if self.enable_interface and self.config_method:
|
||||
netloc = list(urlparse.urlparse(self.url))[1]
|
||||
address = netloc.split(":")[0]
|
||||
address_info = socket.getaddrinfo(address, 8443)
|
||||
url_address_info = socket.getaddrinfo(iface["address"], 8443)
|
||||
update_used_matching_address = any(info in url_address_info for info in address_info)
|
||||
|
||||
self._logger.info("update_used_matching_address: " + str(update_used_matching_address))
|
||||
|
||||
# Populate the body of the request and check for changes
|
||||
if self.enable_interface is not None:
|
||||
update, expected_iface, body = self.get_enable_interface_settings(iface, expected_iface, update, body)
|
||||
|
||||
if self.config_method is not None:
|
||||
update, expected_iface, body = self.get_interface_settings(iface, expected_iface, update, body)
|
||||
|
||||
if self.dns_config_method is not None:
|
||||
update, expected_iface, body = self.get_dns_server_settings(iface, expected_iface, update, body)
|
||||
|
||||
if self.ntp_config_method is not None:
|
||||
update, expected_iface, body = self.get_ntp_server_settings(iface, expected_iface, update, body)
|
||||
|
||||
if self.ssh is not None:
|
||||
update, body = self.get_remote_ssh_settings(settings, update, body)
|
||||
iface["ssh"] = self.ssh
|
||||
expected_iface["ssh"] = self.ssh
|
||||
|
||||
# debug information
|
||||
self._logger.info(pformat(body))
|
||||
self._logger.info(pformat(iface))
|
||||
self._logger.info(pformat(expected_iface))
|
||||
|
||||
if self.check_mode:
|
||||
return update
|
||||
|
||||
if update and not self.check_mode:
|
||||
if not update_used_matching_address:
|
||||
try:
|
||||
(rc, data) = request(self.url + 'storage-systems/%s/configuration/ethernet-interfaces'
|
||||
% self.ssid, method='POST', data=json.dumps(body), headers=HEADERS,
|
||||
timeout=300, ignore_errors=True, **self.creds)
|
||||
if rc == 422:
|
||||
if data['retcode'] == "4" or data['retcode'] == "illegalParam":
|
||||
if not (body['ipv4Enabled'] or iface['ipv6Enabled']):
|
||||
self.module.fail_json(msg="This storage-system already has IPv6 connectivity disabled. "
|
||||
"DHCP configuration for IPv4 is required at a minimum."
|
||||
" Array Id [%s] Message [%s]."
|
||||
% (self.ssid, data['errorMessage']))
|
||||
else:
|
||||
self.module.fail_json(msg="We failed to configure the management interface. Array Id "
|
||||
"[%s] Message [%s]." % (self.ssid, data))
|
||||
elif rc >= 300:
|
||||
self.module.fail_json(
|
||||
msg="We failed to configure the management interface. Array Id [%s] Message [%s]." %
|
||||
(self.ssid, data))
|
||||
|
||||
# This is going to catch cases like a connection failure
|
||||
except Exception as err:
|
||||
self.module.fail_json(
|
||||
msg="Connection failure: we failed to modify the network settings! Array Id [%s]. Error [%s]."
|
||||
% (self.ssid, to_native(err)))
|
||||
else:
|
||||
self.update_api_address_interface_match(body)
|
||||
|
||||
return self.validate_changes(expected_iface) if update and iface["link_status"] != "up" else update
|
||||
|
||||
def update_api_address_interface_match(self, body):
|
||||
"""Change network interface address which matches the api_address"""
|
||||
try:
|
||||
try:
|
||||
(rc, data) = request(self.url + 'storage-systems/%s/configuration/ethernet-interfaces' % self.ssid,
|
||||
use_proxy=False, force=True, ignore_errors=True, method='POST',
|
||||
data=json.dumps(body), headers=HEADERS, timeout=10, **self.creds)
|
||||
except Exception:
|
||||
url_parts = list(urlparse.urlparse(self.url))
|
||||
domain = url_parts[1].split(":")
|
||||
domain[0] = self.address
|
||||
url_parts[1] = ":".join(domain)
|
||||
expected_url = urlparse.urlunparse(url_parts)
|
||||
self._logger.info(pformat(expected_url))
|
||||
|
||||
(rc, data) = request(expected_url + 'storage-systems/%s/configuration/ethernet-interfaces' % self.ssid,
|
||||
headers=HEADERS, timeout=300, **self.creds)
|
||||
return
|
||||
except Exception as err:
|
||||
self._logger.info(type(err))
|
||||
self.module.fail_json(
|
||||
msg="Connection failure: we failed to modify the network settings! Array Id [%s]. Error [%s]."
|
||||
% (self.ssid, to_native(err)))
|
||||
|
||||
def validate_changes(self, expected_iface, retry=6):
|
||||
"""Validate interface changes were applied to the controller interface port. 30 second timeout"""
|
||||
if self.interface != expected_iface:
|
||||
time.sleep(5)
|
||||
if retry:
|
||||
return self.validate_changes(expected_iface, retry - 1)
|
||||
|
||||
self.module.fail_json(msg="Update failure: we failed to verify the necessary state change.")
|
||||
|
||||
return True
|
||||
|
||||
def check_health(self):
|
||||
"""It's possible, due to a previous operation, for the API to report a 424 (offline) status for the
|
||||
storage-system. Therefore, we run a manual check with retries to attempt to contact the system before we
|
||||
continue.
|
||||
"""
|
||||
try:
|
||||
(rc, data) = request(self.url + 'storage-systems/%s/controllers'
|
||||
% self.ssid, headers=HEADERS,
|
||||
ignore_errors=True, **self.creds)
|
||||
|
||||
# We've probably recently changed the interface settings and it's still coming back up: retry.
|
||||
if rc == 424:
|
||||
if self.retries < self.MAX_RETRIES:
|
||||
self.retries += 1
|
||||
self._logger.info("We hit a 424, retrying in 5s.")
|
||||
time.sleep(5)
|
||||
self.check_health()
|
||||
else:
|
||||
self.module.fail_json(
|
||||
msg="We failed to pull storage-system information. Array Id [%s] Message [%s]." %
|
||||
(self.ssid, data))
|
||||
elif rc >= 300:
|
||||
self.module.fail_json(
|
||||
msg="We failed to pull storage-system information. Array Id [%s] Message [%s]." %
|
||||
(self.ssid, data))
|
||||
# This is going to catch cases like a connection failure
|
||||
except Exception as err:
|
||||
if self.retries < self.MAX_RETRIES:
|
||||
self._logger.info("We hit a connection failure, retrying in 5s.")
|
||||
self.retries += 1
|
||||
time.sleep(5)
|
||||
self.check_health()
|
||||
else:
|
||||
self.module.fail_json(
|
||||
msg="Connection failure: we failed to modify the network settings! Array Id [%s]. Error [%s]."
|
||||
% (self.ssid, to_native(err)))
|
||||
|
||||
def update(self):
|
||||
"""Update storage system with necessary changes."""
|
||||
# Check if the storage array can be contacted
|
||||
self.check_health()
|
||||
|
||||
# make the necessary changes to the storage system
|
||||
settings = self.controllers[self.controller]
|
||||
iface = self.interface
|
||||
self._logger.info(pformat(settings))
|
||||
self._logger.info(pformat(iface))
|
||||
update = self.update_array(settings, iface)
|
||||
|
||||
self.module.exit_json(msg="The interface settings have been updated.", changed=update)
|
||||
|
||||
def __call__(self, *args, **kwargs):
|
||||
self.update()
|
||||
|
||||
|
||||
def main():
|
||||
iface = MgmtInterface()
|
||||
iface()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
|
@ -0,0 +1,684 @@
|
|||
# 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 ansible.compat.tests.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()
|
Loading…
Reference in a new issue