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

# (c) 2020, 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_policy
version_added: '1.5.0'
short_description: Manage FlashArray File System Policies
description:
- Manage FlashArray file system policies for NFS, SMB and snapshot
author:
- Pure Storage Ansible Team (@sdodsley) <pure-ansible-team@purestorage.com>
options:
  name:
    description:
    - Name of the policy
    type: str
    required: true
  state:
    description:
    - Define whether the policy should exist or not.
    default: present
    choices: [ absent, present ]
    type: str
  policy:
    description:
    - The type of policy to use
    choices: [ nfs, smb, snapshot, quota, autodir, password ]
    required: true
    type: str
  enabled:
    description:
    - Define if policy is enabled or not
    type: bool
    default: true
  smb_anon_allowed:
    description:
    - Specifies whether access to information is allowed for anonymous users
    type: bool
    default: false
  client:
    description:
    - Specifies which SMB or NFS clients are given access
    - Accepted notation, IP, IP mask, or hostname
    type: str
  smb_encrypt:
    description:
    - Specifies whether the remote client is required to use SMB encryption
    type: bool
    default: false
  nfs_access:
    description:
    - Specifies access control for the export
    choices: [ root-squash, no-root-squash, all-squash ]
    type: str
    default: no-root-squash
  nfs_permission:
    description:
    - Specifies which read-write client access permissions are allowed for the export
    choices: [ ro, rw ]
    default: rw
    type: str
  nfs_version:
    description:
    - NFS protocol version allowed for the export
    type: list
    elements: str
    choices: [ nfsv3, nfsv4 ]
    version_added: "1.22.0"
  user_mapping:
    description:
    - Defines if user mapping is enabled
    type: bool
    default: true
    version_added: 1.14.0
  snap_at:
    description:
    - Specifies the number of hours since midnight at which to take a snapshot
      or the hour including AM/PM
    - Can only be set on the rule with the smallest I(snap_every) value.
    - Cannot be set if the I(snap_every) value is not measured in days.
    - Can only be set for at most one rule in the same policy.
    type: str
  snap_every:
    description:
    - Specifies the interval between snapshots, in minutes.
    - The value for all rules must be multiples of one another.
    - Must be unique for each rule in the same policy.
    - Value must be between 5 and 525600.
    type: int
  snap_keep_for:
    description:
    - Specifies the period that snapshots are retained before they are eradicated, in minutes.
    - Cannot be less than the I(snap_every) value of the rule.
    - Value must be unique for each rule in the same policy.
    - Value must be between 5 and 525600.
    type: int
  snap_client_name:
    description:
    - The customizable portion of the client visible snapshot name.
    type: str
  snap_suffix:
    description:
    - The snapshot suffix name
    - The suffix value can only be set for one rule in the same policy
    - The suffix value can only be set on a rule with the same ``keep_for`` value and ``every`` value
    - The suffix value can only be set on the rule with the largest ``keep_for`` value
    - If not specified, defaults to a monotonically increasing number generated by the system.
    type: str
    version_added: 1.10.0
  rename:
    description:
    - New name of policy
    type: str
  directory:
    description:
    - Directories to have the quota rule applied to.
    type: list
    elements: str
    version_added: 1.9.0
  quota_limit:
    description:
    - Logical space limit of the share in M, G, T or P units. See examples.
    - If size is not set at filesystem creation time the filesystem size becomes unlimited.
    - This value cannot be set to 0.
    type: str
    version_added: 1.9.0
  quota_notifications:
    description:
    - Targets to notify when usage approaches the quota limit.
    - The list of notification targets is a comma-separated string
    - If not specified, notification targets are not assigned.
    type: list
    elements: str
    choices: [ user, group ]
    version_added: 1.9.0
  quota_enforced:
    description:
    - Defines if the directory quota is enforced.
    default: true
    type: bool
  ignore_usage:
    description:
    -  Flag used to override checks for quota management
       operations.
    - If set to true, directory usage is not checked against the
      quota_limits that are set.
    - If set to false, the actual logical bytes in use are prevented
      from exceeding the limits set on the directory.
    - Client operations might be impacted.
    - If the limit exceeds the quota, the client operation is not allowed.
    default: false
    type: bool
    version_added: 1.9.0
  anonuid:
    description:
    - The ID to which any users whose UID is affected by I(access) of
      I(root-squash) or I(all-squash) will be mapped to.
    - Clear using "".
    type: str
    default: "65534"
    version_added: 1.14.0
  anongid:
    description:
    - The ID to which any users whose GID is affected by I(access) of
      I(root-squash) or I(all-squash) will be mapped to.
    - This is ignored when I(user_mapping) is enabled.
    - Clear using "".
    type: str
    default: "65534"
    version_added: 1.14.0
  security:
    description:
    - The security flavors to use for accessing files on a mount point.
    - If the server does not support the requested flavor, the mount operation fails.
    - This operation updates all rules of the specified policy.
    type: list
    elements: str
    choices: [ auth_sys, krb5, krb5i, krb5p ]
    version_added: 1.25.0
  access_based_enumeration:
    description:
    - Defines if access based enumeration for SMB is enabled
    type: bool
    default: false
    version_added: 1.26.0
  enforce_dictionary_check:
    description:
    - If I(true), test password against dictionary of known leaked passwords.
    - Only applies to passwords longer than 6 characters.
    type: bool
    version_added: 1.33.0
  enforce_username_check:
    description:
    - If I(true), the username cannot be a substring of the password.
    - It only applies to usernames of 3 characters and longer
    type: bool
    version_added: 1.33.0
  max_login_attempts:
    description:
    - Maximum number of failed logins before account is locked
    type: int
    version_added: 1.33.0
  min_password_length:
    description:
    - Minimum user password length
    type: int
    version_added: 1.33.0
  lockout_duration:
    description:
    - Account lockout duration, in seconds, after I(max_login_attempts) exceeded
    - Range between 1 second and 90 days (7776000 seconds)
    type: int
    version_added: 1.33.0
  password_history:
    description:
    - The number of passwords tracked to prevent reuse of passwords.
    - A value of 0 will not check password history
    type: int
    version_added: 1.33.0
  min_character_groups:
    description:
    - The minimum number of character groups required to be present in a password.
    type: int
    version_added: 1.33.0
  min_characters_per_group:
    description:
    - The minimum number of characters per group to count the group as present.
    - Maximum is limited by the minimum password length divided by the number of character groups
    type: int
    version_added: 1.33.0
extends_documentation_fragment:
- purestorage.flasharray.purestorage.fa
"""

EXAMPLES = r"""
- name: Create an NFS policy with initial rule
  purestorage.flasharray.purefa_policy:
    name: export1
    policy: nfs
    nfs_access: root-squash
    nfs_permission: ro
    client: client1
    fa_url: 10.10.10.2
    api_token: e31060a7-21fc-e277-6240-25983c6c4592

- name: Create an empty NFS policy with no rules
  purestorage.flasharray.purefa_policy:
    name: export1
    policy: nfs
    fa_url: 10.10.10.2
    api_token: e31060a7-21fc-e277-6240-25983c6c4592

- name: Create an empty snapshot policy with no rules
  purestorage.flasharray.purefa_policy:
    name: snap1
    policy: snapshot
    fa_url: 10.10.10.2
    api_token: e31060a7-21fc-e277-6240-25983c6c4592

- name: Create an empty snapshot policy with single directory member
  purestorage.flasharray.purefa_policy:
    name: snap1
    policy: snapshot
    directory: "foo:bar"
    fa_url: 10.10.10.2
    api_token: e31060a7-21fc-e277-6240-25983c6c4592

- name: Disable a policy
  purestorage.flasharray.purefa_policy:
    name: export1
    policy: nfs
    enabled: false
    fa_url: 10.10.10.2
    api_token: e31060a7-21fc-e277-6240-25983c6c4592

- name: Add rule to existing NFS export policy
  purestorage.flasharray.purefa_policy:
    name: export1
    policy: nfs
    nfs_access: root-squash
    nfs_permission: ro
    client: client2
    fa_url: 10.10.10.2
    api_token: e31060a7-21fc-e277-6240-25983c6c4592

- name: Add rule to existing SMB export policy
  purestorage.flasharray.purefa_policy:
    name: export1
    policy: smb
    smb_encrypt: true
    smb_anon_allowed: false
    client: client1
    fa_url: 10.10.10.2
    api_token: e31060a7-21fc-e277-6240-25983c6c4592

