#!/usr/bin/python

# (c) 2018 Piotr Olczak <piotr.olczak@redhat.com>
# (c) 2018-2019, NetApp, Inc
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)

'''
na_ontap_info
'''

from __future__ import absolute_import, division, print_function
__metaclass__ = type

ANSIBLE_METADATA = {'metadata_version': '1.1',
                    'status': ['preview'],
                    'supported_by': 'certified'}

DOCUMENTATION = '''
module: na_ontap_info
author: Piotr Olczak (@dprts) <polczak@redhat.com>
extends_documentation_fragment:
    - netapp.ontap.netapp.na_ontap
short_description: NetApp information gatherer
description:
    - This module allows you to gather various information about ONTAP configuration
version_added: 2.9.0
requirements:
    - netapp_lib
options:
    state:
        type: str
        description:
            - Returns "info"
        default: "info"
        choices: ['info']
    vserver:
        type: str
        description:
            - If present, 'vserver tunneling' will limit the output to the vserver scope.
            - Note that not all subsets are supported on a vserver, and 'all' will trigger an error.
        version_added: '19.11.0'
    gather_subset:
        type: list
        elements: str
        description:
            - When supplied, this argument will restrict the information collected
                to a given subset.  Possible values for this argument include
                "aggregate_info",
                "aggr_efficiency_info",
                "cifs_options_info",
                "cifs_server_info",
                "cifs_share_info",
                "cifs_vserver_security_info",
                "cluster_identity_info",
                "cluster_image_info",
                "cluster_log_forwarding_info",
                "cluster_node_info",
                "cluster_peer_info",
                "cluster_switch_info",
                "clock_info",
                "disk_info",
                "env_sensors_info",
                "event_notification_destination_info",
                "event_notification_info",
                "export_policy_info",
                "export_rule_info",
                "fcp_adapter_info",
                "fcp_alias_info",
                "fcp_service_info",
                "igroup_info",
                "iscsi_service_info",
                "job_schedule_cron_info",
                "kerberos_realm_info",
                "ldap_client",
                "ldap_config",
                "license_info",
                "lun_info",
                "lun_map_info",
                "metrocluster_check_info",
                "metrocluster_info",
                "metrocluster_node_info",
                "net_dev_discovery_info",
                "net_dns_info",
                "net_failover_group_info",
                "net_firewall_info",
                "net_ifgrp_info",
                "net_interface_info",
                "net_interface_service_policy_info",
                "net_ipspaces_info",
                "net_port_info",
                "net_port_broadcast_domain_info",
                "net_routes_info",
                "net_vlan_info",
                "nfs_info",
                "ntfs_dacl_info",
                "ntfs_sd_info",
                "ntp_server_info",
                "nvme_info",
                "nvme_interface_info",
                "nvme_namespace_info",
                "nvme_subsystem_info",
                "ontap_system_version",
                "ontap_version",
                "ontapi_version",
                "qos_adaptive_policy_info",
                "qos_policy_info",
                "qtree_info",
                "quota_report_info",
                "role_info",
                "security_key_manager_key_info",
                "security_login_account_info",
                "security_login_role_config_info",
                "security_login_role_info",
                "service_processor_info",
                "service_processor_network_info",
                "shelf_info"
                "sis_info",
                "sis_policy_info",
                "snapmirror_info",
                "snapmirror_destination_info",
                "snapmirror_policy_info",
                "snapshot_info",
                "snapshot_policy_info",
                "storage_failover_info",
                "storage_bridge_info",
                "subsys_health_info",
                "sysconfig_info",
                "sys_cluster_alerts",
                "volume_info",
                "volume_space_info",
                "vscan_info",
                "vscan_status_info",
                "vscan_scanner_pool_info",
                "vscan_connection_status_all_info",
                "vscan_connection_extended_stats_info",
                "vserver_info",
                "vserver_login_banner_info",
                "vserver_motd_info",
                "vserver_nfs_info",
                "vserver_peer_info",
                Can specify a list of values to include a larger subset.  Values can also be used
                with an initial C(M(!)) to specify that a specific subset should
                not be collected.
            - nvme is supported with ONTAP 9.4 onwards.
            - use "help" to get a list of supported information for your system.
        default: "all"
    max_records:
        type: int
        description:
            - Maximum number of records returned in a single ZAPI call. Valid range is [1..2^32-1].
                This parameter controls internal behavior of this module.
        default: 1024
        version_added: '20.2.0'
    summary:
        description:
            - Boolean flag to control return all attributes of the module info or only the names.
            - If true, only names are returned.
        default: false
        type: bool
        version_added: '20.4.0'
    volume_move_target_aggr_info:
        description:
        - Required options for volume_move_target_aggr_info
        type: dict
        version_added: '20.5.0'
        suboptions:
            volume_name:
                description:
                - Volume name to get target aggr info for
                required: true
                type: str
                version_added: '20.5.0'
            vserver:
                description:
                - vserver the Volume lives on
                required: true
                type: str
                version_added: '20.5.0'
    desired_attributes:
        description:
        - Advanced feature requiring to understand ZAPI internals.
        - Allows to request a specific attribute that is not returned by default, or to limit the returned attributes.
        - A dictionary for the zapi desired-attributes element.
        - An XML tag I(<tag>value</tag>) is a dictionary with tag as the key.
        - Value can be another dictionary, a list of dictionaries, a string, or nothing.
        - eg I(<tag/>) is represented as I(tag:)
        - Only a single subset can be called at a time if this option is set.
        - It is the caller responsibity to make sure key attributes are present in the right position.
        - The module will error out if any key attribute is missing.
        type: dict
        version_added: '20.6.0'
    query:
        description:
        - Advanced feature requiring to understand ZAPI internals.
        - Allows to specify which objects to return.
        - A dictionary for the zapi query element.
        - An XML tag I(<tag>value</tag>) is a dictionary with tag as the key.
        - Value can be another dictionary, a list of dictionaries, a string, or nothing.
        - eg I(<tag/>) is represented as I(tag:)
        - Only a single subset can be called at a time if this option is set.
        type: dict
        version_added: '20.7.0'
    use_native_zapi_tags:
        description:
        - By default, I(-) in the returned dictionary keys are translated to I(_).
        - If set to true, the translation is disabled.
        type: bool
        default: false
        version_added: '20.6.0'
    continue_on_error:
        description:
        - By default, this module fails on the first error.
        - This option allows to provide a list of errors that are not failing the module.
        - Errors in the list are reported in the output, under the related info element, as an "error" entry.
        - Possible values are always, never, missing_vserver_api_error, rpc_error, other_error.
        - missing_vserver_api_error - most likely the API is available at cluster level but not vserver level.
        - rpc_error - some queries are failing because the node cannot reach another node in the cluster.
        - key_error - a query is failing because the returned data does not contain an expected key.
        - for key errors, make sure to report this in Slack.  It may be a change in a new ONTAP version.
        - other_error - anything not in the above list.
        - always will continue on any error, never will fail on any error, they cannot be used with any other keyword.
        type: list
        elements: str
        default: never
'''

