#!/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()