#!/usr/bin/python
# -*- coding: utf-8 -*-

# (c) 2018, 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 = r"""
---
module: purefa_dns
version_added: '1.0.0'
short_description: Configure FlashArray DNS settings
description:
- Set or erase configuration for the DNS settings.
- Nameservers provided will overwrite any existing nameservers.
- From Purity//FA 6.3.3 DNS setting for FA-File can be configured seperately
  to the management DNS settings
author:
- Pure Storage Ansible Team (@sdodsley) <pure-ansible-team@purestorage.com>
options:
  name:
    description:
    - Name of the DNS configuration.
    - Default value only supported for management service
    default: management
    type: str
    version_added: 1.14.0
  state:
    description:
    - Set or delete directory service configuration
    default: present
    type: str
    choices: [ absent, present ]
  domain:
    description:
    - Domain suffix to be appended when perofrming DNS lookups.
    type: str
  nameservers:
    description:
    - List of up to 3 unique DNS server IP addresses. These can be
      IPv4 or IPv6 - No validation is done of the addresses is performed.
    type: list
    elements: str
  service:
    description:
    - Type of ser vice the DNS will work with
    type: str
    version_added: 1.14.0
    choices: [ management, file ]
    default: management
  source:
    description:
    - A virtual network interface (vif)
    type: str
    version_added: 1.14.0
extends_documentation_fragment:
- purestorage.flasharray.purestorage.fa
"""

EXAMPLES = r"""
- name: Delete exisitng DNS settings
  purestorage.flasharray.purefa_dns:
    state: absent
    fa_url: 10.10.10.2
    api_token: e31060a7-21fc-e277-6240-25983c6c4592

- name: Set managemnt DNS settings
  purestorage.flasharray.purefa_dns:
    domain: purestorage.com
    nameservers:
      - 8.8.8.8
      - 8.8.4.4
    fa_url: 10.10.10.2
    api_token: e31060a7-21fc-e277-6240-25983c6c4592

- name: Set file DNS settings
  purestorage.flasharray.purefa_dns:
    domain: purestorage.com
    nameservers:
      - 8.8.8.8
      - 8.8.4.4
    name: ad_dns
    service: file
    fa_url: 10.10.10.2
    api_token: e31060a7-21fc-e277-6240-25983c6c4592