EXAMPLES = '''
- name: Get NetApp info as Cluster Admin (Password Authentication)
  na_ontap_info:
    state: info
    hostname: "na-vsim"
    username: "admin"
    password: "admins_password"
  register: ontap_info
- debug:
    msg: "{{ ontap_info.ontap_info }}"

- name: Get NetApp version as Vserver admin
  na_ontap_info:
    state: info
    hostname: "na-vsim"
    username: "vsadmin"
    vserver: trident_svm
    password: "vsadmins_password"

- name: run ontap info module using vserver tunneling and ignoring errors
  na_ontap_info:
    hostname: "na-vsim"
    username: "admin"
    password: "admins_password"
    vserver: trident_svm
    summary: true
    continue_on_error:
      - missing_vserver_api_error
      - rpc_error

- name: Limit Info Gathering to Aggregate Information as Cluster Admin
  na_ontap_info:
    state: info
    hostname: "na-vsim"
    username: "admin"
    password: "admins_password"
    gather_subset: "aggregate_info"
  register: ontap_info

- name: Limit Info Gathering to Volume and Lun Information as Cluster Admin
  na_ontap_info:
    state: info
    hostname: "na-vsim"
    username: "admin"
    password: "admins_password"
    gather_subset:
      - volume_info
      - lun_info
  register: ontap_info

- name: Gather all info except for volume and lun information as Cluster Admin
  na_ontap_info:
    state: info
    hostname: "na-vsim"
    username: "admin"
    password: "admins_password"
    gather_subset:
      - "!volume_info"
      - "!lun_info"
  register: ontap_info

- name: Gather Volume move information for a specific volume
  na_ontap_info:
    state: info
    hostname: "na-vsim"
    username: "admin"
    password: "admins_password"
    gather_subset: volume_move_target_aggr_info
    volume_move_target_aggr_info:
      volume_name: carchitest
      vserver: ansible

- name: run ontap info module for aggregate module, requesting specific fields
  na_ontap_info:
    # <<: *login
    gather_subset: aggregate_info
    desired_attributes:
      aggr-attributes:
      aggr-inode-attributes:
        files-private-used:
      aggr-raid-attributes:
        aggregate-type:
    use_native_zapi_tags: true
    register: ontap
- debug: var=ontap

- name: run ontap info to get offline volumes with dp in the name
  na_ontap_info:
    # <<: *cert_login
    gather_subset: volume_info
    query:
      volume-attributes:
        volume-id-attributes:
          name: '*dp*'
        volume-state-attributes:
          state: offline
    desired_attributes:
      volume-attributes:
        volume-id-attributes:
          name:
        volume-state-attributes:
          state:
  register: ontap
- debug: var=ontap
'''

RETURN = '''
ontap_info:
    description: Returns various information about NetApp cluster configuration
    returned: always
    type: dict
    sample: '{
        "ontap_info": {
            "aggregate_info": {...},
            "cluster_identity_info": {...},
            "cluster_image_info": {...},
            "cluster_node_info": {...},
            "igroup_info": {...},
            "iscsi_service_info": {...},
            "license_info": {...},
            "lun_info": {...},
            "metrocluster_check_info": {...},
            "metrocluster_info": {...},
            "metrocluster_node_info": {...},
            "net_dns_info": {...},
            "net_ifgrp_info": {...},
            "net_interface_info": {...},
            "net_interface_service_policy_info": {...},
            "net_port_info": {...},
            "ontap_system_version": {...},
            "ontap_version": {...},
            "ontapi_version": {...},
            "qos_policy_info": {...},
            "qos_adaptive_policy_info": {...},
            "qtree_info": {...},
            "quota_report_info": {...},
            "security_key_manager_key_info": {...},
            "security_login_account_info": {...},
            "snapmirror_info": {...}
            "snapmirror_destination_info": {...}
            "storage_bridge_info": {...}
            "storage_failover_info": {...},
            "volume_info": {...},
            "vserver_login_banner_info": {...},
            "vserver_motd_info": {...},
            "vserver_info": {...},
            "vserver_nfs_info": {...},
            "vscan_status_info": {...},
            "vscan_scanner_pool_info": {...},
            "vscan_connection_status_all_info": {...},
            "vscan_connection_extended_stats_info": {...}
    }'
'''

import copy
import traceback
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils._text import to_native
import ansible_collections.netapp.ontap.plugins.module_utils.netapp as netapp_utils


try:
    import xmltodict
    HAS_XMLTODICT = True
except ImportError:
    HAS_XMLTODICT = False

try:
    import json
    HAS_JSON = True
except ImportError:
    HAS_JSON = False

HAS_NETAPP_LIB = netapp_utils.has_netapp_lib()


