From 9034c0786d206a1233fd22336b09e03269f630c8 Mon Sep 17 00:00:00 2001 From: Simon Dodsley Date: Tue, 30 Oct 2018 05:44:33 -0400 Subject: [PATCH] Add Pure Storage FlashBlade module to manage network subnets (#44422) * Add Pure Storage FlashBlade module to manage network interfaces * Add Pure Storage FlashBlade module to manage subnets --- .../storage/purestorage/purefb_subnet.py | 258 ++++++++++++++++++ 1 file changed, 258 insertions(+) create mode 100644 lib/ansible/modules/storage/purestorage/purefb_subnet.py diff --git a/lib/ansible/modules/storage/purestorage/purefb_subnet.py b/lib/ansible/modules/storage/purestorage/purefb_subnet.py new file mode 100644 index 0000000000..2be5173824 --- /dev/null +++ b/lib/ansible/modules/storage/purestorage/purefb_subnet.py @@ -0,0 +1,258 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# (c) 2017, Simon Dodsley (simon@purestorage.com) +# 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: purefb_subnet +version_added: "2.8" +short_description: Manage network subnets in a Pure Storage FlashBlade +description: + - This module manages network subnets on Pure Storage FlashBlade. +author: Simon Dodsley (@sdodsley) +options: + name: + description: + - Subnet Name. + required: true + state: + description: + - Create, delete or modifies a subnet. + required: false + default: present + choices: [ "present", "absent" ] + gateway: + description: + - IPv4 or IPv6 address of subnet gateway. + required: false + mtu: + description: + - MTU size of the subnet. Range is 1280 to 9216. + required: false + default: 1500 + prefix: + description: + - IPv4 or IPv6 address associated with the subnet. + - Supply the prefix length (CIDR) as well as the IP address. + required: false + vlan: + description: + - VLAN ID of the subnet. + required: false + default: 0 +extends_documentation_fragment: + - purestorage.fb +notes: + - Requires the netaddr Python package on the host. +requirements: + - netaddr +''' + +EXAMPLES = ''' +- name: Create new network subnet named foo + purefb_subnet: + name: foo + prefix: "10.21.200.3/24" + gateway: 10.21.200.1 + mtu: 9000 + vlan: 2200 + state: present + fb_url: 10.10.10.2 + api_token: T-55a68eb5-c785-4720-a2ca-8b03903bf641 + +- name: Change configuration of existing subnet foo + purefb_network: + name: foo + state: present + prefix: "10.21.100.3/24" + gateway: 10.21.100.1 + mtu: 1500 + address: 10.21.200.123 + fb_url: 10.10.10.2 + api_token: T-55a68eb5-c785-4720-a2ca-8b03903bf641 + +- name: Delete network subnet named foo + purefb_subnet: + name: foo + state: absent + fb_url: 10.10.10.2 + api_token: T-55a68eb5-c785-4720-a2ca-8b03903bf641''' + +RETURN = ''' +''' + +HAS_PURITY_FB = True +try: + from purity_fb import Subnet +except ImportError: + HAS_PURITY_FB = False + +try: + import netaddr + HAS_NETADDR = True +except ImportError: + HAS_NETADDR = False + +from ansible.module_utils.basic import AnsibleModule +from ansible.module_utils.pure import get_blade, purefb_argument_spec + + +MINIMUM_API_VERSION = '1.3' + + +def get_subnet(module, blade): + """Return Subnet or None""" + subnet = [] + subnet.append(module.params['name']) + try: + res = blade.subnets.list_subnets(names=subnet) + return res.items[0] + except: + return None + + +def create_subnet(module, blade): + """Create Subnet""" + + subnet = [] + subnet.append(module.params['name']) + try: + blade.subnets.create_subnets(names=subnet, + subnet=Subnet(prefix=module.params['prefix'], + vlan=module.params['vlan'], + mtu=module.params['mtu'], + gateway=module.params['gateway'] + ) + ) + changed = True + except: + module.fail_json(msg='Failed to create subnet {0}. Confirm supplied parameters'.format(module.params['name'])) + module.exit_json(changed=changed) + + +def modify_subnet(module, blade): + """Modify Subnet settings""" + changed = False + subnet = get_subnet(module, blade) + subnet_new = [] + subnet_new.append(module.params['name']) + if module.params['prefix']: + if module.params['prefix'] != subnet.prefix: + try: + blade.subnets.update_subnets(names=subnet_new, + subnet=Subnet(prefix=module.params['prefix'])) + changed = True + except: + module.fail_json(msg='Failed to change subnet {0} prefix to {1}'.format(module.params['name'], + module.params['prefix'])) + if module.params['vlan']: + if module.params['vlan'] != subnet.vlan: + try: + blade.subnets.update_subnets(names=subnet_new, + subnet=Subnet(vlan=module.params['vlan'])) + changed = True + except: + module.fail_json(msg='Failed to change subnet {0} VLAN to {1}'.format(module.params['name'], + module.params['vlan'])) + if module.params['gateway']: + if module.params['gateway'] != subnet.gateway: + try: + blade.subnets.update_subnets(names=subnet_new, + subnet=Subnet(gateway=module.params['gateway'])) + changed = True + except: + module.fail_json(msg='Failed to change subnet {0} gateway to {1}'.format(module.params['name'], + module.params['gateway'])) + if module.params['mtu']: + if module.params['mtu'] != subnet.mtu: + try: + blade.subnets.update_subnets(names=subnet_new, + subnet=Subnet(mtu=module.params['mtu'])) + changed = True + except: + module.fail_json(msg='Failed to change subnet {0} MTU to {1}'.format(module.params['name'], + module.params['mtu'])) + module.exit_json(changed=changed) + + +def delete_subnet(module, blade): + """ Delete Subnet""" + subnet = [] + subnet.append(module.params['name']) + try: + blade.subnets.delete_subnets(names=subnet) + changed = True + except: + changed = False + module.exit_json(changed=changed) + + +def main(): + argument_spec = purefb_argument_spec() + argument_spec.update( + dict( + name=dict(required=True), + state=dict(default='present', choices=['present', 'absent']), + gateway=dict(), + mtu=dict(type='int', default=1500), + prefix=dict(), + vlan=dict(type='int', default=0), + ) + ) + + required_if = [["state", "present", ["gateway", 'prefix']]] + + module = AnsibleModule(argument_spec, + required_if=required_if, + supports_check_mode=False) + + if not HAS_PURITY_FB: + module.fail_json(msg='purity_fb sdk is required for this module') + + if not HAS_NETADDR: + module.fail_json(msg='netaddr module is required') + + state = module.params['state'] + blade = get_blade(module) + api_version = blade.api_version.list_versions().versions + if MINIMUM_API_VERSION not in api_version: + module.fail_json(msg='Upgrade Purity//FB to enable this module') + subnet = get_subnet(module, blade) + if state == 'present': + if not (1280 <= module.params['mtu'] <= 9216): + module.fail_json(msg='MTU {0} is out of range (1280 to 9216)'.format(module.params['mtu'])) + if not (0 <= module.params['vlan'] <= 4094): + module.fail_json(msg='VLAN ID {0} is out of range (0 to 4094)'.format(module.params['vlan'])) + if netaddr.IPAddress(module.params['gateway']) not in netaddr.IPNetwork(module.params['prefix']): + module.fail_json(msg='Gateway and subnet are not compatible.') + subnets = blade.subnets.list_subnets() + nrange = netaddr.IPSet([module.params['prefix']]) + for sub in range(0, len(subnets.items)): + if subnets.items[sub].vlan == module.params['vlan'] and subnets.items[sub].name != module.params['name']: + module.fail_json(msg='VLAN ID {0} is already in use.'.format(module.params['vlan'])) + if nrange & netaddr.IPSet([subnets.items[sub].prefix]) and subnets.items[sub].name != module.params['name']: + module.fail_json(msg='Prefix CIDR overlaps with existing subnet.') + + if state == 'present' and not subnet: + create_subnet(module, blade) + elif state == 'present' and subnet: + modify_subnet(module, blade) + elif state == 'absent' and subnet: + delete_subnet(module, blade) + elif state == 'absent' and not subnet: + module.exit_json(changed=False) + + +if __name__ == '__main__': + main()