- name: Add non-suffix rule to existing snapshot export policy
  purestorage.flasharray.purefa_policy:
    name: snap1
    policy: snapshot
    snap_client_name: foo
    snap_every: 15
    snap_keep_for: 1440
    fa_url: 10.10.10.2
    api_token: e31060a7-21fc-e277-6240-25983c6c4592

- name: Add suffix rule to existing snapshot export policy
  purestorage.flasharray.purefa_policy:
    name: snap1
    policy: snapshot
    snap_client_name: foo
    snap_suffix: bar
    snap_every: 1440
    snap_keep_for: 1440
    fa_url: 10.10.10.2
    api_token: e31060a7-21fc-e277-6240-25983c6c4592

- name: Delete policy rule for a client
  purestorage.flasharray.purefa_policy:
    name: export1
    policy: nfs
    client: client2
    state: absent
    fa_url: 10.10.10.2
    api_token: e31060a7-21fc-e277-6240-25983c6c4592

- name: Delete policy
  purestorage.flasharray.purefa_policy:
    name: export1
    policy: nfs
    state: absent
    fa_url: 10.10.10.2
    api_token: e31060a7-21fc-e277-6240-25983c6c4592

- name: Create directory quota policy for directory bar
  purestorage.flasharray.purefa_policy:
    name: foo
    directory:
     - "foo:root"
     - "bar:bin"
    policy: quota
    quota_limit: 10G
    fa_url: 10.10.10.2
    api_token: e31060a7-21fc-e277-6240-25983c6c4592

- name: Delete directory quota policy foo
  purestorage.flasharray.purefa_policy:
    name: foo
    policy: quota
    state: absent
    fa_url: 10.10.10.2
    api_token: e31060a7-21fc-e277-6240-25983c6c4592

- name: Create empty directory quota policy foo
  purestorage.flasharray.purefa_policy:
    name: foo
    policy: quota
    fa_url: 10.10.10.2
    api_token: e31060a7-21fc-e277-6240-25983c6c4592

- name: Detach directory "foo:bar" from quota policy quota1
  purestorage.flasharray.purefa_policy:
    name: quota1
    directory:
     - "foo:bar"
    state: absent
    policy: quota
    fa_url: 10.10.10.2
    api_token: e31060a7-21fc-e277-6240-25983c6c4592

- name: Remove quota rule from quota policy foo
  purestorage.flasharray.purefa_policy:
    name: foo
    policy: quota
    quota_limit: 10G
    state: absent
    fa_url: 10.10.10.2
    api_token: e31060a7-21fc-e277-6240-25983c6c4592

- name: Update password police management
  purestorage.flasharray.purefa_policy:
    name: management
    policy: password
    max_login_attempts: 5
    enforce_username_check: true
    enforce_dictopnary_check: true
    min_password_length: 5
    password_history: 2
    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,
)
from ansible_collections.purestorage.flasharray.plugins.module_utils.version import (
    LooseVersion,
)
from ansible_collections.purestorage.flasharray.plugins.module_utils.common import (
    human_to_bytes,
    convert_to_millisecs,
)

MIN_REQUIRED_API_VERSION = "2.3"
MIN_QUOTA_API_VERSION = "2.7"
MIN_SUFFIX_API_VERSION = "2.9"
USER_MAP_VERSION = "2.15"
ALL_SQUASH_VERSION = "2.16"
AUTODIR_VERSION = "2.24"
NFS_VERSION = "2.26"
SECURITY_VERSION = "2.29"
ABE_VERSION = "2.4"
PASSWORD_VERSION = "2.34"


def rename_policy(module, array):
    """Rename a file system policy"""
    changed = False
    target_exists = bool(
        array.get_policies(names=[module.params["rename"]]).status_code == 200
    )
    if target_exists:
        module.fail_json(
            msg="Rename failed - Target policy {0} already exists".format(
                module.params["rename"]
            )
        )
    if not module.check_mode:
        changed = True
        if module.params["policy"] == "nfs":
            res = array.patch_policies_nfs(
                names=[module.params["name"]],
                policy=flasharray.PolicyPatch(name=module.params["rename"]),
            )
            if res.status_code != 200:
                module.fail_json(
                    msg="Failed to rename NFS policy {0} to {1}".format(
                        module.params["name"], module.params["rename"]
                    )
                )
        elif module.params["policy"] == "smb":
            res = array.patch_policies_smb(
                names=[module.params["name"]],
                policy=flasharray.PolicyPatch(name=module.params["rename"]),
            )
            if res.status_code != 200:
                module.fail_json(
                    msg="Failed to rename SMB policy {0} to {1}".format(
                        module.params["name"], module.params["rename"]
                    )
                )
        elif module.params["policy"] == "snapshot":
            res = array.patch_policies_snapshot(
                names=[module.params["name"]],
                policy=flasharray.PolicyPatch(name=module.params["rename"]),
            )
            if res.status_code != 200:
                module.fail_json(
                    msg="Failed to rename snapshot policy {0} to {1}".format(
                        module.params["name"], module.params["rename"]
                    )
                )
        elif module.params["policy"] == "password":
            module.warn("Password policy rename is not yet supported")
            changed = False
        else:
            res = array.patch_policies_quota(
                names=[module.params["name"]],
                policy=flasharray.PolicyPatch(name=module.params["rename"]),
            )
            if res.status_code != 200:
                module.fail_json(
                    msg="Failed to rename quota policy {0} to {1}".format(
                        module.params["name"], module.params["rename"]
                    )
                )
    module.exit_json(changed=changed)


