File: //usr/lib/python3.9/site-packages/ansible_collections/dellemc/unity/plugins/modules/interface.py
# Copyright: (c) 2022, Dell Technologies
# Apache License version 2.0 (see MODULE-LICENSE or http://www.apache.org/licenses/LICENSE-2.0.txt)
"""Ansible module for managing Interfaces on Unity"""
from __future__ import absolute_import, division, print_function
__metaclass__ = type
DOCUMENTATION = r'''
module: interface
version_added: '1.4.0'
short_description: Manage Interfaces on Unity storage system
description:
- Managing the Interfaces on the Unity storage system includes adding Interfaces to NAS Server, getting
details of interface and deleting configured interfaces.
extends_documentation_fragment:
- dellemc.unity.unity
author:
- Meenakshi Dembi (@dembim) <[email protected]>
options:
nas_server_name:
description:
- Name of the NAS server for which interface will be configured.
type: str
nas_server_id:
description:
- ID of the NAS server for which interface will be configured.
type: str
ethernet_port_name:
description:
- Name of the ethernet port.
type: str
ethernet_port_id:
description:
- ID of the ethernet port.
type: str
role:
description:
- Indicates whether interface is configured as production or backup.
choices: [PRODUCTION, BACKUP]
type: str
interface_ip:
description:
- IP of network interface.
required: true
type: str
netmask:
description:
- Netmask of network interface.
type: str
prefix_length:
description:
- Prefix length is mutually exclusive with I(netmask).
type: int
gateway:
description:
- Gateway of network interface.
type: str
vlan_id:
description:
- Vlan id of the interface.
type: int
state:
description:
- Define whether the interface should exist or not.
choices: [present, absent]
required: true
type: str
notes:
- The I(check_mode) is supported.
- Modify operation for interface is not supported.
'''
EXAMPLES = r'''
- name: Add Interface as Backup to NAS Server
dellemc.unity.interface:
unispherehost: "{{unispherehost}}"
username: "{{username}}"
password: "{{password}}"
validate_certs: "{{validate_certs}}"
nas_server_name: "dummy_nas"
ethernet_port_name: "SP A 4-Port Card Ethernet Port 0"
role: "BACKUP"
interface_ip: "xx.xx.xx.xx"
netmask: "xx.xx.xx.xx"
gateway: "xx.xx.xx.xx"
vlan_id: 324
state: "present"
- name: Add Interface as Production to NAS Server
dellemc.unity.interface:
unispherehost: "{{unispherehost}}"
username: "{{username}}"
password: "{{password}}"
validate_certs: "{{validate_certs}}"
nas_server_name: "dummy_nas"
ethernet_port_name: "SP A 4-Port Card Ethernet Port 0"
role: "PRODUCTION"
interface_ip: "xx.xx.xx.xx"
netmask: "xx.xx.xx.xx"
gateway: "xx.xx.xx.xx"
vlan_id: 324
state: "present"
- name: Get interface details
dellemc.unity.interface:
unispherehost: "{{unispherehost}}"
username: "{{username}}"
password: "{{password}}"
validate_certs: "{{validate_certs}}"
nas_server_name: "dummy_nas"
interface_ip: "xx.xx.xx.xx"
state: "present"
- name: Delete Interface
dellemc.unity.interface:
unispherehost: "{{unispherehost}}"
username: "{{username}}"
password: "{{password}}"
validate_certs: "{{validate_certs}}"
nas_server_name: "dummy_nas"
interface_ip: "xx.xx.xx.xx"
state: "absent"
'''
RETURN = r'''
changed:
description: Whether or not the resource has changed.
returned: always
type: bool
sample: true
interface_details:
description: Details of the interface.
returned: When interface is configured for NAS Server.
type: dict
contains:
existed:
description: Indicates if interface exists.
type: bool
gateway:
description: Gateway of network interface.
type: str
id:
description: Unique identifier interface.
type: str
ip_address:
description: IP address of interface.
type: str
ip_port:
description: Port on which network interface is configured.
type: dict
contains:
id:
description: ID of ip_port.
type: str
ip_protocol_version:
description: IP protocol version.
type: str
is_disabled:
description: Indicates whether interface is disabled.
type: bool
is_preferred:
description: Indicates whether interface is preferred.
type: bool
mac_address:
description: Mac address of ip_port.
type: bool
name:
description: System configured name of interface.
type: bool
nas_server:
description: Details of NAS server where interface is configured.
type: dict
contains:
id:
description: ID of NAS Server.
type: str
sample: {
"existed": true,
"gateway": "xx.xx.xx.xx",
"hash": 8785300560421,
"health": {
"UnityHealth": {
"hash": 8785300565468
}
},
"id": "if_69",
"ip_address": "10.10.10.10",
"ip_port": {
"UnityIpPort": {
"hash": 8785300565300,
"id": "spb_ocp_0_eth0"
}
},
"ip_protocol_version": "IpProtocolVersionEnum.IPv4",
"is_disabled": false,
"is_preferred": true,
"mac_address": "0C:48:C6:9F:57:BF",
"name": "36_APM00213404194",
"nas_server": {
"UnityNasServer": {
"hash": 8785300565417,
"id": "nas_10"
}
},
"netmask": "10.10.10.10",
"replication_policy": null,
"role": "FileInterfaceRoleEnum.PRODUCTION",
"source_parameters": null,
"v6_prefix_length": null,
"vlan_id": 324
}
'''
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.dellemc.unity.plugins.module_utils.storage.dell \
import utils
import ipaddress
from ipaddress import ip_network
LOG = utils.get_logger('interface')
application_type = "Ansible/1.6.0"
class Interface(object):
"""Class with Interface operations"""
def __init__(self):
"""Define all parameters required by this module"""
self.module_params = utils.get_unity_management_host_parameters()
self.module_params.update(get_interface_parameters())
mutually_exclusive = [['nas_server_name', 'nas_server_id'], ['ethernet_port_id', 'ethernet_port_name'], ['netmask', 'prefix_length']]
required_one_of = [['nas_server_name', 'nas_server_id']]
# initialize the Ansible module
self.module = AnsibleModule(
argument_spec=self.module_params,
supports_check_mode=True,
mutually_exclusive=mutually_exclusive,
required_one_of=required_one_of
)
utils.ensure_required_libs(self.module)
self.unity_conn = utils.get_unity_unisphere_connection(
self.module.params, application_type)
LOG.info('Check Mode Flag %s', self.module.check_mode)
def get_interface_details(self, nas_server_obj):
"""Get interface details.
:param: nas_server_obj: NAS server object.
:return: Returns interface details configured on NAS server.
"""
try:
nas_server_obj_properties = nas_server_obj._get_properties()
if nas_server_obj_properties['file_interface']:
for item in nas_server_obj_properties['file_interface']['UnityFileInterfaceList']:
interface_id = self.unity_conn.get_file_interface(_id=item['UnityFileInterface']['id'])
if interface_id.ip_address == self.module.params['interface_ip']:
return interface_id
return None
except Exception as e:
error_msg = "Getting Interface details failed" \
" with error %s" % (str(e))
LOG.error(error_msg)
self.module.fail_json(msg=error_msg)
def get_nas_server_obj(self, nas_server_name, nas_server_id):
"""Get NAS server ID.
:param: nas_server_name: The name of NAS server
:param: nas_server_id: ID of NAS server
:return: Return NAS server object if exists
"""
LOG.info("Getting NAS server object")
try:
if nas_server_name:
obj_nas = self.unity_conn.get_nas_server(name=nas_server_name)
return obj_nas
elif nas_server_id:
obj_nas = self.unity_conn.get_nas_server(_id=nas_server_id)
if obj_nas._get_properties()['existed']:
return obj_nas
else:
msg = "NAS server with id %s does not exist" % (nas_server_id)
LOG.error(msg)
self.module.fail_json(msg=msg)
except Exception as e:
msg = "Failed to get details of NAS server with error: %s" % (str(e))
LOG.error(msg)
self.module.fail_json(msg=msg)
def add_interface(self, nas_server_obj, ethernet_port_id=None, ethernet_port_name=None, role=None, interface_ip=None,
netmask=None, prefix_length=None, gateway=None, vlan_id=None):
"""Adding interface to NAS server.
:param: nas_server_obj: The NAS server object.
:param: ethernet_port_id: ID of ethernet port.
:param: ethernet_port_name: Name of ethernet port.
:param: role: Role of the interface.
:param: interface_ip: IP of interface.
:param: netmask: Netmask for interface.
:param: prefix_length: Prefix length.
:param: gateway: Gateway for interface.
:param: vlan_id: vlan_id for interface.
:return: Return True if interface is configured successfully.
"""
LOG.info("Adding interface to NAS Server")
try:
nas_server_obj_properties = nas_server_obj._get_properties()
if nas_server_obj_properties['file_interface']:
for item in nas_server_obj_properties['file_interface']['UnityFileInterfaceList']:
interface_id = self.unity_conn.get_file_interface(_id=item['UnityFileInterface']['id'])
if interface_id._get_properties()['ip_address'] == self.module.params['interface_ip']:
return False
if role:
role_value = get_role_enum(role)
if ethernet_port_name:
ethernet_port_info = self.unity_conn.get_ethernet_port(name=ethernet_port_name)
ethernet_port_id = ethernet_port_info.id
if not self.module.check_mode:
utils.UnityFileInterface.create(cli=self.unity_conn._cli, nas_server=nas_server_obj.get_id(), ip_port=ethernet_port_id,
role=role_value, ip=interface_ip, netmask=netmask, v6_prefix_length=prefix_length,
gateway=gateway, vlan_id=vlan_id)
return True
except Exception as e:
msg = "Failed to add interface to NAS Server with error: %s" % (str(e))
LOG.error(msg)
self.module.fail_json(msg=msg)
def is_modification_required(self, interface_details):
"""Check if modification is required in existing interface/s configured for NAS Server
:param: interface_details: Existing interface details
:return: True if modification is required
"""
key_list = ['vlan_id', 'gateway', 'netmask']
for item in key_list:
if self.module.params[item] and self.module.params[item] != interface_details[item]:
return True
return False
def delete_interface(self, interface_obj):
"""Delete NFS server.
:param: interface_obj: Interface object.
:return: Return True if interface is deleted.
"""
LOG.info("Deleting interface")
try:
if not self.module.check_mode:
interface_obj.delete()
return True
except Exception as e:
msg = "Failed to delete interface with error: %s" % (str(e))
LOG.error(msg)
self.module.fail_json(msg=msg)
def validate_input_params(self):
"""Validates input parameters"""
param_list = ["nas_server_id", "nas_server_name",
"ethernet_port_name", "ethernet_port_id", "role",
"interface_ip", "netmask", "gateway"]
for param in param_list:
msg = "Please provide valid value for: %s" % param
if self.module.params[param] is not None and \
len(self.module.params[param].strip()) == 0:
errmsg = msg.format(param)
self.module.fail_json(msg=errmsg)
if self.module.params['vlan_id'] is not None and \
(self.module.params['vlan_id'] <= 3 or
self.module.params['vlan_id'] >= 4094):
self.module.fail_json(msg='vlan_id should be in the '
'range of 3 to 4094')
if self.module.params['interface_ip'] and \
not is_valid_ip(self.module.params['interface_ip']):
self.module.fail_json(msg='The value for interface ip is invalid')
if self.module.params['gateway'] and \
not is_valid_ip(self.module.params['gateway']):
self.module.fail_json(msg='The value for gateway is invalid')
if self.module.params['netmask'] and not \
utils.is_valid_netmask(self.module.params['netmask']):
self.module.fail_json(msg='Invalid IPV4 address specified '
'for netmask')
if self.module.params['interface_ip'] and \
(get_ip_version(self.module.params['interface_ip']) == 6):
self.module.fail_json(msg='IPv6 format is not supported')
def validate_create_params(self):
"""Validates input parameters for adding interface"""
if self.module.params['role'] is None:
self.module.fail_json(msg='Role is a mandatory parameter'
' for adding interface to NAS Server.')
if self.module.params['ethernet_port_name'] is None and \
self.module.params['ethernet_port_id'] is None:
self.module.fail_json(msg='ethernet_port_name/ethernet_port_id '
'is mandatory parameter for adding '
'interface to NAS Server.')
def perform_module_operation(self):
"""
Perform different actions on Interface module based on parameters
passed in the playbook
"""
nas_server_id = self.module.params['nas_server_id']
nas_server_name = self.module.params['nas_server_name']
ethernet_port_name = self.module.params['ethernet_port_name']
ethernet_port_id = self.module.params['ethernet_port_id']
role = self.module.params['role']
interface_ip = self.module.params['interface_ip']
netmask = self.module.params['netmask']
prefix_length = self.module.params['prefix_length']
gateway = self.module.params['gateway']
vlan_id = self.module.params['vlan_id']
state = self.module.params['state']
# result is a dictionary that contains changed status and Interface details
result = dict(
changed=False,
interface_details={}
)
modify_flag = False
self.validate_input_params()
interface_details = None
nas_server_obj = self.get_nas_server_obj(nas_server_name, nas_server_id)
interface_obj = self.get_interface_details(nas_server_obj)
if interface_obj and state == 'present':
interface_details = interface_obj._get_properties()
modify_flag = self.is_modification_required(interface_details)
if modify_flag:
self.module.fail_json(msg="Modification of Interfaces for NAS server is not supported through Ansible module")
if not interface_obj and state == 'present':
self.validate_create_params()
result['changed'] = self.add_interface(nas_server_obj, ethernet_port_id, ethernet_port_name, role,
interface_ip, netmask, prefix_length, gateway, vlan_id)
if interface_obj and state == 'absent':
result['changed'] = self.delete_interface(interface_obj)
if result['changed']:
nas_server_obj = self.get_nas_server_obj(nas_server_name, nas_server_id)
interface_obj = self.get_interface_details(nas_server_obj)
if interface_obj:
interface_details = interface_obj._get_properties()
result['interface_details'] = interface_details
self.module.exit_json(**result)
def get_interface_parameters():
"""This method provide parameters required for the ansible
Interface module on Unity"""
return dict(
nas_server_id=dict(type='str'),
nas_server_name=dict(type='str'),
ethernet_port_name=dict(type='str'),
ethernet_port_id=dict(type='str'),
role=dict(type='str', choices=['PRODUCTION', 'BACKUP']),
interface_ip=dict(required=True, type='str'),
netmask=dict(type='str'),
prefix_length=dict(type='int'),
gateway=dict(type='str'),
vlan_id=dict(type='int'),
state=dict(required=True, type='str', choices=['present', 'absent'])
)
def get_role_enum(role):
"""Getting correct enum values for role
:param: role: Indicates role of interface.
:return: enum value for role.
"""
if utils.FileInterfaceRoleEnum[role]:
role = utils.FileInterfaceRoleEnum[role]
return role
def is_valid_ip(address):
"""Validating IP address format
:param: address: IP address to be validated for format.
"""
try:
ipaddress.ip_address(address)
return True
except ValueError:
return False
def get_ip_version(val):
"""Returns IP address version
:param: val: IP address to be validated for version.
"""
try:
val = u'{0}'.format(val)
ip = ip_network(val, strict=False)
return ip.version
except ValueError:
return 0
def main():
"""Create Unity Interface object and perform action on it
based on user input from playbook"""
obj = Interface()
obj.perform_module_operation()
if __name__ == '__main__':
main()