"""

RETURN = r"""
"""

HAS_PURESTORAGE = True
try:
    from pypureclient import flasharray
except ImportError:
    HAS_PURESTORAGE = False

from ansible.module_utils.basic import AnsibleModule
from ansible_collections.purestorage.flasharray.plugins.module_utils.purefa import (
    get_system,
    get_array,
    purefa_argument_spec,
)

MULTIPLE_DNS = "2.15"


def remove(duplicate):
    final_list = []
    for num in duplicate:
        if num not in final_list:
            final_list.append(num)
    return final_list


def _get_source(module, array):
    res = array.get_network_interfaces(names=[module.params["source"]])
    if res.status_code == 200:
        return True
    else:
        return False


def delete_dns(module, array):
    """Delete DNS settings"""
    changed = False
    current_dns = array.get_dns()
    if current_dns["domain"] == "" and current_dns["nameservers"] == [""]:
        module.exit_json(changed=changed)
    else:
        try:
            changed = True
            if not module.check_mode:
                array.set_dns(domain="", nameservers=[])
        except Exception:
            module.fail_json(msg="Delete DNS settigs failed")
    module.exit_json(changed=changed)


def create_dns(module, array):
    """Set DNS settings"""
    changed = False
    current_dns = array.get_dns()
    if current_dns["domain"] != module.params["domain"] or sorted(
        module.params["nameservers"]
    ) != sorted(current_dns["nameservers"]):
        try:
            changed = True
            if not module.check_mode:
                array.set_dns(
                    domain=module.params["domain"],
                    nameservers=module.params["nameservers"][0:3],
                )
        except Exception:
            module.fail_json(msg="Set DNS settings failed: Check configuration")
    module.exit_json(changed=changed)


def update_multi_dns(module, array):
    """Update a DNS configuration"""
    changed = False
    current_dns = list(array.get_dns(names=[module.params["name"]]).items)[0]
    new_dns = current_dns
    if module.params["domain"] and current_dns.domain != module.params["domain"]:
        new_dns.domain = module.params["domain"]
        changed = True
    if module.params["service"] and current_dns.services != [module.params["service"]]:
        module.fail_json(msg="Changing service type is not permitted")
    if module.params["nameservers"] and sorted(current_dns.nameservers) != sorted(
        module.params["nameservers"]
    ):
        new_dns.nameservers = module.params["nameservers"]
        changed = True
    if (
        module.params["source"] or module.params["source"] == ""
    ) and current_dns.source.name != module.params["source"]:
        new_dns.source.name = module.params["source"]
        changed = True
    if changed and not module.check_mode:
        res = array.patch_dns(
            names=[module.params["name"]],
            dns=flasharray.Dns(
                domain=new_dns.domain,
                nameservers=new_dns.nameservers,
                source=flasharray.ReferenceNoId(module.params["source"]),
            ),
        )
        if res.status_code != 200:
            module.fail_json(
                msg="Update to DNS service {0} failed. Error: {1}".format(
                    module.params["name"], res.errors[0].message
                )
            )
    module.exit_json(changed=changed)


def delete_multi_dns(module, array):
    """Delete a DNS configuration"""
    changed = True
    if module.params["name"] == "management":
        res = array.patch_dns(
            names=[module.params["name"]],
            dns=flasharray.DnsPatch(domain="", nameservers=[]),
        )
        if res.status_code != 200:
            module.fail_json(
                msg="Management DNS configuration not deleted. Error: {0}".format(
                    res.errors[0].message
                )
            )
    else:
        if not module.check_mode:
            res = array.delete_dns(names=[module.params["name"]])
            if res.status_code != 200:
                module.fail_json(
                    msg="Failed to delete DNS configuration {0}. Error: {1}".format(
                        module.params["name"], res.errors[0].message
                    )
                )
    module.exit_json(changed=changed)


def create_multi_dns(module, array):
    """Create a DNS configuration"""
    changed = True
    if not module.check_mode:
        if module.params["service"] == "file":
            if module.params["source"]:
                res = array.post_dns(
                    names=[module.params["name"]],
                    dns=flasharray.DnsPost(
                        services=[module.params["service"]],
                        domain=module.params["domain"],
                        nameservers=module.params["nameservers"],
                        source=flasharray.ReferenceNoId(
                            module.params["source"].lower()
                        ),
                    ),
                )
            else:
                res = array.post_dns(
                    names=[module.params["name"]],
                    dns=flasharray.DnsPost(
                        services=[module.params["service"]],
                        domain=module.params["domain"],
                        nameservers=module.params["nameservers"],
                    ),
                )
        else:
            res = array.create_dns(
                names=[module.params["name"]],
                services=[module.params["service"]],
                domain=module.params["domain"],
                nameservers=module.params["nameservers"],
            )
        if res.status_code != 200:
            module.fail_json(
                msg="Failed to create {0} DNS configuration {1}. Error: {2}".format(
                    module.params["service"],
                    module.params["name"],
                    res.errors[0].message,
                )
            )
    module.exit_json(changed=changed)


def main():
    argument_spec = purefa_argument_spec()
    argument_spec.update(
        dict(
            state=dict(type="str", default="present", choices=["absent", "present"]),
            name=dict(type="str", default="management"),
            service=dict(
                type="str", default="management", choices=["management", "file"]
            ),
            domain=dict(type="str"),
            source=dict(type="str"),
            nameservers=dict(type="list", elements="str"),
        )
    )

    module = AnsibleModule(argument_spec, supports_check_mode=True)

    state = module.params["state"]
    array = get_system(module)
    api_version = array._list_available_rest_versions()
    if module.params["nameservers"]:
        module.params["nameservers"] = remove(module.params["nameservers"])
        if module.params["service"] == "management":
            module.params["nameservers"] = module.params["nameservers"][0:3]

    if MULTIPLE_DNS in api_version:
        array = get_array(module)
        configs = list(array.get_dns().items)
        exists = False
        for config in range(0, len(configs)):
            if configs[config].name == module.params["name"]:
                exists = True
        if (
            module.params["service"] == "management"
            and module.params["name"] != "management"
            and not exists
        ):
            module.warn("Overriding configuration name to management")
            module.params["name"] = "management"
        if module.params["source"] and not _get_source(module, array):
            module.fail_json(
                msg="Specified VIF {0} does not exist.".format(module.params["source"])
            )
        if state == "present" and exists:
            update_multi_dns(module, array)
        elif state == "present" and not exists:
            if len(configs) == 2:
                module.fail_json(
                    msg="Only 2 DNS configurations are currently "
                    "supported. One for management and one for file services"
                )
            create_multi_dns(module, array)
        elif exists and state == "absent":
            delete_multi_dns(module, array)
        else:
            module.exit_json(changed=False)
    else:
        if state == "absent":
            delete_dns(module, array)
        elif state == "present":
            if not module.params["domain"] or not module.params["nameservers"]:
                module.fail_json(
                    msg="`domain` and `nameservers` are required for DNS configuration"
                )
            create_dns(module, array)
        else:
            module.exit_json(changed=False)


if __name__ == "__main__":
    main()