def delete_policy(module, array):
    """Delete a file system policy or rule within a policy"""
    changed = True
    if not module.check_mode:
        changed = False
        if module.params["policy"] == "password":
            module.warn("Password policy deletion is not yet supported")
            changed = False
        if module.params["policy"] == "nfs":
            if not module.params["client"]:
                res = array.delete_policies_nfs(names=[module.params["name"]])
                if res.status_code == 200:
                    changed = True
                else:
                    module.fail_json(
                        msg="Deletion of NFS policy {0} failed. Error: {1}".format(
                            module.params["name"], res.errors[0].message
                        )
                    )
            else:
                rules = list(
                    array.get_policies_nfs_client_rules(
                        policy_names=[module.params["name"]]
                    ).items
                )
                if rules:
                    rule_name = ""
                    for rule in range(0, len(rules)):
                        if rules[rule].client == module.params["client"]:
                            rule_name = rules[rule].name
                            break
                    if rule_name:
                        deleted = bool(
                            array.delete_policies_nfs_client_rules(
                                policy_names=[module.params["name"]], names=[rule_name]
                            ).status_code
                            == 200
                        )
                        if deleted:
                            changed = True
                        else:
                            module.fail_json(
                                msg="Failed to delete client {0} from NFS policy {1}. Error: {2}".format(
                                    module.params["client"],
                                    module.params["name"],
                                    deleted.errors[0].message,
                                )
                            )
        elif module.params["policy"] == "smb":
            if not module.params["client"]:
                res = array.delete_policies_smb(names=[module.params["name"]])
                if res.status_code == 200:
                    changed = True
                else:
                    module.fail_json(
                        msg="Deletion of SMB policy {0} failed. Error: {1}".format(
                            module.params["name"], res.errors[0].message
                        )
                    )
            else:
                rules = list(
                    array.get_policies_smb_client_rules(
                        policy_names=[module.params["name"]]
                    ).items
                )
                if rules:
                    rule_name = ""
                    for rule in range(0, len(rules)):
                        if rules[rule].client == module.params["client"]:
                            rule_name = rules[rule].name
                            break
                    if rule_name:
                        deleted = bool(
                            array.delete_policies_smb_client_rules(
                                policy_names=[module.params["name"]], names=[rule_name]
                            ).status_code
                            == 200
                        )
                        if deleted:
                            changed = True
                        else:
                            module.fail_json(
                                msg="Failed to delete client {0} from SMB policy {1}. Error: {2}".format(
                                    module.params["client"],
                                    module.params["name"],
                                    deleted.errors[0].message,
                                )
                            )
        elif module.params["policy"] == "snapshot":
            if not module.params["snap_client_name"] and not module.params["directory"]:
                res = array.delete_policies_snapshot(names=[module.params["name"]])
                if res.status_code == 200:
                    changed = True
                else:
                    module.fail_json(
                        msg="Deletion of Snapshot policy {0} failed. Error: {1}".format(
                            module.params["name"], res.errors[0].message
                        )
                    )
            if module.params["directory"]:
                dirs = []
                old_dirs = []
                current_dirs = list(
                    array.get_directories_policies_snapshot(
                        policy_names=[module.params["name"]]
                    ).items
                )
                if current_dirs:
                    for current_dir in range(0, len(current_dirs)):
                        dirs.append(current_dirs[current_dir].member.name)
                    for old_dir in range(0, len(module.params["directory"])):
                        if module.params["directory"][old_dir] in dirs:
                            old_dirs.append(module.params["directory"][old_dir])
                else:
                    pass
                if old_dirs:
                    changed = True
                    for rem_dir in range(0, len(old_dirs)):
                        if not module.check_mode:
                            directory_removed = (
                                array.delete_directories_policies_snapshot(
                                    member_names=[old_dirs[rem_dir]],
                                    policy_names=module.params["name"],
                                )
                            )
                            if directory_removed.status_code != 200:
                                module.fail_json(
                                    msg="Failed to remove directory from Snapshot policy {0}. Error: {1}".format(
                                        module.params["name"],
                                        directory_removed.errors[0].message,
                                    )
                                )
            if module.params["snap_client_name"]:
                rules = list(
                    array.get_policies_snapshot_rules(
                        policy_names=[module.params["name"]]
                    ).items
                )
                if rules:
                    rule_name = ""
                    for rule in range(0, len(rules)):
                        if rules[rule].client_name == module.params["snap_client_name"]:
                            rule_name = rules[rule].name
                            break
                    if rule_name:
                        deleted = bool(
                            array.delete_policies_snapshot_rules(
                                policy_names=[module.params["name"]], names=[rule_name]
                            ).status_code
                            == 200
                        )
                        if deleted:
                            changed = True
                        else:
                            module.fail_json(
                                msg="Failed to delete client {0} from Snapshot policy {1}. Error: {2}".format(
                                    module.params["snap_client_name"],
                                    module.params["name"],
                                    deleted.errors[0].message,
                                )
                            )
        elif module.params["policy"] == "autodir":
            if not module.params["directory"]:
                res = array.delete_policies_autodir(names=[module.params["name"]])
                if res.status_code == 200:
                    changed = True
                else:
                    module.fail_json(
                        msg="Deletion of Autodir policy {0} failed. Error: {1}".format(
                            module.params["name"], res.errors[0].message
                        )
                    )
            if module.params["directory"]:
                dirs = []
                old_dirs = []
                current_dirs = list(
                    array.get_directories_policies_autodir(
                        policy_names=[module.params["name"]]
                    ).items
                )
                if current_dirs:
                    for current_dir in range(0, len(current_dirs)):
                        dirs.append(current_dirs[current_dir].member.name)
                    for old_dir in range(0, len(module.params["directory"])):
                        if module.params["directory"][old_dir] in dirs:
                            old_dirs.append(module.params["directory"][old_dir])
                else:
                    pass
                if old_dirs:
                    changed = True
                    for rem_dir in range(0, len(old_dirs)):
                        if not module.check_mode:
                            directory_removed = (
                                array.delete_directories_policies_autodir(
                                    member_names=[old_dirs[rem_dir]],
                                    policy_names=module.params["name"],
                                )
                            )
                            if directory_removed.status_code != 200:
                                module.fail_json(
                                    msg="Failed to remove directory from Autodir policy {0}. Error: {1}".format(
                                        module.params["name"],
                                        directory_removed.errors[0].message,
                                    )
                                )
        else:  # quota
            if module.params["quota_limit"]:
                quota_limit = human_to_bytes(module.params["quota_limit"])
                rules = list(
                    array.get_policies_quota_rules(
                        policy_names=[module.params["name"]]
                    ).items
                )
                if rules:
                    for rule in range(0, len(rules)):
                        if rules[rule].quota_limit == quota_limit:
                            if (
                                module.params["quota_enforced"] == rules[rule].enforced
                                and ",".join(module.params["quota_notifications"])
                                == rules[rule].notifications
                            ):
                                res = array.delete_policies_quota_rules(
                                    policy_names=[module.params["name"]],
                                    names=[rules[rule].name],
                                )
                                if res.status_code == 200:
                                    changed = True
                                else:
                                    module.fail_json(
                                        msg="Deletion of Quota rule failed. Error: {0}".format(
                                            res.errors[0].message
                                        )
                                    )
            if module.params["directory"]:
                members = list(
                    array.get_policies_quota_members(
                        policy_names=[module.params["name"]]
                    ).items
                )
                if members:
                    for member in range(0, len(members)):
                        if members[member].member.name in module.params["directory"]:
                            res = array.delete_policies_quota_members(
                                policy_names=[module.params["name"]],
                                member_names=[members[member].member.name],
                                member_types="directories",
                            )
                            if res.status_code != 200:
                                module.fail_json(
                                    msg="Deletion of Quota member {0} from policy {1}. Error: {2}".format(
                                        members[member].member.name,
                                        module.params["name"],
                                        res.errors[0].message,
                                    )
                                )
                            else:
                                changed = True
            if not module.params["quota_limit"] and not module.params["directory"]:
                members = list(
                    array.get_policies_quota_members(
                        policy_names=[module.params["name"]]
                    ).items
                )
                if members:
                    member_names = []
                    for member in range(0, len(members)):
                        member_names.append(members[member].member.name)
                    res = array.delete_policies_quota_members(
                        policy_names=[module.params["name"]],
                        member_names=member_names,
                        member_types="directories",
                    )
                    if res.status_code != 200:
                        module.fail_json(
                            msg="Deletion of Quota members {0} failed. Error: {1}".format(
                                module.params["name"], res.errors[0].message
                            )
                        )
                res = array.delete_policies_quota(names=[module.params["name"]])
                if res.status_code == 200:
                    changed = True
                else:
                    module.fail_json(
                        msg="Deletion of Quota policy {0} failed. Error: {1}".format(
                            module.params["name"], res.errors[0].message
                        )
                    )
    module.exit_json(changed=changed)


