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