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

# (c) 2019, 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: purefb_info
version_added: '1.0.0'
short_description: Collect information from Pure Storage FlashBlade
description:
  - Collect information from a Pure Storage FlashBlade running the
    Purity//FB operating system. By default, the module will collect basic
    information including hosts, host groups, protection
    groups and volume counts. Additional information can be collected
    based on the configured set of arguements.
author:
  - Pure Storage Ansible Team (@sdodsley) <pure-ansible-team@purestorage.com>
options:
  gather_subset:
    description:
      - When supplied, this argument will define the information to be collected.
        Possible values for this include all, minimum, config, performance,
        capacity, network, subnets, lags, filesystems, snapshots, buckets,
        replication, policies, arrays, accounts, admins, ad, kerberos,
        drives, servers and fleet.
    required: false
    type: list
    elements: str
    default: minimum
extends_documentation_fragment:
  - purestorage.flashblade.purestorage.fb
"""

EXAMPLES = r"""
- name: collect default set of info
  purestorage.flashblade.purefb_info:
    fb_url: 10.10.10.2
    api_token: T-55a68eb5-c785-4720-a2ca-8b03903bf641
  register: blade_info
- name: show default information
  debug:
    msg: "{{ blade_info['purefb_info']['default'] }}"

- name: collect configuration and capacity info
  purestorage.flashblade.purefb_info:
    gather_subset:
      - config
    fb_url: 10.10.10.2
    api_token: T-55a68eb5-c785-4720-a2ca-8b03903bf641
  register: blade_info
- name: show config information
  debug:
    msg: "{{ blade_info['purefb_info']['config'] }}"

- name: collect all info
  purestorage.flashblade.purefb_info:
    gather_subset:
      - all
    fb_url: 10.10.10.2
    api_token: T-55a68eb5-c785-4720-a2ca-8b03903bf641
  register: blade_info
- name: show all information
  debug:
    msg: "{{ blade_info['purefb_info'] }}"
"""

RETURN = r"""
purefb_info:
  description: Returns the information collected from the FlashBlade
  returned: always
  type: dict