def create_policy(module, array, all_squash):
    """Create a file system export"""
    changed = True
    if not module.check_mode:
        changed = False
        if module.params["policy"] == "nfs":
            created = array.post_policies_nfs(
                names=[module.params["name"]],
                policy=flasharray.PolicyPost(enabled=module.params["enabled"]),
            )

            if created.status_code == 200:
                changed = True
                if module.params["client"]:
                    if all_squash:
                        rules = flasharray.PolicyrulenfsclientpostRules(
                            access=module.params["nfs_access"],
                            anongid=module.params["anongid"],
                            anonuid=module.params["anonuid"],
                            client=module.params["client"],
                            permission=module.params["nfs_permission"],
                        )
                    else:
                        rules = flasharray.PolicyrulenfsclientpostRules(
                            access=module.params["nfs_access"],
                            client=module.params["client"],
                            permission=module.params["nfs_permission"],
                        )
                    rule = flasharray.PolicyRuleNfsClientPost(rules=[rules])
                    rule_created = array.post_policies_nfs_client_rules(
                        policy_names=[module.params["name"]], rules=rule
                    )
                    if rule_created.status_code != 200:
                        module.fail_json(
                            msg="Failed to create rule for NFS policy {0}. Error: {1}".format(
                                module.params["name"], rule_created.errors[0].message
                            )
                        )
                policy = flasharray.PolicyNfsPatch(
                    user_mapping_enabled=module.params["user_mapping"],
                )
                res = array.patch_policies_nfs(
                    names=[module.params["name"]], policy=policy
                )
                if res.status_code != 200:
                    module.fail_json(
                        msg="Failed to set NFS policy user_mapping {0}. Error: {1}".format(
                            module.params["name"], res.errors[0].message
                        )
                    )
                if (
                    LooseVersion(array.get_rest_version()) >= LooseVersion(NFS_VERSION)
                    and module.params["client"]
                    and module.params["nfs_version"]
                ):
                    policy = flasharray.PolicyNfsPatch(
                        nfs_version=module.params["nfs_version"],
                    )
                    res = array.patch_policies_nfs(
                        names=[module.params["name"]], policy=policy
                    )
                    if res.status_code != 200:
                        module.fail_json(
                            msg="Failed to set NFS policy version {0}. Error: {1}".format(
                                module.params["name"], res.errors[0].message
                            )
                        )
                if (
                    LooseVersion(array.get_rest_version())
                    >= LooseVersion(SECURITY_VERSION)
                    and module.params["security"]
                ):
                    policy = flasharray.PolicyNfsPatch(
                        security=module.params["security"],
                    )
                    res = array.patch_policies_nfs(
                        names=[module.params["name"]], policy=policy
                    )
                    if res.status_code != 200:
                        module.fail_json(
                            msg="Failed to set NFS policy security {0}. Error: {1}".format(
                                module.params["name"], res.errors[0].message
                            )
                        )
            else:
                module.fail_json(
                    msg="Failed to create NFS policy {0}. Error: {1}".format(
                        module.params["name"], created.errors[0].message
                    )
                )
        elif module.params["policy"] == "smb":
            created = array.post_policies_smb(
                names=[module.params["name"]],
                policy=flasharray.PolicyPost(enabled=module.params["enabled"]),
            )
            if created.status_code == 200:
                if LooseVersion(ABE_VERSION) <= LooseVersion(array.get_rest_version()):
                    res = array.patch_policies_smb(
                        names=[module.params["name"]],
                        policy=flasharray.PolicySmbPatch(
                            access_based_enumeration_enabled=module.params[
                                "access_based_enumeration"
                            ]
                        ),
                    )
                    if res.status_code != 200:
                        module.fail_json(
                            msg="Failed to set SMB policy {0}. Error: {1}".format(
                                module.params["name"], res.errors[0].message
                            )
                        )
                if module.params["client"]:
                    rules = flasharray.PolicyrulesmbclientpostRules(
                        anonymous_access_allowed=module.params["smb_anon_allowed"],
                        client=module.params["client"],
                        smb_encryption_required=module.params["smb_encrypt"],
                    )
                    rule = flasharray.PolicyRuleSmbClientPost(rules=[rules])
                    rule_created = array.post_policies_smb_client_rules(
                        policy_names=[module.params["name"]], rules=rule
                    )
                    if rule_created.status_code != 200:
                        module.fail_json(
                            msg="Failed to create rule for SMB policy {0}. Error: {1}".format(
                                module.params["name"], rule_created.errors[0].message
                            )
                        )
                changed = True
            else:
                module.fail_json(
                    msg="Failed to create SMB policy {0}. Error: {1}".format(
                        module.params["name"], created.errors[0].message
                    )
                )
        elif module.params["policy"] == "snapshot":
            suffix_enabled = bool(
                LooseVersion(array.get_rest_version())
                >= LooseVersion(MIN_SUFFIX_API_VERSION)
            )
            created = array.post_policies_snapshot(
                names=[module.params["name"]],
                policy=flasharray.PolicyPost(enabled=module.params["enabled"]),
            )
            if created.status_code == 200:
                changed = True
                if module.params["snap_client_name"]:
                    if module.params["snap_keep_for"] < module.params["snap_every"]:
                        module.fail_json(
                            msg="Retention period (snap_keep_for) cannot be less than snapshot interval (snap_every)."
                        )
                    if module.params["snap_at"]:
                        if not module.params["snap_every"] % 1440 == 0:
                            module.fail_json(
                                msg="snap_at time can only be set if snap_every is multiple of 1440"
                            )
                        if suffix_enabled:
                            rules = flasharray.PolicyrulesnapshotpostRules(
                                at=convert_to_millisecs(module.params["snap_at"]),
                                client_name=module.params["snap_client_name"],
                                every=module.params["snap_every"] * 60000,
                                keep_for=module.params["snap_keep_for"] * 60000,
                                suffix=module.params["snap_suffix"],
                            )
                        else:
                            rules = flasharray.PolicyrulesnapshotpostRules(
                                at=convert_to_millisecs(module.params["snap_at"]),
                                client_name=module.params["snap_client_name"],
                                every=module.params["snap_every"] * 60000,
                                keep_for=module.params["snap_keep_for"] * 60000,
                            )
                    else:
                        if suffix_enabled:
                            rules = flasharray.PolicyrulesnapshotpostRules(
                                client_name=module.params["snap_client_name"],
                                every=module.params["snap_every"] * 60000,
                                keep_for=module.params["snap_keep_for"] * 60000,
                                suffix=module.params["snap_suffix"],
                            )
                        else:
                            rules = flasharray.PolicyrulesnapshotpostRules(
                                client_name=module.params["snap_client_name"],
                                every=module.params["snap_every"] * 60000,
                                keep_for=module.params["snap_keep_for"] * 60000,
                            )
                    rule = flasharray.PolicyRuleSnapshotPost(rules=[rules])
                    rule_created = array.post_policies_snapshot_rules(
                        policy_names=[module.params["name"]], rules=rule
                    )
                    if rule_created.status_code != 200:
                        module.fail_json(
                            msg="Failed to create rule for Snapshot policy {0}. Error: {1}".format(
                                module.params["name"], rule_created.errors[0].message
                            )
                        )
                if module.params["directory"]:
                    policies = flasharray.DirectoryPolicyPost(
                        policies=[
                            flasharray.DirectorypolicypostPolicies(
                                policy=flasharray.Reference(name=module.params["name"])
                            )
                        ]
                    )
                    directory_added = array.post_directories_policies_snapshot(
                        member_names=module.params["directory"], policies=policies
                    )
                    if directory_added.status_code != 200:
                        module.fail_json(
                            msg="Failed to add directory for Snapshot policy {0}. Error: {1}".format(
                                module.params["name"],
                                directory_added.errors[0].message,
                            )
                        )
            else:
                module.fail_json(
                    msg="Failed to create Snapshot policy {0}. Error: {1}".format(
                        module.params["name"], created.errors[0].message
                    )
                )
        elif module.params["policy"] == "snapshot":
            module.warn("Password policy creation is not yet supported")
            changed = False
        elif module.params["policy"] == "autodir":
            created = array.post_policies_autodir(
                names=[module.params["name"]],
                policy=flasharray.PolicyPost(enabled=module.params["enabled"]),
            )
            if created.status_code == 200:
                changed = True
                if module.params["directory"]:
                    policies = flasharray.DirectoryPolicyPost(
                        policies=[
                            flasharray.DirectorypolicypostPolicies(
                                policy=flasharray.Reference(name=module.params["name"])
                            )
                        ]
                    )
                    directory_added = array.post_directories_policies_autodir(
                        member_names=module.params["directory"], policies=policies
                    )
                    if directory_added.status_code != 200:
                        module.fail_json(
                            msg="Failed to add directory for Autodir policy {0}. Error: {1}".format(
                                module.params["name"],
                                directory_added.errors[0].message,
                            )
                        )
            else:
                module.fail_json(
                    msg="Failed to create Autodir policy {0}. Error: {1}".format(
                        module.params["name"], created.errors[0].message
                    )
                )
        else:  # quota
            created = array.post_policies_quota(
                names=[module.params["name"]],
                policy=flasharray.PolicyPost(enabled=module.params["enabled"]),
            )
            if created.status_code == 200:
                changed = True
                if module.params["quota_limit"]:
                    quota = human_to_bytes(module.params["quota_limit"])
                    rules = flasharray.PolicyrulequotapostRules(
                        enforced=module.params["quota_enforced"],
                        quota_limit=quota,
                        notifications=",".join(module.params["quota_notifications"]),
                    )
                    rule = flasharray.PolicyRuleQuotaPost(rules=[rules])
                    quota_created = array.post_policies_quota_rules(
                        policy_names=[module.params["name"]],
                        rules=rule,
                        ignore_usage=module.params["ignore_usage"],
                    )
                    if quota_created.status_code != 200:
                        module.fail_json(
                            msg="Failed to create rule for Quota policy {0}. Error: {1}".format(
                                module.params["name"], quota_created.errors[0].message
                            )
                        )
                if module.params["directory"]:
                    members = []
                    for mem in range(0, len(module.params["directory"])):
                        members.append(
                            flasharray.PolicymemberpostMembers(
                                member=flasharray.ReferenceWithType(
                                    name=module.params["directory"][mem],
                                    resource_type="directories",
                                )
                            )
                        )
                    member = flasharray.PolicyMemberPost(members=members)
                    members_created = array.post_policies_quota_members(
                        policy_names=[module.params["name"]],
                        members=member,
                        ignore_usage=module.params["ignore_usage"],
                    )
                    if members_created.status_code != 200:
                        module.fail_json(
                            msg="Failed to add members to Quota policy {0}. Error: {1}".format(
                                module.params["name"],
                                members_created.errors[0].message,
                            )
                        )
            else:
                module.fail_json(
                    msg="Failed to create Quota policy {0}. Error: {1}".format(
                        module.params["name"], created.errors[0].message
                    )
                )
    module.exit_json(changed=changed)


