1#!/usr/local/bin/python3.8
2
3# (c) 2018 Piotr Olczak <piotr.olczak@redhat.com>
4# (c) 2018-2019, NetApp, Inc
5# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
6
7'''
8na_ontap_info
9'''
10
11from __future__ import absolute_import, division, print_function
12__metaclass__ = type
13
14ANSIBLE_METADATA = {'metadata_version': '1.1',
15                    'status': ['preview'],
16                    'supported_by': 'certified'}
17
18DOCUMENTATION = '''
19module: na_ontap_info
20author: Piotr Olczak (@dprts) <polczak@redhat.com>
21extends_documentation_fragment:
22    - netapp.ontap.netapp.na_ontap
23short_description: NetApp information gatherer
24description:
25    - This module allows you to gather various information about ONTAP configuration
26version_added: 2.9.0
27requirements:
28    - netapp_lib
29options:
30    state:
31        type: str
32        description:
33            - deprecated as of 21.1.0.
34            - this option was ignored and continues to be ignored.
35    vserver:
36        type: str
37        description:
38            - If present, 'vserver tunneling' will limit the output to the vserver scope.
39            - Note that not all subsets are supported on a vserver, and 'all' will trigger an error.
40        version_added: '19.11.0'
41    gather_subset:
42        type: list
43        elements: str
44        description:
45            - When supplied, this argument will restrict the information collected to a given subset.  Possible values for this argument include
46            - "aggregate_info"
47            - "aggr_efficiency_info"
48            - "autosupport_check_info"
49            - "cifs_options_info"
50            - "cifs_server_info"
51            - "cifs_share_info"
52            - "cifs_vserver_security_info"
53            - "cluster_identity_info"
54            - "cluster_image_info"
55            - "cluster_log_forwarding_info"
56            - "cluster_node_info"
57            - "cluster_peer_info"
58            - "cluster_switch_info"
59            - "clock_info"
60            - "disk_info"
61            - "env_sensors_info"
62            - "event_notification_destination_info"
63            - "event_notification_info"
64            - "export_policy_info"
65            - "export_rule_info"
66            - "fcp_adapter_info"
67            - "fcp_alias_info"
68            - "fcp_service_info"
69            - "igroup_info"
70            - "iscsi_service_info"
71            - "job_schedule_cron_info"
72            - "kerberos_realm_info"
73            - "ldap_client"
74            - "ldap_config"
75            - "license_info"
76            - "lun_info"
77            - "lun_map_info"
78            - "metrocluster_check_info"
79            - "metrocluster_info"
80            - "metrocluster_node_info"
81            - "net_dev_discovery_info"
82            - "net_dns_info"
83            - "net_failover_group_info"
84            - "net_firewall_info"
85            - "net_ifgrp_info"
86            - "net_interface_info"
87            - "net_interface_service_policy_info"
88            - "net_ipspaces_info"
89            - "net_port_info"
90            - "net_port_broadcast_domain_info"
91            - "net_routes_info"
92            - "net_vlan_info"
93            - "nfs_info"
94            - "ntfs_dacl_info"
95            - "ntfs_sd_info"
96            - "ntp_server_info"
97            - "nvme_info"
98            - "nvme_interface_info"
99            - "nvme_namespace_info"
100            - "nvme_subsystem_info"
101            - "ontap_system_version"
102            - "ontap_version"
103            - "ontapi_version"
104            - "qos_adaptive_policy_info"
105            - "qos_policy_info"
106            - "qtree_info"
107            - "quota_report_info"
108            - "role_info"
109            - "security_key_manager_key_info"
110            - "security_login_account_info"
111            - "security_login_role_config_info"
112            - "security_login_role_info"
113            - "service_processor_info"
114            - "service_processor_network_info"
115            - "shelf_info"
116            - "sis_info"
117            - "sis_policy_info"
118            - "snapmirror_info"
119            - "snapmirror_destination_info"
120            - "snapmirror_policy_info"
121            - "snapshot_info"
122            - "snapshot_policy_info"
123            - "storage_failover_info"
124            - "storage_bridge_info"
125            - "subsys_health_info"
126            - "sysconfig_info"
127            - "sys_cluster_alerts"
128            - "volume_info"
129            - "volume_space_info"
130            - "vscan_info"
131            - "vscan_status_info"
132            - "vscan_scanner_pool_info"
133            - "vscan_connection_status_all_info"
134            - "vscan_connection_extended_stats_info"
135            - "vserver_info"
136            - "vserver_login_banner_info"
137            - "vserver_motd_info"
138            - "vserver_nfs_info"
139            - "vserver_peer_info"
140            - Can specify a list of values to include a larger subset.
141            - Values can also be used with an initial C(M(!)) to specify that a specific subset should not be collected.
142            - nvme is supported with ONTAP 9.4 onwards.
143            - use "help" to get a list of supported information for your system.
144        default: "all"
145    max_records:
146        type: int
147        description:
148            - Maximum number of records returned in a single ZAPI call. Valid range is [1..2^32-1].
149                This parameter controls internal behavior of this module.
150        default: 1024
151        version_added: '20.2.0'
152    summary:
153        description:
154            - Boolean flag to control return all attributes of the module info or only the names.
155            - If true, only names are returned.
156        default: false
157        type: bool
158        version_added: '20.4.0'
159    volume_move_target_aggr_info:
160        description:
161        - Required options for volume_move_target_aggr_info
162        type: dict
163        version_added: '20.5.0'
164        suboptions:
165            volume_name:
166                description:
167                - Volume name to get target aggr info for
168                required: true
169                type: str
170                version_added: '20.5.0'
171            vserver:
172                description:
173                - vserver the Volume lives on
174                required: true
175                type: str
176                version_added: '20.5.0'
177    desired_attributes:
178        description:
179        - Advanced feature requiring to understand ZAPI internals.
180        - Allows to request a specific attribute that is not returned by default, or to limit the returned attributes.
181        - A dictionary for the zapi desired-attributes element.
182        - An XML tag I(<tag>value</tag>) is a dictionary with tag as the key.
183        - Value can be another dictionary, a list of dictionaries, a string, or nothing.
184        - eg I(<tag/>) is represented as I(tag:)
185        - Only a single subset can be called at a time if this option is set.
186        - It is the caller responsibity to make sure key attributes are present in the right position.
187        - The module will error out if any key attribute is missing.
188        type: dict
189        version_added: '20.6.0'
190    query:
191        description:
192        - Advanced feature requiring to understand ZAPI internals.
193        - Allows to specify which objects to return.
194        - A dictionary for the zapi query element.
195        - An XML tag I(<tag>value</tag>) is a dictionary with tag as the key.
196        - Value can be another dictionary, a list of dictionaries, a string, or nothing.
197        - eg I(<tag/>) is represented as I(tag:)
198        - Only a single subset can be called at a time if this option is set.
199        type: dict
200        version_added: '20.7.0'
201    use_native_zapi_tags:
202        description:
203        - By default, I(-) in the returned dictionary keys are translated to I(_).
204        - If set to true, the translation is disabled.
205        type: bool
206        default: false
207        version_added: '20.6.0'
208    continue_on_error:
209        description:
210        - By default, this module fails on the first error.
211        - This option allows to provide a list of errors that are not failing the module.
212        - Errors in the list are reported in the output, under the related info element, as an "error" entry.
213        - Possible values are always, never, missing_vserver_api_error, rpc_error, other_error.
214        - missing_vserver_api_error - most likely the API is available at cluster level but not vserver level.
215        - rpc_error - some queries are failing because the node cannot reach another node in the cluster.
216        - key_error - a query is failing because the returned data does not contain an expected key.
217        - for key errors, make sure to report this in Slack.  It may be a change in a new ONTAP version.
218        - other_error - anything not in the above list.
219        - always will continue on any error, never will fail on any error, they cannot be used with any other keyword.
220        type: list
221        elements: str
222        default: never
223'''
224
225EXAMPLES = '''
226- name: Get NetApp info as Cluster Admin (Password Authentication)
227  na_ontap_info:
228    hostname: "na-vsim"
229    username: "admin"
230    password: "admins_password"
231  register: ontap_info
232- debug:
233    msg: "{{ ontap_info.ontap_info }}"
234
235- name: Get NetApp version as Vserver admin
236  na_ontap_info:
237    hostname: "na-vsim"
238    username: "vsadmin"
239    vserver: trident_svm
240    password: "vsadmins_password"
241
242- name: run ontap info module using vserver tunneling and ignoring errors
243  na_ontap_info:
244    hostname: "na-vsim"
245    username: "admin"
246    password: "admins_password"
247    vserver: trident_svm
248    summary: true
249    continue_on_error:
250      - missing_vserver_api_error
251      - rpc_error
252
253- name: Limit Info Gathering to Aggregate Information as Cluster Admin
254  na_ontap_info:
255    hostname: "na-vsim"
256    username: "admin"
257    password: "admins_password"
258    gather_subset: "aggregate_info"
259  register: ontap_info
260
261- name: Limit Info Gathering to Volume and Lun Information as Cluster Admin
262  na_ontap_info:
263    hostname: "na-vsim"
264    username: "admin"
265    password: "admins_password"
266    gather_subset:
267      - volume_info
268      - lun_info
269  register: ontap_info
270
271- name: Gather all info except for volume and lun information as Cluster Admin
272  na_ontap_info:
273    hostname: "na-vsim"
274    username: "admin"
275    password: "admins_password"
276    gather_subset:
277      - "!volume_info"
278      - "!lun_info"
279  register: ontap_info
280
281- name: Gather Volume move information for a specific volume
282  na_ontap_info:
283    hostname: "na-vsim"
284    username: "admin"
285    password: "admins_password"
286    gather_subset: volume_move_target_aggr_info
287    volume_move_target_aggr_info:
288      volume_name: carchitest
289      vserver: ansible
290
291- name: run ontap info module for aggregate module, requesting specific fields
292  na_ontap_info:
293    # <<: *login
294    gather_subset: aggregate_info
295    desired_attributes:
296      aggr-attributes:
297      aggr-inode-attributes:
298        files-private-used:
299      aggr-raid-attributes:
300        aggregate-type:
301    use_native_zapi_tags: true
302    register: ontap
303- debug: var=ontap
304
305- name: run ontap info to get offline volumes with dp in the name
306  na_ontap_info:
307    # <<: *cert_login
308    gather_subset: volume_info
309    query:
310      volume-attributes:
311        volume-id-attributes:
312          name: '*dp*'
313        volume-state-attributes:
314          state: offline
315    desired_attributes:
316      volume-attributes:
317        volume-id-attributes:
318          name:
319        volume-state-attributes:
320          state:
321  register: ontap
322- debug: var=ontap
323'''
324
325RETURN = '''
326ontap_info:
327    description: Returns various information about NetApp cluster configuration
328    returned: always
329    type: dict
330    sample: '{
331        "ontap_info": {
332            "aggregate_info": {...},
333            "autosupport_check_info": {...},
334            "cluster_identity_info": {...},
335            "cluster_image_info": {...},
336            "cluster_node_info": {...},
337            "igroup_info": {...},
338            "iscsi_service_info": {...},
339            "license_info": {...},
340            "lun_info": {...},
341            "metrocluster_check_info": {...},
342            "metrocluster_info": {...},
343            "metrocluster_node_info": {...},
344            "net_dns_info": {...},
345            "net_ifgrp_info": {...},
346            "net_interface_info": {...},
347            "net_interface_service_policy_info": {...},
348            "net_port_info": {...},
349            "ontap_system_version": {...},
350            "ontap_version": {...},
351            "ontapi_version": {...},
352            "qos_policy_info": {...},
353            "qos_adaptive_policy_info": {...},
354            "qtree_info": {...},
355            "quota_report_info": {...},
356            "security_key_manager_key_info": {...},
357            "security_login_account_info": {...},
358            "snapmirror_info": {...}
359            "snapmirror_destination_info": {...}
360            "storage_bridge_info": {...}
361            "storage_failover_info": {...},
362            "volume_info": {...},
363            "vserver_login_banner_info": {...},
364            "vserver_motd_info": {...},
365            "vserver_info": {...},
366            "vserver_nfs_info": {...},
367            "vscan_status_info": {...},
368            "vscan_scanner_pool_info": {...},
369            "vscan_connection_status_all_info": {...},
370            "vscan_connection_extended_stats_info": {...}
371    }'
372'''
373
374import copy
375import traceback
376from ansible.module_utils.basic import AnsibleModule
377from ansible.module_utils._text import to_native
378import ansible_collections.netapp.ontap.plugins.module_utils.netapp as netapp_utils
379
380IMPORT_ERRORS = list()
381try:
382    import xmltodict
383    HAS_XMLTODICT = True
384except ImportError as exc:
385    HAS_XMLTODICT = False
386    IMPORT_ERRORS.append(str(exc))
387
388try:
389    import json
390    HAS_JSON = True
391except ImportError as exc:
392    HAS_JSON = False
393    IMPORT_ERRORS.append(str(exc))
394
395HAS_NETAPP_LIB = netapp_utils.has_netapp_lib()
396
397
398class NetAppONTAPGatherInfo(object):
399    '''Class with gather info methods'''
400
401    def __init__(self, module, max_records):
402        self.module = module
403        self.max_records = str(max_records)
404        volume_move_target_aggr_info = module.params.get('volume_move_target_aggr_info', dict())
405        if volume_move_target_aggr_info is None:
406            volume_move_target_aggr_info = dict()
407        self.netapp_info = dict()
408        self.desired_attributes = module.params['desired_attributes']
409        self.query = module.params['query']
410        self.translate_keys = not module.params['use_native_zapi_tags']
411        self.warnings = list()  # warnings will be added to the info results, if any
412        self.set_error_flags()
413
414        # thanks to coreywan (https://github.com/ansible/ansible/pull/47016)
415        # for starting this
416        # min_version identifies the ontapi version which supports this ZAPI
417        # use 0 if it is supported since 9.1
418        self.info_subsets = {
419            'cluster_identity_info': {
420                'method': self.get_generic_get_iter,
421                'kwargs': {
422                    'call': 'cluster-identity-get',
423                    'attributes_list_tag': 'attributes',
424                    'attribute': 'cluster-identity-info',
425                    'key_fields': 'cluster-name',
426                },
427                'min_version': '0',
428            },
429            'cluster_image_info': {
430                'method': self.get_generic_get_iter,
431                'kwargs': {
432                    'call': 'cluster-image-get-iter',
433                    'attribute': 'cluster-image-info',
434                    'key_fields': 'node-id',
435                    'query': {'max-records': self.max_records},
436                },
437                'min_version': '0',
438            },
439            'cluster_log_forwarding_info': {
440                'method': self.get_generic_get_iter,
441                'kwargs': {
442                    'call': 'cluster-log-forward-get-iter',
443                    'attribute': 'cluster-log-forward-info',
444                    'key_fields': ('destination', 'port'),
445                    'query': {'max-records': self.max_records},
446                },
447                'min_version': '0',
448            },
449            'cluster_node_info': {
450                'method': self.get_generic_get_iter,
451                'kwargs': {
452                    'call': 'cluster-node-get-iter',
453                    'attribute': 'cluster-node-info',
454                    'key_fields': 'node-name',
455                    'query': {'max-records': self.max_records},
456                },
457                'min_version': '0',
458            },
459            'security_login_account_info': {
460                'method': self.get_generic_get_iter,
461                'kwargs': {
462                    'call': 'security-login-get-iter',
463                    'attribute': 'security-login-account-info',
464                    'key_fields': ('vserver', 'user-name', 'application', 'authentication-method'),
465                    'query': {'max-records': self.max_records},
466                },
467                'min_version': '0',
468            },
469            'security_login_role_config_info': {
470                'method': self.get_generic_get_iter,
471                'kwargs': {
472                    'call': 'security-login-role-config-get-iter',
473                    'attribute': 'security-login-role-config-info',
474                    'key_fields': ('vserver', 'role-name'),
475                    'query': {'max-records': self.max_records},
476                },
477                'min_version': '0',
478            },
479            'security_login_role_info': {
480                'method': self.get_generic_get_iter,
481                'kwargs': {
482                    'call': 'security-login-role-get-iter',
483                    'attribute': 'security-login-role-info',
484                    'key_fields': ('vserver', 'role-name', 'command-directory-name'),
485                    'query': {'max-records': self.max_records},
486                },
487                'min_version': '0',
488            },
489            'aggregate_info': {
490                'method': self.get_generic_get_iter,
491                'kwargs': {
492                    'call': 'aggr-get-iter',
493                    'attribute': 'aggr-attributes',
494                    'key_fields': 'aggregate-name',
495                    'query': {'max-records': self.max_records},
496                },
497                'min_version': '0',
498            },
499            'volume_info': {
500                'method': self.get_generic_get_iter,
501                'kwargs': {
502                    'call': 'volume-get-iter',
503                    'attribute': 'volume-attributes',
504                    'key_fields': ('name', 'owning-vserver-name'),
505                    'query': {'max-records': self.max_records},
506                },
507                'min_version': '0',
508            },
509            'license_info': {
510                'method': self.get_generic_get_iter,
511                'kwargs': {
512                    'call': 'license-v2-list-info',
513                    'attributes_list_tag': None,
514                    'attribute': 'licenses',
515                },
516                'min_version': '0',
517            },
518            'lun_info': {
519                'method': self.get_generic_get_iter,
520                'kwargs': {
521                    'call': 'lun-get-iter',
522                    'attribute': 'lun-info',
523                    'key_fields': ('vserver', 'path'),
524                    'query': {'max-records': self.max_records},
525                },
526                'min_version': '0',
527            },
528            'metrocluster_check_info': {
529                'method': self.get_generic_get_iter,
530                'kwargs': {
531                    'call': 'metrocluster-check-get-iter',
532                    'attribute': 'metrocluster-check-info',
533                    'fail_on_error': False,
534                },
535                'min_version': '0',
536            },
537            'metrocluster_info': {
538                'method': self.get_generic_get_iter,
539                'kwargs': {
540                    'call': 'metrocluster-get',
541                    'attribute': 'metrocluster-info',
542                    'attributes_list_tag': 'attributes',
543                },
544                'min_version': '0',
545            },
546            'metrocluster_node_info': {
547                'method': self.get_generic_get_iter,
548                'kwargs': {
549                    'call': 'metrocluster-node-get-iter',
550                    'attribute': 'metrocluster-node-info',
551                    'key_fields': ('cluster-name', 'node-name'),
552                },
553                'min_version': '0',
554            },
555            'net_dns_info': {
556                'method': self.get_generic_get_iter,
557                'kwargs': {
558                    'call': 'net-dns-get-iter',
559                    'attribute': 'net-dns-info',
560                    'key_fields': 'vserver-name',
561                    'query': {'max-records': self.max_records},
562                },
563                'min_version': '0',
564            },
565            'net_interface_info': {
566                'method': self.get_generic_get_iter,
567                'kwargs': {
568                    'call': 'net-interface-get-iter',
569                    'attribute': 'net-interface-info',
570                    'key_fields': 'interface-name',
571                    'query': {'max-records': self.max_records},
572                },
573                'min_version': '0',
574            },
575            'net_interface_service_policy_info': {
576                'method': self.get_generic_get_iter,
577                'kwargs': {
578                    'call': 'net-interface-service-policy-get-iter',
579                    'attribute': 'net-interface-service-policy-info',
580                    'key_fields': ('vserver', 'policy'),
581                    'query': {'max-records': self.max_records},
582                },
583                'min_version': '150',
584            },
585            'net_port_info': {
586                'method': self.get_generic_get_iter,
587                'kwargs': {
588                    'call': 'net-port-get-iter',
589                    'attribute': 'net-port-info',
590                    'key_fields': ('node', 'port'),
591                    'query': {'max-records': self.max_records},
592                },
593                'min_version': '0',
594            },
595            'security_key_manager_key_info': {
596                'method': self.get_generic_get_iter,
597                'kwargs': {
598                    'call': 'security-key-manager-key-get-iter',
599                    'attribute': 'security-key-manager-key-info',
600                    'key_fields': ('node', 'key-id'),
601                    'query': {'max-records': self.max_records},
602                },
603                'min_version': '0',
604            },
605            'storage_failover_info': {
606                'method': self.get_generic_get_iter,
607                'kwargs': {
608                    'call': 'cf-get-iter',
609                    'attribute': 'storage-failover-info',
610                    'key_fields': 'node',
611                    'query': {'max-records': self.max_records},
612                },
613                'min_version': '0',
614            },
615            'vserver_motd_info': {
616                'method': self.get_generic_get_iter,
617                'kwargs': {
618                    'call': 'vserver-motd-get-iter',
619                    'attribute': 'vserver-motd-info',
620                    'key_fields': 'vserver',
621                    'query': {'max-records': self.max_records},
622                },
623                'min_version': '0',
624            },
625            'vserver_login_banner_info': {
626                'method': self.get_generic_get_iter,
627                'kwargs': {
628                    'call': 'vserver-login-banner-get-iter',
629                    'attribute': 'vserver-login-banner-info',
630                    'key_fields': 'vserver',
631                    'query': {'max-records': self.max_records},
632                },
633                'min_version': '0',
634            },
635            'vserver_info': {
636                'method': self.get_generic_get_iter,
637                'kwargs': {
638                    'call': 'vserver-get-iter',
639                    'attribute': 'vserver-info',
640                    'key_fields': 'vserver-name',
641                    'query': {'max-records': self.max_records},
642                },
643                'min_version': '0',
644            },
645            'vserver_nfs_info': {
646                'method': self.get_generic_get_iter,
647                'kwargs': {
648                    'call': 'nfs-service-get-iter',
649                    'attribute': 'nfs-info',
650                    'key_fields': 'vserver',
651                    'query': {'max-records': self.max_records},
652                },
653                'min_version': '0',
654            },
655            'net_ifgrp_info': {
656                'method': self.get_ifgrp_info,
657                'kwargs': {},
658                'min_version': '0',
659            },
660            'ontap_system_version': {
661                'method': self.get_generic_get_iter,
662                'kwargs': {
663                    'call': 'system-get-version',
664                    'attributes_list_tag': None,
665                },
666                'min_version': '0',
667            },
668            'ontap_version': {
669                'method': self.ontapi,
670                'kwargs': {},
671                'min_version': '0',
672            },
673            'ontapi_version': {
674                'method': self.ontapi,
675                'kwargs': {},
676                'min_version': '0',
677            },
678            'clock_info': {
679                'method': self.get_generic_get_iter,
680                'kwargs': {
681                    'call': 'clock-get-clock',
682                    'attributes_list_tag': None,
683                },
684                'min_version': '0'
685            },
686            'system_node_info': {
687                'method': self.get_generic_get_iter,
688                'kwargs': {
689                    'call': 'system-node-get-iter',
690                    'attribute': 'node-details-info',
691                    'key_fields': 'node',
692                    'query': {'max-records': self.max_records},
693                },
694                'min_version': '0',
695            },
696            'igroup_info': {
697                'method': self.get_generic_get_iter,
698                'kwargs': {
699                    'call': 'igroup-get-iter',
700                    'attribute': 'initiator-group-info',
701                    'key_fields': ('vserver', 'initiator-group-name'),
702                    'query': {'max-records': self.max_records},
703                },
704                'min_version': '0',
705            },
706            'iscsi_service_info': {
707                'method': self.get_generic_get_iter,
708                'kwargs': {
709                    'call': 'iscsi-service-get-iter',
710                    'attribute': 'iscsi-service-info',
711                    'key_fields': 'vserver',
712                    'query': {'max-records': self.max_records},
713                },
714                'min_version': '0',
715            },
716            'qos_policy_info': {
717                'method': self.get_generic_get_iter,
718                'kwargs': {
719                    'call': 'qos-policy-group-get-iter',
720                    'attribute': 'qos-policy-group-info',
721                    'key_fields': 'policy-group',
722                    'query': {'max-records': self.max_records},
723                },
724                'min_version': '0',
725            },
726            'qtree_info': {
727                'method': self.get_generic_get_iter,
728                'kwargs': {
729                    'call': 'qtree-list-iter',
730                    'attribute': 'qtree-info',
731                    'key_fields': ('vserver', 'volume', 'id'),
732                    'query': {'max-records': self.max_records},
733                },
734                'min_version': '0',
735            },
736            'quota_report_info': {
737                'method': self.get_generic_get_iter,
738                'kwargs': {
739                    'call': 'quota-report-iter',
740                    'attribute': 'quota',
741                    'key_fields': ('vserver', 'volume', 'tree', 'quota-type', 'quota-target'),
742                    'query': {'max-records': self.max_records},
743                },
744                'min_version': '0',
745            },
746            'vscan_status_info': {
747                'method': self.get_generic_get_iter,
748                'kwargs': {
749                    'call': 'vscan-status-get-iter',
750                    'attribute': 'vscan-status-info',
751                    'key_fields': 'vserver',
752                    'query': {'max-records': self.max_records},
753                },
754                'min_version': '0',
755            },
756            'vscan_scanner_pool_info': {
757                'method': self.get_generic_get_iter,
758                'kwargs': {
759                    'call': 'vscan-scanner-pool-get-iter',
760                    'attribute': 'vscan-scanner-pool-info',
761                    'key_fields': 'vserver',
762                    'query': {'max-records': self.max_records},
763                },
764                'min_version': '0',
765            },
766            'vscan_connection_status_all_info': {
767                'method': self.get_generic_get_iter,
768                'kwargs': {
769                    'call': 'vscan-connection-status-all-get-iter',
770                    'attribute': 'vscan-connection-status-all-info',
771                    'key_fields': 'vserver',
772                    'query': {'max-records': self.max_records},
773                },
774                'min_version': '0',
775            },
776            'vscan_connection_extended_stats_info': {
777                'method': self.get_generic_get_iter,
778                'kwargs': {
779                    'call': 'vscan-connection-extended-stats-get-iter',
780                    'attribute': 'vscan-connection-extended-stats-info',
781                    'key_fields': 'vserver',
782                    'query': {'max-records': self.max_records},
783                },
784                'min_version': '0',
785            },
786            'snapshot_info': {
787                'method': self.get_generic_get_iter,
788                'kwargs': {
789                    'call': 'snapshot-get-iter',
790                    'attribute': 'snapshot-info',
791                    'key_fields': ('vserver', 'volume', 'name'),
792                    'query': {'max-records': self.max_records},
793                },
794                'min_version': '0',
795            },
796            'storage_bridge_info': {
797                'method': self.get_generic_get_iter,
798                'kwargs': {
799                    'call': 'storage-bridge-get-iter',
800                    'attribute': 'storage-bridge-info',
801                    'key_fields': 'name',
802                    'query': {'max-records': self.max_records},
803                },
804                'min_version': '0',
805            },
806            # supported in ONTAP 9.3 and onwards
807            'qos_adaptive_policy_info': {
808                'method': self.get_generic_get_iter,
809                'kwargs': {
810                    'call': 'qos-adaptive-policy-group-get-iter',
811                    'attribute': 'qos-adaptive-policy-group-info',
812                    'key_fields': 'policy-group',
813                    'query': {'max-records': self.max_records},
814                },
815                'min_version': '130',
816            },
817            # supported in ONTAP 9.4 and onwards
818            'nvme_info': {
819                'method': self.get_generic_get_iter,
820                'kwargs': {
821                    'call': 'nvme-get-iter',
822                    'attribute': 'nvme-target-service-info',
823                    'key_fields': 'vserver',
824                    'query': {'max-records': self.max_records},
825                },
826                'min_version': '140',
827            },
828            'nvme_interface_info': {
829                'method': self.get_generic_get_iter,
830                'kwargs': {
831                    'call': 'nvme-interface-get-iter',
832                    'attribute': 'nvme-interface-info',
833                    'key_fields': 'vserver',
834                    'query': {'max-records': self.max_records},
835                },
836                'min_version': '140',
837            },
838            'nvme_subsystem_info': {
839                'method': self.get_generic_get_iter,
840                'kwargs': {
841                    'call': 'nvme-subsystem-get-iter',
842                    'attribute': 'nvme-subsystem-info',
843                    'key_fields': 'subsystem',
844                    'query': {'max-records': self.max_records},
845                },
846                'min_version': '140',
847            },
848            'nvme_namespace_info': {
849                'method': self.get_generic_get_iter,
850                'kwargs': {
851                    'call': 'nvme-namespace-get-iter',
852                    'attribute': 'nvme-namespace-info',
853                    'key_fields': 'path',
854                    'query': {'max-records': self.max_records},
855                },
856                'min_version': '140',
857            },
858
859            # Alpha Order
860
861            'aggr_efficiency_info': {
862                'method': self.get_generic_get_iter,
863                'kwargs': {
864                    'call': 'aggr-efficiency-get-iter',
865                    'attribute': 'aggr-efficiency-info',
866                    'key_fields': ('node', 'aggregate'),
867                    'query': {'max-records': self.max_records},
868                },
869                'min_version': '140',
870            },
871            'autosupport_check_info': {
872                'method': self.get_generic_get_iter,
873                'kwargs': {
874                    'call': 'autosupport-check-iter',
875                    'attribute': 'autosupport-check-info',
876                    'key_fields': ('node-name', 'check-type', 'error-detail'),
877                    'query': {'max-records': self.max_records},
878                },
879                'min_version': '0',
880            },
881            'cifs_options_info': {
882                'method': self.get_generic_get_iter,
883                'kwargs': {
884                    'call': 'cifs-options-get-iter',
885                    'attribute': 'cifs-options',
886                    'key_fields': ('vserver'),
887                    'query': {'max-records': self.max_records},
888                },
889                'min_version': '0',
890            },
891            'cifs_server_info': {
892                'method': self.get_generic_get_iter,
893                'kwargs': {
894                    'call': 'cifs-server-get-iter',
895                    'attribute': 'cifs-server-config',
896                    # preferred key is <vserver>:<domain>:<cifs-server>
897                    # alternate key is <vserver>:<domain-workgroup>:<cifs-server>
898                    'key_fields': ('vserver', ('domain', 'domain-workgroup'), 'cifs-server'),
899                    'query': {'max-records': self.max_records},
900                },
901                'min_version': '0',
902            },
903            'cifs_share_info': {
904                'method': self.get_generic_get_iter,
905                'kwargs': {
906                    'call': 'cifs-share-get-iter',
907                    'attribute': 'cifs-share',
908                    'key_fields': ('share-name', 'path', 'cifs-server'),
909                    'query': {'max-records': self.max_records},
910                },
911                'min_version': '0',
912            },
913            'cifs_vserver_security_info': {
914                'method': self.get_generic_get_iter,
915                'kwargs': {
916                    'call': 'cifs-security-get-iter',
917                    'attribute': 'cifs-security',
918                    'key_fields': ('vserver'),
919                    'query': {'max-records': self.max_records},
920                },
921                'min_version': '0',
922            },
923            'cluster_peer_info': {
924                'method': self.get_generic_get_iter,
925                'kwargs': {
926                    'call': 'cluster-peer-get-iter',
927                    'attribute': 'cluster-peer-info',
928                    'key_fields': ('cluster-name', 'remote-cluster-name'),
929                    'query': {'max-records': self.max_records},
930                },
931                'min_version': '0',
932            },
933            'cluster_switch_info': {
934                'method': self.get_generic_get_iter,
935                'kwargs': {
936                    'call': 'cluster-switch-get-iter',
937                    'attribute': 'cluster-switch-info',
938                    'key_fields': ('device', 'model', 'serial-number'),
939                    'query': {'max-records': self.max_records},
940                },
941                'min_version': '160',
942            },
943            'disk_info': {
944                'method': self.get_generic_get_iter,
945                'kwargs': {
946                    'call': 'storage-disk-get-iter',
947                    'attribute': 'storage-disk-info',
948                    'key_fields': ('disk-name'),
949                    'query': {'max-records': self.max_records},
950                },
951                'min_version': '0',
952            },
953            'env_sensors_info': {
954                'method': self.get_generic_get_iter,
955                'kwargs': {
956                    'call': 'environment-sensors-get-iter',
957                    'attribute': 'environment-sensors-info',
958                    'key_fields': ('node-name', 'sensor-name'),
959                    'query': {'max-records': self.max_records},
960                    'fail_on_error': False,
961                },
962                'min_version': '0',
963            },
964            'event_notification_destination_info': {
965                'method': self.get_generic_get_iter,
966                'kwargs': {
967                    'call': 'ems-event-notification-destination-get-iter',
968                    'attribute': 'event-notification-destination-info',
969                    'key_fields': ('name', 'type'),
970                    'query': {'max-records': self.max_records},
971                },
972                'min_version': '0',
973            },
974            'event_notification_info': {
975                'method': self.get_generic_get_iter,
976                'kwargs': {
977                    'call': 'ems-event-notification-get-iter',
978                    'attribute': 'event-notification',
979                    'key_fields': ('id'),
980                    'query': {'max-records': self.max_records},
981                },
982                'min_version': '0',
983            },
984            'export_policy_info': {
985                'method': self.get_generic_get_iter,
986                'kwargs': {
987                    'call': 'export-policy-get-iter',
988                    'attribute': 'export-policy-info',
989                    'key_fields': ('vserver', 'policy-name'),
990                    'query': {'max-records': self.max_records},
991                },
992                'min_version': '0',
993            },
994            'export_rule_info': {
995                'method': self.get_generic_get_iter,
996                'kwargs': {
997                    'call': 'export-rule-get-iter',
998                    'attribute': 'export-rule-info',
999                    'key_fields': ('vserver-name', 'policy-name', 'rule-index'),
1000                    'query': {'max-records': self.max_records},
1001                },
1002                'min_version': '0',
1003            },
1004            'fcp_adapter_info': {
1005                'method': self.get_generic_get_iter,
1006                'kwargs': {
1007                    'call': 'ucm-adapter-get-iter',
1008                    'attribute': 'uc-adapter-info',
1009                    'key_fields': ('adapter-name', 'node-name'),
1010                    'query': {'max-records': self.max_records},
1011                },
1012                'min_version': '0',
1013            },
1014            'fcp_alias_info': {
1015                'method': self.get_generic_get_iter,
1016                'kwargs': {
1017                    'call': 'fcp-wwpnalias-get-iter',
1018                    'attribute': 'aliases-info',
1019                    'key_fields': ('aliases-alias', 'vserver'),
1020                    'query': {'max-records': self.max_records},
1021                },
1022                'min_version': '0',
1023            },
1024            'fcp_service_info': {
1025                'method': self.get_generic_get_iter,
1026                'kwargs': {
1027                    'call': 'fcp-service-get-iter',
1028                    'attribute': 'fcp-service-info',
1029                    'key_fields': ('vserver'),
1030                    'query': {'max-records': self.max_records},
1031                },
1032                'min_version': '0',
1033            },
1034            'job_schedule_cron_info': {
1035                'method': self.get_generic_get_iter,
1036                'kwargs': {
1037                    'call': 'job-schedule-cron-get-iter',
1038                    'attribute': 'job-schedule-cron-info',
1039                    'key_fields': ('job-schedule-name', 'job-schedule-cluster'),
1040                    'query': {'max-records': self.max_records},
1041                },
1042                'min_version': '0',
1043            },
1044            'kerberos_realm_info': {
1045                'method': self.get_generic_get_iter,
1046                'kwargs': {
1047                    'call': 'kerberos-realm-get-iter',
1048                    'attribute': 'kerberos-realm',
1049                    'key_fields': ('vserver-name', 'realm'),
1050                    'query': {'max-records': self.max_records},
1051                },
1052                'min_version': '0',
1053            },
1054            'ldap_client': {
1055                'method': self.get_generic_get_iter,
1056                'kwargs': {
1057                    'call': 'ldap-client-get-iter',
1058                    'attribute': 'ldap-client',
1059                    'key_fields': ('vserver'),
1060                    'query': {'max-records': self.max_records},
1061                },
1062                'min_version': '0',
1063            },
1064            'ldap_config': {
1065                'method': self.get_generic_get_iter,
1066                'kwargs': {
1067                    'call': 'ldap-config-get-iter',
1068                    'attribute': 'ldap-config',
1069                    'key_fields': ('vserver'),
1070                    'query': {'max-records': self.max_records},
1071                },
1072                'min_version': '0',
1073            },
1074            'lun_map_info': {
1075                'method': self.get_generic_get_iter,
1076                'kwargs': {
1077                    'call': 'lun-map-get-iter',
1078                    'attribute': 'lun-map-info',
1079                    'key_fields': ('initiator-group', 'lun-id', 'node', 'path', 'vserver'),
1080                    'query': {'max-records': self.max_records},
1081                },
1082                'min_version': '0',
1083            },
1084            'net_dev_discovery_info': {
1085                'method': self.get_generic_get_iter,
1086                'kwargs': {
1087                    'call': 'net-device-discovery-get-iter',
1088                    'attribute': 'net-device-discovery-info',
1089                    'key_fields': ('port'),
1090                    'query': {'max-records': self.max_records},
1091                },
1092                'min_version': '0',
1093            },
1094            'net_failover_group_info': {
1095                'method': self.get_generic_get_iter,
1096                'kwargs': {
1097                    'call': 'net-failover-group-get-iter',
1098                    'attribute': 'net-failover-group-info',
1099                    'key_fields': ('vserver', 'failover-group'),
1100                    'query': {'max-records': self.max_records},
1101                },
1102                'min_version': '0',
1103            },
1104            'net_firewall_info': {
1105                'method': self.get_generic_get_iter,
1106                'kwargs': {
1107                    'call': 'net-firewall-policy-get-iter',
1108                    'attribute': 'net-firewall-policy-info',
1109                    'key_fields': ('policy', 'vserver', 'service'),
1110                    'query': {'max-records': self.max_records},
1111                },
1112                'min_version': '0',
1113            },
1114            'net_ipspaces_info': {
1115                'method': self.get_generic_get_iter,
1116                'kwargs': {
1117                    'call': 'net-ipspaces-get-iter',
1118                    'attribute': 'net-ipspaces-info',
1119                    'key_fields': ('ipspace'),
1120                    'query': {'max-records': self.max_records},
1121                },
1122                'min_version': '0',
1123            },
1124            'net_port_broadcast_domain_info': {
1125                'method': self.get_generic_get_iter,
1126                'kwargs': {
1127                    'call': 'net-port-broadcast-domain-get-iter',
1128                    'attribute': 'net-port-broadcast-domain-info',
1129                    'key_fields': ('broadcast-domain', 'ipspace'),
1130                    'query': {'max-records': self.max_records},
1131                },
1132                'min_version': '0',
1133            },
1134            'net_routes_info': {
1135                'method': self.get_generic_get_iter,
1136                'kwargs': {
1137                    'call': 'net-routes-get-iter',
1138                    'attribute': 'net-vs-routes-info',
1139                    'key_fields': ('vserver', 'destination', 'gateway'),
1140                    'query': {'max-records': self.max_records},
1141                },
1142                'min_version': '0',
1143            },
1144            'net_vlan_info': {
1145                'method': self.get_generic_get_iter,
1146                'kwargs': {
1147                    'call': 'net-vlan-get-iter',
1148                    'attribute': 'vlan-info',
1149                    'key_fields': ('interface-name', 'node'),
1150                    'query': {'max-records': self.max_records},
1151                },
1152                'min_version': '0',
1153            },
1154            'nfs_info': {
1155                'method': self.get_generic_get_iter,
1156                'kwargs': {
1157                    'call': 'nfs-service-get-iter',
1158                    'attribute': 'nfs-info',
1159                    'key_fields': ('vserver'),
1160                    'query': {'max-records': self.max_records},
1161                },
1162                'min_version': '0',
1163            },
1164            'ntfs_dacl_info': {
1165                'method': self.get_generic_get_iter,
1166                'kwargs': {
1167                    'call': 'file-directory-security-ntfs-dacl-get-iter',
1168                    'attribute': 'file-directory-security-ntfs-dacl',
1169                    'key_fields': ('vserver', 'ntfs-sd', 'account', 'access-type'),
1170                    'query': {'max-records': self.max_records},
1171                },
1172                'min_version': '0',
1173            },
1174            'ntfs_sd_info': {
1175                'method': self.get_generic_get_iter,
1176                'kwargs': {
1177                    'call': 'file-directory-security-ntfs-get-iter',
1178                    'attribute': 'file-directory-security-ntfs',
1179                    'key_fields': ('vserver', 'ntfs-sd'),
1180                    'query': {'max-records': self.max_records},
1181                },
1182                'min_version': '0',
1183            },
1184            'ntp_server_info': {
1185                'method': self.get_generic_get_iter,
1186                'kwargs': {
1187                    'call': 'ntp-server-get-iter',
1188                    'attribute': 'ntp-server-info',
1189                    'key_fields': ('server-name'),
1190                    'query': {'max-records': self.max_records},
1191                },
1192                'min_version': '0',
1193            },
1194            'role_info': {
1195                'method': self.get_generic_get_iter,
1196                'kwargs': {
1197                    'call': 'security-login-role-get-iter',
1198                    'attribute': 'security-login-role-info',
1199                    'key_fields': ('vserver', 'role-name', 'access-level', 'command-directory-name'),
1200                    'query': {'max-records': self.max_records},
1201                },
1202                'min_version': '0',
1203            },
1204            'service_processor_info': {
1205                'method': self.get_generic_get_iter,
1206                'kwargs': {
1207                    'call': 'service-processor-get-iter',
1208                    'attribute': 'service-processor-info',
1209                    'key_fields': ('node'),
1210                    'query': {'max-records': self.max_records},
1211                },
1212                'min_version': '0',
1213            },
1214            'service_processor_network_info': {
1215                'method': self.get_generic_get_iter,
1216                'kwargs': {
1217                    'call': 'service-processor-network-get-iter',
1218                    'attribute': 'service-processor-network-info',
1219                    # don't use key_fieldss, as we cannot build a key with optional key_fieldss
1220                    # without a key, we'll get a list of dictionaries
1221                    'query': {'max-records': self.max_records},
1222                },
1223                'min_version': '0',
1224            },
1225            'shelf_info': {
1226                'method': self.get_generic_get_iter,
1227                'kwargs': {
1228                    'call': 'storage-shelf-info-get-iter',
1229                    'attribute': 'storage-shelf-info',
1230                    'key_fields': ('shelf-id', 'serial-number'),
1231                    'query': {'max-records': self.max_records},
1232                },
1233                'min_version': '0',
1234            },
1235            'sis_info': {
1236                'method': self.get_generic_get_iter,
1237                'kwargs': {
1238                    'call': 'sis-get-iter',
1239                    'attribute': 'sis-status-info',
1240                    'key_fields': 'path',
1241                    'query': {'max-records': self.max_records},
1242                },
1243                'min_version': '0',
1244            },
1245            'sis_policy_info': {
1246                'method': self.get_generic_get_iter,
1247                'kwargs': {
1248                    'call': 'sis-policy-get-iter',
1249                    'attribute': 'sis-policy-info',
1250                    'key_fields': ('vserver', 'policy-name'),
1251                    'query': {'max-records': self.max_records},
1252                },
1253                'min_version': '0',
1254            },
1255            'snapmirror_info': {
1256                'method': self.get_generic_get_iter,
1257                'kwargs': {
1258                    'call': 'snapmirror-get-iter',
1259                    'attribute': 'snapmirror-info',
1260                    'key_fields': 'destination-location',
1261                    'query': {'max-records': self.max_records},
1262                },
1263                'min_version': '140',
1264            },
1265            'snapmirror_destination_info': {
1266                'method': self.get_generic_get_iter,
1267                'kwargs': {
1268                    'call': 'snapmirror-get-destination-iter',
1269                    'attribute': 'snapmirror-destination-info',
1270                    'key_fields': 'destination-location',
1271                    'query': {'max-records': self.max_records},
1272                },
1273                'min_version': '140',
1274            },
1275            'snapmirror_policy_info': {
1276                'method': self.get_generic_get_iter,
1277                'kwargs': {
1278                    'call': 'snapmirror-policy-get-iter',
1279                    'attribute': 'snapmirror-policy-info',
1280                    'key_fields': ('vserver-name', 'policy-name'),
1281                    'query': {'max-records': self.max_records},
1282                },
1283                'min_version': '0',
1284            },
1285            'snapshot_policy_info': {
1286                'method': self.get_generic_get_iter,
1287                'kwargs': {
1288                    'call': 'snapshot-policy-get-iter',
1289                    'attribute': 'snapshot-policy-info',
1290                    'key_fields': ('vserver-name', 'policy'),
1291                    'query': {'max-records': self.max_records},
1292                },
1293                'min_version': '0',
1294            },
1295            'subsys_health_info': {
1296                'method': self.get_generic_get_iter,
1297                'kwargs': {
1298                    'call': 'diagnosis-subsystem-config-get-iter',
1299                    'attribute': 'diagnosis-subsystem-config-info',
1300                    'key_fields': 'subsystem',
1301                    'query': {'max-records': self.max_records},
1302                },
1303                'min_version': '0',
1304            },
1305            'sys_cluster_alerts': {
1306                'method': self.get_generic_get_iter,
1307                'kwargs': {
1308                    'call': 'diagnosis-alert-get-iter',
1309                    'attribute': 'diagnosis-alert-info',
1310                    'key_fields': ('node', 'alerting-resource'),
1311                    'query': {'max-records': self.max_records},
1312                },
1313                'min_version': '0',
1314            },
1315            'sysconfig_info': {
1316                'method': self.get_generic_get_iter,
1317                'kwargs': {
1318                    'call': 'system-get-node-info-iter',
1319                    'attribute': 'system-info',
1320                    'key_fields': ('system-name'),
1321                    'query': {'max-records': self.max_records},
1322                },
1323                'min_version': '0',
1324            },
1325            'volume_move_target_aggr_info': {
1326                'method': self.get_generic_get_iter,
1327                'kwargs': {
1328                    'call': 'volume-move-target-aggr-get-iter',
1329                    'attribute': 'volume-move-target-aggr-info',
1330                    'query': {'max-records': self.max_records,
1331                              'volume-name': volume_move_target_aggr_info.get('volume_name', None),
1332                              'vserver': volume_move_target_aggr_info.get('vserver', None)},
1333                    'fail_on_error': False,
1334                },
1335                'min_version': '0',
1336            },
1337            'volume_space_info': {
1338                'method': self.get_generic_get_iter,
1339                'kwargs': {
1340                    'call': 'volume-space-get-iter',
1341                    'attribute': 'space-info',
1342                    'key_fields': ('vserver', 'volume'),
1343                    'query': {'max-records': self.max_records},
1344                },
1345                'min_version': '0',
1346            },
1347            'vscan_info': {
1348                'method': self.get_generic_get_iter,
1349                'kwargs': {
1350                    'call': 'vscan-status-get-iter',
1351                    'attribute': 'vscan-status-info',
1352                    'key_fields': ('vserver'),
1353                    'query': {'max-records': self.max_records},
1354                },
1355                'min_version': '0',
1356            },
1357            'vserver_peer_info': {
1358                'method': self.get_generic_get_iter,
1359                'kwargs': {
1360                    'call': 'vserver-peer-get-iter',
1361                    'attribute': 'vserver-peer-info',
1362                    'key_fields': ('vserver', 'remote-vserver-name'),
1363                    'query': {'max-records': self.max_records},
1364                },
1365                'min_version': '0',
1366            },
1367        }
1368
1369        # use vserver tunneling if vserver is present (not None)
1370        self.server = netapp_utils.setup_na_ontap_zapi(module=self.module, vserver=module.params['vserver'])
1371
1372    def ontapi(self):
1373        '''Method to get ontapi version'''
1374
1375        api = 'system-get-ontapi-version'
1376        api_call = netapp_utils.zapi.NaElement(api)
1377        try:
1378            results = self.server.invoke_successfully(api_call, enable_tunneling=True)
1379            ontapi_version = results.get_child_content('minor-version')
1380            return ontapi_version if ontapi_version is not None else '0'
1381        except netapp_utils.zapi.NaApiError as error:
1382            self.module.fail_json(msg="Error calling API %s: %s" %
1383                                  (api, to_native(error)), exception=traceback.format_exc())
1384
1385    def call_api(self, call, attributes_list_tag='attributes-list', query=None, fail_on_error=True):
1386        '''Main method to run an API call'''
1387
1388        api_call = netapp_utils.zapi.NaElement(call)
1389        initial_result = None
1390        result = None
1391
1392        if query:
1393            for key, val in query.items():
1394                # Can val be nested?
1395                api_call.add_new_child(key, val)
1396
1397        if self.desired_attributes is not None:
1398            api_call.translate_struct(self.desired_attributes)
1399        if self.query is not None:
1400            api_call.translate_struct(self.query)
1401        try:
1402            initial_result = self.server.invoke_successfully(api_call, enable_tunneling=True)
1403            next_tag = initial_result.get_child_by_name('next-tag')
1404            result = copy.copy(initial_result)
1405
1406            while next_tag:
1407                next_tag_call = netapp_utils.zapi.NaElement(call)
1408                if query:
1409                    for key, val in query.items():
1410                        next_tag_call.add_new_child(key, val)
1411
1412                next_tag_call.add_new_child("tag", next_tag.get_content(), True)
1413                next_result = self.server.invoke_successfully(next_tag_call, enable_tunneling=True)
1414
1415                next_tag = next_result.get_child_by_name('next-tag')
1416                if attributes_list_tag is None:
1417                    self.module.fail_json(msg="Error calling API %s: %s" %
1418                                          (api_call.to_string(), "'next-tag' is not expected for this API"))
1419
1420                result_attr = result.get_child_by_name(attributes_list_tag)
1421                new_records = next_result.get_child_by_name(attributes_list_tag)
1422                if new_records:
1423                    for record in new_records.get_children():
1424                        result_attr.add_child_elem(record)
1425
1426            return result, None
1427
1428        except netapp_utils.zapi.NaApiError as error:
1429            if call in ['security-key-manager-key-get-iter']:
1430                return result, None
1431            kind, error_message = netapp_utils.classify_zapi_exception(error)
1432            if kind == 'missing_vserver_api_error':
1433                # for missing_vserver_api_error, the API is already in error_message
1434                error_message = "Error invalid API.  %s" % error_message
1435            else:
1436                error_message = "Error calling API %s: %s" % (call, error_message)
1437            if self.error_flags[kind] and fail_on_error:
1438                self.module.fail_json(msg=error_message, exception=traceback.format_exc())
1439            return None, error_message
1440
1441    def get_ifgrp_info(self):
1442        '''Method to get network port ifgroups info'''
1443
1444        try:
1445            net_port_info = self.netapp_info['net_port_info']
1446        except KeyError:
1447            net_port_info_calls = self.info_subsets['net_port_info']
1448            net_port_info = net_port_info_calls['method'](**net_port_info_calls['kwargs'])
1449        interfaces = net_port_info.keys()
1450
1451        ifgrps = []
1452        for ifn in interfaces:
1453            if net_port_info[ifn]['port_type'] == 'if_group':
1454                ifgrps.append(ifn)
1455
1456        net_ifgrp_info = dict()
1457        for ifgrp in ifgrps:
1458            query = dict()
1459            query['node'], query['ifgrp-name'] = ifgrp.split(':')
1460
1461            tmp = self.get_generic_get_iter('net-port-ifgrp-get', key_fields=('node', 'ifgrp-name'),
1462                                            attribute='net-ifgrp-info', query=query,
1463                                            attributes_list_tag='attributes')
1464            net_ifgrp_info = net_ifgrp_info.copy()
1465            net_ifgrp_info.update(tmp)
1466        return net_ifgrp_info
1467
1468    def get_generic_get_iter(self, call, attribute=None, key_fields=None, query=None, attributes_list_tag='attributes-list', fail_on_error=True):
1469        '''Method to run a generic get-iter call'''
1470
1471        generic_call, error = self.call_api(call, attributes_list_tag, query, fail_on_error=fail_on_error)
1472
1473        if error is not None:
1474            return {'error': error}
1475
1476        if generic_call is None:
1477            return None
1478
1479        if attributes_list_tag is None:
1480            attributes_list = generic_call
1481        else:
1482            attributes_list = generic_call.get_child_by_name(attributes_list_tag)
1483
1484        if attributes_list is None:
1485            return None
1486
1487        if key_fields is None:
1488            out = []
1489        else:
1490            out = {}
1491
1492        iteration = 0
1493        for child in attributes_list.get_children():
1494            iteration += 1
1495            dic = xmltodict.parse(child.to_string(), xml_attribs=False)
1496
1497            if attribute is not None:
1498                dic = dic[attribute]
1499
1500            info = json.loads(json.dumps(dic))
1501            if self.translate_keys:
1502                info = convert_keys(info)
1503            if isinstance(key_fields, str):
1504                try:
1505                    unique_key = _finditem(dic, key_fields)
1506                except KeyError as exc:
1507                    error_message = 'Error: key %s not found for %s, got: %s' % (str(exc), call, repr(info))
1508                    if self.error_flags['key_error']:
1509                        self.module.fail_json(msg=error_message, exception=traceback.format_exc())
1510                    unique_key = 'Error_%d_key_not_found_%s' % (iteration, exc.args[0])
1511            elif isinstance(key_fields, tuple):
1512                try:
1513                    unique_key = ':'.join([_finditem(dic, el) for el in key_fields])
1514                except KeyError as exc:
1515                    error_message = 'Error: key %s not found for %s, got: %s' % (str(exc), call, repr(info))
1516                    if self.error_flags['key_error']:
1517                        self.module.fail_json(msg=error_message, exception=traceback.format_exc())
1518                    unique_key = 'Error_%d_key_not_found_%s' % (iteration, exc.args[0])
1519            else:
1520                unique_key = None
1521            if unique_key is not None:
1522                out = out.copy()
1523                out.update({unique_key: info})
1524            else:
1525                out.append(info)
1526
1527        if attributes_list_tag is None and key_fields is None:
1528            if len(out) == 1:
1529                # flatten the list as only 1 element is expected
1530                out = out[0]
1531            elif len(out) > 1:
1532                # aggregate a list of dictionaries into a single dict
1533                # make sure we only have dicts and no key duplication
1534                dic = dict()
1535                key_count = 0
1536                for item in out:
1537                    if not isinstance(item, dict):
1538                        # abort if we don't see a dict
1539                        key_count = -1
1540                        break
1541                    dic.update(item)
1542                    key_count += len(item)
1543                if key_count == len(dic):
1544                    # no duplicates!
1545                    out = dic
1546
1547        return out
1548
1549    def send_ems_event(self):
1550        ''' use vserver if available, or cluster vserver '''
1551        if self.module.params['vserver']:
1552            server = self.server
1553        else:
1554            results = netapp_utils.get_cserver(self.server)
1555            if results is None:
1556                # most likely we're on a vserver interface already
1557                server = self.server
1558            else:
1559                server = netapp_utils.setup_na_ontap_zapi(module=self.module, vserver=results)
1560        netapp_utils.ems_log_event("na_ontap_info", server)
1561
1562    def get_all(self, gather_subset):
1563        '''Method to get all subsets'''
1564
1565        self.send_ems_event()
1566
1567        self.netapp_info['ontapi_version'] = self.ontapi()
1568        self.netapp_info['ontap_version'] = self.netapp_info['ontapi_version']
1569
1570        run_subset = self.get_subset(gather_subset, self.netapp_info['ontapi_version'])
1571        if 'ontap_version' in gather_subset:
1572            if netapp_utils.has_feature(self.module, 'deprecation_warning'):
1573                self.netapp_info['deprecation_warning'] = 'ontap_version is deprecated, please use ontapi_version'
1574        if 'help' in gather_subset:
1575            self.netapp_info['help'] = sorted(run_subset)
1576        else:
1577            if self.desired_attributes is not None:
1578                if len(run_subset) > 1:
1579                    self.module.fail_json(msg="desired_attributes option is only supported with a single subset")
1580                self.sanitize_desired_attributes()
1581            if self.query is not None:
1582                if len(run_subset) > 1:
1583                    self.module.fail_json(msg="query option is only supported with a single subset")
1584                self.sanitize_query()
1585            for subset in run_subset:
1586                call = self.info_subsets[subset]
1587                self.netapp_info[subset] = call['method'](**call['kwargs'])
1588
1589        if self.warnings:
1590            self.netapp_info['module_warnings'] = self.warnings
1591
1592        return self.netapp_info
1593
1594    def get_subset(self, gather_subset, version):
1595        '''Method to get a single subset'''
1596
1597        runable_subsets = set()
1598        exclude_subsets = set()
1599        usable_subsets = [key for key in self.info_subsets if version >= self.info_subsets[key]['min_version']]
1600        if 'help' in gather_subset:
1601            return usable_subsets
1602        for subset in gather_subset:
1603            if subset == 'all':
1604                runable_subsets.update(usable_subsets)
1605                return runable_subsets
1606            if subset.startswith('!'):
1607                subset = subset[1:]
1608                if subset == 'all':
1609                    return set()
1610                exclude = True
1611            else:
1612                exclude = False
1613
1614            if subset not in usable_subsets:
1615                if subset not in self.info_subsets.keys():
1616                    self.module.fail_json(msg='Bad subset: %s' % subset)
1617                self.module.fail_json(msg='Remote system at version %s does not support %s' %
1618                                      (version, subset))
1619
1620            if exclude:
1621                exclude_subsets.add(subset)
1622            else:
1623                runable_subsets.add(subset)
1624
1625        if not runable_subsets:
1626            runable_subsets.update(usable_subsets)
1627
1628        runable_subsets.difference_update(exclude_subsets)
1629
1630        return runable_subsets
1631
1632    def get_summary(self, ontap_info):
1633        for info in ontap_info:
1634            if '_info' in info and ontap_info[info] is not None and isinstance(ontap_info[info], dict):
1635                # don't summarize errors
1636                if 'error' not in ontap_info[info]:
1637                    ontap_info[info] = ontap_info[info].keys()
1638        return ontap_info
1639
1640    def sanitize_desired_attributes(self):
1641        ''' add top 'desired-attributes' if absent
1642            check for _ as more likely ZAPI does not take them
1643        '''
1644        da_key = 'desired-attributes'
1645        if da_key not in self.desired_attributes:
1646            desired_attributes = dict()
1647            desired_attributes[da_key] = self.desired_attributes
1648            self.desired_attributes = desired_attributes
1649        self.check_for___in_keys(self.desired_attributes)
1650
1651    def sanitize_query(self):
1652        ''' add top 'query' if absent
1653            check for _ as more likely ZAPI does not take them
1654        '''
1655        key = 'query'
1656        if key not in self.query:
1657            query = dict()
1658            query[key] = self.query
1659            self.query = query
1660        self.check_for___in_keys(self.query)
1661
1662    def check_for___in_keys(self, d_param):
1663        '''Method to warn on underscore in a ZAPI tag'''
1664        if isinstance(d_param, dict):
1665            for key, val in d_param.items():
1666                self.check_for___in_keys(val)
1667                if '_' in key:
1668                    self.warnings.append("Underscore in ZAPI tag: %s, do you mean '-'?" % key)
1669        elif isinstance(d_param, list):
1670            for val in d_param:
1671                self.check_for___in_keys(val)
1672
1673    def set_error_flags(self):
1674        error_flags = self.module.params['continue_on_error']
1675        generic_flags = ('always', 'never')
1676        if len(error_flags) > 1:
1677            for key in generic_flags:
1678                if key in error_flags:
1679                    self.module.fail_json(msg="%s needs to be the only keyword in 'continue_on_error' option." % key)
1680        specific_flags = ('rpc_error', 'missing_vserver_api_error', 'key_error', 'other_error')
1681        for key in error_flags:
1682            if key not in generic_flags and key not in specific_flags:
1683                self.module.fail_json(msg="%s is not a valid keyword in 'continue_on_error' option." % key)
1684        self.error_flags = dict()
1685        for flag in specific_flags:
1686            self.error_flags[flag] = True
1687            for key in error_flags:
1688                if key == 'always' or key == flag:
1689                    self.error_flags[flag] = False
1690
1691
1692# https://stackoverflow.com/questions/14962485/finding-a-key-recursively-in-a-dictionary
1693def __finditem(obj, key):
1694
1695    if key in obj:
1696        if obj[key] is None:
1697            return "None"
1698        return obj[key]
1699    for dummy, val in obj.items():
1700        if isinstance(val, dict):
1701            item = __finditem(val, key)
1702            if item is not None:
1703                return item
1704    return None
1705
1706
1707def _finditem(obj, keys):
1708    ''' if keys is a string, use it as a key
1709        if keys is a tuple, stop on the first valid key
1710        if no valid key is found, raise a KeyError '''
1711
1712    value = None
1713    if isinstance(keys, str):
1714        value = __finditem(obj, keys)
1715    elif isinstance(keys, tuple):
1716        for key in keys:
1717            value = __finditem(obj, key)
1718            if value is not None:
1719                break
1720    if value is not None:
1721        return value
1722    raise KeyError(str(keys))
1723
1724
1725def convert_keys(d_param):
1726    '''Method to convert hyphen to underscore'''
1727
1728    if isinstance(d_param, dict):
1729        out = {}
1730        for key, val in d_param.items():
1731            val = convert_keys(val)
1732            out[key.replace('-', '_')] = val
1733        return out
1734    elif isinstance(d_param, list):
1735        return [convert_keys(val) for val in d_param]
1736    return d_param
1737
1738
1739def main():
1740    '''Execute action'''
1741
1742    argument_spec = netapp_utils.na_ontap_host_argument_spec()
1743    argument_spec.update(dict(
1744        state=dict(type='str'),
1745        gather_subset=dict(default=['all'], type='list', elements='str'),
1746        vserver=dict(type='str', required=False),
1747        max_records=dict(type='int', default=1024, required=False),
1748        summary=dict(type='bool', default=False, required=False),
1749        volume_move_target_aggr_info=dict(
1750            type="dict",
1751            required=False,
1752            options=dict(
1753                volume_name=dict(type='str', required=True),
1754                vserver=dict(type='str', required=True)
1755            )
1756        ),
1757        desired_attributes=dict(type='dict', required=False),
1758        use_native_zapi_tags=dict(type='bool', required=False, default=False),
1759        continue_on_error=dict(type='list', required=False, elements='str', default=['never']),
1760        query=dict(type='dict', required=False),
1761    ))
1762
1763    module = AnsibleModule(
1764        argument_spec=argument_spec,
1765        supports_check_mode=True
1766    )
1767
1768    if not HAS_NETAPP_LIB:
1769        module.fail_json(msg=netapp_utils.netapp_lib_is_required())
1770    if not HAS_XMLTODICT:
1771        module.fail_json(msg="the python xmltodict module is required.  Import error: %s" % str(IMPORT_ERRORS))
1772    if not HAS_JSON:
1773        module.fail_json(msg="the python json module is required.  Import error: %s" % str(IMPORT_ERRORS))
1774
1775    gather_subset = module.params['gather_subset']
1776    summary = module.params['summary']
1777    if gather_subset is None:
1778        gather_subset = ['all']
1779    max_records = module.params['max_records']
1780    gf_obj = NetAppONTAPGatherInfo(module, max_records)
1781    gf_all = gf_obj.get_all(gather_subset)
1782    if summary:
1783        gf_all = gf_obj.get_summary(gf_all)
1784    results = {'changed': False}
1785    if module.params['state'] is not None:
1786        results['state'] = module.params['state']
1787        results['warnings'] = "option 'state' is deprecated."
1788    module.exit_json(ontap_info=gf_all, **results)
1789
1790
1791if __name__ == '__main__':
1792    main()
1793