From da578b8fffc9f1139f23d8fb20bf62107ace4ed5 Mon Sep 17 00:00:00 2001 From: Chris Archibald Date: Thu, 24 May 2018 12:58:56 -0700 Subject: [PATCH] fourth set of modules (#40490) * fourth set of modules * fix issues * fix issues * changes for review * make changes to vlan --- .../modules/storage/netapp/na_ontap_lun.py | 395 ++++++++++++++++++ .../storage/netapp/na_ontap_net_ifgrp.py | 324 ++++++++++++++ .../storage/netapp/na_ontap_net_port.py | 233 +++++++++++ .../storage/netapp/na_ontap_net_routes.py | 245 +++++++++++ .../storage/netapp/na_ontap_net_vlan.py | 201 +++++++++ 5 files changed, 1398 insertions(+) create mode 100644 lib/ansible/modules/storage/netapp/na_ontap_lun.py create mode 100644 lib/ansible/modules/storage/netapp/na_ontap_net_ifgrp.py create mode 100644 lib/ansible/modules/storage/netapp/na_ontap_net_port.py create mode 100644 lib/ansible/modules/storage/netapp/na_ontap_net_routes.py create mode 100644 lib/ansible/modules/storage/netapp/na_ontap_net_vlan.py diff --git a/lib/ansible/modules/storage/netapp/na_ontap_lun.py b/lib/ansible/modules/storage/netapp/na_ontap_lun.py new file mode 100644 index 00000000000..a9c645130e9 --- /dev/null +++ b/lib/ansible/modules/storage/netapp/na_ontap_lun.py @@ -0,0 +1,395 @@ +#!/usr/bin/python + +# (c) 2017, 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: na_ontap_lun + +short_description: Manage NetApp Ontap luns +extends_documentation_fragment: + - netapp.na_ontap +version_added: '2.6' +author: Sumit Kumar (sumit4@netapp.com), Suhas Bangalore Shekar (bsuhas@netapp.com) + +description: +- Create, destroy, resize luns on NetApp Ontap. + +options: + + state: + description: + - Whether the specified lun should exist or not. + choices: ['present', 'absent'] + default: present + + name: + description: + - The name of the lun to manage. + required: true + + flexvol_name: + description: + - The name of the FlexVol the lun should exist on. + required: true + + size: + description: + - The size of the lun in C(size_unit). + - Required when C(state=present). + + size_unit: + description: + - The unit used to interpret the size parameter. + choices: ['bytes', 'b', 'kb', 'mb', 'gb', 'tb', 'pb', 'eb', 'zb', 'yb'] + default: 'gb' + + force_resize: + description: + Forcibly reduce the size. This is required for reducing the size of the LUN to avoid accidentally + reducing the LUN size. + type: bool + default: false + + force_remove: + description: + - If "true", override checks that prevent a LUN from being destroyed if it is online and mapped. + - If "false", destroying an online and mapped LUN will fail. + type: bool + default: false + + force_remove_fenced: + description: + - If "true", override checks that prevent a LUN from being destroyed while it is fenced. + - If "false", attempting to destroy a fenced LUN will fail. + - The default if not specified is "false". This field is available in Data ONTAP 8.2 and later. + type: bool + default: false + + vserver: + required: true + description: + - The name of the vserver to use. + + ostype: + description: + - The os type for the LUN. + default: 'image' + + space_reserve: + description: + - This can be set to "false" which will create a LUN without any space being reserved. + type: bool + default: True + +''' + +EXAMPLES = """ +- name: Create LUN + na_ontap_lun: + state: present + name: ansibleLUN + flexvol_name: ansibleVolume + vserver: ansibleVServer + size: 5 + size_unit: mb + ostype: linux + space_reserve: True + hostname: "{{ netapp_hostname }}" + username: "{{ netapp_username }}" + password: "{{ netapp_password }}" + +- name: Resize Lun + na_ontap_lun: + state: present + name: ansibleLUN + force_resize: True + flexvol_name: ansibleVolume + vserver: ansibleVServer + size: 5 + size_unit: gb + hostname: "{{ netapp_hostname }}" + username: "{{ netapp_username }}" + password: "{{ netapp_password }}" +""" + +RETURN = """ + +""" + +import traceback + +from ansible.module_utils.basic import AnsibleModule +from ansible.module_utils._text import to_native +import ansible.module_utils.netapp as netapp_utils + +HAS_NETAPP_LIB = netapp_utils.has_netapp_lib() + + +class NetAppOntapLUN(object): + + def __init__(self): + + self._size_unit_map = dict( + bytes=1, + b=1, + kb=1024, + mb=1024 ** 2, + gb=1024 ** 3, + tb=1024 ** 4, + pb=1024 ** 5, + eb=1024 ** 6, + zb=1024 ** 7, + yb=1024 ** 8 + ) + + self.argument_spec = netapp_utils.na_ontap_host_argument_spec() + self.argument_spec.update(dict( + state=dict(required=False, choices=['present', 'absent'], default='present'), + name=dict(required=True, type='str'), + size=dict(type='int'), + size_unit=dict(default='gb', + choices=['bytes', 'b', 'kb', 'mb', 'gb', 'tb', + 'pb', 'eb', 'zb', 'yb'], type='str'), + force_resize=dict(default=False, type='bool'), + force_remove=dict(default=False, type='bool'), + force_remove_fenced=dict(default=False, type='bool'), + flexvol_name=dict(required=True, type='str'), + vserver=dict(required=True, type='str'), + ostype=dict(required=False, type='str', default='image'), + space_reserve=dict(required=False, type='bool', default=True), + )) + + self.module = AnsibleModule( + argument_spec=self.argument_spec, + required_if=[ + ('state', 'present', ['size']) + ], + supports_check_mode=True + ) + + parameters = self.module.params + + # set up state variables + self.state = parameters['state'] + self.name = parameters['name'] + self.size_unit = parameters['size_unit'] + if parameters['size'] is not None: + self.size = parameters['size'] * self._size_unit_map[self.size_unit] + else: + self.size = None + self.force_resize = parameters['force_resize'] + self.force_remove = parameters['force_remove'] + self.force_remove_fenced = parameters['force_remove_fenced'] + self.flexvol_name = parameters['flexvol_name'] + self.vserver = parameters['vserver'] + self.ostype = parameters['ostype'] + self.space_reserve = parameters['space_reserve'] + + if HAS_NETAPP_LIB is False: + self.module.fail_json(msg="the python NetApp-Lib module is required") + else: + self.server = netapp_utils.setup_na_ontap_zapi(module=self.module, vserver=self.vserver) + + def get_lun(self): + """ + Return details about the LUN + + :return: Details about the lun + :rtype: dict + """ + + luns = [] + tag = None + while True: + lun_info = netapp_utils.zapi.NaElement('lun-get-iter') + if tag: + lun_info.add_new_child('tag', tag, True) + + query_details = netapp_utils.zapi.NaElement('lun-info') + query_details.add_new_child('vserver', self.vserver) + query_details.add_new_child('volume', self.flexvol_name) + + query = netapp_utils.zapi.NaElement('query') + query.add_child_elem(query_details) + + lun_info.add_child_elem(query) + + result = self.server.invoke_successfully(lun_info, True) + if result.get_child_by_name('num-records') and int(result.get_child_content('num-records')) >= 1: + attr_list = result.get_child_by_name('attributes-list') + luns.extend(attr_list.get_children()) + + tag = result.get_child_content('next-tag') + + if tag is None: + break + + # The LUNs have been extracted. + # Find the specified lun and extract details. + return_value = None + for lun in luns: + path = lun.get_child_content('path') + _rest, _splitter, found_name = path.rpartition('/') + + if found_name == self.name: + size = lun.get_child_content('size') + + # Find out if the lun is attached + attached_to = None + lun_id = None + if lun.get_child_content('mapped') == 'true': + lun_map_list = netapp_utils.zapi.NaElement.create_node_with_children( + 'lun-map-list-info', **{'path': path}) + + result = self.server.invoke_successfully( + lun_map_list, enable_tunneling=True) + + igroups = result.get_child_by_name('initiator-groups') + if igroups: + for igroup_info in igroups.get_children(): + igroup = igroup_info.get_child_content( + 'initiator-group-name') + attached_to = igroup + lun_id = igroup_info.get_child_content('lun-id') + + return_value = { + 'name': found_name, + 'size': size, + 'attached_to': attached_to, + 'lun_id': lun_id + } + else: + continue + + return return_value + + def create_lun(self): + """ + Create LUN with requested name and size + """ + path = '/vol/%s/%s' % (self.flexvol_name, self.name) + lun_create = netapp_utils.zapi.NaElement.create_node_with_children( + 'lun-create-by-size', **{'path': path, + 'size': str(self.size), + 'ostype': self.ostype, + 'space-reservation-enabled': str(self.space_reserve)}) + + try: + self.server.invoke_successfully(lun_create, enable_tunneling=True) + except netapp_utils.zapi.NaApiError as e: + self.module.fail_json(msg="Error provisioning lun %s of size %s: %s" % (self.name, self.size, to_native(e)), + exception=traceback.format_exc()) + + def delete_lun(self): + """ + Delete requested LUN + """ + path = '/vol/%s/%s' % (self.flexvol_name, self.name) + + lun_delete = netapp_utils.zapi.NaElement.create_node_with_children( + 'lun-destroy', **{'path': path, + 'force': str(self.force_remove), + 'destroy-fenced-lun': + str(self.force_remove_fenced)}) + + try: + self.server.invoke_successfully(lun_delete, enable_tunneling=True) + except netapp_utils.zapi.NaApiError as e: + self.module.fail_json(msg="Error deleting lun %s: %s" % (path, to_native(e)), + exception=traceback.format_exc()) + + def resize_lun(self): + """ + Resize requested LUN. + + :return: True if LUN was actually re-sized, false otherwise. + :rtype: bool + """ + path = '/vol/%s/%s' % (self.flexvol_name, self.name) + + lun_resize = netapp_utils.zapi.NaElement.create_node_with_children( + 'lun-resize', **{'path': path, + 'size': str(self.size), + 'force': str(self.force_resize)}) + try: + self.server.invoke_successfully(lun_resize, enable_tunneling=True) + except netapp_utils.zapi.NaApiError as e: + if to_native(e.code) == "9042": + # Error 9042 denotes the new LUN size being the same as the + # old LUN size. This happens when there's barely any difference + # in the two sizes. For example, from 8388608 bytes to + # 8194304 bytes. This should go away if/when the default size + # requested/reported to/from the controller is changed to a + # larger unit (MB/GB/TB). + return False + else: + self.module.fail_json(msg="Error resizing lun %s: %s" % (path, to_native(e)), + exception=traceback.format_exc()) + + return True + + def apply(self): + property_changed = False + size_changed = False + lun_exists = False + netapp_utils.ems_log_event("na_ontap_lun", self.server) + lun_detail = self.get_lun() + + if lun_detail: + lun_exists = True + current_size = lun_detail['size'] + + if self.state == 'absent': + property_changed = True + + elif self.state == 'present': + if not int(current_size) == self.size: + size_changed = True + property_changed = True + + else: + if self.state == 'present': + property_changed = True + + if property_changed: + if self.module.check_mode: + pass + else: + if self.state == 'present': + if not lun_exists: + self.create_lun() + + else: + if size_changed: + # Ensure that size was actually changed. Please + # read notes in 'resize_lun' function for details. + size_changed = self.resize_lun() + if not size_changed: + property_changed = False + + elif self.state == 'absent': + self.delete_lun() + + changed = property_changed or size_changed + # TODO: include other details about the lun (size, etc.) + self.module.exit_json(changed=changed) + + +def main(): + v = NetAppOntapLUN() + v.apply() + + +if __name__ == '__main__': + main() diff --git a/lib/ansible/modules/storage/netapp/na_ontap_net_ifgrp.py b/lib/ansible/modules/storage/netapp/na_ontap_net_ifgrp.py new file mode 100644 index 00000000000..00082b7cfad --- /dev/null +++ b/lib/ansible/modules/storage/netapp/na_ontap_net_ifgrp.py @@ -0,0 +1,324 @@ +#!/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: na_ontap_net_ifgrp +short_description: Create, modify, destroy the network interface group +extends_documentation_fragment: + - netapp.na_ontap +version_added: '2.6' +author: +- Chris Archibald (carchi@netapp.com), Kevin Hutton (khutton@netapp.com), Suhas Bangalore Shekar (bsuhas@netapp.com) +description: +- Create, modify, destroy the network interface group +options: + state: + description: + - Whether the specified network interface group should exist or not. + choices: ['present', 'absent'] + default: present + + distribution_function: + description: + - Specifies the traffic distribution function for the ifgrp. + choices: ['mac', 'ip', 'sequential', 'port'] + + name: + description: + - Specifies the interface group name. + required: true + + mode: + description: + - Specifies the link policy for the ifgrp. + + node: + description: + - Specifies the name of node. + required: true + + port: + description: + - Adds the specified port. + +""" + +EXAMPLES = """ + - name: create ifgrp + na_ontap_net_ifgrp: + state=present + username={{ netapp_username }} + password={{ netapp_password }} + hostname={{ netapp_hostname }} + distribution_function=ip + name=a0c + port=e0d + mode=multimode + node={{ Vsim node name }} + - name: delete ifgrp + na_ontap_net_ifgrp: + state=absent + username={{ netapp_username }} + password={{ netapp_password }} + hostname={{ netapp_hostname }} + name=a0c + node={{ Vsim node name }} +""" + +RETURN = """ + +""" + +import traceback + +from ansible.module_utils.basic import AnsibleModule +from ansible.module_utils._text import to_native +import ansible.module_utils.netapp as netapp_utils + +HAS_NETAPP_LIB = netapp_utils.has_netapp_lib() + + +class NetAppOntapIfGrp(object): + """ + Create, Modifies and Destroys a IfGrp + """ + def __init__(self): + """ + Initialize the Ontap IfGrp class + """ + self.argument_spec = netapp_utils.na_ontap_host_argument_spec() + self.argument_spec.update(dict( + state=dict(required=False, choices=['present', 'absent'], default='present'), + distribution_function=dict(required=False, type='str', choices=['mac', 'ip', 'sequential', 'port']), + name=dict(required=True, type='str'), + mode=dict(required=False, type='str'), + node=dict(required=True, type='str'), + port=dict(required=False, type='str'), + )) + + self.module = AnsibleModule( + argument_spec=self.argument_spec, + required_if=[ + ('state', 'present', ['distribution_function', 'mode']) + ], + supports_check_mode=True + ) + + parameters = self.module.params + + # set up state variables + self.state = parameters['state'] + self.distribution_function = parameters['distribution_function'] + self.name = parameters['name'] + self.mode = parameters['mode'] + self.node = parameters['node'] + self.port = parameters['port'] + + if HAS_NETAPP_LIB is False: + self.module.fail_json(msg="the python NetApp-Lib module is required") + else: + self.server = netapp_utils.setup_na_ontap_zapi(module=self.module) + return + + def get_if_grp(self): + """ + Return details about the if_group + :param: + name : Name of the if_group + + :return: Details about the if_group. None if not found. + :rtype: dict + """ + if_group_iter = netapp_utils.zapi.NaElement('net-port-get-iter') + if_group_info = netapp_utils.zapi.NaElement('net-port-info') + if_group_info.add_new_child('port', self.name) + if_group_info.add_new_child('port-type', 'if_group') + query = netapp_utils.zapi.NaElement('query') + query.add_child_elem(if_group_info) + if_group_iter.add_child_elem(query) + result = self.server.invoke_successfully(if_group_iter, True) + + return_value = None + + if result.get_child_by_name('num-records') and \ + int(result.get_child_content('num-records')) >= 1: + + if_group_attributes = result.get_child_by_name('attributes-list').get_child_by_name('net-port-info') + distribution_function = if_group_attributes.get_child_content('ifgrp-distribution-function') + name = if_group_attributes.get_child_content('port') + mode = if_group_attributes.get_child_content('ifgrp-mode') + ports = if_group_attributes.get_child_content('ifgrp-port') + node = if_group_attributes.get_child_content('node') + + return_value = { + 'name': name, + 'distribution_function': distribution_function, + 'mode': mode, + 'node': node, + 'ports': ports + } + + return return_value + + def get_if_grp_ports(self): + """ + Return ports of the if_group + :param: + name : Name of the if_group + + :return: Ports of the if_group. None if not found. + :rtype: dict + """ + if_group_iter = netapp_utils.zapi.NaElement('net-port-ifgrp-get') + if_group_iter.add_new_child('ifgrp-name', self.name) + if_group_iter.add_new_child('node', self.node) + result = self.server.invoke_successfully(if_group_iter, True) + + return_value = None + + if result.get_child_by_name('attributes'): + if_group_attributes = result.get_child_by_name('attributes').get_child_by_name('net-ifgrp-info') + name = if_group_attributes.get_child_content('ifgrp-name') + mode = if_group_attributes.get_child_content('mode') + port_list = [] + if if_group_attributes.get_child_by_name('ports'): + ports = if_group_attributes.get_child_by_name('ports').get_children() + for each in ports: + port_list.append(each.get_content()) + node = if_group_attributes.get_child_content('node') + return_value = { + 'name': name, + 'mode': mode, + 'node': node, + 'ports': port_list + } + return return_value + + def create_if_grp(self): + """ + Creates a new ifgrp + """ + route_obj = netapp_utils.zapi.NaElement("net-port-ifgrp-create") + route_obj.add_new_child("distribution-function", self.distribution_function) + route_obj.add_new_child("ifgrp-name", self.name) + route_obj.add_new_child("mode", self.mode) + route_obj.add_new_child("node", self.node) + try: + self.server.invoke_successfully(route_obj, True) + except netapp_utils.zapi.NaApiError as error: + self.module.fail_json(msg='Error creating if_group %s: %s' % (self.name, to_native(error)), + exception=traceback.format_exc()) + + def delete_if_grp(self): + """ + Deletes a ifgrp + """ + route_obj = netapp_utils.zapi.NaElement("net-port-ifgrp-destroy") + route_obj.add_new_child("ifgrp-name", self.name) + route_obj.add_new_child("node", self.node) + try: + self.server.invoke_successfully(route_obj, True) + except netapp_utils.zapi.NaApiError as error: + self.module.fail_json(msg='Error deleting if_group %s: %s' % (self.name, to_native(error)), + exception=traceback.format_exc()) + + def add_port_to_if_grp(self): + """ + adds port to a ifgrp + """ + route_obj = netapp_utils.zapi.NaElement("net-port-ifgrp-add-port") + route_obj.add_new_child("ifgrp-name", self.name) + route_obj.add_new_child("port", self.port) + route_obj.add_new_child("node", self.node) + try: + self.server.invoke_successfully(route_obj, True) + except netapp_utils.zapi.NaApiError as error: + self.module.fail_json(msg='Error adding port %s to if_group %s: %s' % + (self.port, self.name, to_native(error)), + exception=traceback.format_exc()) + + def remove_port_to_if_grp(self): + """ + removes port from a ifgrp + """ + route_obj = netapp_utils.zapi.NaElement("net-port-ifgrp-remove-port") + route_obj.add_new_child("ifgrp-name", self.name) + route_obj.add_new_child("port", self.port) + route_obj.add_new_child("node", self.node) + try: + self.server.invoke_successfully(route_obj, True) + except netapp_utils.zapi.NaApiError as error: + self.module.fail_json(msg='Error removing port %s to if_group %s: %s' % + (self.port, self.name, to_native(error)), + exception=traceback.format_exc()) + + def apply(self): + changed = False + ifgroup_exists = False + add_ports_exists = True + remove_ports_exists = False + results = netapp_utils.get_cserver(self.server) + cserver = netapp_utils.setup_na_ontap_zapi(module=self.module, vserver=results) + netapp_utils.ems_log_event("na_ontap_net_ifgrp", cserver) + if_group_detail = self.get_if_grp() + if if_group_detail: + ifgroup_exists = True + ifgrp_ports_detail = self.get_if_grp_ports() + if self.state == 'absent': + changed = True + if self.port: + if self.port in ifgrp_ports_detail['ports']: + remove_ports_exists = True + elif self.state == 'present': + if self.port: + if not ifgrp_ports_detail['ports']: + add_ports_exists = False + changed = True + else: + if self.port not in ifgrp_ports_detail['ports']: + add_ports_exists = False + changed = True + else: + if self.state == 'present': + changed = True + + if changed: + if self.module.check_mode: + pass + else: + if self.state == 'present': + if not ifgroup_exists: + self.create_if_grp() + if self.port: + self.add_port_to_if_grp() + else: + if not add_ports_exists: + self.add_port_to_if_grp() + elif self.state == 'absent': + if remove_ports_exists: + self.remove_port_to_if_grp() + self.delete_if_grp() + + self.module.exit_json(changed=changed) + + +def main(): + """ + Creates the NetApp Ontap Net Route object and runs the correct play task + """ + obj = NetAppOntapIfGrp() + obj.apply() + + +if __name__ == '__main__': + main() diff --git a/lib/ansible/modules/storage/netapp/na_ontap_net_port.py b/lib/ansible/modules/storage/netapp/na_ontap_net_port.py new file mode 100644 index 00000000000..0c47048826f --- /dev/null +++ b/lib/ansible/modules/storage/netapp/na_ontap_net_port.py @@ -0,0 +1,233 @@ +#!/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: na_ontap_net_port +short_description: Manage NetApp Ontap network ports. +extends_documentation_fragment: + - netapp.na_ontap +version_added: '2.6' +author: +- Chris Archibald (carchi@netapp.com), Kevin Hutton (khutton@netapp.com), Suhas Bangalore Shekar (bsuhas@netapp.com) +description: +- Modify a Ontap network port. +options: + state: + description: + - Whether the specified net port should exist or not. + choices: ['present'] + default: present + node: + description: + - Specifies the name of node. + required: true + port: + description: + - Specifies the name of port. + required: true + mtu: + description: + - Specifies the maximum transmission unit (MTU) reported by the port. + autonegotiate_admin: + description: + - Enables or disables Ethernet auto-negotiation of speed, + duplex and flow control. + duplex_admin: + description: + - Specifies the user preferred duplex setting of the port. + speed_admin: + description: + - Specifies the user preferred speed setting of the port. + flowcontrol_admin: + description: + - Specifies the user preferred flow control setting of the port. + ipspace: + description: + - Specifies the port's associated IPspace name. + - The 'Cluster' ipspace is reserved for cluster ports. +""" + +EXAMPLES = """ + - name: Modify Net Port + na_ontap_net_port: + state=present + username={{ netapp_username }} + password={{ netapp_password }} + hostname={{ netapp_hostname }} + node={{ Vsim server name }} + port=e0d + autonegotiate_admin=true +""" + +RETURN = """ + +""" + +from ansible.module_utils.basic import AnsibleModule +import ansible.module_utils.netapp as netapp_utils + +HAS_NETAPP_LIB = netapp_utils.has_netapp_lib() + + +class NetAppOntapNetPort(object): + """ + Modify a Net port + """ + + def __init__(self): + """ + Initialize the Ontap Net Port Class + """ + self.argument_spec = netapp_utils.na_ontap_host_argument_spec() + self.argument_spec.update(dict( + state=dict(required=False, choices=['present'], default='present'), + node=dict(required=True, type="str"), + port=dict(required=True, type="str"), + mtu=dict(required=False, type="str", default=None), + autonegotiate_admin=dict(required=False, type="str", default=None), + duplex_admin=dict(required=False, type="str", default=None), + speed_admin=dict(required=False, type="str", default=None), + flowcontrol_admin=dict(required=False, type="str", default=None), + ipspace=dict(required=False, type="str", default=None), + )) + + self.module = AnsibleModule( + argument_spec=self.argument_spec, + supports_check_mode=True + ) + + p = self.module.params + + # set up state variables + self.state = p['state'] + self.node = p['node'] + self.port = p['port'] + # the following option are optional, but at least one need to be set + self.mtu = p['mtu'] + self.autonegotiate_admin = p["autonegotiate_admin"] + self.duplex_admin = p["duplex_admin"] + self.speed_admin = p["speed_admin"] + self.flowcontrol_admin = p["flowcontrol_admin"] + + if HAS_NETAPP_LIB is False: + self.module.fail_json( + msg="the python NetApp-Lib module is required") + else: + self.server = netapp_utils.setup_na_ontap_zapi(module=self.module) + return + + def get_net_port(self): + """ + Return details about the net port + + :return: Details about the net port. None if not found. + :rtype: dict + """ + net_port_info = netapp_utils.zapi.NaElement('net-port-get-iter') + net_port_attributes = netapp_utils.zapi.NaElement('net-port-info') + net_port_attributes.add_new_child('node', self.node) + net_port_attributes.add_new_child('port', self.port) + query = netapp_utils.zapi.NaElement('query') + query.add_child_elem(net_port_attributes) + net_port_info.add_child_elem(query) + result = self.server.invoke_successfully(net_port_info, True) + return_value = None + + if result.get_child_by_name('num-records') and \ + int(result.get_child_content('num-records')) >= 1: + + net_port_attributes = result.get_child_by_name('attributes-list').\ + get_child_by_name('net-port-info') + return_value = { + 'node': net_port_attributes.get_child_content('node'), + 'port': net_port_attributes.get_child_content('port'), + 'mtu': net_port_attributes.get_child_content('mtu'), + 'autonegotiate_admin': net_port_attributes.get_child_content( + 'is-administrative-auto-negotiate'), + 'duplex_admin': net_port_attributes.get_child_content( + 'administrative-duplex'), + 'speed_admin': net_port_attributes.get_child_content( + 'administrative-speed'), + 'flowcontrol_admin': net_port_attributes.get_child_content( + 'administrative-flowcontrol'), + } + return return_value + + def modify_net_port(self): + """ + Modify a port + """ + port_obj = netapp_utils.zapi.NaElement('net-port-modify') + port_obj.add_new_child("node", self.node) + port_obj.add_new_child("port", self.port) + # The following options are optional. + # We will only call them if they are not set to None + if self.mtu: + port_obj.add_new_child("mtu", self.mtu) + if self.autonegotiate_admin: + port_obj.add_new_child( + "is-administrative-auto-negotiate", self.autonegotiate_admin) + if self.duplex_admin: + port_obj.add_new_child("administrative-duplex", self.duplex_admin) + if self.speed_admin: + port_obj.add_new_child("administrative-speed", self.speed_admin) + if self.flowcontrol_admin: + port_obj.add_new_child( + "administrative-flowcontrol", self.flowcontrol_admin) + self.server.invoke_successfully(port_obj, True) + + def apply(self): + """ + Run Module based on play book + """ + changed = False + net_port_exists = False + results = netapp_utils.get_cserver(self.server) + cserver = netapp_utils.setup_na_ontap_zapi( + module=self.module, vserver=results) + netapp_utils.ems_log_event("na_ontap_net_port", cserver) + net_port_details = self.get_net_port() + if net_port_details: + net_port_exists = True + if self.state == 'present': + if (self.mtu and self.mtu != net_port_details['mtu']) or \ + (self.autonegotiate_admin and + self.autonegotiate_admin != net_port_details['autonegotiate_admin']) or \ + (self.duplex_admin and + self.duplex_admin != net_port_details['duplex_admin']) or \ + (self.speed_admin and + self.speed_admin != net_port_details['speed_admin']) or \ + (self.flowcontrol_admin and + self.flowcontrol_admin != net_port_details['flowcontrol_admin']): + changed = True + + if changed: + if self.module.check_mode: + pass + else: + if self.state == 'present': + if net_port_exists: + self.modify_net_port() + self.module.exit_json(changed=changed) + + +def main(): + """ + Create the NetApp Ontap Net Port Object and modify it + """ + obj = NetAppOntapNetPort() + obj.apply() + + +if __name__ == '__main__': + main() diff --git a/lib/ansible/modules/storage/netapp/na_ontap_net_routes.py b/lib/ansible/modules/storage/netapp/na_ontap_net_routes.py new file mode 100644 index 00000000000..4f9820877b1 --- /dev/null +++ b/lib/ansible/modules/storage/netapp/na_ontap_net_routes.py @@ -0,0 +1,245 @@ +#!/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: na_ontap_net_routes +short_description: Manage NetApp Ontap network routes +extends_documentation_fragment: + - netapp.na_ontap +version_added: '2.6' +author: +- Chris Archibald (carchi@netapp.com), Kevin Hutton (khutton@netapp.com) +description: +- Modify a Ontap network routes +options: + state: + description: + - Whether you want to create or delete a network route + choices: ['present', 'absent'] + default: present + vserver: + description: + - The name of the vserver + required: true + destination: + description: + - Specify the route destination. Example 10.7.125.5/20, fd20:13::/64 + required: true + gateway: + description: + - Specify the route gateway. Example 10.7.125.1, fd20:13::1 + required: true + metric: + description: + - Specify the route metric. + - If this field is not provided the default will be set to 30 + new_destination: + description: + - Specify the new route destination. + new_gateway: + description: + - Specify the new route gateway. + new_metric: + description: + - Specify the new route metric. +''' + +EXAMPLES = """ + - name: create route + na_ontap_net_routes: + state=present + vserver={{ Vserver name }} + username={{ netapp_username }} + password={{ netapp_password }} + hostname={{ netapp_hostname }} + destination=10.7.125.5/20 + gateway=10.7.125.1 + metric=30 +""" + +RETURN = """ + +""" + +import traceback + +from ansible.module_utils.basic import AnsibleModule +from ansible.module_utils._text import to_native +import ansible.module_utils.netapp as netapp_utils + +HAS_NETAPP_LIB = netapp_utils.has_netapp_lib() + + +class NetAppOntapNetRoutes(object): + """ + Create, Modifies and Destroys a Net Route + """ + + def __init__(self): + """ + Initialize the Ontap Net Route class + """ + self.argument_spec = netapp_utils.na_ontap_host_argument_spec() + self.argument_spec.update(dict( + state=dict(required=False, choices=[ + 'present', 'absent'], default='present'), + vserver=dict(required=True, type='str'), + destination=dict(required=True, type='str'), + gateway=dict(required=True, type='str'), + metric=dict(required=False, type='str', default=None), + new_destination=dict(required=False, type='str', default=None), + new_gateway=dict(required=False, type='str', default=None), + new_metric=dict(required=False, type='str', default=None), + )) + + self.module = AnsibleModule( + argument_spec=self.argument_spec, + supports_check_mode=True + ) + + parameters = self.module.params + + # set up state variables + self.state = parameters['state'] + self.vserver = parameters['vserver'] + self.destination = parameters['destination'] + self.gateway = parameters['gateway'] + self.metric = parameters['metric'] + self.new_destination = parameters['new_destination'] + self.new_gateway = parameters['new_destination'] + self.new_metric = parameters['new_metric'] + + if HAS_NETAPP_LIB is False: + self.module.fail_json( + msg="the python NetApp-Lib module is required") + else: + self.server = netapp_utils.setup_na_ontap_zapi( + module=self.module, vserver=self.vserver) + return + + def create_net_route(self): + """ + Creates a new Route + """ + route_obj = netapp_utils.zapi.NaElement('net-routes-create') + route_obj.add_new_child("destination", self.destination) + route_obj.add_new_child("gateway", self.gateway) + if self.metric: + route_obj.add_new_child("metric", self.metric) + try: + self.server.invoke_successfully(route_obj, True) + except netapp_utils.zapi.NaApiError as error: + self.module.fail_json(msg='Error creating net route: %s' + % (to_native(error)), + exception=traceback.format_exc()) + + def delete_net_route(self): + """ + Deletes a given Route + """ + route_obj = netapp_utils.zapi.NaElement('net-routes-destroy') + route_obj.add_new_child("destination", self.destination) + route_obj.add_new_child("gateway", self.gateway) + try: + self.server.invoke_successfully(route_obj, True) + except netapp_utils.zapi.NaApiError as error: + self.module.fail_json(msg='Error deleting net route: %s' + % (to_native(error)), + exception=traceback.format_exc()) + + def modify_net_route(self): + """ + Modify a net route + """ + self.delete_net_route() + route_obj = netapp_utils.zapi.NaElement('net-routes-create') + if self.new_destination: + route_obj.add_new_child("destination", self.new_destination) + else: + route_obj.add_new_child("destination", self.destination) + if self.new_gateway: + route_obj.add_new_child("gateway", self.new_gateway) + else: + route_obj.add_new_child("gateway", self.gateway) + if self.new_metric: + route_obj.add_new_child("metric", self.new_metric) + else: + route_obj.add_new_child("metric", self.metric) + # if anything goes wrong creating the new net route, + # restore the old one + try: + self.server.invoke_successfully(route_obj, True) + except netapp_utils.zapi.NaApiError: + self.create_net_route() + + def does_route_exists(self): + """ + Checks to see if a route exist or not + :return: True if a route exists, False if it does not + """ + route_obj = netapp_utils.zapi.NaElement('net-routes-get') + route_obj.add_new_child("destination", self.destination) + route_obj.add_new_child("gateway", self.gateway) + try: + self.server.invoke_successfully(route_obj, True) + except netapp_utils.zapi.NaApiError: + return False + return True + + def apply(self): + """ + Run Module based on play book + """ + changed = False + netapp_utils.ems_log_event("na_ontap_net_routes", self.server) + route_exists = False + existing_route = self.does_route_exists() + + if existing_route: + route_exists = True + if self.state == 'absent': + changed = True + elif self.state == 'present': + if self.new_destination or \ + self.new_gateway or self.new_metric: + changed = True + else: + if self.state == 'present': + changed = True + + if changed: + if self.module.check_mode: + pass + else: + if not route_exists: + if self.state == 'present': + self.create_net_route() + else: + if self.state == 'present': + self.modify_net_route() + elif self.state == 'absent': + self.delete_net_route() + + self.module.exit_json(changed=changed) + + +def main(): + """ + Creates the NetApp Ontap Net Route object and runs the correct play task + """ + obj = NetAppOntapNetRoutes() + obj.apply() + + +if __name__ == '__main__': + main() diff --git a/lib/ansible/modules/storage/netapp/na_ontap_net_vlan.py b/lib/ansible/modules/storage/netapp/na_ontap_net_vlan.py new file mode 100644 index 00000000000..6ae3aedec5e --- /dev/null +++ b/lib/ansible/modules/storage/netapp/na_ontap_net_vlan.py @@ -0,0 +1,201 @@ +#!/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: na_ontap_net_vlan +short_description: Manage NetApp Ontap network vlan +extends_documentation_fragment: + - netapp.ontap +version_added: '2.6' +author: Chris Archibald (carchi@netapp.com), Kevin Hutton (khutton@netapp.com) +description: +- Create or Delete a network vlan +options: + state: + description: + - Whether the specified network vlan should exist or not + choices: ['present', 'absent'] + default: present + parent_interface: + description: + - The interface that hosts the vlan interface. + required: true + vlanid: + description: + - The vlan id. Ranges from 1 to 4094. + required: true + node: + description: + - Node name of vlan interface. + interface_name: + description: + - Name of vlan interface. The name must be of the format - + gvrp_enabled: + type: bool + description: + - GVRP is deprecated and this attribute is ignored in cluster mode. +''' + +EXAMPLES = """ + - name: create vlan + na_ontap_net_vlan: + state=present + vlanid=13 + node={{ vlan node }} + parent_interface={{ vlan parent interface name }} + username={{ netapp_username }} + password={{ netapp_password }} + hostname={{ netapp_hostname }} +""" + +RETURN = """ + +""" + +from ansible.module_utils.basic import AnsibleModule +import ansible.module_utils.netapp as netapp_utils + +HAS_NETAPP_LIB = netapp_utils.has_netapp_lib() + + +class NetAppOntapVlan(object): + """ + Created, and destorys Net Vlans's + """ + def __init__(self): + """ + Initializes the NetAppOntapVlan function + """ + self.argument_spec = netapp_utils.ontap_sf_host_argument_spec() + self.argument_spec.update(dict( + state=dict(required=False, choices=['present', 'absent'], default='present'), + parent_interface=dict(required=True, type='str'), + vlanid=dict(required=True, type='str'), + node=dict(required=True, type='str'), + interface_name=dict(required=False, type='str'), + gvrp_enabled=dict(required=False, type='bool', default=False), + )) + + self.module = AnsibleModule( + argument_spec=self.argument_spec, + supports_check_mode=True + ) + p = self.module.params + + # set up state variables + self.state = p['state'] + self.parent_interface = p['parent_interface'] + self.vlanid = p['vlanid'] + self.node = p['node'] + self.interface_name = p['interface_name'] + self.gvrp_enabled = p['gvrp_enabled'] + + if HAS_NETAPP_LIB is False: + self.module.fail_json(msg="the python NetApp-Lib module is required") + else: + self.server = netapp_utils.setup_ontap_zapi(module=self.module) + return + + def create_vlan(self): + """ + Creates a new vlan + """ + vlan_obj = netapp_utils.zapi.NaElement("net-vlan-create") + vlan_info = self.create_vlan_info() + + vlan_obj.add_child_elem(vlan_info) + self.server.invoke_successfully(vlan_obj, True) + + def delete_vlan(self): + """ + Deletes a vland + """ + vlan_obj = netapp_utils.zapi.NaElement("net-vlan-delete") + vlan_info = self.create_vlan_info() + + vlan_obj.add_child_elem(vlan_info) + self.server.invoke_successfully(vlan_obj, True) + + def does_vlan_exist(self): + """ + Checks to see if a vlan already exists or not + :return: Returns True if the vlan exists, false if it dosn't + """ + vlan_obj = netapp_utils.zapi.NaElement("net-vlan-get") + vlan_obj.add_new_child("interface-name", self.parent_interface + "-" + self.vlanid) + vlan_obj.add_new_child("node", self.node) + try: + result = self.server.invoke_successfully(vlan_obj, True) + result.get_child_by_name("attributes").get_child_by_name("vlan-info").get_child_by_name("interface-name") + except netapp_utils.zapi.NaApiError: + return False + return True + + def create_vlan_info(self): + """ + Create a vlan_info object to be used in a create/delete + :return: + """ + vlan_info = netapp_utils.zapi.NaElement("vlan-info") + + # set up the vlan_info object: + vlan_info.add_new_child("parent-interface", self.parent_interface) + vlan_info.add_new_child("vlanid", self.vlanid) + # add the optional line if they exist. + if self.node: + vlan_info.add_new_child("node", self.node) + if self.interface_name: + vlan_info.add_new_child("interface-name", self.interface_name) + if self.gvrp_enabled: + vlan_info.add_new_child("gvrp-enabled", 'true') + else: + vlan_info.add_new_child("gvrp-enabled", 'false') + return vlan_info + + def apply(self): + """ + check the option in the playbook to see what needs to be done + :return: + """ + changed = False + result = None + results = netapp_utils.get_cserver(self.server) + cserver = netapp_utils.setup_ontap_zapi(module=self.module, vserver=results) + netapp_utils.ems_log_event("na_ontap_net_vlan", cserver) + existing_vlan = self.does_vlan_exist() + if existing_vlan: + if self.state == 'absent': # delete + changed = True + else: + if self.state == 'present': # create + changed = True + if changed: + if self.module.check_mode: + pass + else: + if self.state == 'present': + self.create_vlan() + elif self.state == 'absent': + self.delete_vlan() + self.module.exit_json(changed=changed, meta=result) + + +def main(): + """ + Creates the NetApp Ontap vlan object, and runs the correct play task. + """ + v = NetAppOntapVlan() + v.apply() + + +if __name__ == '__main__': + main()