def update_policy(module, array, api_version, all_squash):
    """Update an existing policy including add/remove rules"""
    changed = changed_dir = changed_rule = changed_enable = changed_quota = (
        changed_member
    ) = changed_user_map = changed_abe = changed_nfs = False
    if module.params["policy"] == "nfs":
        current_policy = list(
            array.get_policies_nfs(names=[module.params["name"]]).items
        )[0]
        try:
            current_enabled = current_policy.enabled
            if USER_MAP_VERSION in api_version:
                current_user_map = list(
                    array.get_policies_nfs(names=[module.params["name"]]).items
                )[0].user_mapping_enabled
        except Exception:
            module.fail_json(
                msg="Incorrect policy type specified for existing policy {0}".format(
                    module.params["name"]
                )
            )
        if module.params["nfs_version"] and sorted(
            module.params["nfs_version"]
        ) != sorted(getattr(current_policy, "nfs_version", [])):
            changed_nfs = True
            if not module.check_mode:
                res = array.patch_policies_nfs(
                    names=[module.params["name"]],
                    policy=flasharray.PolicyNfsPatch(
                        nfs_version=module.params["nfs_version"]
                    ),
                )
                if res.status_code != 200:
                    module.exit_json(
                        msg="Failed to change NFS version for NFS policy {0}".format(
                            module.params["name"]
                        )
                    )
        if (
            module.params["user_mapping"]
            and current_user_map != module.params["user_mapping"]
        ):
            changed_user_map = True
            if not module.check_mode:
                res = array.patch_policies_nfs(
                    names=[module.params["name"]],
                    policy=flasharray.PolicyNfsPatch(
                        user_mapping_enabled=module.params["user_mapping"]
                    ),
                )
                if res.status_code != 200:
                    module.exit_json(
                        msg="Failed to enable/disable User Mapping for NFS policy {0}".format(
                            module.params["name"]
                        )
                    )
        if current_enabled != module.params["enabled"]:
            changed_enable = True
            if not module.check_mode:
                res = array.patch_policies_nfs(
                    names=[module.params["name"]],
                    policy=flasharray.PolicyPatch(enabled=module.params["enabled"]),
                )
                if res.status_code != 200:
                    module.exit_json(
                        msg="Failed to enable/disable NFS policy {0}".format(
                            module.params["name"]
                        )
                    )
        if module.params["client"]:
            rules = list(
                array.get_policies_nfs_client_rules(
                    policy_names=[module.params["name"]]
                ).items
            )
            if rules:
                rule_name = ""
                for rule in range(0, len(rules)):
                    if rules[rule].client == module.params["client"]:
                        rule_name = rules[rule].name
                        break
                if not rule_name:
                    if LooseVersion(NFS_VERSION) > LooseVersion(
                        array.get_rest_version()
                    ):
                        if all_squash:
                            rules = flasharray.PolicyrulenfsclientpostRules(
                                permission=module.params["nfs_permission"],
                                client=module.params["client"],
                                anongid=module.params["anongid"],
                                anonuid=module.params["anonuid"],
                                access=module.params["nfs_access"],
                            )
                        else:
                            rules = flasharray.PolicyrulenfsclientpostRules(
                                permission=module.params["nfs_permission"],
                                client=module.params["client"],
                                access=module.params["nfs_access"],
                                nfs_version=module.params["nfs_version"],
                            )
                    elif (
                        LooseVersion(SECURITY_VERSION)
                        > LooseVersion(array.get_rest_version())
                        <= LooseVersion(NFS_VERSION)
                    ):
                        if all_squash:
                            rules = flasharray.PolicyrulenfsclientpostRules(
                                permission=module.params["nfs_permission"],
                                client=module.params["client"],
                                anongid=module.params["anongid"],
                                anonuid=module.params["anonuid"],
                                access=module.params["nfs_access"],
                                nfs_version=module.params["nfs_version"],
                            )
                        else:
                            rules = flasharray.PolicyrulenfsclientpostRules(
                                permission=module.params["nfs_permission"],
                                client=module.params["client"],
                                access=module.params["nfs_access"],
                            )
                    else:
                        if module.params["security"]:
                            if all_squash:
                                rules = flasharray.PolicyrulenfsclientpostRules(
                                    permission=module.params["nfs_permission"],
                                    client=module.params["client"],
                                    anongid=module.params["anongid"],
                                    anonuid=module.params["anonuid"],
                                    access=module.params["nfs_access"],
                                    nfs_version=module.params["nfs_version"],
                                    security=module.params["security"],
                                )
                            else:
                                rules = flasharray.PolicyrulenfsclientpostRules(
                                    permission=module.params["nfs_permission"],
                                    client=module.params["client"],
                                    access=module.params["nfs_access"],
                                    security=module.params["security"],
                                )
                        else:
                            if all_squash:
                                rules = flasharray.PolicyrulenfsclientpostRules(
                                    permission=module.params["nfs_permission"],
                                    client=module.params["client"],
                                    anongid=module.params["anongid"],
                                    anonuid=module.params["anonuid"],
                                    access=module.params["nfs_access"],
                                    nfs_version=module.params["nfs_version"],
                                )
                            else:
                                rules = flasharray.PolicyrulenfsclientpostRules(
                                    permission=module.params["nfs_permission"],
                                    client=module.params["client"],
                                    access=module.params["nfs_access"],
                                )
                    rule = flasharray.PolicyRuleNfsClientPost(rules=[rules])
                    changed_rule = True
                    if not module.check_mode:
                        rule_created = array.post_policies_nfs_client_rules(
                            policy_names=[module.params["name"]], rules=rule
                        )
                        if rule_created.status_code != 200:
                            module.fail_json(
                                msg="Failed to create new rule for NFS policy {0}. Error: {1}".format(
                                    module.params["name"],
                                    rule_created.errors[0].message,
                                )
                            )
            else:
                if all_squash:
                    rules = flasharray.PolicyrulenfsclientpostRules(
                        permission=module.params["nfs_permission"],
                        anongid=module.params["anongid"],
                        anonuid=module.params["anonuid"],
                        client=module.params["client"],
                        access=module.params["nfs_access"],
                    )
                else:
                    rules = flasharray.PolicyrulenfsclientpostRules(
                        permission=module.params["nfs_permission"],
                        client=module.params["client"],
                        access=module.params["nfs_access"],
                    )
                rule = flasharray.PolicyRuleNfsClientPost(rules=[rules])
                changed_rule = True
                if not module.check_mode:
                    rule_created = array.post_policies_nfs_client_rules(
                        policy_names=[module.params["name"]], rules=rule
                    )
                    if rule_created.status_code != 200:
                        module.fail_json(
                            msg="Failed to create new rule for SMB policy {0}. Error: {1}".format(
                                module.params["name"], rule_created.errors[0].message
                            )
                        )
    elif module.params["policy"] == "smb":
        try:
            current = list(array.get_policies_smb(names=[module.params["name"]]).items)[
                0
            ]
            current_enabled = current.enabled
            current_access_based_enumeration = current.access_based_enumeration_enabled
        except Exception:
            module.fail_json(
                msg="Incorrect policy type specified for existing policy {0}".format(
                    module.params["name"]
                )
            )
        if (
            "access_based_enumeration" in module.params
            and current_access_based_enumeration
            != module.params["access_based_enumeration"]
        ):
            changed_abe = True
            if not module.check_mode:
                res = array.patch_policies_smb(
                    names=[module.params["name"]],
                    policy=flasharray.PolicySmbPatch(
                        access_based_enumeration_enabled=module.params[
                            "access_based_enumeration"
                        ]
                    ),
                )
                if res.status_code != 200:
                    module.exit_json(
                        msg="Failed to enable/disable Access based enueration for SMB policy {0}".format(
                            module.params["name"]
                        )
                    )
        if current_enabled != module.params["enabled"]:
            changed_enable = True
            if not module.check_mode:
                res = array.patch_policies_smb(
                    names=[module.params["name"]],
                    policy=flasharray.PolicyPatch(enabled=module.params["enabled"]),
                )
                if res.status_code != 200:
                    module.exit_json(
                        msg="Failed to enable/disable SMB policy {0}".format(
                            module.params["name"]
                        )
                    )
        if module.params["client"]:
            rules = list(
                array.get_policies_smb_client_rules(
                    policy_names=[module.params["name"]]
                ).items
            )
            if rules:
                rule_name = ""
                for rule in range(0, len(rules)):
                    if rules[rule].client == module.params["client"]:
                        rule_name = rules[rule].name
                        break
                if not rule_name:
                    rules = flasharray.PolicyrulesmbclientpostRules(
                        anonymous_access_allowed=module.params["smb_anon_allowed"],
                        client=module.params["client"],
                        smb_encryption_required=module.params["smb_encrypt"],
                    )
                    rule = flasharray.PolicyRuleSmbClientPost(rules=[rules])
                    changed_rule = True
                    if not module.check_mode:
                        rule_created = array.post_policies_smb_client_rules(
                            policy_names=[module.params["name"]], rules=rule
                        )
                        if rule_created.status_code != 200:
                            module.fail_json(
                                msg="Failed to create new rule for SMB policy {0}. Error: {1}".format(
                                    module.params["name"],
                                    rule_created.errors[0].message,
                                )
                            )
            else:
                rules = flasharray.PolicyrulesmbclientpostRules(
                    anonymous_access_allowed=module.params["smb_anon_allowed"],
                    client=module.params["client"],
                    smb_encryption_required=module.params["smb_encrypt"],
                )
                rule = flasharray.PolicyRuleSmbClientPost(rules=[rules])
                changed_rule = True
                if not module.check_mode:
                    rule_created = array.post_policies_smb_client_rules(
                        policy_names=[module.params["name"]], rules=rule
                    )
                    if rule_created.status_code != 200:
                        module.fail_json(
                            msg="Failed to create new rule for SMB policy {0}. Error: {1}".format(
                                module.params["name"], rule_created.errors[0].message
                            )
                        )
    elif module.params["policy"] == "snapshot":
        suffix_enabled = bool(
            LooseVersion(array.get_rest_version())
            >= LooseVersion(MIN_SUFFIX_API_VERSION)
        )
        try:
            current_enabled = list(
                array.get_policies_snapshot(names=[module.params["name"]]).items
            )[0].enabled
        except Exception:
            module.fail_json(
                msg="Incorrect policy type specified for existing policy {0}".format(
                    module.params["name"]
                )
            )
        if current_enabled != module.params["enabled"]:
            changed_enable = True
            if not module.check_mode:
                res = array.patch_policies_snapshot(
                    names=[module.params["name"]],
                    policy=flasharray.PolicyPatch(enabled=module.params["enabled"]),
                )
                if res.status_code != 200:
                    module.exit_json(
                        msg="Failed to enable/disable snapshot policy {0}".format(
                            module.params["name"]
                        )
                    )
        if module.params["directory"]:
            dirs = []
            new_dirs = []
            current_dirs = list(
                array.get_directories_policies_snapshot(
                    policy_names=[module.params["name"]]
                ).items
            )
            if current_dirs:
                for current_dir in range(0, len(current_dirs)):
                    dirs.append(current_dirs[current_dir].member.name)
                for new_dir in range(0, len(module.params["directory"])):
                    if module.params["directory"][new_dir] not in dirs:
                        changed_dir = True
                        new_dirs.append(module.params["directory"][new_dir])
            else:
                new_dirs = module.params["directory"]
            if new_dirs:
                policies = flasharray.DirectoryPolicyPost(
                    policies=[
                        flasharray.DirectorypolicypostPolicies(
                            policy=flasharray.Reference(name=module.params["name"])
                        )
                    ]
                )
                changed_dir = True
                for add_dir in range(0, len(new_dirs)):
                    if not module.check_mode:
                        directory_added = array.post_directories_policies_snapshot(
                            member_names=[new_dirs[add_dir]], policies=policies
                        )
                        if directory_added.status_code != 200:
                            module.fail_json(
                                msg="Failed to add new directory to Snapshot policy {0}. Error: {1}".format(
                                    module.params["name"],
                                    directory_added.errors[0].message,
                                )
                            )
        if module.params["snap_client_name"]:
            if module.params["snap_at"]:
                if not module.params["snap_every"] % 1440 == 0:
                    module.fail_json(
                        msg="snap_at time can only be set if snap_every is multiple of 1440"
                    )
            if module.params["snap_keep_for"] < module.params["snap_every"]:
                module.fail_json(
                    msg="Retention period (snap_keep_for) cannot be less than snapshot interval (snap_every)."
                )
            if (
                module.params["snap_keep_for"] != module.params["snap_every"]
                and module.params["snap_suffix"]
            ):
                module.fail_json(
                    msg="Suffix (snap_suufix) can only be applied when `snap_keep_for` and `snap_every` are equal."
                )
            rules = list(
                array.get_policies_snapshot_rules(
                    policy_names=[module.params["name"]]
                ).items
            )
            if rules:
                for rule in range(0, len(rules)):
                    if (
                        rules[rule].client_name == module.params["snap_client_name"]
                        and int(rules[rule].every / 60000)
                        == module.params["snap_every"]
                        and int(rules[rule].keep_for / 60000)
                        == module.params["snap_keep_for"]
                    ):
                        module.exit_json(changed=False)
                if module.params["snap_keep_for"] < module.params["snap_every"]:
                    module.fail_json(
                        msg="Retention period (snap_keep_for) cannot be less than snapshot interval (snap_every)."
                    )
                if module.params["snap_at"]:
                    if not module.params["snap_every"] % 1440 == 0:
                        module.fail_json(
                            msg="snap_at time can only be set if snap_every is multiple of 1440"
                        )
                    if suffix_enabled:
                        rules = flasharray.PolicyrulesnapshotpostRules(
                            at=convert_to_millisecs(module.params["snap_at"]),
                            client_name=module.params["snap_client_name"],
                            every=module.params["snap_every"] * 60000,
                            keep_for=module.params["snap_keep_for"] * 60000,
                            suffix=module.params["snap_suffix"],
                        )
                    else:
                        rules = flasharray.PolicyrulesnapshotpostRules(
                            at=convert_to_millisecs(module.params["snap_at"]),
                            client_name=module.params["snap_client_name"],
                            every=module.params["snap_every"] * 60000,
                            keep_for=module.params["snap_keep_for"] * 60000,
                        )
                else:
                    if suffix_enabled:
                        rules = flasharray.PolicyrulesnapshotpostRules(
                            client_name=module.params["snap_client_name"],
                            every=module.params["snap_every"] * 60000,
                            keep_for=module.params["snap_keep_for"] * 60000,
                            suffix=module.params["snap_suffix"],
                        )
                    else:
                        rules = flasharray.PolicyrulesnapshotpostRules(
                            client_name=module.params["snap_client_name"],
                            every=module.params["snap_every"] * 60000,
                            keep_for=module.params["snap_keep_for"] * 60000,
                        )
                    rule = flasharray.PolicyRuleSnapshotPost(rules=[rules])
                    changed_rule = True
                    if not module.check_mode:
                        rule_created = array.post_policies_snapshot_rules(
                            policy_names=[module.params["name"]], rules=rule
                        )
                        if rule_created.status_code != 200:
                            err_no = len(rule_created.errors) - 1
                            module.fail_json(
                                msg="Failed to create new rule for Snapshot policy {0}. Error: {1}".format(
                                    module.params["name"],
                                    rule_created.errors[err_no].message,
                                )
                            )
            else:
                if module.params["snap_keep_for"] < module.params["snap_every"]:
                    module.fail_json(
                        msg="Retention period (snap_keep_for) cannot be less than snapshot interval (snap_every)."
                    )
                if module.params["snap_at"]:
                    if not module.params["snap_every"] % 1440 == 0:
                        module.fail_json(
                            msg="snap_at time can only be set if snap_every is multiple of 1440"
                        )
                    if suffix_enabled:
                        rules = flasharray.PolicyrulesnapshotpostRules(
                            at=convert_to_millisecs(module.params["snap_at"]),
                            client_name=module.params["snap_client_name"],
                            every=module.params["snap_every"] * 60000,
                            keep_for=module.params["snap_keep_for"] * 60000,
                            suffix=module.params["snap_suffix"],
                        )
                    else:
                        rules = flasharray.PolicyrulesnapshotpostRules(
                            at=convert_to_millisecs(module.params["snap_at"]),
                            client_name=module.params["snap_client_name"],
                            every=module.params["snap_every"] * 60000,
                            keep_for=module.params["snap_keep_for"] * 60000,
                        )
                else:
                    if suffix_enabled:
                        rules = flasharray.PolicyrulesnapshotpostRules(
                            client_name=module.params["snap_client_name"],
                            every=module.params["snap_every"] * 60000,
                            keep_for=module.params["snap_keep_for"] * 60000,
                            suffix=module.params["snap_suffix"],
                        )
                    else:
                        rules = flasharray.PolicyrulesnapshotpostRules(
                            client_name=module.params["snap_client_name"],
                            every=module.params["snap_every"] * 60000,
                            keep_for=module.params["snap_keep_for"] * 60000,
                        )
                rule = flasharray.PolicyRuleSnapshotPost(rules=[rules])
                changed_rule = True
                if not module.check_mode:
                    rule_created = array.post_policies_snapshot_rules(
                        policy_names=[module.params["name"]], rules=rule
                    )
                    if rule_created.status_code != 200:
                        err_no = len(rule_created.errors) - 1
                        module.fail_json(
                            msg="Failed to create new rule for Snapshot policy {0}. Error: {1}".format(
                                module.params["name"],
                                rule_created.errors[err_no].message,
                            )
                        )
    elif module.params["policy"] == "autodir":
        try:
            current_enabled = list(
                array.get_policies_autodir(names=[module.params["name"]]).items
            )[0].enabled
        except Exception:
            module.fail_json(
                msg="Incorrect policy type specified for existing policy {0}".format(
                    module.params["name"]
                )
            )
        if current_enabled != module.params["enabled"]:
            changed_enable = True
            if not module.check_mode:
                res = array.patch_policies_autodir(
                    names=[module.params["name"]],
                    policy=flasharray.PolicyPatch(enabled=module.params["enabled"]),
                )
                if res.status_code != 200:
                    module.exit_json(
                        msg="Failed to enable/disable autodir policy {0}".format(
                            module.params["name"]
                        )
                    )
        if module.params["directory"]:
            dirs = []
            new_dirs = []
            current_dirs = list(
                array.get_directories_policies_autodir(
                    policy_names=[module.params["name"]]
                ).items
            )
            if current_dirs:
                for current_dir in range(0, len(current_dirs)):
                    dirs.append(current_dirs[current_dir].member.name)
                for new_dir in range(0, len(module.params["directory"])):
                    if module.params["directory"][new_dir] not in dirs:
                        changed_dir = True
                        new_dirs.append(module.params["directory"][new_dir])
            else:
                new_dirs = module.params["directory"]
            if new_dirs:
                policies = flasharray.DirectoryPolicyPost(
                    policies=[
                        flasharray.DirectorypolicypostPolicies(
                            policy=flasharray.Reference(name=module.params["name"])
                        )
                    ]
                )
                changed_dir = True
                for add_dir in range(0, len(new_dirs)):
                    if not module.check_mode:
                        directory_added = array.post_directories_policies_autodir(
                            member_names=[new_dirs[add_dir]], policies=policies
                        )
                        if directory_added.status_code != 200:
                            module.fail_json(
                                msg="Failed to add new directory to Autodir policy {0}. Error: {1}".format(
                                    module.params["name"],
                                    directory_added.errors[0].message,
                                )
                            )
    elif module.params["policy"] == "password":
        try:
            current_enabled = list(
                array.get_policies_password(names=[module.params["name"]]).items
            )[0].enabled
        except Exception:
            module.fail_json(
                msg="Incorrect policy type specified for existing policy {0}".format(
                    module.params["name"]
                )
            )
        if current_enabled != module.params["enabled"]:
            changed_enable = True
            if not module.check_mode:
                res = array.patch_policies_password(
                    names=[module.params["name"]],
                    policy=flasharray.PolicyPatch(enabled=module.params["enabled"]),
                )
                if res.status_code != 200:
                    module.exit_json(
                        msg="Failed to enable/disable password policy {0}".format(
                            module.params["name"]
                        )
                    )
        pwd_policy = list(
            array.get_policies_password(names=[module.params["name"]]).items
        )[0]
        current_pwd_policy = {
            "enforce_dictionary_check": pwd_policy.enforce_dictionary_check,
            "enforce_username_check": pwd_policy.enforce_username_check,
            "lockout_duration": pwd_policy.lockout_duration,
            "min_character_groups": pwd_policy.min_character_groups,
            "min_characters_per_group": pwd_policy.min_characters_per_group,
            "min_password_length": pwd_policy.min_password_length,
            "max_login_attempts": getattr(pwd_policy, "max_login_attempts", 0),
            "password_history": getattr(pwd_policy, "password_history", 0),
        }
        new_pwd_policy = current_pwd_policy
        if (
            module.params("enforce_dictionary_check")
            != current_pwd_policy.enforce_dictionary_check
        ):
            new_pwd_policy.enforce_dictionary_check = module.params(
                "enforce_dictionary_check"
            )
            changed = True
        if (
            module.params("enforce_username_check")
            != current_pwd_policy.enforce_username_check
        ):
            new_pwd_policy.enforce_username_check = module.params(
                "enforce_username_check"
            )
            changed = True
        if (
            module.params("min_character_groups")
            != current_pwd_policy.min_character_groups
        ):
            new_pwd_policy.min_character_groups = module.params("min_character_groups")
            changed = True
        if (
            module.params("min_characters_per_group")
            != current_pwd_policy.min_characters_per_group
        ):
            new_pwd_policy.min_characters_per_group = module.params(
                "min_characters_per_group"
            )
            changed = True
        if (
            module.params("min_password_length")
            != current_pwd_policy.min_password_length
        ):
            new_pwd_policy.min_password_length = module.params("min_password_length")
            changed = True
        if (
            module.params("password_history")
            and module.params("password_history") != current_pwd_policy.password_history
        ):
            new_pwd_policy.password_history = module.params("password_history")
            changed = True
        if (
            module.params("max_login_attempts")
            and module.params("max_login_attempts")
            != current_pwd_policy.max_login_attempts
        ):
            new_pwd_policy.max_login_attempts = module.params("max_login_attempts")
            changed = True
        if module.params("lockout_duration"):
            if (
                module.params("lockout_duration") * 1000
                != current_pwd_policy.lockout_duration
            ):
                current_pwd_policy.lockout_duration = (
                    module.params("lockout_duration") * 1000
                )
                changed = True
        if changed and not module.check_mode:
            res = array.patch_policies_password(
                names=[module.params["name"]],
                policy=flasharray.PolicyPassword(
                    max_login_attempts=new_pwd_policy.max_login_attempts,
                    min_password_length=new_pwd_policy.min_password_length,
                    password_history=new_pwd_policy.password_history,
                    min_password_age=new_pwd_policy.min_password_age,
                    min_character_groups=new_pwd_policy.min_character_groups,
                    min_characters_per_group=new_pwd_policy.min_characters_per_group,
                    enforce_username_check=new_pwd_policy.enforce_username_check,
                    enforce_dictionary_check=new_pwd_policy.enforce_dictionary_check,
                    lockout_duration=new_pwd_policy.lockout_duration,
                ),
            )
            if res.status_code != 200:
                module.fail_json(
                    msg="Failed to change password policy {0}. Error: {1}".format(
                        module.params["name"],
                        res.errors[0].message,
                    )
                )
    else:  # quota
        current_enabled = list(
            array.get_policies_quota(names=[module.params["name"]]).items
        )[0].enabled
        if current_enabled != module.params["enabled"]:
            changed_quota = True
            if not module.check_mode:
                res = array.patch_policies_quota(
                    names=[module.params["name"]],
                    policy=flasharray.PolicyPatch(enabled=module.params["enabled"]),
                )
                if res.status_code != 200:
                    module.exit_json(
                        msg="Failed to enable/disable snapshot policy {0}".format(
                            module.params["name"]
                        )
                    )
        if module.params["directory"]:
            current_members = list(
                array.get_policies_quota_members(
                    policy_names=[module.params["name"]]
                ).items
            )
            if current_members:
                if module.params["state"] == "absent":
                    for member in range(0, len(current_members)):
                        if (
                            current_members[member].member.name
                            in module.params["directory"]
                        ):
                            changed_member = True
                            if not module.check_mode:
                                res = array.delete_policies_quota_members(
                                    policy_names=[module.params["name"]],
                                    member_names=[current_members[member].member.name],
                                )
                                if res.status_code != 200:
                                    module.fail_json(
                                        msg="Failed to delete rule {0} from quota policy {1}. Error: {2}".format(
                                            current_members[member].member.name,
                                            module.params["name"],
                                            rule_created.errors[0].message,
                                        )
                                    )
                else:
                    members = []
                    cmembers = []
                    for cmem in range(0, len(current_members)):
                        cmembers.append(current_members[cmem].member.name)
                    mem_diff = list(set(module.params["directory"]) - set(cmembers))
                    if mem_diff:
                        for mem in range(0, len(mem_diff)):
                            members.append(
                                flasharray.PolicymemberpostMembers(
                                    member=flasharray.ReferenceWithType(
                                        name=mem_diff[mem],
                                        resource_type="directories",
                                    )
                                )
                            )
                        member = flasharray.PolicyMemberPost(members=members)
                        changed_member = True
                        if not module.check_mode:
                            members_created = array.post_policies_quota_members(
                                policy_names=[module.params["name"]],
                                members=member,
                                ignore_usage=module.params["ignore_usage"],
                            )
                            if members_created.status_code != 200:
                                module.fail_json(
                                    msg="Failed to update members for Quota policy {0}. Error: {1}".format(
                                        module.params["name"],
                                        members_created.errors[0].message,
                                    )
                                )
            else:
                members = []
                for mem in range(0, len(module.params["directory"])):
                    members.append(
                        flasharray.PolicymemberpostMembers(
                            member=flasharray.ReferenceWithType(
                                name=module.params["directory"][mem],
                                resource_type="directories",
                            )
                        )
                    )
                member = flasharray.PolicyMemberPost(members=members)
                changed_member = True
                if not module.check_mode:
                    members_created = array.post_policies_quota_members(
                        policy_names=[module.params["name"]],
                        members=member,
                        ignore_usage=module.params["ignore_usage"],
                    )
                    if members_created.status_code != 200:
                        module.fail_json(
                            msg="Failed to update members for Quota policy {0}. Error: {1}".format(
                                module.params["name"],
                                members_created.errors[0].message,
                            )
                        )
        if module.params["quota_limit"]:
            quota = human_to_bytes(module.params["quota_limit"])
            current_rules = list(
                array.get_policies_quota_rules(
                    policy_names=[module.params["name"]]
                ).items
            )
            if current_rules:
                one_enforced = False
                for check_rule in range(0, len(current_rules)):
                    if current_rules[check_rule].enforced:
                        one_enforced = True
                for rule in range(0, len(current_rules)):
                    rule_exists = False
                    if not module.params["quota_notifications"]:
                        current_notifications = "none"
                    else:
                        current_notifications = ",".join(
                            module.params["quota_notifications"]
                        )
                    if bool(
                        (current_rules[rule].quota_limit == quota)
                        and (
                            current_rules[rule].enforced
                            == module.params["quota_enforced"]
                        )
                        and (current_rules[rule].notifications == current_notifications)
                    ):
                        rule_exists = True
                        break

                if not rule_exists:
                    if module.params["quota_enforced"] and one_enforced:
                        module.fail_json(
                            msg="Only one enforced rule can be defined per policy"
                        )
                    rules = flasharray.PolicyrulequotapostRules(
                        enforced=module.params["quota_enforced"],
                        quota_limit=quota,
                        notifications=",".join(module.params["quota_notifications"]),
                    )
                    rule = flasharray.PolicyRuleQuotaPost(rules=[rules])
                    changed_quota = True
                    if not module.check_mode:
                        quota_created = array.post_policies_quota_rules(
                            policy_names=[module.params["name"]],
                            rules=rule,
                            ignore_usage=module.params["ignore_usage"],
                        )
                        if quota_created.status_code != 200:
                            module.fail_json(
                                msg="Failed to add new rule to Quota policy {0}. Error: {1}".format(
                                    module.params["name"],
                                    quota_created.errors[0].message,
                                )
                            )
            else:
                rules = flasharray.PolicyrulequotapostRules(
                    enforced=module.params["quota_enforced"],
                    quota_limit=quota,
                    notifications=",".join(module.params["quota_notifications"]),
                )
                rule = flasharray.PolicyRuleQuotaPost(rules=[rules])
                changed_quota = True
                if not module.check_mode:
                    quota_created = array.post_policies_quota_rules(
                        policy_names=[module.params["name"]],
                        rules=rule,
                        ignore_usage=module.params["ignore_usage"],
                    )
                    if quota_created.status_code != 200:
                        module.fail_json(
                            msg="Failed to add rule to Quota policy {0}. Error: {1}".format(
                                module.params["name"], quota_created.errors[0].message
                            )
                        )

    if (
        changed_rule
        or changed_enable
        or changed_quota
        or changed_member
        or changed_dir
        or changed_user_map
        or changed_abe
        or changed_nfs
    ):
        changed = True
    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"]),
            nfs_access=dict(
                type="str",
                default="no-root-squash",
                choices=["root-squash", "no-root-squash", "all-squash"],
            ),
            nfs_permission=dict(type="str", default="rw", choices=["rw", "ro"]),
            policy=dict(
                type="str",
                required=True,
                choices=["nfs", "smb", "snapshot", "quota", "autodir", "password"],
            ),
            name=dict(type="str", required=True),
            rename=dict(type="str"),
            client=dict(type="str"),
            enabled=dict(type="bool", default=True),
            snap_at=dict(type="str"),
            snap_every=dict(type="int"),
            snap_keep_for=dict(type="int"),
            snap_client_name=dict(type="str"),
            snap_suffix=dict(type="str"),
            smb_anon_allowed=dict(type="bool", default=False),
            smb_encrypt=dict(type="bool", default=False),
            ignore_usage=dict(type="bool", default=False),
            quota_enforced=dict(type="bool", default=True),
            quota_limit=dict(type="str"),
            anongid=dict(type="str", default="65534"),
            anonuid=dict(type="str", default="65534"),
            quota_notifications=dict(
                type="list", elements="str", choices=["user", "group"]
            ),
            user_mapping=dict(type="bool", default=True),
            directory=dict(type="list", elements="str"),
            nfs_version=dict(
                type="list",
                elements="str",
                choices=["nfsv3", "nfsv4"],
            ),
            security=dict(
                type="list",
                elements="str",
                choices=["auth_sys", "krb5", "krb5i", "krb5p"],
            ),
            access_based_enumeration=dict(type="bool", default=False),
            enforce_dictionary_check=dict(type="bool"),
            enforce_username_check=dict(type="bool"),
            lockout_duration=dict(type="int"),
            min_character_groups=dict(type="int"),
            min_characters_per_group=dict(type="int"),
            min_password_length=dict(type="int", no_log=False),
            password_history=dict(type="int", no_log=False),
            max_login_attempts=dict(type="int"),
        )
    )

    required_together = [["snap_keep_for", "snap_every"]]
    module = AnsibleModule(
        argument_spec, required_together=required_together, supports_check_mode=True
    )

    if not HAS_PURESTORAGE:
        module.fail_json(msg="py-pure-client sdk is required for this module")

    array = get_system(module)
    api_version = array._list_available_rest_versions()
    if MIN_REQUIRED_API_VERSION not in api_version:
        module.fail_json(
            msg="FlashArray REST version not supported. "
            "Minimum version required: {0}".format(MIN_REQUIRED_API_VERSION)
        )
    if module.params["policy"] == "quota" and MIN_QUOTA_API_VERSION not in api_version:
        module.fail_json(
            msg="FlashArray REST version not supportedi for directory quotas. "
            "Minimum version required: {0}".format(MIN_QUOTA_API_VERSION)
        )
    if module.params["policy"] == "autodir" and AUTODIR_VERSION not in api_version:
        module.fail_json(
            msg="FlashArray REST version not supported for autodir policies. "
            "Minimum version required: {0}".format(AUTODIR_VERSION)
        )
    if module.params["policy"] == "password" and PASSWORD_VERSION not in api_version:
        module.fail_json(
            msg="FlashArray REST version not supported for password policies. "
            "Minimum version required: {0}".format(PASSWORD_VERSION)
        )
    if (
        module.params["policy"] == "password"
        and module.params["lockout"]
        and not 1 <= module.params["lockout"] <= 7776000
    ):
        module.fail_json(msg="Lockout must be between 1 and 7776000 seconds")
    array = get_array(module)
    state = module.params["state"]
    if module.params["quota_notifications"]:
        module.params["quota_notifications"].sort(reverse=True)
        quota_notifications = []
        [
            quota_notifications.append(x)
            for x in module.params["quota_notifications"]
            if x not in quota_notifications
        ]
        module.params["quota_notifications"] = quota_notifications
    else:
        module.params["quota_notifications"] = []

    if (
        module.params["nfs_access"] == "all-squash"
        and ALL_SQUASH_VERSION not in api_version
    ):
        module.fail_json(
            msg="all-squash is not supported in this version of Purity//FA"
        )

    all_squash = ALL_SQUASH_VERSION in api_version
    exists = bool(array.get_policies(names=[module.params["name"]]).status_code == 200)

    if state == "present" and not exists:
        create_policy(module, array, all_squash)
    elif state == "present" and exists and module.params["rename"]:
        rename_policy(module, array)
    elif state == "present" and exists:
        update_policy(module, array, api_version, all_squash)
    elif state == "absent" and exists:
        delete_policy(module, array)

    module.exit_json(changed=False)


if __name__ == "__main__":
    main()