"""


from ansible.module_utils.basic import AnsibleModule
from ansible_collections.purestorage.flashblade.plugins.module_utils.purefb import (
    get_blade,
    get_system,
    purefb_argument_spec,
)
from datetime import datetime, timezone


MIN_REQUIRED_API_VERSION = "1.3"
HARD_LIMIT_API_VERSION = "1.4"
POLICIES_API_VERSION = "1.5"
CERT_GROUPS_API_VERSION = "1.8"
REPLICATION_API_VERSION = "1.9"
MULTIPROTOCOL_API_VERSION = "1.11"
MIN_32_API = "2.0"
LIFECYCLE_API_VERSION = "2.1"
SMB_MODE_API_VERSION = "2.2"
NFS_POLICY_API_VERSION = "2.3"
VSO_VERSION = "2.4"
DRIVES_API_VERSION = "2.5"
SECURITY_API_VERSION = "2.7"
BUCKET_API_VERSION = "2.8"
SMB_CLIENT_API_VERSION = "2.10"
SPACE_API_VERSION = "2.11"
PUBLIC_API_VERSION = "2.12"
NAP_API_VERSION = "2.13"
RA_DURATION_API_VERSION = "2.14"
SMTP_ENCRYPT_API_VERSION = "2.15"
SERVERS_API_VERSION = "2.16"
FLEET_API_VERSION = "2.17"


def _millisecs_to_time(millisecs):
    if millisecs:
        return (str(int(millisecs / 3600000 % 24)).zfill(2) + ":00",)
    return None


def _bytes_to_human(bytes_number):
    if bytes_number:
        labels = ["B/s", "KB/s", "MB/s", "GB/s", "TB/s", "PB/s"]
        i = 0
        double_bytes = bytes_number
        while i < len(labels) and bytes_number >= 1024:
            double_bytes = bytes_number / 1024.0
            i += 1
            bytes_number = bytes_number / 1024
        return str(round(double_bytes, 2)) + " " + labels[i]
    return None


def generate_default_dict(module, blade):
    default_info = {}
    defaults = blade.arrays.list_arrays().items[0]
    default_info["flashblade_name"] = defaults.name
    default_info["purity_version"] = defaults.version
    default_info["filesystems"] = len(blade.file_systems.list_file_systems().items)
    default_info["snapshots"] = len(
        blade.file_system_snapshots.list_file_system_snapshots().items
    )
    default_info["buckets"] = len(blade.buckets.list_buckets().items)
    default_info["object_store_users"] = len(
        blade.object_store_users.list_object_store_users().items
    )
    default_info["object_store_accounts"] = len(
        blade.object_store_accounts.list_object_store_accounts().items
    )
    default_info["blades"] = len(blade.blade.list_blades().items)
    default_info["certificates"] = len(blade.certificates.list_certificates().items)
    default_info["total_capacity"] = blade.arrays.list_arrays_space().items[0].capacity
    api_version = blade.api_version.list_versions().versions
    default_info["api_versions"] = api_version
    if POLICIES_API_VERSION in api_version:
        default_info["policies"] = len(blade.policies.list_policies().items)
    if CERT_GROUPS_API_VERSION in api_version:
        default_info["certificate_groups"] = len(
            blade.certificate_groups.list_certificate_groups().items
        )
    if REPLICATION_API_VERSION in api_version:
        default_info["fs_replicas"] = len(
            blade.file_system_replica_links.list_file_system_replica_links().items
        )
        default_info["remote_credentials"] = len(
            blade.object_store_remote_credentials.list_object_store_remote_credentials().items
        )
        default_info["bucket_replicas"] = len(
            blade.bucket_replica_links.list_bucket_replica_links().items
        )
        default_info["connected_arrays"] = len(
            blade.array_connections.list_array_connections().items
        )
        default_info["targets"] = len(blade.targets.list_targets().items)
        default_info["kerberos_keytabs"] = len(blade.keytabs.list_keytabs().items)
    # This section is just for REST 2.x features
    if MIN_32_API in api_version:
        blade = get_system(module)
        blade_info = list(blade.get_arrays().items)[0]
        default_info["syslog_servers"] = len(blade.get_syslog_servers().items)
        default_info["object_store_virtual_hosts"] = len(
            blade.get_object_store_virtual_hosts().items
        )
        default_info["api_clients"] = len(blade.get_api_clients().items)
        default_info["idle_timeout"] = int(blade_info.idle_timeout / 60000)
        if list(blade.get_arrays_eula().items)[0].signature.accepted:
            default_info["EULA"] = "Signed"
        else:
            default_info["EULA"] = "Not Signed"
        if NFS_POLICY_API_VERSION in api_version:
            admin_settings = list(blade.get_admins_settings().items)[0]
            default_info["max_login_attempts"] = admin_settings.max_login_attempts
            default_info["min_password_length"] = admin_settings.min_password_length
            if admin_settings.lockout_duration:
                default_info["lockout_duration"] = (
                    str(admin_settings.lockout_duration / 1000) + " seconds"
                )
        if NFS_POLICY_API_VERSION in api_version:
            default_info["smb_mode"] = getattr(blade_info, "smb_mode", None)
        if VSO_VERSION in api_version:
            default_info["timezone"] = blade_info.time_zone
        if DRIVES_API_VERSION in api_version:
            default_info["product_type"] = getattr(
                blade_info, "product_type", "Unknown"
            )
        if SECURITY_API_VERSION in api_version:
            dar = blade_info.encryption.data_at_rest
            default_info["encryption"] = {
                "data_at_rest_enabled": dar.enabled,
                "data_at_rest_algorithms": dar.algorithms,
                "data_at_rest_entropy_source": dar.entropy_source,
            }
            keys = list(blade.get_support_verification_keys().items)
            default_info["support_keys"] = {}
            for key in range(0, len(keys)):
                keyname = keys[key].name
                default_info["support_keys"][keyname] = {keys[key].verification_key}
            default_info["security_update"] = getattr(
                blade_info, "security_update", None
            )
        if NAP_API_VERSION in api_version:
            default_info["network_access_protocol"] = getattr(
                blade_info.network_access_policy, "name", "None"
            )
        ra_info = list(blade.get_support().items)[0]
        if ra_info.remote_assist_active:
            ra_expires = datetime.fromtimestamp(
                int(ra_info.remote_assist_expires) / 1000
            ).strftime("%Y-%m-%d %H:%M:%S")
            ra_opened = datetime.fromtimestamp(
                int(ra_info.remote_assist_opened) / 1000
            ).strftime("%Y-%m-%d %H:%M:%S")
        else:
            ra_expires = ra_opened = None
        default_info["remote_assist"] = {
            "phonehome_enabled": ra_info.phonehome_enabled,
            "proxy": ra_info.proxy,
            "ra_active": ra_info.remote_assist_active,
            "ra_expires": ra_expires,
            "ra_opened": ra_opened,
            "ra_status": ra_info.remote_assist_status,
        }
        if RA_DURATION_API_VERSION in api_version:
            default_info["remote_assist"][
                "ra_duration"
            ] = ra_info.remote_assist_duration
    return default_info


def generate_perf_dict(blade):
    perf_info = {}
    total_perf = blade.arrays.list_arrays_performance()
    http_perf = blade.arrays.list_arrays_performance(protocol="http")
    s3_perf = blade.arrays.list_arrays_performance(protocol="s3")
    nfs_perf = blade.arrays.list_arrays_performance(protocol="nfs")
    perf_info["aggregate"] = {
        "bytes_per_op": total_perf.items[0].bytes_per_op,
        "bytes_per_read": total_perf.items[0].bytes_per_read,
        "bytes_per_write": total_perf.items[0].bytes_per_write,
        "read_bytes_per_sec": total_perf.items[0].read_bytes_per_sec,
        "reads_per_sec": total_perf.items[0].reads_per_sec,
        "usec_per_other_op": total_perf.items[0].usec_per_other_op,
        "usec_per_read_op": total_perf.items[0].usec_per_read_op,
        "usec_per_write_op": total_perf.items[0].usec_per_write_op,
        "write_bytes_per_sec": total_perf.items[0].write_bytes_per_sec,
        "writes_per_sec": total_perf.items[0].writes_per_sec,
    }
    perf_info["http"] = {
        "bytes_per_op": http_perf.items[0].bytes_per_op,
        "bytes_per_read": http_perf.items[0].bytes_per_read,
        "bytes_per_write": http_perf.items[0].bytes_per_write,
        "read_bytes_per_sec": http_perf.items[0].read_bytes_per_sec,
        "reads_per_sec": http_perf.items[0].reads_per_sec,
        "usec_per_other_op": http_perf.items[0].usec_per_other_op,
        "usec_per_read_op": http_perf.items[0].usec_per_read_op,
        "usec_per_write_op": http_perf.items[0].usec_per_write_op,
        "write_bytes_per_sec": http_perf.items[0].write_bytes_per_sec,
        "writes_per_sec": http_perf.items[0].writes_per_sec,
    }
    perf_info["s3"] = {
        "bytes_per_op": s3_perf.items[0].bytes_per_op,
        "bytes_per_read": s3_perf.items[0].bytes_per_read,
        "bytes_per_write": s3_perf.items[0].bytes_per_write,
        "read_bytes_per_sec": s3_perf.items[0].read_bytes_per_sec,
        "reads_per_sec": s3_perf.items[0].reads_per_sec,
        "usec_per_other_op": s3_perf.items[0].usec_per_other_op,
        "usec_per_read_op": s3_perf.items[0].usec_per_read_op,
        "usec_per_write_op": s3_perf.items[0].usec_per_write_op,
        "write_bytes_per_sec": s3_perf.items[0].write_bytes_per_sec,
        "writes_per_sec": s3_perf.items[0].writes_per_sec,
    }
    perf_info["nfs"] = {
        "bytes_per_op": nfs_perf.items[0].bytes_per_op,
        "bytes_per_read": nfs_perf.items[0].bytes_per_read,
        "bytes_per_write": nfs_perf.items[0].bytes_per_write,
        "read_bytes_per_sec": nfs_perf.items[0].read_bytes_per_sec,
        "reads_per_sec": nfs_perf.items[0].reads_per_sec,
        "usec_per_other_op": nfs_perf.items[0].usec_per_other_op,
        "usec_per_read_op": nfs_perf.items[0].usec_per_read_op,
        "usec_per_write_op": nfs_perf.items[0].usec_per_write_op,
        "write_bytes_per_sec": nfs_perf.items[0].write_bytes_per_sec,
        "writes_per_sec": nfs_perf.items[0].writes_per_sec,
    }
    api_version = blade.api_version.list_versions().versions
    if REPLICATION_API_VERSION in api_version:
        file_repl_perf = (
            blade.array_connections.list_array_connections_performance_replication(
                type="file-system"
            )
        )
        obj_repl_perf = (
            blade.array_connections.list_array_connections_performance_replication(
                type="object-store"
            )
        )
        if len(file_repl_perf.total):
            perf_info["file_replication"] = {
                "received_bytes_per_sec": file_repl_perf.total[
                    0
                ].periodic.received_bytes_per_sec,
                "transmitted_bytes_per_sec": file_repl_perf.total[
                    0
                ].periodic.transmitted_bytes_per_sec,
            }
        if len(obj_repl_perf.total):
            perf_info["object_replication"] = {
                "received_bytes_per_sec": obj_repl_perf.total[
                    0
                ].periodic.received_bytes_per_sec,
                "transmitted_bytes_per_sec": obj_repl_perf.total[
                    0
                ].periodic.transmitted_bytes_per_sec,
            }
    return perf_info


def generate_config_dict(module, blade):
    config_info = {}
    bladev2 = get_system(module)
    api_version = blade.api_version.list_versions().versions
    if SERVERS_API_VERSION in api_version:
        config_info["dns"] = {}
        dns_configs = list(bladev2.get_dns().items)
        for config in range(0, len(dns_configs)):
            config_info["dns"][dns_configs[config].name] = {
                "nameservers": dns_configs[config].nameservers,
                "domain": dns_configs[config].domain,
            }
            config_info["dns"][dns_configs[config].name]["source"] = getattr(
                dns_configs[config].source, "name", None
            )
    else:
        config_info["dns"] = blade.dns.list_dns().items[0].to_dict()
    if SMTP_ENCRYPT_API_VERSION in api_version:
        snmp_config = list(bladev2.get_smtp_servers().items)[0]
        config_info["smtp"] = {
            "relay_host": snmp_config.relay_host,
            "sender_domain": snmp_config.sender_domain,
            "encryption_mode": snmp_config.encryption_mode,
            "name": snmp_config.name,
        }
    else:
        config_info["smtp"] = blade.smtp.list_smtp().items[0].to_dict()
    try:
        config_info["alert_watchers"] = (
            blade.alert_watchers.list_alert_watchers().items[0].to_dict()
        )
    except Exception:
        config_info["alert_watchers"] = ""
    if HARD_LIMIT_API_VERSION in api_version:
        config_info["array_management"] = (
            blade.directory_services.list_directory_services(names=["management"])
            .items[0]
            .to_dict()
        )
        config_info["directory_service_roles"] = {}
        roles = blade.directory_services.list_directory_services_roles()
        for role in range(0, len(roles.items)):
            role_name = roles.items[role].name
            config_info["directory_service_roles"][role_name] = {
                "group": roles.items[role].group,
                "group_base": roles.items[role].group_base,
            }
    config_info["nfs_directory_service"] = (
        blade.directory_services.list_directory_services(names=["nfs"])
        .items[0]
        .to_dict()
    )
    if SERVERS_API_VERSION not in api_version:
        config_info["smb_directory_service"] = (
            blade.directory_services.list_directory_services(names=["smb"])
            .items[0]
            .to_dict()
        )
    config_info["ntp"] = blade.arrays.list_arrays().items[0].ntp_servers
    config_info["ssl_certs"] = blade.certificates.list_certificates().items[0].to_dict()
    if CERT_GROUPS_API_VERSION in api_version:
        try:
            config_info["certificate_groups"] = (
                blade.certificate_groups.list_certificate_groups().items[0].to_dict()
            )
        except Exception:
            config_info["certificate_groups"] = ""
    if RA_DURATION_API_VERSION in api_version:
        config_info["syslog_servers"] = {}
        syslog_servers = list(bladev2.get_syslog_servers().items)
        for server in range(0, len(syslog_servers)):
            server_name = syslog_servers[server].name
            config_info["syslog_servers"][server_name] = {
                "uri": syslog_servers[server].uri,
                "services": syslog_servers[server].services,
            }
    if REPLICATION_API_VERSION in api_version:
        config_info["snmp_agents"] = {}
        snmp_agents = blade.snmp_agents.list_snmp_agents()
        for agent in range(0, len(snmp_agents.items)):
            agent_name = snmp_agents.items[agent].name
            config_info["snmp_agents"][agent_name] = {
                "version": snmp_agents.items[agent].version,
                "engine_id": snmp_agents.items[agent].engine_id,
            }
            if config_info["snmp_agents"][agent_name]["version"] == "v3":
                config_info["snmp_agents"][agent_name]["auth_protocol"] = (
                    snmp_agents.items[agent].v3.auth_protocol
                )
                config_info["snmp_agents"][agent_name]["privacy_protocol"] = (
                    snmp_agents.items[agent].v3.privacy_protocol
                )
                config_info["snmp_agents"][agent_name]["user"] = snmp_agents.items[
                    agent
                ].v3.user
        config_info["snmp_managers"] = {}
        snmp_managers = blade.snmp_managers.list_snmp_managers()
        for manager in range(0, len(snmp_managers.items)):
            mgr_name = snmp_managers.items[manager].name
            config_info["snmp_managers"][mgr_name] = {
                "version": snmp_managers.items[manager].version,
                "host": snmp_managers.items[manager].host,
                "notification": snmp_managers.items[manager].notification,
            }
            if config_info["snmp_managers"][mgr_name]["version"] == "v3":
                config_info["snmp_managers"][mgr_name]["auth_protocol"] = (
                    snmp_managers.items[manager].v3.auth_protocol
                )
                config_info["snmp_managers"][mgr_name]["privacy_protocol"] = (
                    snmp_managers.items[manager].v3.privacy_protocol
                )
                config_info["snmp_managers"][mgr_name]["user"] = snmp_managers.items[
                    manager
                ].v3.user
    if SMTP_ENCRYPT_API_VERSION in api_version:
        config_info["saml2sso"] = {}
        saml2 = list(bladev2.get_sso_saml2_idps().items)
        if saml2:
            config_info["saml2sso"] = {
                "enabled": saml2[0].enabled,
                "array_url": saml2[0].array_url,
                "name": saml2[0].name,
                "idp": {
                    "url": getattr(saml2[0].idp, "url", None),
                    "encrypt_enabled": saml2[0].idp.encrypt_assertion_enabled,
                    "sign_enabled": saml2[0].idp.sign_request_enabled,
                    "metadata_url": saml2[0].idp.metadata_url,
                },
                "sp": {
                    "decrypt_cred": getattr(
                        saml2[0].sp.decryption_credential, "name", None
                    ),
                    "sign_cred": getattr(saml2[0].sp.signing_credential, "name", None),
                },
            }
    return config_info


def generate_subnet_dict(blade):
    sub_info = {}
    subnets = blade.subnets.list_subnets()
    for sub in range(0, len(subnets.items)):
        sub_name = subnets.items[sub].name
        if subnets.items[sub].enabled:
            sub_info[sub_name] = {
                "gateway": subnets.items[sub].gateway,
                "mtu": subnets.items[sub].mtu,
                "vlan": subnets.items[sub].vlan,
                "prefix": subnets.items[sub].prefix,
                "services": subnets.items[sub].services,
            }
            sub_info[sub_name]["lag"] = subnets.items[sub].link_aggregation_group.name
            sub_info[sub_name]["interfaces"] = []
            for iface in range(0, len(subnets.items[sub].interfaces)):
                sub_info[sub_name]["interfaces"].append(
                    {"name": subnets.items[sub].interfaces[iface].name}
                )
    return sub_info


def generate_lag_dict(blade):
    lag_info = {}
    groups = blade.link_aggregation_groups.list_link_aggregation_groups()
    for groupcnt in range(0, len(groups.items)):
        lag_name = groups.items[groupcnt].name
        lag_info[lag_name] = {
            "lag_speed": groups.items[groupcnt].lag_speed,
            "port_speed": groups.items[groupcnt].port_speed,
            "status": groups.items[groupcnt].status,
        }
        lag_info[lag_name]["ports"] = []
        for port in range(0, len(groups.items[groupcnt].ports)):
            lag_info[lag_name]["ports"].append(
                {"name": groups.items[groupcnt].ports[port].name}
            )
    return lag_info


def generate_admin_dict(module, blade):
    admin_info = {}
    api_version = blade.api_version.list_versions().versions
    if MULTIPROTOCOL_API_VERSION in api_version:
        admins = blade.admins.list_admins()
        for admin in range(0, len(admins.items)):
            admin_name = admins.items[admin].name
            admin_info[admin_name] = {
                "api_token_timeout": admins.items[admin].api_token_timeout,
                "public_key": admins.items[admin].public_key,
                "local": admins.items[admin].is_local,
            }

    if MIN_32_API in api_version:
        bladev2 = get_system(module)
        admins = list(bladev2.get_admins().items)
        for admin in range(0, len(admins)):
            admin_name = admins[admin].name
            if admins[admin].api_token.expires_at:
                admin_info[admin_name]["token_expires"] = datetime.fromtimestamp(
                    admins[admin].api_token.expires_at / 1000
                ).strftime("%Y-%m-%d %H:%M:%S")
            else:
                admin_info[admin_name]["token_expires"] = None
            if admins[admin].api_token.created_at:
                admin_info[admin_name]["token_created"] = datetime.fromtimestamp(
                    admins[admin].api_token.created_at / 1000
                ).strftime("%Y-%m-%d %H:%M:%S")
            else:
                admin_info[admin_name]["token_created"] = None
            admin_info[admin_name]["role"] = admins[admin].role.name
            if NFS_POLICY_API_VERSION in api_version:
                admin_info[admin_name]["locked"] = admins[admin].locked
                admin_info[admin_name]["lockout_remaining"] = admins[
                    admin
                ].lockout_remaining
    return admin_info


def generate_targets_dict(blade):
    targets_info = {}
    targets = blade.targets.list_targets()
    for target in range(0, len(targets.items)):
        target_name = targets.items[target].name
        targets_info[target_name] = {
            "address": targets.items[target].address,
            "status": targets.items[target].status,
            "status_details": targets.items[target].status_details,
        }
    return targets_info


def generate_remote_creds_dict(blade):
    remote_creds_info = {}
    remote_creds = (
        blade.object_store_remote_credentials.list_object_store_remote_credentials()
    )
    for cred_cnt in range(0, len(remote_creds.items)):
        cred_name = remote_creds.items[cred_cnt].name
        remote_creds_info[cred_name] = {
            "access_key": remote_creds.items[cred_cnt].access_key_id,
            "remote_array": remote_creds.items[cred_cnt].remote.name,
        }
    return remote_creds_info


def generate_file_repl_dict(blade):
    file_repl_info = {}
    file_links = blade.file_system_replica_links.list_file_system_replica_links()
    for linkcnt in range(0, len(file_links.items)):
        fs_name = file_links.items[linkcnt].local_file_system.name
        file_repl_info[fs_name] = {
            "direction": file_links.items[linkcnt].direction,
            "lag": file_links.items[linkcnt].lag,
            "status": file_links.items[linkcnt].status,
            "remote_fs": file_links.items[linkcnt].remote.name
            + ":"
            + file_links.items[linkcnt].remote_file_system.name,
            "recovery_point": file_links.items[linkcnt].recovery_point,
        }
        file_repl_info[fs_name]["policies"] = []
        for policy_cnt in range(0, len(file_links.items[linkcnt].policies)):
            file_repl_info[fs_name]["policies"].append(
                file_links.items[linkcnt].policies[policy_cnt].display_name
            )
    return file_repl_info


def generate_bucket_repl_dict(module, blade):
    bucket_repl_info = {}
    bucket_links = blade.bucket_replica_links.list_bucket_replica_links()
    for linkcnt in range(0, len(bucket_links.items)):
        bucket_name = bucket_links.items[linkcnt].local_bucket.name
        bucket_repl_info[bucket_name] = {
            "direction": bucket_links.items[linkcnt].direction,
            "lag": bucket_links.items[linkcnt].lag,
            "paused": bucket_links.items[linkcnt].paused,
            "status": bucket_links.items[linkcnt].status,
            "remote_bucket": bucket_links.items[linkcnt].remote_bucket.name,
            "remote_credentials": bucket_links.items[linkcnt].remote_credentials.name,
            "recovery_point": bucket_links.items[linkcnt].recovery_point,
            "object_backlog": {},
        }
    api_version = blade.api_version.list_versions().versions
    if SMB_MODE_API_VERSION in api_version:
        blade = get_system(module)
        bucket_links = list(blade.get_bucket_replica_links().items)
        for linkcnt in range(0, len(bucket_links)):
            bucket_name = bucket_links[linkcnt].local_bucket.name
            bucket_repl_info[bucket_name]["object_backlog"] = {
                "bytes_count": bucket_links[linkcnt].object_backlog.bytes_count,
                "delete_ops_count": bucket_links[
                    linkcnt
                ].object_backlog.delete_ops_count,
                "other_ops_count": bucket_links[linkcnt].object_backlog.other_ops_count,
                "put_ops_count": bucket_links[linkcnt].object_backlog.put_ops_count,
            }
            bucket_repl_info[bucket_name]["cascading_enabled"] = bucket_links[
                linkcnt
            ].cascading_enabled
    return bucket_repl_info


def generate_network_dict(blade):
    net_info = {}
    ports = blade.network_interfaces.list_network_interfaces()
    for portcnt in range(0, len(ports.items)):
        int_name = ports.items[portcnt].name
        if ports.items[portcnt].enabled:
            net_info[int_name] = {
                "type": ports.items[portcnt].type,
                "mtu": ports.items[portcnt].mtu,
                "vlan": ports.items[portcnt].vlan,
                "address": ports.items[portcnt].address,
                "services": ports.items[portcnt].services,
                "gateway": ports.items[portcnt].gateway,
                "netmask": ports.items[portcnt].netmask,
            }
    return net_info


def generate_capacity_dict(module, blade):
    capacity_info = {}
    api_version = blade.api_version.list_versions().versions
    if SPACE_API_VERSION in api_version:
        blade2 = get_system(module)
        total_cap = list(blade2.get_arrays_space().items)[0]
        file_cap = list(blade2.get_arrays_space(type="file-system").items)[0]
        object_cap = list(blade2.get_arrays_space(type="object-store").items)[0]
        capacity_info["total"] = total_cap.capacity
        capacity_info["aggregate"] = {
            "data_reduction": total_cap.space.data_reduction,
            "snapshots": total_cap.space.snapshots,
            "total_physical": total_cap.space.total_physical,
            "unique": total_cap.space.unique,
            "virtual": total_cap.space.virtual,
            "total_provisioned": total_cap.space.total_provisioned,
            "available_provisioned": total_cap.space.available_provisioned,
            "available_ratio": total_cap.space.available_ratio,
            "destroyed": total_cap.space.destroyed,
            "destroyed_virtual": total_cap.space.destroyed_virtual,
            "shared": getattr(total_cap.space, "shared", None),
        }
        capacity_info["file-system"] = {
            "data_reduction": file_cap.space.data_reduction,
            "snapshots": file_cap.space.snapshots,
            "total_physical": file_cap.space.total_physical,
            "unique": file_cap.space.unique,
            "virtual": file_cap.space.virtual,
            "total_provisioned": total_cap.space.total_provisioned,
            "available_provisioned": total_cap.space.available_provisioned,
            "available_ratio": total_cap.space.available_ratio,
            "destroyed": total_cap.space.destroyed,
            "destroyed_virtual": total_cap.space.destroyed_virtual,
            "shared": getattr(total_cap.space, "shared", None),
        }
        capacity_info["object-store"] = {
            "data_reduction": object_cap.space.data_reduction,
            "snapshots": object_cap.space.snapshots,
            "total_physical": object_cap.space.total_physical,
            "unique": object_cap.space.unique,
            "virtual": file_cap.space.virtual,
            "total_provisioned": total_cap.space.total_provisioned,
            "available_provisioned": total_cap.space.available_provisioned,
            "available_ratio": total_cap.space.available_ratio,
            "destroyed": total_cap.space.destroyed,
            "destroyed_virtual": total_cap.space.destroyed_virtual,
            "shared": getattr(total_cap.space, "shared", None),
        }
    else:
        total_cap = blade.arrays.list_arrays_space()
        file_cap = blade.arrays.list_arrays_space(type="file-system")
        object_cap = blade.arrays.list_arrays_space(type="object-store")
        capacity_info["total"] = total_cap.items[0].capacity
        capacity_info["aggregate"] = {
            "data_reduction": total_cap.items[0].space.data_reduction,
            "snapshots": total_cap.items[0].space.snapshots,
            "total_physical": total_cap.items[0].space.total_physical,
            "unique": total_cap.items[0].space.unique,
            "virtual": total_cap.items[0].space.virtual,
        }
        capacity_info["file-system"] = {
            "data_reduction": file_cap.items[0].space.data_reduction,
            "snapshots": file_cap.items[0].space.snapshots,
            "total_physical": file_cap.items[0].space.total_physical,
            "unique": file_cap.items[0].space.unique,
            "virtual": file_cap.items[0].space.virtual,
        }
        capacity_info["object-store"] = {
            "data_reduction": object_cap.items[0].space.data_reduction,
            "snapshots": object_cap.items[0].space.snapshots,
            "total_physical": object_cap.items[0].space.total_physical,
            "unique": object_cap.items[0].space.unique,
            "virtual": file_cap.items[0].space.virtual,
        }

    return capacity_info


def generate_snap_dict(blade):
    snap_info = {}
    snaps = blade.file_system_snapshots.list_file_system_snapshots()
    api_version = blade.api_version.list_versions().versions
    for snap in range(0, len(snaps.items)):
        snapshot = snaps.items[snap].name
        snap_info[snapshot] = {
            "destroyed": snaps.items[snap].destroyed,
            "source": snaps.items[snap].source,
            "suffix": snaps.items[snap].suffix,
            "source_destroyed": snaps.items[snap].source_destroyed,
            "created": datetime.fromtimestamp(
                snaps.items[snap].created / 1000,
                tz=timezone.utc,
            ).strftime("%Y-%m-%dT%H:%M:%SZ"),
            "time_remaining": getattr(snaps.items[snap], "time_remaining", None),
        }
        if REPLICATION_API_VERSION in api_version:
            snap_info[snapshot]["owner"] = snaps.items[snap].owner.name
            snap_info[snapshot]["owner_destroyed"] = snaps.items[snap].owner_destroyed
            snap_info[snapshot]["source_display_name"] = snaps.items[
                snap
            ].source_display_name
            snap_info[snapshot]["source_is_local"] = snaps.items[snap].source_is_local
            snap_info[snapshot]["source_location"] = snaps.items[
                snap
            ].source_location.name
            snap_info[snapshot]["policies"] = []
            if PUBLIC_API_VERSION in api_version:
                if hasattr(snaps.items[snap], "policies"):
                    for policy in range(0, len(snaps.items[snap].policies)):
                        snap_info[snapshot]["policies"].append(
                            {
                                "name": snaps.items[snap].policies[policy].name,
                                "location": snaps.items[snap]
                                .policies[policy]
                                .location.name,
                            }
                        )
    return snap_info


def generate_snap_transfer_dict(blade):
    snap_transfer_info = {}
    snap_transfers = blade.file_system_snapshots.list_file_system_snapshots_transfer()
    for snap_transfer in range(0, len(snap_transfers.items)):
        transfer = snap_transfers.items[snap_transfer].name
        snap_transfer_info[transfer] = {
            "completed": snap_transfers.items[snap_transfer].completed,
            "data_transferred": snap_transfers.items[snap_transfer].data_transferred,
            "progress": snap_transfers.items[snap_transfer].progress,
            "direction": snap_transfers.items[snap_transfer].direction,
            "remote": snap_transfers.items[snap_transfer].remote.name,
            "remote_snapshot": snap_transfers.items[snap_transfer].remote_snapshot.name,
            "started": snap_transfers.items[snap_transfer].started,
            "status": snap_transfers.items[snap_transfer].status,
        }
    return snap_transfer_info


def generate_array_conn_dict(module, blade):
    array_conn_info = {}
    arraysv2 = {}
    api_version = blade.api_version.list_versions().versions
    arrays = blade.array_connections.list_array_connections()
    if NFS_POLICY_API_VERSION in api_version:
        bladev2 = get_system(module)
        arraysv2 = list(bladev2.get_array_connections().items)
    for arraycnt in range(0, len(arrays.items)):
        array = arrays.items[arraycnt].remote.name
        array_conn_info[array] = {
            "encrypted": arrays.items[arraycnt].encrypted,
            "replication_addresses": arrays.items[arraycnt].replication_addresses,
            "management_address": arrays.items[arraycnt].management_address,
            "status": arrays.items[arraycnt].status,
            "version": arrays.items[arraycnt].version,
            "throttle": [],
        }
        if arrays.items[arraycnt].encrypted:
            array_conn_info[array]["ca_certificate_group"] = arrays.items[
                arraycnt
            ].ca_certificate_group.name
        for v2array in range(0, len(arraysv2)):
            if arraysv2[v2array].remote.name == array:
                array_conn_info[array]["throttle"] = {
                    "default_limit": _bytes_to_human(
                        arraysv2[v2array].throttle.default_limit
                    ),
                    "window_limit": _bytes_to_human(
                        arraysv2[v2array].throttle.window_limit
                    ),
                    "window_start": _millisecs_to_time(
                        arraysv2[v2array].throttle.window.start
                    ),
                    "window_end": _millisecs_to_time(
                        arraysv2[v2array].throttle.window.end
                    ),
                }
    return array_conn_info


def generate_policies_dict(blade):
    policies_info = {}
    policies = blade.policies.list_policies()
    for policycnt in range(0, len(policies.items)):
        policy = policies.items[policycnt].name
        policies_info[policy] = {}
        policies_info[policy]["enabled"] = policies.items[policycnt].enabled
        if policies.items[policycnt].rules:
            policies_info[policy]["rules"] = (
                policies.items[policycnt].rules[0].to_dict()
            )
    return policies_info


def generate_bucket_dict(module, blade):
    bucket_info = {}
    buckets = blade.buckets.list_buckets()
    for bckt in range(0, len(buckets.items)):
        bucket = buckets.items[bckt].name
        bucket_info[bucket] = {
            "versioning": buckets.items[bckt].versioning,
            "bucket_type": getattr(buckets.items[bckt], "bucket_type", None),
            "object_count": buckets.items[bckt].object_count,
            "id": buckets.items[bckt].id,
            "account_name": buckets.items[bckt].account.name,
            "data_reduction": buckets.items[bckt].space.data_reduction,
            "snapshot_space": buckets.items[bckt].space.snapshots,
            "total_physical_space": buckets.items[bckt].space.total_physical,
            "unique_space": buckets.items[bckt].space.unique,
            "virtual_space": buckets.items[bckt].space.virtual,
            "total_provisioned_space": getattr(
                buckets.items[bckt].space, "total_provisioned", None
            ),
            "available_provisioned_space": getattr(
                buckets.items[bckt].space, "available_provisioned", None
            ),
            "available_ratio": getattr(
                buckets.items[bckt].space, "available_ratio", None
            ),
            "destroyed_space": getattr(buckets.items[bckt].space, "destroyed", None),
            "destroyed_virtual_space": getattr(
                buckets.items[bckt].space, "destroyed_virtual", None
            ),
            "created": buckets.items[bckt].created,
            "destroyed": buckets.items[bckt].destroyed,
            "time_remaining": buckets.items[bckt].time_remaining,
            "time_remaining_status": getattr(
                buckets.items[bckt], "time_remaining_status", None
            ),
            "lifecycle_rules": {},
        }
    api_version = blade.api_version.list_versions().versions
    if LIFECYCLE_API_VERSION in api_version:
        blade = get_system(module)
        for bckt in range(0, len(buckets.items)):
            if buckets.items[bckt].destroyed:
                # skip processing buckets marked as destroyed
                continue
            all_rules = list(
                blade.get_lifecycle_rules(bucket_ids=[buckets.items[bckt].id]).items
            )
            for rule in range(0, len(all_rules)):
                bucket_name = all_rules[rule].bucket.name
                rule_id = all_rules[rule].rule_id
                if all_rules[rule].keep_previous_version_for:
                    keep_previous_version_for = int(
                        all_rules[rule].keep_previous_version_for / 86400000
                    )
                else:
                    keep_previous_version_for = None
                if all_rules[rule].keep_current_version_for:
                    keep_current_version_for = int(
                        all_rules[rule].keep_current_version_for / 86400000
                    )
                else:
                    keep_current_version_for = None
                if all_rules[rule].abort_incomplete_multipart_uploads_after:
                    abort_incomplete_multipart_uploads_after = int(
                        all_rules[rule].abort_incomplete_multipart_uploads_after
                        / 86400000
                    )
                else:
                    abort_incomplete_multipart_uploads_after = None
                if all_rules[rule].keep_current_version_until:
                    keep_current_version_until = datetime.fromtimestamp(
                        all_rules[rule].keep_current_version_until / 1000
                    ).strftime("%Y-%m-%d")
                else:
                    keep_current_version_until = None
                bucket_info[bucket_name]["lifecycle_rules"][rule_id] = {
                    "keep_previous_version_for (days)": keep_previous_version_for,
                    "keep_current_version_for (days)": keep_current_version_for,
                    "keep_current_version_until": keep_current_version_until,
                    "prefix": all_rules[rule].prefix,
                    "enabled": all_rules[rule].enabled,
                    "abort_incomplete_multipart_uploads_after (days)": abort_incomplete_multipart_uploads_after,
                    "cleanup_expired_object_delete_marker": all_rules[
                        rule
                    ].cleanup_expired_object_delete_marker,
                }
        if VSO_VERSION in api_version:
            buckets = list(blade.get_buckets().items)
            for bucket in range(0, len(buckets)):
                bucket_info[buckets[bucket].name]["bucket_type"] = buckets[
                    bucket
                ].bucket_type
            if BUCKET_API_VERSION in api_version:
                for bucket in range(0, len(buckets)):
                    bucket_info[buckets[bucket].name]["retention_lock"] = buckets[
                        bucket
                    ].retention_lock
                    bucket_info[buckets[bucket].name]["quota_limit"] = buckets[
                        bucket
                    ].quota_limit
                    bucket_info[buckets[bucket].name]["object_lock_config"] = {
                        "enabled": buckets[bucket].object_lock_config.enabled,
                        "freeze_locked_objects": buckets[
                            bucket
                        ].object_lock_config.freeze_locked_objects,
                    }
                    if buckets[bucket].object_lock_config.enabled:
                        bucket_info[buckets[bucket].name]["object_lock_config"][
                            "default_retention"
                        ] = getattr(
                            buckets[bucket].object_lock_config, "default_retention", ""
                        )
                        bucket_info[buckets[bucket].name]["object_lock_config"][
                            "default_retention_mode"
                        ] = getattr(
                            buckets[bucket].object_lock_config,
                            "default_retention_mode",
                            "",
                        )
                    bucket_info[buckets[bucket].name]["eradication_config"] = {
                        "eradication_delay": buckets[
                            bucket
                        ].eradication_config.eradication_delay,
                        "manual_eradication": buckets[
                            bucket
                        ].eradication_config.manual_eradication,
                    }
                    if NAP_API_VERSION in api_version:
                        bucket_info[buckets[bucket].name]["eradication_config"][
                            "eradication_mode"
                        ] = buckets[bucket].eradication_config.eradication_mode
                    if PUBLIC_API_VERSION in api_version:
                        bucket_info[buckets[bucket].name]["public_status"] = buckets[
                            bucket
                        ].public_status
                        bucket_info[buckets[bucket].name]["public_access_config"] = {
                            "block_new_public_policies": buckets[
                                bucket
                            ].public_access_config.block_new_public_policies,
                            "block_public_access": buckets[
                                bucket
                            ].public_access_config.block_public_access,
                        }

    return bucket_info


def generate_kerb_dict(blade):
    kerb_info = {}
    keytabs = list(blade.get_keytabs().items)
    for ktab in range(0, len(keytabs)):
        keytab_name = keytabs[ktab].prefix
        kerb_info[keytab_name] = {}
        for key in range(0, len(keytabs)):
            if keytabs[key].prefix == keytab_name:
                kerb_info[keytab_name][keytabs[key].suffix] = {
                    "fqdn": keytabs[key].fqdn,
                    "kvno": keytabs[key].kvno,
                    "principal": keytabs[key].principal,
                    "realm": keytabs[key].realm,
                    "encryption_type": keytabs[key].encryption_type,
                    "server": None,
                    "source": None,
                }
                if hasattr(keytabs[key], "server"):
                    kerb_info[keytab_name][keytabs[key].suffix]["server"] = getattr(
                        keytabs[key].server, "name", None
                    )
                if hasattr(keytabs[key], "source"):
                    kerb_info[keytab_name][keytabs[key].suffix]["source"] = getattr(
                        keytabs[key].source, "name", None
                    )
    return kerb_info


def generate_ad_dict(blade):
    ad_info = {}
    active_directory = blade.get_active_directory()
    if active_directory.total_item_count != 0:
        ad_accounts = list(active_directory.items)
        for adir in range(0, len(ad_accounts)):
            name = ad_accounts[adir].name
            ad_info[name] = {
                "computer": ad_accounts[adir].computer_name,
                "domain": ad_accounts[adir].domain,
                "directory_servers": ad_accounts[adir].directory_servers,
                "kerberos_servers": ad_accounts[adir].kerberos_servers,
                "service_principals": ad_accounts[adir].service_principal_names,
                "join_ou": ad_accounts[adir].join_ou,
                "encryption_types": ad_accounts[adir].encryption_types,
                "global_catalog_servers": getattr(
                    ad_accounts[adir], "global_catalog_servers", None
                ),
                "server": None,
            }
            if hasattr(ad_accounts[adir], "server"):
                ad_info[name]["server"] = getattr(
                    ad_accounts[adir].server, "name", None
                )
    return ad_info


def generate_bucket_access_policies_dict(blade):
    policies_info = {}
    buckets = list(blade.get_buckets().items)
    for bucket in range(0, len(buckets)):
        policies = list(
            blade.get_buckets_bucket_access_policies(
                bucket_names=[buckets[bucket].name]
            ).items
        )
        for policy in range(0, len(policies)):
            policy_name = policies[policy].name
            policies_info[policy_name] = {
                "description": policies[policy].description,
                "enabled": policies[policy].enabled,
                "local": policies[policy].is_local,
                "rules": [],
            }
            for rule in range(0, len(policies[policy].rules)):
                policies_info[policy_name]["rules"].append(
                    {
                        "actions": policies[policy].rules[rule].actions,
                        "resources": policies[policy].rules[rule].resources,
                        "all_principals": policies[policy].rules[rule].principals.all,
                        "effect": policies[policy].rules[rule].effect,
                        "name": policies[policy].rules[rule].name,
                    }
                )
    return policies_info


def generate_bucket_cross_object_policies_dict(blade):
    policies_info = {}
    buckets = list(blade.get_buckets().items)
    for bucket in range(0, len(buckets)):
        policies = list(
            blade.get_buckets_cross_origin_resource_sharing_policies(
                bucket_names=[buckets[bucket].name]
            ).items
        )
        for policy in range(0, len(policies)):
            policy_name = policies[policy].name
            policies_info[policy_name] = {
                "allowed_headers": policies[policy].allowed_headers,
                "allowed_methods": policies[policy].allowed_methods,
                "allowed_origins": policies[policy].allowed_origins,
            }
    return policies_info


def generate_object_store_access_policies_dict(blade):
    policies_info = {}
    policies = list(blade.get_object_store_access_policies().items)
    for policy in range(0, len(policies)):
        policy_name = policies[policy].name
        policies_info[policy_name] = {
            "ARN": policies[policy].arn,
            "description": policies[policy].description,
            "enabled": policies[policy].enabled,
            "local": policies[policy].is_local,
            "rules": [],
        }
        for rule in range(0, len(policies[policy].rules)):
            policies_info[policy_name]["rules"].append(
                {
                    "actions": policies[policy].rules[rule].actions,
                    "conditions": {
                        "source_ips": policies[policy]
                        .rules[rule]
                        .conditions.source_ips,
                        "s3_delimiters": policies[policy]
                        .rules[rule]
                        .conditions.s3_delimiters,
                        "s3_prefixes": policies[policy]
                        .rules[rule]
                        .conditions.s3_prefixes,
                    },
                    "effect": policies[policy].rules[rule].effect,
                    "name": policies[policy].rules[rule].name,
                }
            )
    return policies_info


def generate_nfs_export_policies_dict(blade):
    policies_info = {}
    policies = list(blade.get_nfs_export_policies().items)
    for policy in range(0, len(policies)):
        policy_name = policies[policy].name
        policies_info[policy_name] = {
            "local": policies[policy].is_local,
            "enabled": policies[policy].enabled,
            "rules": [],
        }
        for rule in range(0, len(policies[policy].rules)):
            policies_info[policy_name]["rules"].append(
                {
                    "access": policies[policy].rules[rule].access,
                    "anongid": policies[policy].rules[rule].anongid,
                    "anonuid": policies[policy].rules[rule].anonuid,
                    "atime": policies[policy].rules[rule].atime,
                    "client": policies[policy].rules[rule].client,
                    "fileid_32bit": policies[policy].rules[rule].fileid_32bit,
                    "permission": policies[policy].rules[rule].permission,
                    "secure": policies[policy].rules[rule].secure,
                    "security": policies[policy].rules[rule].security,
                    "index": policies[policy].rules[rule].index,
                }
            )
    return policies_info


def generate_smb_client_policies_dict(blade):
    policies_info = {}
    policies = list(blade.get_smb_client_policies().items)
    for policy in range(0, len(policies)):
        policy_name = policies[policy].name
        policies_info[policy_name] = {
            "local": policies[policy].is_local,
            "enabled": policies[policy].enabled,
            "version": policies[policy].version,
            "rules": [],
        }
        for rule in range(0, len(policies[policy].rules)):
            policies_info[policy_name]["rules"].append(
                {
                    "name": policies[policy].rules[rule].name,
                    "change": getattr(policies[policy].rules[rule], "change", None),
                    "full_control": getattr(
                        policies[policy].rules[rule], "full_control", None
                    ),
                    "principal": getattr(
                        policies[policy].rules[rule], "principal", None
                    ),
                    "read": getattr(policies[policy].rules[rule], "read", None),
                    "client": getattr(policies[policy].rules[rule], "client", None),
                    "index": getattr(policies[policy].rules[rule], "index", None),
                    "policy_version": getattr(
                        policies[policy].rules[rule], "policy_version", None
                    ),
                    "encryption": getattr(
                        policies[policy].rules[rule], "encryption", None
                    ),
                    "permission": getattr(
                        policies[policy].rules[rule], "permission", None
                    ),
                }
            )
    return policies_info


def generate_object_store_accounts_dict(blade):
    account_info = {}
    accounts = list(blade.get_object_store_accounts().items)
    for account in range(0, len(accounts)):
        acc_name = accounts[account].name
        account_info[acc_name] = {
            "object_count": accounts[account].object_count,
            "data_reduction": accounts[account].space.data_reduction,
            "snapshots_space": accounts[account].space.snapshots,
            "total_physical_space": accounts[account].space.total_physical,
            "unique_space": accounts[account].space.unique,
            "virtual_space": accounts[account].space.virtual,
            "total_provisioned_space": getattr(
                accounts[account].space, "total_provisioned", None
            ),
            "available_provisioned_space": getattr(
                accounts[account].space, "available_provisioned", None
            ),
            "available_ratio": getattr(
                accounts[account].space, "available_ratio", None
            ),
            "destroyed_space": getattr(accounts[account].space, "destroyed", None),
            "destroyed_virtual_space": getattr(
                accounts[account].space, "destroyed_virtual", None
            ),
            "quota_limit": getattr(accounts[account], "quota_limit", None),
            "hard_limit_enabled": getattr(
                accounts[account], "hard_limit_enabled", None
            ),
            "total_provisioned": getattr(
                accounts[account].space, "total_provisioned", None
            ),
            "users": {},
        }
        try:
            account_info[acc_name]["bucket_defaults"] = {
                "hard_limit_enabled": accounts[
                    account
                ].bucket_defaults.hard_limit_enabled,
                "quota_limit": accounts[account].bucket_defaults.quota_limit,
            }
        except AttributeError:
            pass
        try:
            account_info[acc_name]["public_access_config"] = {
                "block_new_public_policies": accounts[
                    account
                ].public_access_config.block_new_public_policies,
                "block_public_access": accounts[
                    account
                ].public_access_config.block_public_access,
            }
        except AttributeError:
            pass
        acc_users = list(
            blade.get_object_store_users(filter='name="' + acc_name + '/*"').items
        )
        for acc_user in range(0, len(acc_users)):
            user_name = acc_users[acc_user].name.split("/")[1]
            account_info[acc_name]["users"][user_name] = {"keys": [], "policies": []}
            if (
                blade.get_object_store_access_keys(
                    filter='user.name="' + acc_users[acc_user].name + '"'
                ).total_item_count
                != 0
            ):
                access_keys = list(
                    blade.get_object_store_access_keys(
                        filter='user.name="' + acc_users[acc_user].name + '"'
                    ).items
                )
                for key in range(0, len(access_keys)):
                    account_info[acc_name]["users"][user_name]["keys"].append(
                        {
                            "name": access_keys[key].name,
                            "enabled": bool(access_keys[key].enabled),
                        }
                    )
            if (
                blade.get_object_store_access_policies_object_store_users(
                    member_names=[acc_users[acc_user].name]
                ).total_item_count
                != 0
            ):
                policies = list(
                    blade.get_object_store_access_policies_object_store_users(
                        member_names=[acc_users[acc_user].name]
                    ).items
                )
                for policy in range(0, len(policies)):
                    account_info[acc_name]["users"][user_name]["policies"].append(
                        policies[policy].policy.name
                    )
    return account_info


def generate_fs_dict(module, blade):
    api_version = blade.api_version.list_versions().versions
    if SMB_MODE_API_VERSION in api_version:
        bladev2 = get_system(module)
        fsys_v2 = list(bladev2.get_file_systems().items)
    fs_info = {}
    fsys = blade.file_systems.list_file_systems()
    for fsystem in range(0, len(fsys.items)):
        share = fsys.items[fsystem].name
        fs_info[share] = {
            "fast_remove": fsys.items[fsystem].fast_remove_directory_enabled,
            "snapshot_enabled": fsys.items[fsystem].snapshot_directory_enabled,
            "provisioned": fsys.items[fsystem].provisioned,
            "destroyed": fsys.items[fsystem].destroyed,
            "nfs_rules": fsys.items[fsystem].nfs.rules,
            "nfs_v3": getattr(fsys.items[fsystem].nfs, "v3_enabled", False),
            "nfs_v4_1": getattr(fsys.items[fsystem].nfs, "v4_1_enabled", False),
            "user_quotas": {},
            "group_quotas": {},
        }
        if fsys.items[fsystem].http.enabled:
            fs_info[share]["http"] = fsys.items[fsystem].http.enabled
        if fsys.items[fsystem].smb.enabled:
            fs_info[share]["smb_mode"] = fsys.items[fsystem].smb.acl_mode
        api_version = blade.api_version.list_versions().versions
        if MULTIPROTOCOL_API_VERSION in api_version:
            fs_info[share]["multi_protocol"] = {
                "safegaurd_acls": fsys.items[fsystem].multi_protocol.safeguard_acls,
                "access_control_style": fsys.items[
                    fsystem
                ].multi_protocol.access_control_style,
            }
        if HARD_LIMIT_API_VERSION in api_version:
            fs_info[share]["hard_limit"] = fsys.items[fsystem].hard_limit_enabled
        if REPLICATION_API_VERSION in api_version:
            fs_info[share]["promotion_status"] = fsys.items[fsystem].promotion_status
            fs_info[share]["requested_promotion_state"] = fsys.items[
                fsystem
            ].requested_promotion_state
            fs_info[share]["writable"] = fsys.items[fsystem].writable
            fs_info[share]["source"] = {
                "is_local": fsys.items[fsystem].source.is_local,
                "name": fsys.items[fsystem].source.name,
            }
        if SMB_MODE_API_VERSION in api_version:
            for v2fs in range(0, len(fsys_v2)):
                if fsys_v2[v2fs].name == share:
                    fs_info[share]["default_group_quota"] = fsys_v2[
                        v2fs
                    ].default_group_quota
                    fs_info[share]["default_user_quota"] = fsys_v2[
                        v2fs
                    ].default_user_quota
                    if NFS_POLICY_API_VERSION in api_version:
                        fs_info[share]["export_policy"] = fsys_v2[
                            v2fs
                        ].nfs.export_policy.name
        if VSO_VERSION in api_version:
            for v2fs in range(0, len(fsys_v2)):
                if fsys_v2[v2fs].name == share:
                    try:
                        fs_groups = True
                        fs_group_quotas = list(
                            bladev2.get_quotas_groups(file_system_names=[share]).items
                        )
                    except Exception:
                        fs_groups = False
                    try:
                        fs_users = True
                        fs_user_quotas = list(
                            bladev2.get_quotas_users(file_system_names=[share]).items
                        )
                    except Exception:
                        fs_users = False
                    if fs_groups:
                        for group_quota in range(0, len(fs_group_quotas)):
                            group_name = fs_group_quotas[group_quota].name.rsplit("/")[
                                1
                            ]
                            fs_info[share]["group_quotas"][group_name] = {
                                "group_id": fs_group_quotas[group_quota].group.id,
                                "group_name": fs_group_quotas[group_quota].group.name,
                                "quota": fs_group_quotas[group_quota].quota,
                                "usage": fs_group_quotas[group_quota].usage,
                            }
                    if fs_users:
                        for user_quota in range(0, len(fs_user_quotas)):
                            user_name = fs_user_quotas[user_quota].name.rsplit("/")[1]
                            fs_info[share]["user_quotas"][user_name] = {
                                "user_id": fs_user_quotas[user_quota].user.id,
                                "user_name": fs_user_quotas[user_quota].user.name,
                                "quota": fs_user_quotas[user_quota].quota,
                                "usage": fs_user_quotas[user_quota].usage,
                            }
            if PUBLIC_API_VERSION in api_version:
                for v2fs in range(0, len(fsys_v2)):
                    if fsys_v2[v2fs].name == share:
                        fs_info[share]["smb_client_policy"] = getattr(
                            fsys_v2[v2fs].smb.client_policy, "name", None
                        )
                        fs_info[share]["smb_share_policy"] = getattr(
                            fsys_v2[v2fs].smb.share_policy, "name", None
                        )
                        fs_info[share]["smb_continuous_availability_enabled"] = fsys_v2[
                            v2fs
                        ].smb.continuous_availability_enabled
                        fs_info[share]["multi_protocol_access_control_style"] = getattr(
                            fsys_v2[v2fs].multi_protocol, "access_control_style", None
                        )
                        fs_info[share]["multi_protocol_safeguard_acls"] = fsys_v2[
                            v2fs
                        ].multi_protocol.safeguard_acls

    return fs_info


def generate_drives_dict(blade):
    """
    Drives information is only available for the Legend chassis.
    The Legend chassis product_name has // in it so only bother if
    that is the case.
    """
    drives_info = {}
    drives = list(blade.get_drives().items)
    if "//" in list(blade.get_arrays().items)[0].product_type:
        for drive in range(0, len(drives)):
            name = drives[drive].name
            drives_info[name] = {
                "progress": getattr(drives[drive], "progress", None),
                "raw_capacity": getattr(drives[drive], "raw_capacity", None),
                "status": getattr(drives[drive], "status", None),
                "details": getattr(drives[drive], "details", None),
                "type": getattr(drives[drive], "type", None),
            }
    return drives_info


def generate_servers_dict(blade):
    servers_info = {}
    servers = list(blade.get_servers().items)
    for server in range(0, len(servers)):
        name = servers[server].name
        servers_info[name] = {
            "created": datetime.fromtimestamp(servers[server].created / 1000).strftime(
                "%Y-%m-%d %H:%M:%S"
            ),
            "dns": [],
            "directory_services": [],
        }
        for d_serv in range(0, len(servers[server].directory_services)):
            servers_info[name]["directory_services"].append(
                servers[server].directory_services[d_serv].name
            )
        for dns in range(0, len(servers[server].dns)):
            servers_info[name]["dns"].append(servers[server].dns[dns].name)
    return servers_info


def generate_fleet_dict(blade):
    fleet_info = {}
    fleet = list(blade.get_fleets().items)
    if fleet:
        fleet_name = list(blade.get_fleets().items)[0].name
        fleet_info[fleet_name] = {
            "members": {},
        }
        members = list(blade.get_fleets_members().items)
        for member in range(0, len(members)):
            name = members[member].member.name
            fleet_info[fleet_name]["members"][name] = {
                "status": members[member].status,
                "status_details": members[member].status_details,
            }
    return fleet_info


def main():
    argument_spec = purefb_argument_spec()
    argument_spec.update(
        dict(gather_subset=dict(default="minimum", type="list", elements="str"))
    )

    module = AnsibleModule(argument_spec, supports_check_mode=True)

    blade = get_blade(module)
    versions = blade.api_version.list_versions().versions

    if MIN_REQUIRED_API_VERSION not in versions:
        module.fail_json(
            msg="Minimum FlashBlade REST version required: {0}".format(
                MIN_REQUIRED_API_VERSION
            )
        )
    if not module.params["gather_subset"]:
        module.params["gather_subset"] = ["minimum"]
    subset = [test.lower() for test in module.params["gather_subset"]]
    valid_subsets = (
        "all",
        "minimum",
        "config",
        "performance",
        "capacity",
        "network",
        "subnets",
        "lags",
        "filesystems",
        "snapshots",
        "buckets",
        "arrays",
        "replication",
        "policies",
        "accounts",
        "admins",
        "ad",
        "kerberos",
        "drives",
        "servers",
        "fleet",
    )
    subset_test = (test in valid_subsets for test in subset)
    if not all(subset_test):
        module.fail_json(
            msg="value must gather_subset must be one or more of: %s, got: %s"
            % (",".join(valid_subsets), ",".join(subset))
        )

    info = {}

    api_version = blade.api_version.list_versions().versions
    if "minimum" in subset or "all" in subset:
        info["default"] = generate_default_dict(module, blade)
    if "performance" in subset or "all" in subset:
        info["performance"] = generate_perf_dict(blade)
    if "config" in subset or "all" in subset:
        info["config"] = generate_config_dict(module, blade)
    if "capacity" in subset or "all" in subset:
        info["capacity"] = generate_capacity_dict(module, blade)
    if "lags" in subset or "all" in subset:
        info["lag"] = generate_lag_dict(blade)
    if "network" in subset or "all" in subset:
        info["network"] = generate_network_dict(blade)
    if "subnets" in subset or "all" in subset:
        info["subnet"] = generate_subnet_dict(blade)
    if "filesystems" in subset or "all" in subset:
        info["filesystems"] = generate_fs_dict(module, blade)
    if "admins" in subset or "all" in subset:
        info["admins"] = generate_admin_dict(module, blade)
    if "snapshots" in subset or "all" in subset:
        info["snapshots"] = generate_snap_dict(blade)
    if "buckets" in subset or "all" in subset:
        info["buckets"] = generate_bucket_dict(module, blade)
    if POLICIES_API_VERSION in api_version:
        if "policies" in subset or "all" in subset:
            info["policies"] = generate_policies_dict(blade)
            info["snapshot_policies"] = generate_policies_dict(blade)
    if REPLICATION_API_VERSION in api_version:
        if "arrays" in subset or "all" in subset:
            info["arrays"] = generate_array_conn_dict(module, blade)
        if "replication" in subset or "all" in subset:
            info["file_replication"] = generate_file_repl_dict(blade)
            info["bucket_replication"] = generate_bucket_repl_dict(module, blade)
            info["snap_transfers"] = generate_snap_transfer_dict(blade)
            info["remote_credentials"] = generate_remote_creds_dict(blade)
            info["targets"] = generate_targets_dict(blade)
    if MIN_32_API in api_version:
        # Calls for data only available from Purity//FB 3.2 and higher
        blade = get_system(module)
        if "accounts" in subset or "all" in subset:
            info["accounts"] = generate_object_store_accounts_dict(blade)
        if "ad" in subset or "all" in subset:
            info["active_directory"] = generate_ad_dict(blade)
        if "kerberos" in subset or "all" in subset:
            info["kerberos"] = generate_kerb_dict(blade)
        if "policies" in subset or "all" in subset:
            if SMB_MODE_API_VERSION in api_version:
                info["access_policies"] = generate_object_store_access_policies_dict(
                    blade
                )
            if PUBLIC_API_VERSION in api_version:
                info["bucket_access_policies"] = generate_bucket_access_policies_dict(
                    blade
                )
                info["bucket_cross_origin_policies"] = (
                    generate_bucket_cross_object_policies_dict(blade)
                )
            if NFS_POLICY_API_VERSION in api_version:
                info["export_policies"] = generate_nfs_export_policies_dict(blade)
            if SMB_CLIENT_API_VERSION in api_version:
                info["share_policies"] = generate_smb_client_policies_dict(blade)
            if FLEET_API_VERSION in api_version:
                info["fleet"] = generate_fleet_dict(blade)
        if "drives" in subset or "all" in subset and DRIVES_API_VERSION in api_version:
            info["drives"] = generate_drives_dict(blade)
        if (
            "servers" in subset
            or "all" in subset
            and SERVERS_API_VERSION in api_version
        ):
            info["servers"] = generate_servers_dict(blade)
    module.exit_json(changed=False, purefb_info=info)


if __name__ == "__main__":
    main()
