summaryrefslogtreecommitdiffstats
path: root/ansible_collections/dellemc/unity/plugins/modules/interface.py
diff options
context:
space:
mode:
Diffstat (limited to 'ansible_collections/dellemc/unity/plugins/modules/interface.py')
-rw-r--r--ansible_collections/dellemc/unity/plugins/modules/interface.py521
1 files changed, 521 insertions, 0 deletions
diff --git a/ansible_collections/dellemc/unity/plugins/modules/interface.py b/ansible_collections/dellemc/unity/plugins/modules/interface.py
new file mode 100644
index 00000000..47707baa
--- /dev/null
+++ b/ansible_collections/dellemc/unity/plugins/modules/interface.py
@@ -0,0 +1,521 @@
+#!/usr/bin/python
+# 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) <ansible.team@dell.com>
+
+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.5.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:
+ if 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()