class NetAppONTAPGatherInfo(object):
    '''Class with gather info methods'''

    def __init__(self, module, max_records):
        self.module = module
        self.max_records = str(max_records)
        volume_move_target_aggr_info = module.params.get('volume_move_target_aggr_info', dict())
        if volume_move_target_aggr_info is None:
            volume_move_target_aggr_info = dict()
        self.netapp_info = dict()
        self.desired_attributes = module.params['desired_attributes']
        self.query = module.params['query']
        self.translate_keys = not module.params['use_native_zapi_tags']
        self.warnings = list()  # warnings will be added to the info results, if any
        self.set_error_flags()

        # thanks to coreywan (https://github.com/ansible/ansible/pull/47016)
        # for starting this
        # min_version identifies the ontapi version which supports this ZAPI
        # use 0 if it is supported since 9.1
        self.info_subsets = {
            'cluster_identity_info': {
                'method': self.get_generic_get_iter,
                'kwargs': {
                    'call': 'cluster-identity-get',
                    'attributes_list_tag': 'attributes',
                    'attribute': 'cluster-identity-info',
                    'key_fields': 'cluster-name',
                },
                'min_version': '0',
            },
            'cluster_image_info': {
                'method': self.get_generic_get_iter,
                'kwargs': {
                    'call': 'cluster-image-get-iter',
                    'attribute': 'cluster-image-info',
                    'key_fields': 'node-id',
                    'query': {'max-records': self.max_records},
                },
                'min_version': '0',
            },
            'cluster_log_forwarding_info': {
                'method': self.get_generic_get_iter,
                'kwargs': {
                    'call': 'cluster-log-forward-get-iter',
                    'attribute': 'cluster-log-forward-info',
                    'key_fields': ('destination', 'port'),
                    'query': {'max-records': self.max_records},
                },
                'min_version': '0',
            },
            'cluster_node_info': {
                'method': self.get_generic_get_iter,
                'kwargs': {
                    'call': 'cluster-node-get-iter',
                    'attribute': 'cluster-node-info',
                    'key_fields': 'node-name',
                    'query': {'max-records': self.max_records},
                },
                'min_version': '0',
            },
            'security_login_account_info': {
                'method': self.get_generic_get_iter,
                'kwargs': {
                    'call': 'security-login-get-iter',
                    'attribute': 'security-login-account-info',
                    'key_fields': ('vserver', 'user-name', 'application', 'authentication-method'),
                    'query': {'max-records': self.max_records},
                },
                'min_version': '0',
            },
            'security_login_role_config_info': {
                'method': self.get_generic_get_iter,
                'kwargs': {
                    'call': 'security-login-role-config-get-iter',
                    'attribute': 'security-login-role-config-info',
                    'key_fields': ('vserver', 'role-name'),
                    'query': {'max-records': self.max_records},
                },
                'min_version': '0',
            },
            'security_login_role_info': {
                'method': self.get_generic_get_iter,
                'kwargs': {
                    'call': 'security-login-role-get-iter',
                    'attribute': 'security-login-role-info',
                    'key_fields': ('vserver', 'role-name', 'command-directory-name'),
                    'query': {'max-records': self.max_records},
                },
                'min_version': '0',
            },
            'aggregate_info': {
                'method': self.get_generic_get_iter,
                'kwargs': {
                    'call': 'aggr-get-iter',
                    'attribute': 'aggr-attributes',
                    'key_fields': 'aggregate-name',
                    'query': {'max-records': self.max_records},
                },
                'min_version': '0',
            },
            'volume_info': {
                'method': self.get_generic_get_iter,
                'kwargs': {
                    'call': 'volume-get-iter',
                    'attribute': 'volume-attributes',
                    'key_fields': ('name', 'owning-vserver-name'),
                    'query': {'max-records': self.max_records},
                },
                'min_version': '0',
            },
            'license_info': {
                'method': self.get_generic_get_iter,
                'kwargs': {
                    'call': 'license-v2-list-info',
                    'attributes_list_tag': None,
                    'attribute': 'licenses',
                },
                'min_version': '0',
            },
            'lun_info': {
                'method': self.get_generic_get_iter,
                'kwargs': {
                    'call': 'lun-get-iter',
                    'attribute': 'lun-info',
                    'key_fields': ('vserver', 'path'),
                    'query': {'max-records': self.max_records},
                },
                'min_version': '0',
            },
            'metrocluster_check_info': {
                'method': self.get_generic_get_iter,
                'kwargs': {
                    'call': 'metrocluster-check-get-iter',
                    'attribute': 'metrocluster-check-info',
                    'fail_on_error': False,
                },
                'min_version': '0',
            },
            'metrocluster_info': {
                'method': self.get_generic_get_iter,
                'kwargs': {
                    'call': 'metrocluster-get',
                    'attribute': 'metrocluster-info',
                    'attributes_list_tag': 'attributes',
                },
                'min_version': '0',
            },
            'metrocluster_node_info': {
                'method': self.get_generic_get_iter,
                'kwargs': {
                    'call': 'metrocluster-node-get-iter',
                    'attribute': 'metrocluster-node-info',
                    'key_fields': ('cluster-name', 'node-name'),
                },
                'min_version': '0',
            },
            'net_dns_info': {
                'method': self.get_generic_get_iter,
                'kwargs': {
                    'call': 'net-dns-get-iter',
                    'attribute': 'net-dns-info',
                    'key_fields': 'vserver-name',
                    'query': {'max-records': self.max_records},
                },
                'min_version': '0',
            },
            'net_interface_info': {
                'method': self.get_generic_get_iter,
                'kwargs': {
                    'call': 'net-interface-get-iter',
                    'attribute': 'net-interface-info',
                    'key_fields': 'interface-name',
                    'query': {'max-records': self.max_records},
                },
                'min_version': '0',
            },
            'net_interface_service_policy_info': {
                'method': self.get_generic_get_iter,
                'kwargs': {
                    'call': 'net-interface-service-policy-get-iter',
                    'attribute': 'net-interface-service-policy-info',
                    'key_fields': ('vserver', 'policy'),
                    'query': {'max-records': self.max_records},
                },
                'min_version': '150',
            },
            'net_port_info': {
                'method': self.get_generic_get_iter,
                'kwargs': {
                    'call': 'net-port-get-iter',
                    'attribute': 'net-port-info',
                    'key_fields': ('node', 'port'),
                    'query': {'max-records': self.max_records},
                },
                'min_version': '0',
            },
            'security_key_manager_key_info': {
                'method': self.get_generic_get_iter,
                'kwargs': {
                    'call': 'security-key-manager-key-get-iter',
                    'attribute': 'security-key-manager-key-info',
                    'key_fields': ('node', 'key-id'),
                    'query': {'max-records': self.max_records},
                },
                'min_version': '0',
            },
            'storage_failover_info': {
                'method': self.get_generic_get_iter,
                'kwargs': {
                    'call': 'cf-get-iter',
                    'attribute': 'storage-failover-info',
                    'key_fields': 'node',
                    'query': {'max-records': self.max_records},
                },
                'min_version': '0',
            },
            'vserver_motd_info': {
                'method': self.get_generic_get_iter,
                'kwargs': {
                    'call': 'vserver-motd-get-iter',
                    'attribute': 'vserver-motd-info',
                    'key_fields': 'vserver',
                    'query': {'max-records': self.max_records},
                },
                'min_version': '0',
            },
            'vserver_login_banner_info': {
                'method': self.get_generic_get_iter,
                'kwargs': {
                    'call': 'vserver-login-banner-get-iter',
                    'attribute': 'vserver-login-banner-info',
                    'key_fields': 'vserver',
                    'query': {'max-records': self.max_records},
                },
                'min_version': '0',
            },
            'vserver_info': {
                'method': self.get_generic_get_iter,
                'kwargs': {
                    'call': 'vserver-get-iter',
                    'attribute': 'vserver-info',
                    'key_fields': 'vserver-name',
                    'query': {'max-records': self.max_records},
                },
                'min_version': '0',
            },
            'vserver_nfs_info': {
                'method': self.get_generic_get_iter,
                'kwargs': {
                    'call': 'nfs-service-get-iter',
                    'attribute': 'nfs-info',
                    'key_fields': 'vserver',
                    'query': {'max-records': self.max_records},
                },
                'min_version': '0',
            },
            'net_ifgrp_info': {
                'method': self.get_ifgrp_info,
                'kwargs': {},
                'min_version': '0',
            },
            'ontap_system_version': {
                'method': self.get_generic_get_iter,
                'kwargs': {
                    'call': 'system-get-version',
                    'attributes_list_tag': None,
                },
                'min_version': '0',
            },
            'ontap_version': {
                'method': self.ontapi,
                'kwargs': {},
                'min_version': '0',
            },
            'ontapi_version': {
                'method': self.ontapi,
                'kwargs': {},
                'min_version': '0',
            },
            'clock_info': {
                'method': self.get_generic_get_iter,
                'kwargs': {
                    'call': 'clock-get-clock',
                    'attributes_list_tag': None,
                },
                'min_version': '0'
            },
            'system_node_info': {
                'method': self.get_generic_get_iter,
                'kwargs': {
                    'call': 'system-node-get-iter',
                    'attribute': 'node-details-info',
                    'key_fields': 'node',
                    'query': {'max-records': self.max_records},
                },
                'min_version': '0',
            },
            'igroup_info': {
                'method': self.get_generic_get_iter,
                'kwargs': {
                    'call': 'igroup-get-iter',
                    'attribute': 'initiator-group-info',
                    'key_fields': ('vserver', 'initiator-group-name'),
                    'query': {'max-records': self.max_records},
                },
                'min_version': '0',
            },
            'iscsi_service_info': {
                'method': self.get_generic_get_iter,
                'kwargs': {
                    'call': 'iscsi-service-get-iter',
                    'attribute': 'iscsi-service-info',
                    'key_fields': 'vserver',
                    'query': {'max-records': self.max_records},
                },
                'min_version': '0',
            },
            'qos_policy_info': {
                'method': self.get_generic_get_iter,
                'kwargs': {
                    'call': 'qos-policy-group-get-iter',
                    'attribute': 'qos-policy-group-info',
                    'key_fields': 'policy-group',
                    'query': {'max-records': self.max_records},
                },
                'min_version': '0',
            },
            'qtree_info': {
                'method': self.get_generic_get_iter,
                'kwargs': {
                    'call': 'qtree-list-iter',
                    'attribute': 'qtree-info',
                    'key_fields': ('vserver', 'volume', 'id'),
                    'query': {'max-records': self.max_records},
                },
                'min_version': '0',
            },
            'quota_report_info': {
                'method': self.get_generic_get_iter,
                'kwargs': {
                    'call': 'quota-report-iter',
                    'attribute': 'quota',
                    'key_fields': ('vserver', 'volume', 'tree', 'quota-type', 'quota-target'),
                    'query': {'max-records': self.max_records},
                },
                'min_version': '0',
            },
            'vscan_status_info': {
                'method': self.get_generic_get_iter,
                'kwargs': {
                    'call': 'vscan-status-get-iter',
                    'attribute': 'vscan-status-info',
                    'key_fields': 'vserver',
                    'query': {'max-records': self.max_records},
                },
                'min_version': '0',
            },
            'vscan_scanner_pool_info': {
                'method': self.get_generic_get_iter,
                'kwargs': {
                    'call': 'vscan-scanner-pool-get-iter',
                    'attribute': 'vscan-scanner-pool-info',
                    'key_fields': 'vserver',
                    'query': {'max-records': self.max_records},
                },
                'min_version': '0',
            },
            'vscan_connection_status_all_info': {
                'method': self.get_generic_get_iter,
                'kwargs': {
                    'call': 'vscan-connection-status-all-get-iter',
                    'attribute': 'vscan-connection-status-all-info',
                    'key_fields': 'vserver',
                    'query': {'max-records': self.max_records},
                },
                'min_version': '0',
            },
            'vscan_connection_extended_stats_info': {
                'method': self.get_generic_get_iter,
                'kwargs': {
                    'call': 'vscan-connection-extended-stats-get-iter',
                    'attribute': 'vscan-connection-extended-stats-info',
                    'key_fields': 'vserver',
                    'query': {'max-records': self.max_records},
                },
                'min_version': '0',
            },
            'snapshot_info': {
                'method': self.get_generic_get_iter,
                'kwargs': {
                    'call': 'snapshot-get-iter',
                    'attribute': 'snapshot-info',
                    'key_fields': ('vserver', 'volume', 'name'),
                    'query': {'max-records': self.max_records},
                },
                'min_version': '0',
            },
            'storage_bridge_info': {
                'method': self.get_generic_get_iter,
                'kwargs': {
                    'call': 'storage-bridge-get-iter',
                    'attribute': 'storage-bridge-info',
                    'key_fields': 'name',
                    'query': {'max-records': self.max_records},
                },
                'min_version': '0',
            },
            # supported in ONTAP 9.3 and onwards
            'qos_adaptive_policy_info': {
                'method': self.get_generic_get_iter,
                'kwargs': {
                    'call': 'qos-adaptive-policy-group-get-iter',
                    'attribute': 'qos-adaptive-policy-group-info',
                    'key_fields': 'policy-group',
                    'query': {'max-records': self.max_records},
                },
                'min_version': '130',
            },
            # supported in ONTAP 9.4 and onwards
            'nvme_info': {
                'method': self.get_generic_get_iter,
                'kwargs': {
                    'call': 'nvme-get-iter',
                    'attribute': 'nvme-target-service-info',
                    'key_fields': 'vserver',
                    'query': {'max-records': self.max_records},
                },
                'min_version': '140',
            },
            'nvme_interface_info': {
                'method': self.get_generic_get_iter,
                'kwargs': {
                    'call': 'nvme-interface-get-iter',
                    'attribute': 'nvme-interface-info',
                    'key_fields': 'vserver',
                    'query': {'max-records': self.max_records},
                },
                'min_version': '140',
            },
            'nvme_subsystem_info': {
                'method': self.get_generic_get_iter,
                'kwargs': {
                    'call': 'nvme-subsystem-get-iter',
                    'attribute': 'nvme-subsystem-info',
                    'key_fields': 'subsystem',
                    'query': {'max-records': self.max_records},
                },
                'min_version': '140',
            },
            'nvme_namespace_info': {
                'method': self.get_generic_get_iter,
                'kwargs': {
                    'call': 'nvme-namespace-get-iter',
                    'attribute': 'nvme-namespace-info',
                    'key_fields': 'path',
                    'query': {'max-records': self.max_records},
                },
                'min_version': '140',
            },

            # Alpha Order

            'aggr_efficiency_info': {
                'method': self.get_generic_get_iter,
                'kwargs': {
                    'call': 'aggr-efficiency-get-iter',
                    'attribute': 'aggr-efficiency-info',
                    'key_fields': ('node', 'aggregate'),
                    'query': {'max-records': self.max_records},
                },
                'min_version': '140',
            },
            'cifs_options_info': {
                'method': self.get_generic_get_iter,
                'kwargs': {
                    'call': 'cifs-options-get-iter',
                    'attribute': 'cifs-options',
                    'key_fields': ('vserver'),
                    'query': {'max-records': self.max_records},
                },
                'min_version': '0',
            },
            'cifs_server_info': {
                'method': self.get_generic_get_iter,
                'kwargs': {
                    'call': 'cifs-server-get-iter',
                    'attribute': 'cifs-server-config',
                    # preferred key is <vserver>:<domain>:<cifs-server>
                    # alternate key is <vserver>:<domain-workgroup>:<cifs-server>
                    'key_fields': ('vserver', ('domain', 'domain-workgroup'), 'cifs-server'),
                    'query': {'max-records': self.max_records},
                },
                'min_version': '0',
            },
            'cifs_share_info': {
                'method': self.get_generic_get_iter,
                'kwargs': {
                    'call': 'cifs-share-get-iter',
                    'attribute': 'cifs-share',
                    'key_fields': ('share-name', 'path', 'cifs-server'),
                    'query': {'max-records': self.max_records},
                },
                'min_version': '0',
            },
            'cifs_vserver_security_info': {
                'method': self.get_generic_get_iter,
                'kwargs': {
                    'call': 'cifs-security-get-iter',
                    'attribute': 'cifs-security',
                    'key_fields': ('vserver'),
                    'query': {'max-records': self.max_records},
                },
                'min_version': '0',
            },
            'cluster_peer_info': {
                'method': self.get_generic_get_iter,
                'kwargs': {
                    'call': 'cluster-peer-get-iter',
                    'attribute': 'cluster-peer-info',
                    'key_fields': ('cluster-name', 'remote-cluster-name'),
                    'query': {'max-records': self.max_records},
                },
                'min_version': '0',
            },
            'cluster_switch_info': {
                'method': self.get_generic_get_iter,
                'kwargs': {
                    'call': 'cluster-switch-get-iter',
                    'attribute': 'cluster-switch-info',
                    'key_fields': ('device', 'model', 'serial-number'),
                    'query': {'max-records': self.max_records},
                },
                'min_version': '160',
            },
            'disk_info': {
                'method': self.get_generic_get_iter,
                'kwargs': {
                    'call': 'storage-disk-get-iter',
                    'attribute': 'storage-disk-info',
                    'key_fields': ('disk-name'),
                    'query': {'max-records': self.max_records},
                },
                'min_version': '0',
            },
            'env_sensors_info': {
                'method': self.get_generic_get_iter,
                'kwargs': {
                    'call': 'environment-sensors-get-iter',
                    'attribute': 'environment-sensors-info',
                    'key_fields': ('node-name', 'sensor-name'),
                    'query': {'max-records': self.max_records},
                    'fail_on_error': False,
                },
                'min_version': '0',
            },
            'event_notification_destination_info': {
                'method': self.get_generic_get_iter,
                'kwargs': {
                    'call': 'ems-event-notification-destination-get-iter',
                    'attribute': 'event-notification-destination-info',
                    'key_fields': ('name', 'type'),
                    'query': {'max-records': self.max_records},
                },
                'min_version': '0',
            },
            'event_notification_info': {
                'method': self.get_generic_get_iter,
                'kwargs': {
                    'call': 'ems-event-notification-get-iter',
                    'attribute': 'event-notification',
                    'key_fields': ('id'),
                    'query': {'max-records': self.max_records},
                },
                'min_version': '0',
            },
            'export_policy_info': {
                'method': self.get_generic_get_iter,
                'kwargs': {
                    'call': 'export-policy-get-iter',
                    'attribute': 'export-policy-info',
                    'key_fields': ('vserver', 'policy-name'),
                    'query': {'max-records': self.max_records},
                },
                'min_version': '0',
            },
            'export_rule_info': {
                'method': self.get_generic_get_iter,
                'kwargs': {
                    'call': 'export-rule-get-iter',
                    'attribute': 'export-rule-info',
                    'key_fields': ('vserver-name', 'policy-name', 'rule-index'),
                    'query': {'max-records': self.max_records},
                },
                'min_version': '0',
            },
            'fcp_adapter_info': {
                'method': self.get_generic_get_iter,
                'kwargs': {
                    'call': 'ucm-adapter-get-iter',
                    'attribute': 'uc-adapter-info',
                    'key_fields': ('adapter-name', 'node-name'),
                    'query': {'max-records': self.max_records},
                },
                'min_version': '0',
            },
            'fcp_alias_info': {
                'method': self.get_generic_get_iter,
                'kwargs': {
                    'call': 'fcp-wwpnalias-get-iter',
                    'attribute': 'aliases-info',
                    'key_fields': ('aliases-alias', 'vserver'),
                    'query': {'max-records': self.max_records},
                },
                'min_version': '0',
            },
            'fcp_service_info': {
                'method': self.get_generic_get_iter,
                'kwargs': {
                    'call': 'fcp-service-get-iter',
                    'attribute': 'fcp-service-info',
                    'key_fields': ('vserver'),
                    'query': {'max-records': self.max_records},
                },
                'min_version': '0',
            },
            'job_schedule_cron_info': {
                'method': self.get_generic_get_iter,
                'kwargs': {
                    'call': 'job-schedule-cron-get-iter',
                    'attribute': 'job-schedule-cron-info',
                    'key_fields': ('job-schedule-name', 'job-schedule-cluster'),
                    'query': {'max-records': self.max_records},
                },
                'min_version': '0',
            },
            'kerberos_realm_info': {
                'method': self.get_generic_get_iter,
                'kwargs': {
                    'call': 'kerberos-realm-get-iter',
                    'attribute': 'kerberos-realm',
                    'key_fields': ('vserver-name', 'realm'),
                    'query': {'max-records': self.max_records},
                },
                'min_version': '0',
            },
            'ldap_client': {
                'method': self.get_generic_get_iter,
                'kwargs': {
                    'call': 'ldap-client-get-iter',
                    'attribute': 'ldap-client',
                    'key_fields': ('vserver'),
                    'query': {'max-records': self.max_records},
                },
                'min_version': '0',
            },
            'ldap_config': {
                'method': self.get_generic_get_iter,
                'kwargs': {
                    'call': 'ldap-config-get-iter',
                    'attribute': 'ldap-config',
                    'key_fields': ('vserver'),
                    'query': {'max-records': self.max_records},
                },
                'min_version': '0',
            },
            'lun_map_info': {
                'method': self.get_generic_get_iter,
                'kwargs': {
                    'call': 'lun-map-get-iter',
                    'attribute': 'lun-map-info',
                    'key_fields': ('initiator-group', 'lun-id', 'node', 'path', 'vserver'),
                    'query': {'max-records': self.max_records},
                },
                'min_version': '0',
            },
            'net_dev_discovery_info': {
                'method': self.get_generic_get_iter,
                'kwargs': {
                    'call': 'net-device-discovery-get-iter',
                    'attribute': 'net-device-discovery-info',
                    'key_fields': ('port'),
                    'query': {'max-records': self.max_records},
                },
                'min_version': '0',
            },
            'net_failover_group_info': {
                'method': self.get_generic_get_iter,
                'kwargs': {
                    'call': 'net-failover-group-get-iter',
                    'attribute': 'net-failover-group-info',
                    'key_fields': ('vserver', 'failover-group'),
                    'query': {'max-records': self.max_records},
                },
                'min_version': '0',
            },
            'net_firewall_info': {
                'method': self.get_generic_get_iter,
                'kwargs': {
                    'call': 'net-firewall-policy-get-iter',
                    'attribute': 'net-firewall-policy-info',
                    'key_fields': ('policy', 'vserver', 'service'),
                    'query': {'max-records': self.max_records},
                },
                'min_version': '0',
            },
            'net_ipspaces_info': {
                'method': self.get_generic_get_iter,
                'kwargs': {
                    'call': 'net-ipspaces-get-iter',
                    'attribute': 'net-ipspaces-info',
                    'key_fields': ('ipspace'),
                    'query': {'max-records': self.max_records},
                },
                'min_version': '0',
            },
            'net_port_broadcast_domain_info': {
                'method': self.get_generic_get_iter,
                'kwargs': {
                    'call': 'net-port-broadcast-domain-get-iter',
                    'attribute': 'net-port-broadcast-domain-info',
                    'key_fields': ('broadcast-domain', 'ipspace'),
                    'query': {'max-records': self.max_records},
                },
                'min_version': '0',
            },
            'net_routes_info': {
                'method': self.get_generic_get_iter,
                'kwargs': {
                    'call': 'net-routes-get-iter',
                    'attribute': 'net-vs-routes-info',
                    'key_fields': ('vserver', 'destination', 'gateway'),
                    'query': {'max-records': self.max_records},
                },
                'min_version': '0',
            },
            'net_vlan_info': {
                'method': self.get_generic_get_iter,
                'kwargs': {
                    'call': 'net-vlan-get-iter',
                    'attribute': 'vlan-info',
                    'key_fields': ('interface-name', 'node'),
                    'query': {'max-records': self.max_records},
                },
                'min_version': '0',
            },
            'nfs_info': {
                'method': self.get_generic_get_iter,
                'kwargs': {
                    'call': 'nfs-service-get-iter',
                    'attribute': 'nfs-info',
                    'key_fields': ('vserver'),
                    'query': {'max-records': self.max_records},
                },
                'min_version': '0',
            },
            'ntfs_dacl_info': {
                'method': self.get_generic_get_iter,
                'kwargs': {
                    'call': 'file-directory-security-ntfs-dacl-get-iter',
                    'attribute': 'file-directory-security-ntfs-dacl',
                    'key_fields': ('vserver', 'ntfs-sd', 'account', 'access-type'),
                    'query': {'max-records': self.max_records},
                },
                'min_version': '0',
            },
            'ntfs_sd_info': {
                'method': self.get_generic_get_iter,
                'kwargs': {
                    'call': 'file-directory-security-ntfs-get-iter',
                    'attribute': 'file-directory-security-ntfs',
                    'key_fields': ('vserver', 'ntfs-sd'),
                    'query': {'max-records': self.max_records},
                },
                'min_version': '0',
            },
            'ntp_server_info': {
                'method': self.get_generic_get_iter,
                'kwargs': {
                    'call': 'ntp-server-get-iter',
                    'attribute': 'ntp-server-info',
                    'key_fields': ('server-name'),
                    'query': {'max-records': self.max_records},
                },
                'min_version': '0',
            },
            'role_info': {
                'method': self.get_generic_get_iter,
                'kwargs': {
                    'call': 'security-login-role-get-iter',
                    'attribute': 'security-login-role-info',
                    'key_fields': ('vserver', 'role-name', 'access-level', 'command-directory-name'),
                    'query': {'max-records': self.max_records},
                },
                'min_version': '0',
            },
            'service_processor_info': {
                'method': self.get_generic_get_iter,
                'kwargs': {
                    'call': 'service-processor-get-iter',
                    'attribute': 'service-processor-info',
                    'key_fields': ('node'),
                    'query': {'max-records': self.max_records},
                },
                'min_version': '0',
            },
            'service_processor_network_info': {
                'method': self.get_generic_get_iter,
                'kwargs': {
                    'call': 'service-processor-network-get-iter',
                    'attribute': 'service-processor-network-info',
                    # don't use key_fieldss, as we cannot build a key with optional key_fieldss
                    # without a key, we'll get a list of dictionaries
                    'query': {'max-records': self.max_records},
                },
                'min_version': '0',
            },
            'shelf_info': {
                'method': self.get_generic_get_iter,
                'kwargs': {
                    'call': 'storage-shelf-info-get-iter',
                    'attribute': 'storage-shelf-info',
                    'key_fields': ('shelf-id', 'serial-number'),
                    'query': {'max-records': self.max_records},
                },
                'min_version': '0',
            },
            'sis_info': {
                'method': self.get_generic_get_iter,
                'kwargs': {
                    'call': 'sis-get-iter',
                    'attribute': 'sis-status-info',
                    'key_fields': 'path',
                    'query': {'max-records': self.max_records},
                },
                'min_version': '0',
            },
            'sis_policy_info': {
                'method': self.get_generic_get_iter,
                'kwargs': {
                    'call': 'sis-policy-get-iter',
                    'attribute': 'sis-policy-info',
                    'key_fields': ('vserver', 'policy-name'),
                    'query': {'max-records': self.max_records},
                },
                'min_version': '0',
            },
            'snapmirror_info': {
                'method': self.get_generic_get_iter,
                'kwargs': {
                    'call': 'snapmirror-get-iter',
                    'attribute': 'snapmirror-info',
                    'key_fields': 'destination-location',
                    'query': {'max-records': self.max_records},
                },
                'min_version': '140',
            },
            'snapmirror_destination_info': {
                'method': self.get_generic_get_iter,
                'kwargs': {
                    'call': 'snapmirror-get-destination-iter',
                    'attribute': 'snapmirror-destination-info',
                    'key_fields': 'destination-location',
                    'query': {'max-records': self.max_records},
                },
                'min_version': '140',
            },
            'snapmirror_policy_info': {
                'method': self.get_generic_get_iter,
                'kwargs': {
                    'call': 'snapmirror-policy-get-iter',
                    'attribute': 'snapmirror-policy-info',
                    'key_fields': ('vserver-name', 'policy-name'),
                    'query': {'max-records': self.max_records},
                },
                'min_version': '0',
            },
            'snapshot_policy_info': {
                'method': self.get_generic_get_iter,
                'kwargs': {
                    'call': 'snapshot-policy-get-iter',
                    'attribute': 'snapshot-policy-info',
                    'key_fields': ('vserver-name', 'policy'),
                    'query': {'max-records': self.max_records},
                },
                'min_version': '0',
            },
            'subsys_health_info': {
                'method': self.get_generic_get_iter,
                'kwargs': {
                    'call': 'diagnosis-subsystem-config-get-iter',
                    'attribute': 'diagnosis-subsystem-config-info',
                    'key_fields': 'subsystem',
                    'query': {'max-records': self.max_records},
                },
                'min_version': '0',
            },
            'sys_cluster_alerts': {
                'method': self.get_generic_get_iter,
                'kwargs': {
                    'call': 'diagnosis-alert-get-iter',
                    'attribute': 'diagnosis-alert-info',
                    'key_fields': ('node', 'alerting-resource'),
                    'query': {'max-records': self.max_records},
                },
                'min_version': '0',
            },
            'sysconfig_info': {
                'method': self.get_generic_get_iter,
                'kwargs': {
                    'call': 'system-get-node-info-iter',
                    'attribute': 'system-info',
                    'key_fields': ('system-name'),
                    'query': {'max-records': self.max_records},
                },
                'min_version': '0',
            },
            'volume_move_target_aggr_info': {
                'method': self.get_generic_get_iter,
                'kwargs': {
                    'call': 'volume-move-target-aggr-get-iter',
                    'attribute': 'volume-move-target-aggr-info',
                    'query': {'max-records': self.max_records,
                              'volume-name': volume_move_target_aggr_info.get('volume_name', None),
                              'vserver': volume_move_target_aggr_info.get('vserver', None)},
                    'fail_on_error': False,
                },
                'min_version': '0',
            },
            'volume_space_info': {
                'method': self.get_generic_get_iter,
                'kwargs': {
                    'call': 'volume-space-get-iter',
                    'attribute': 'space-info',
                    'key_fields': ('vserver', 'volume'),
                    'query': {'max-records': self.max_records},
                },
                'min_version': '0',
            },
            'vscan_info': {
                'method': self.get_generic_get_iter,
                'kwargs': {
                    'call': 'vscan-status-get-iter',
                    'attribute': 'vscan-status-info',
                    'key_fields': ('vserver'),
                    'query': {'max-records': self.max_records},
                },
                'min_version': '0',
            },
            'vserver_peer_info': {
                'method': self.get_generic_get_iter,
                'kwargs': {
                    'call': 'vserver-peer-get-iter',
                    'attribute': 'vserver-peer-info',
                    'key_fields': ('vserver', 'remote-vserver-name'),
                    'query': {'max-records': self.max_records},
                },
                'min_version': '0',
            },
        }

        if HAS_NETAPP_LIB is False:
            self.module.fail_json(msg="the python NetApp-Lib module is required")
        else:
            # use vserver tunneling if vserver is present (not None)
            self.server = netapp_utils.setup_na_ontap_zapi(module=self.module, vserver=module.params['vserver'])

    def ontapi(self):
        '''Method to get ontapi version'''

        api = 'system-get-ontapi-version'
        api_call = netapp_utils.zapi.NaElement(api)
        try:
            results = self.server.invoke_successfully(api_call, enable_tunneling=True)
            ontapi_version = results.get_child_content('minor-version')
            return ontapi_version if ontapi_version is not None else '0'
        except netapp_utils.zapi.NaApiError as error:
            self.module.fail_json(msg="Error calling API %s: %s" %
                                  (api, to_native(error)), exception=traceback.format_exc())

    def call_api(self, call, attributes_list_tag='attributes-list', query=None, fail_on_error=True):
        '''Main method to run an API call'''

        api_call = netapp_utils.zapi.NaElement(call)
        initial_result = None
        result = None

        if query:
            for key, val in query.items():
                # Can val be nested?
                api_call.add_new_child(key, val)

        if self.desired_attributes is not None:
            api_call.translate_struct(self.desired_attributes)
        if self.query is not None:
            api_call.translate_struct(self.query)
        try:
            initial_result = self.server.invoke_successfully(api_call, enable_tunneling=True)
            next_tag = initial_result.get_child_by_name('next-tag')
            result = copy.copy(initial_result)

            while next_tag:
                next_tag_call = netapp_utils.zapi.NaElement(call)
                if query:
                    for key, val in query.items():
                        next_tag_call.add_new_child(key, val)

                next_tag_call.add_new_child("tag", next_tag.get_content(), True)
                next_result = self.server.invoke_successfully(next_tag_call, enable_tunneling=True)

                next_tag = next_result.get_child_by_name('next-tag')
                if attributes_list_tag is None:
                    self.module.fail_json(msg="Error calling API %s: %s" %
                                          (api_call.to_string(), "'next-tag' is not expected for this API"))

                result_attr = result.get_child_by_name(attributes_list_tag)
                new_records = next_result.get_child_by_name(attributes_list_tag)
                if new_records:
                    for record in new_records.get_children():
                        result_attr.add_child_elem(record)

            return result, None

        except netapp_utils.zapi.NaApiError as error:
            if call in ['security-key-manager-key-get-iter']:
                return result, None
            kind, error_message = netapp_utils.classify_zapi_exception(error)
            if kind == 'missing_vserver_api_error':
                # for missing_vserver_api_error, the API is already in error_message
                error_message = "Error invalid API.  %s" % error_message
            else:
                error_message = "Error calling API %s: %s" % (call, error_message)
            if self.error_flags[kind] and fail_on_error:
                self.module.fail_json(msg=error_message, exception=traceback.format_exc())
            return None, error_message

    def get_ifgrp_info(self):
        '''Method to get network port ifgroups info'''

        try:
            net_port_info = self.netapp_info['net_port_info']
        except KeyError:
            net_port_info_calls = self.info_subsets['net_port_info']
            net_port_info = net_port_info_calls['method'](**net_port_info_calls['kwargs'])
        interfaces = net_port_info.keys()

        ifgrps = []
        for ifn in interfaces:
            if net_port_info[ifn]['port_type'] == 'if_group':
                ifgrps.append(ifn)

        net_ifgrp_info = dict()
        for ifgrp in ifgrps:
            query = dict()
            query['node'], query['ifgrp-name'] = ifgrp.split(':')

            tmp = self.get_generic_get_iter('net-port-ifgrp-get', key_fields=('node', 'ifgrp-name'),
                                            attribute='net-ifgrp-info', query=query,
                                            attributes_list_tag='attributes')
            net_ifgrp_info = net_ifgrp_info.copy()
            net_ifgrp_info.update(tmp)
        return net_ifgrp_info

    def get_generic_get_iter(self, call, attribute=None, key_fields=None, query=None, attributes_list_tag='attributes-list', fail_on_error=True):
        '''Method to run a generic get-iter call'''

        generic_call, error = self.call_api(call, attributes_list_tag, query, fail_on_error=fail_on_error)

        if error is not None:
            return {'error': error}

        if generic_call is None:
            return None

        if attributes_list_tag is None:
            attributes_list = generic_call
        else:
            attributes_list = generic_call.get_child_by_name(attributes_list_tag)

        if attributes_list is None:
            return None

        if key_fields is None:
            out = []
        else:
            out = {}

        iteration = 0
        for child in attributes_list.get_children():
            iteration += 1
            dic = xmltodict.parse(child.to_string(), xml_attribs=False)

            if attribute is not None:
                dic = dic[attribute]

            info = json.loads(json.dumps(dic))
            if self.translate_keys:
                info = convert_keys(info)
            if isinstance(key_fields, str):
                try:
                    unique_key = _finditem(dic, key_fields)
                except KeyError as exc:
                    error_message = 'Error: key %s not found for %s, got: %s' % (str(exc), call, repr(info))
                    if self.error_flags['key_error']:
                        self.module.fail_json(msg=error_message, exception=traceback.format_exc())
                    unique_key = 'Error_%d_key_not_found_%s' % (iteration, exc.args[0])
            elif isinstance(key_fields, tuple):
                try:
                    unique_key = ':'.join([_finditem(dic, el) for el in key_fields])
                except KeyError as exc:
                    error_message = 'Error: key %s not found for %s, got: %s' % (str(exc), call, repr(info))
                    if self.error_flags['key_error']:
                        self.module.fail_json(msg=error_message, exception=traceback.format_exc())
                    unique_key = 'Error_%d_key_not_found_%s' % (iteration, exc.args[0])
            else:
                unique_key = None
            if unique_key is not None:
                out = out.copy()
                out.update({unique_key: info})
            else:
                out.append(info)

        if attributes_list_tag is None and key_fields is None:
            if len(out) == 1:
                # flatten the list as only 1 element is expected
                out = out[0]
            elif len(out) > 1:
                # aggregate a list of dictionaries into a single dict
                # make sure we only have dicts and no key duplication
                dic = dict()
                key_count = 0
                for item in out:
                    if not isinstance(item, dict):
                        # abort if we don't see a dict
                        key_count = -1
                        break
                    dic.update(item)
                    key_count += len(item)
                if key_count == len(dic):
                    # no duplicates!
                    out = dic

        return out

    def send_ems_event(self):
        ''' use vserver if available, or cluster vserver '''
        if self.module.params['vserver']:
            server = self.server
        else:
            results = netapp_utils.get_cserver(self.server)
            if results is None:
                # most likely we're on a vserver interface already
                server = self.server
            else:
                server = netapp_utils.setup_na_ontap_zapi(module=self.module, vserver=results)
        netapp_utils.ems_log_event("na_ontap_info", server)

    def get_all(self, gather_subset):
        '''Method to get all subsets'''

        self.send_ems_event()

        self.netapp_info['ontapi_version'] = self.ontapi()
        self.netapp_info['ontap_version'] = self.netapp_info['ontapi_version']

        run_subset = self.get_subset(gather_subset, self.netapp_info['ontapi_version'])
        if 'ontap_version' in gather_subset:
            if netapp_utils.has_feature(self.module, 'deprecation_warning'):
                self.netapp_info['deprecation_warning'] = 'ontap_version is deprecated, please use ontapi_version'
        if 'help' in gather_subset:
            self.netapp_info['help'] = sorted(run_subset)
        else:
            if self.desired_attributes is not None:
                if len(run_subset) > 1:
                    self.module.fail_json(msg="desired_attributes option is only supported with a single subset")
                self.sanitize_desired_attributes()
            if self.query is not None:
                if len(run_subset) > 1:
                    self.module.fail_json(msg="query option is only supported with a single subset")
                self.sanitize_query()
            for subset in run_subset:
                call = self.info_subsets[subset]
                self.netapp_info[subset] = call['method'](**call['kwargs'])

        if self.warnings:
            self.netapp_info['module_warnings'] = self.warnings

        return self.netapp_info

    def get_subset(self, gather_subset, version):
        '''Method to get a single subset'''

        runable_subsets = set()
        exclude_subsets = set()
        usable_subsets = [key for key in self.info_subsets if version >= self.info_subsets[key]['min_version']]
        if 'help' in gather_subset:
            return usable_subsets
        for subset in gather_subset:
            if subset == 'all':
                runable_subsets.update(usable_subsets)
                return runable_subsets
            if subset.startswith('!'):
                subset = subset[1:]
                if subset == 'all':
                    return set()
                exclude = True
            else:
                exclude = False

            if subset not in usable_subsets:
                if subset not in self.info_subsets.keys():
                    self.module.fail_json(msg='Bad subset: %s' % subset)
                self.module.fail_json(msg='Remote system at version %s does not support %s' %
                                      (version, subset))

            if exclude:
                exclude_subsets.add(subset)
            else:
                runable_subsets.add(subset)

        if not runable_subsets:
            runable_subsets.update(usable_subsets)

        runable_subsets.difference_update(exclude_subsets)

        return runable_subsets

    def get_summary(self, ontap_info):
        for info in ontap_info:
            if '_info' in info and ontap_info[info] is not None and isinstance(ontap_info[info], dict):
                # don't summarize errors
                if 'error' not in ontap_info[info]:
                    ontap_info[info] = ontap_info[info].keys()
        return ontap_info

    def sanitize_desired_attributes(self):
        ''' add top 'desired-attributes' if absent
            check for _ as more likely ZAPI does not take them
        '''
        da_key = 'desired-attributes'
        if da_key not in self.desired_attributes:
            desired_attributes = dict()
            desired_attributes[da_key] = self.desired_attributes
            self.desired_attributes = desired_attributes
        self.check_for___in_keys(self.desired_attributes)

    def sanitize_query(self):
        ''' add top 'query' if absent
            check for _ as more likely ZAPI does not take them
        '''
        key = 'query'
        if key not in self.query:
            query = dict()
            query[key] = self.query
            self.query = query
        self.check_for___in_keys(self.query)

    def check_for___in_keys(self, d_param):
        '''Method to warn on underscore in a ZAPI tag'''
        if isinstance(d_param, dict):
            for key, val in d_param.items():
                self.check_for___in_keys(val)
                if '_' in key:
                    self.warnings.append("Underscore in ZAPI tag: %s, do you mean '-'?" % key)
        elif isinstance(d_param, list):
            for val in d_param:
                self.check_for___in_keys(val)

    def set_error_flags(self):
        error_flags = self.module.params['continue_on_error']
        generic_flags = ('always', 'never')
        if len(error_flags) > 1:
            for key in generic_flags:
                if key in error_flags:
                    self.module.fail_json(msg="%s needs to be the only keyword in 'continue_on_error' option." % key)
        specific_flags = ('rpc_error', 'missing_vserver_api_error', 'key_error', 'other_error')
        for key in error_flags:
            if key not in generic_flags and key not in specific_flags:
                self.module.fail_json(msg="%s is not a valid keyword in 'continue_on_error' option." % key)
        self.error_flags = dict()
        for flag in specific_flags:
            self.error_flags[flag] = True
            for key in error_flags:
                if key == 'always' or key == flag:
                    self.error_flags[flag] = False


