#!/usr/bin/python # -*- coding: utf-8 -*- # # Copyright (c) 2018, David Passante <@dpassante> # 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: cs_vlan_ip_range short_description: Manages VLAN IP ranges on Apache CloudStack based clouds. description: - Create and delete VLAN IP range. author: David Passante (@dpassante) options: network: description: - The network name or id. - Required if I(for_virtual_network) and I(physical_network) are not set. type: str physical_network: description: - The physical network name or id. type: str start_ip: description: - The beginning IPv4 address in the VLAN IP range. - Only considered on create. type: str required: true end_ip: description: - The ending IPv4 address in the VLAN IP range. - If not specified, value of I(start_ip) is used. - Only considered on create. type: str gateway: description: - The gateway of the VLAN IP range. - Required if I(state=present). type: str netmask: description: - The netmask of the VLAN IP range. - Required if I(state=present). type: str start_ipv6: description: - The beginning IPv6 address in the IPv6 network range. - Only considered on create. type: str end_ipv6: description: - The ending IPv6 address in the IPv6 network range. - If not specified, value of I(start_ipv6) is used. - Only considered on create. type: str gateway_ipv6: description: - The gateway of the IPv6 network. - Only considered on create. type: str cidr_ipv6: description: - The CIDR of IPv6 network, must be at least /64. type: str vlan: description: - The ID or VID of the network. - If not specified, will be defaulted to the vlan of the network. type: str state: description: - State of the network ip range. type: str default: present choices: [ present, absent ] zone: description: - The Zone ID of the VLAN IP range. - If not set, default zone is used. type: str domain: description: - Domain of the account owning the VLAN. type: str account: description: - Account who owns the VLAN. - Mutually exclusive with I(project). type: str project: description: - Project who owns the VLAN. - Mutually exclusive with I(account). type: str for_virtual_network: description: - C(yes) if VLAN is of Virtual type, C(no) if Direct. - If set to C(yes) but neither I(physical_network) or I(network) is set CloudStack will try to add the VLAN range to the Physical Network with a Public traffic type. type: bool default: no extends_documentation_fragment: - community.general.cloudstack ''' EXAMPLES = ''' - name: create a VLAN IP range for network test cs_vlan_ip_range: network: test vlan: 98 start_ip: 10.2.4.10 end_ip: 10.2.4.100 gateway: 10.2.4.1 netmask: 255.255.255.0 zone: zone-02 delegate_to: localhost - name: remove a VLAN IP range for network test cs_vlan_ip_range: state: absent network: test start_ip: 10.2.4.10 end_ip: 10.2.4.100 zone: zone-02 delegate_to: localhost ''' RETURN = ''' --- id: description: UUID of the VLAN IP range. returned: success type: str sample: 04589590-ac63-4ffc-93f5-b698b8ac38b6 network: description: The network of vlan range returned: if available type: str sample: test vlan: description: The ID or VID of the VLAN. returned: success type: str sample: vlan://98 gateway: description: IPv4 gateway. returned: success type: str sample: 10.2.4.1 netmask: description: IPv4 netmask. returned: success type: str sample: 255.255.255.0 gateway_ipv6: description: IPv6 gateway. returned: if available type: str sample: 2001:db8::1 cidr_ipv6: description: The CIDR of IPv6 network. returned: if available type: str sample: 2001:db8::/64 zone: description: Name of zone. returned: success type: str sample: zone-02 domain: description: Domain name of the VLAN IP range. returned: success type: str sample: ROOT account: description: Account who owns the network. returned: if available type: str sample: example account project: description: Project who owns the network. returned: if available type: str sample: example project for_systemvms: description: Whether VLAN IP range is dedicated to system vms or not. returned: success type: bool sample: false for_virtual_network: description: Whether VLAN IP range is of Virtual type or not. returned: success type: bool sample: false physical_network: description: The physical network VLAN IP range belongs to. returned: success type: str sample: 04589590-ac63-4ffc-93f5-b698b8ac38b6 start_ip: description: The start ip of the VLAN IP range. returned: success type: str sample: 10.2.4.10 end_ip: description: The end ip of the VLAN IP range. returned: success type: str sample: 10.2.4.100 start_ipv6: description: The start ipv6 of the VLAN IP range. returned: if available type: str sample: 2001:db8::10 end_ipv6: description: The end ipv6 of the VLAN IP range. returned: if available type: str sample: 2001:db8::50 ''' from ansible.module_utils.basic import AnsibleModule from ansible_collections.community.general.plugins.module_utils.cloudstack import ( AnsibleCloudStack, cs_argument_spec, cs_required_together, ) class AnsibleCloudStackVlanIpRange(AnsibleCloudStack): def __init__(self, module): super(AnsibleCloudStackVlanIpRange, self).__init__(module) self.returns = { 'startip': 'start_ip', 'endip': 'end_ip', 'physicalnetworkid': 'physical_network', 'vlan': 'vlan', 'forsystemvms': 'for_systemvms', 'forvirtualnetwork': 'for_virtual_network', 'gateway': 'gateway', 'netmask': 'netmask', 'ip6gateway': 'gateway_ipv6', 'ip6cidr': 'cidr_ipv6', 'startipv6': 'start_ipv6', 'endipv6': 'end_ipv6', } self.ip_range = None def get_vlan_ip_range(self): if not self.ip_range: args = { 'zoneid': self.get_zone(key='id'), 'projectid': self.get_project(key='id'), 'account': self.get_account(key='name'), 'domainid': self.get_domain(key='id'), 'networkid': self.get_network(key='id'), } res = self.query_api('listVlanIpRanges', **args) if res: ip_range_list = res['vlaniprange'] params = { 'startip': self.module.params.get('start_ip'), 'endip': self.get_or_fallback('end_ip', 'start_ip'), } for ipr in ip_range_list: if params['startip'] == ipr['startip'] and params['endip'] == ipr['endip']: self.ip_range = ipr break return self.ip_range def present_vlan_ip_range(self): ip_range = self.get_vlan_ip_range() if not ip_range: ip_range = self.create_vlan_ip_range() return ip_range def create_vlan_ip_range(self): self.result['changed'] = True vlan = self.module.params.get('vlan') args = { 'zoneid': self.get_zone(key='id'), 'projectid': self.get_project(key='id'), 'account': self.get_account(key='name'), 'domainid': self.get_domain(key='id'), 'startip': self.module.params.get('start_ip'), 'endip': self.get_or_fallback('end_ip', 'start_ip'), 'netmask': self.module.params.get('netmask'), 'gateway': self.module.params.get('gateway'), 'startipv6': self.module.params.get('start_ipv6'), 'endipv6': self.get_or_fallback('end_ipv6', 'start_ipv6'), 'ip6gateway': self.module.params.get('gateway_ipv6'), 'ip6cidr': self.module.params.get('cidr_ipv6'), 'vlan': self.get_network(key='vlan') if not vlan else vlan, 'networkid': self.get_network(key='id'), 'forvirtualnetwork': self.module.params.get('for_virtual_network'), } if self.module.params.get('physical_network'): args['physicalnetworkid'] = self.get_physical_network(key='id') if not self.module.check_mode: res = self.query_api('createVlanIpRange', **args) self.ip_range = res['vlan'] return self.ip_range def absent_vlan_ip_range(self): ip_range = self.get_vlan_ip_range() if ip_range: self.result['changed'] = True args = { 'id': ip_range['id'], } if not self.module.check_mode: self.query_api('deleteVlanIpRange', **args) return ip_range def main(): argument_spec = cs_argument_spec() argument_spec.update(dict( network=dict(type='str'), physical_network=dict(type='str'), zone=dict(type='str'), start_ip=dict(type='str', required=True), end_ip=dict(type='str'), gateway=dict(type='str'), netmask=dict(type='str'), start_ipv6=dict(type='str'), end_ipv6=dict(type='str'), gateway_ipv6=dict(type='str'), cidr_ipv6=dict(type='str'), vlan=dict(type='str'), state=dict(choices=['present', 'absent'], default='present'), domain=dict(type='str'), account=dict(type='str'), project=dict(type='str'), for_virtual_network=dict(type='bool', default=False), )) module = AnsibleModule( argument_spec=argument_spec, required_together=cs_required_together(), mutually_exclusive=( ['account', 'project'], ), required_if=(("state", "present", ("gateway", "netmask")),), supports_check_mode=True, ) acs_vlan_ip_range = AnsibleCloudStackVlanIpRange(module) state = module.params.get('state') if state == 'absent': ipr = acs_vlan_ip_range.absent_vlan_ip_range() else: ipr = acs_vlan_ip_range.present_vlan_ip_range() result = acs_vlan_ip_range.get_result(ipr) module.exit_json(**result) if __name__ == '__main__': main()