1#!/usr/bin/python 2# -*- coding: utf-8 -*- 3 4# (c) 2017, Simon Dodsley (simon@purestorage.com) 5# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) 6 7from __future__ import absolute_import, division, print_function 8__metaclass__ = type 9 10 11ANSIBLE_METADATA = {'metadata_version': '1.1', 12 'status': ['preview'], 13 'supported_by': 'community'} 14 15 16DOCUMENTATION = ''' 17--- 18module: purefb_subnet 19version_added: "2.8" 20short_description: Manage network subnets in a Pure Storage FlashBlade 21description: 22 - This module manages network subnets on Pure Storage FlashBlade. 23author: Pure Storage Ansible Team (@sdodsley) <pure-ansible-team@purestorage.com> 24options: 25 name: 26 description: 27 - Subnet Name. 28 required: true 29 type: str 30 state: 31 description: 32 - Create, delete or modifies a subnet. 33 required: false 34 default: present 35 choices: [ "present", "absent" ] 36 type: str 37 gateway: 38 description: 39 - IPv4 or IPv6 address of subnet gateway. 40 required: false 41 type: str 42 mtu: 43 description: 44 - MTU size of the subnet. Range is 1280 to 9216. 45 required: false 46 default: 1500 47 type: int 48 prefix: 49 description: 50 - IPv4 or IPv6 address associated with the subnet. 51 - Supply the prefix length (CIDR) as well as the IP address. 52 required: false 53 type: str 54 vlan: 55 description: 56 - VLAN ID of the subnet. 57 required: false 58 default: 0 59 type: int 60extends_documentation_fragment: 61 - purestorage.fb 62notes: 63 - Requires the netaddr Python package on the host. 64requirements: 65 - netaddr 66''' 67 68EXAMPLES = ''' 69- name: Create new network subnet named foo 70 purefb_subnet: 71 name: foo 72 prefix: "10.21.200.3/24" 73 gateway: 10.21.200.1 74 mtu: 9000 75 vlan: 2200 76 state: present 77 fb_url: 10.10.10.2 78 api_token: T-55a68eb5-c785-4720-a2ca-8b03903bf641 79 80- name: Change configuration of existing subnet foo 81 purefb_network: 82 name: foo 83 state: present 84 prefix: "10.21.100.3/24" 85 gateway: 10.21.100.1 86 mtu: 1500 87 address: 10.21.200.123 88 fb_url: 10.10.10.2 89 api_token: T-55a68eb5-c785-4720-a2ca-8b03903bf641 90 91- name: Delete network subnet named foo 92 purefb_subnet: 93 name: foo 94 state: absent 95 fb_url: 10.10.10.2 96 api_token: T-55a68eb5-c785-4720-a2ca-8b03903bf641''' 97 98RETURN = ''' 99''' 100 101HAS_PURITY_FB = True 102try: 103 from purity_fb import Subnet 104except ImportError: 105 HAS_PURITY_FB = False 106 107try: 108 import netaddr 109 HAS_NETADDR = True 110except ImportError: 111 HAS_NETADDR = False 112 113from ansible.module_utils.basic import AnsibleModule 114from ansible.module_utils.pure import get_blade, purefb_argument_spec 115 116 117MINIMUM_API_VERSION = '1.3' 118 119 120def get_subnet(module, blade): 121 """Return Subnet or None""" 122 subnet = [] 123 subnet.append(module.params['name']) 124 try: 125 res = blade.subnets.list_subnets(names=subnet) 126 return res.items[0] 127 except Exception: 128 return None 129 130 131def create_subnet(module, blade): 132 """Create Subnet""" 133 134 subnet = [] 135 subnet.append(module.params['name']) 136 try: 137 blade.subnets.create_subnets(names=subnet, 138 subnet=Subnet(prefix=module.params['prefix'], 139 vlan=module.params['vlan'], 140 mtu=module.params['mtu'], 141 gateway=module.params['gateway'] 142 ) 143 ) 144 changed = True 145 except Exception: 146 module.fail_json(msg='Failed to create subnet {0}. Confirm supplied parameters'.format(module.params['name'])) 147 module.exit_json(changed=changed) 148 149 150def modify_subnet(module, blade): 151 """Modify Subnet settings""" 152 changed = False 153 subnet = get_subnet(module, blade) 154 subnet_new = [] 155 subnet_new.append(module.params['name']) 156 if module.params['prefix']: 157 if module.params['prefix'] != subnet.prefix: 158 try: 159 blade.subnets.update_subnets(names=subnet_new, 160 subnet=Subnet(prefix=module.params['prefix'])) 161 changed = True 162 except Exception: 163 module.fail_json(msg='Failed to change subnet {0} prefix to {1}'.format(module.params['name'], 164 module.params['prefix'])) 165 if module.params['vlan']: 166 if module.params['vlan'] != subnet.vlan: 167 try: 168 blade.subnets.update_subnets(names=subnet_new, 169 subnet=Subnet(vlan=module.params['vlan'])) 170 changed = True 171 except Exception: 172 module.fail_json(msg='Failed to change subnet {0} VLAN to {1}'.format(module.params['name'], 173 module.params['vlan'])) 174 if module.params['gateway']: 175 if module.params['gateway'] != subnet.gateway: 176 try: 177 blade.subnets.update_subnets(names=subnet_new, 178 subnet=Subnet(gateway=module.params['gateway'])) 179 changed = True 180 except Exception: 181 module.fail_json(msg='Failed to change subnet {0} gateway to {1}'.format(module.params['name'], 182 module.params['gateway'])) 183 if module.params['mtu']: 184 if module.params['mtu'] != subnet.mtu: 185 try: 186 blade.subnets.update_subnets(names=subnet_new, 187 subnet=Subnet(mtu=module.params['mtu'])) 188 changed = True 189 except Exception: 190 module.fail_json(msg='Failed to change subnet {0} MTU to {1}'.format(module.params['name'], 191 module.params['mtu'])) 192 module.exit_json(changed=changed) 193 194 195def delete_subnet(module, blade): 196 """ Delete Subnet""" 197 subnet = [] 198 subnet.append(module.params['name']) 199 try: 200 blade.subnets.delete_subnets(names=subnet) 201 changed = True 202 except Exception: 203 changed = False 204 module.exit_json(changed=changed) 205 206 207def main(): 208 argument_spec = purefb_argument_spec() 209 argument_spec.update( 210 dict( 211 name=dict(required=True), 212 state=dict(default='present', choices=['present', 'absent']), 213 gateway=dict(), 214 mtu=dict(type='int', default=1500), 215 prefix=dict(), 216 vlan=dict(type='int', default=0), 217 ) 218 ) 219 220 required_if = [["state", "present", ["gateway", 'prefix']]] 221 222 module = AnsibleModule(argument_spec, 223 required_if=required_if, 224 supports_check_mode=False) 225 226 if not HAS_PURITY_FB: 227 module.fail_json(msg='purity_fb sdk is required for this module') 228 229 if not HAS_NETADDR: 230 module.fail_json(msg='netaddr module is required') 231 232 state = module.params['state'] 233 blade = get_blade(module) 234 api_version = blade.api_version.list_versions().versions 235 if MINIMUM_API_VERSION not in api_version: 236 module.fail_json(msg='Upgrade Purity//FB to enable this module') 237 subnet = get_subnet(module, blade) 238 if state == 'present': 239 if not (1280 <= module.params['mtu'] <= 9216): 240 module.fail_json(msg='MTU {0} is out of range (1280 to 9216)'.format(module.params['mtu'])) 241 if not (0 <= module.params['vlan'] <= 4094): 242 module.fail_json(msg='VLAN ID {0} is out of range (0 to 4094)'.format(module.params['vlan'])) 243 if netaddr.IPAddress(module.params['gateway']) not in netaddr.IPNetwork(module.params['prefix']): 244 module.fail_json(msg='Gateway and subnet are not compatible.') 245 subnets = blade.subnets.list_subnets() 246 nrange = netaddr.IPSet([module.params['prefix']]) 247 for sub in range(0, len(subnets.items)): 248 if subnets.items[sub].vlan == module.params['vlan'] and subnets.items[sub].name != module.params['name']: 249 module.fail_json(msg='VLAN ID {0} is already in use.'.format(module.params['vlan'])) 250 if nrange & netaddr.IPSet([subnets.items[sub].prefix]) and subnets.items[sub].name != module.params['name']: 251 module.fail_json(msg='Prefix CIDR overlaps with existing subnet.') 252 253 if state == 'present' and not subnet: 254 create_subnet(module, blade) 255 elif state == 'present' and subnet: 256 modify_subnet(module, blade) 257 elif state == 'absent' and subnet: 258 delete_subnet(module, blade) 259 elif state == 'absent' and not subnet: 260 module.exit_json(changed=False) 261 262 263if __name__ == '__main__': 264 main() 265