# https://stackoverflow.com/questions/14962485/finding-a-key-recursively-in-a-dictionary
def __finditem(obj, key):

    if key in obj:
        if obj[key] is None:
            return "None"
        return obj[key]
    for dummy, val in obj.items():
        if isinstance(val, dict):
            item = __finditem(val, key)
            if item is not None:
                return item
    return None


def _finditem(obj, keys):
    ''' if keys is a string, use it as a key
        if keys is a tuple, stop on the first valid key
        if no valid key is found, raise a KeyError '''

    value = None
    if isinstance(keys, str):
        value = __finditem(obj, keys)
    elif isinstance(keys, tuple):
        for key in keys:
            value = __finditem(obj, key)
            if value is not None:
                break
    if value is not None:
        return value
    raise KeyError(str(keys))


def convert_keys(d_param):
    '''Method to convert hyphen to underscore'''

    if isinstance(d_param, dict):
        out = {}
        for key, val in d_param.items():
            val = convert_keys(val)
            out[key.replace('-', '_')] = val
        return out
    elif isinstance(d_param, list):
        return [convert_keys(val) for val in d_param]
    return d_param


def main():
    '''Execute action'''

    argument_spec = netapp_utils.na_ontap_host_argument_spec()
    argument_spec.update(dict(
        state=dict(type='str', default='info', choices=['info']),
        gather_subset=dict(default=['all'], type='list', elements='str'),
        vserver=dict(type='str', required=False),
        max_records=dict(type='int', default=1024, required=False),
        summary=dict(type='bool', default=False, required=False),
        volume_move_target_aggr_info=dict(
            type="dict",
            required=False,
            options=dict(
                volume_name=dict(type='str', required=True),
                vserver=dict(type='str', required=True)
            )
        ),
        desired_attributes=dict(type='dict', required=False),
        use_native_zapi_tags=dict(type='bool', required=False, default=False),
        continue_on_error=dict(type='list', required=False, elements='str', default=['never']),
        query=dict(type='dict', required=False),
    ))

    module = AnsibleModule(
        argument_spec=argument_spec,
        supports_check_mode=True
    )

    if not HAS_XMLTODICT:
        module.fail_json(msg="xmltodict missing")

    if not HAS_JSON:
        module.fail_json(msg="json missing")

    state = module.params['state']
    gather_subset = module.params['gather_subset']
    summary = module.params['summary']
    if gather_subset is None:
        gather_subset = ['all']
    max_records = module.params['max_records']
    gf_obj = NetAppONTAPGatherInfo(module, max_records)
    gf_all = gf_obj.get_all(gather_subset)
    if summary:
        gf_all = gf_obj.get_summary(gf_all)
    result = {'state': state, 'changed': False}
    module.exit_json(ontap_info=gf_all, **result)


if __name__ == '__main__':
    main()
