summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAnsible Core Team <info@ansible.com>2020-03-09 09:40:33 +0000
committerAnsible Core Team <info@ansible.com>2020-03-09 09:40:33 +0000
commit741ff30da275a421dde72d80b53e02cca56ed7e3 (patch)
treeec179fa62278b1a160f633cc87d6c1d18dac3016
parentf1c4ac5056704bde3ca936d492d2a731ab38db52 (diff)
downloadansible-741ff30da275a421dde72d80b53e02cca56ed7e3.tar.gz
Migrated to netapp.ontap
-rw-r--r--lib/ansible/module_utils/netapp.py744
-rw-r--r--lib/ansible/module_utils/netapp_elementsw_module.py156
-rw-r--r--lib/ansible/module_utils/netapp_module.py270
-rw-r--r--lib/ansible/modules/storage/netapp/na_ontap_aggregate.py474
-rw-r--r--lib/ansible/modules/storage/netapp/na_ontap_autosupport.py275
-rw-r--r--lib/ansible/modules/storage/netapp/na_ontap_broadcast_domain.py436
-rw-r--r--lib/ansible/modules/storage/netapp/na_ontap_broadcast_domain_ports.py215
-rw-r--r--lib/ansible/modules/storage/netapp/na_ontap_cg_snapshot.py219
-rw-r--r--lib/ansible/modules/storage/netapp/na_ontap_cifs.py306
-rw-r--r--lib/ansible/modules/storage/netapp/na_ontap_cifs_acl.py238
-rw-r--r--lib/ansible/modules/storage/netapp/na_ontap_cifs_server.py329
-rw-r--r--lib/ansible/modules/storage/netapp/na_ontap_cluster.py287
-rw-r--r--lib/ansible/modules/storage/netapp/na_ontap_cluster_ha.py133
-rw-r--r--lib/ansible/modules/storage/netapp/na_ontap_cluster_peer.py295
-rw-r--r--lib/ansible/modules/storage/netapp/na_ontap_command.py228
-rw-r--r--lib/ansible/modules/storage/netapp/na_ontap_disks.py205
-rw-r--r--lib/ansible/modules/storage/netapp/na_ontap_dns.py294
-rw-r--r--lib/ansible/modules/storage/netapp/na_ontap_export_policy.py231
-rw-r--r--lib/ansible/modules/storage/netapp/na_ontap_export_policy_rule.py431
-rw-r--r--lib/ansible/modules/storage/netapp/na_ontap_fcp.py210
-rw-r--r--lib/ansible/modules/storage/netapp/na_ontap_firewall_policy.py351
-rw-r--r--lib/ansible/modules/storage/netapp/na_ontap_firmware_upgrade.py461
-rw-r--r--lib/ansible/modules/storage/netapp/na_ontap_flexcache.py474
-rw-r--r--lib/ansible/modules/storage/netapp/na_ontap_igroup.py346
-rw-r--r--lib/ansible/modules/storage/netapp/na_ontap_igroup_initiator.py183
-rw-r--r--lib/ansible/modules/storage/netapp/na_ontap_info.py619
-rw-r--r--lib/ansible/modules/storage/netapp/na_ontap_interface.py449
-rw-r--r--lib/ansible/modules/storage/netapp/na_ontap_ipspace.py258
-rw-r--r--lib/ansible/modules/storage/netapp/na_ontap_iscsi.py272
-rw-r--r--lib/ansible/modules/storage/netapp/na_ontap_job_schedule.py297
-rw-r--r--lib/ansible/modules/storage/netapp/na_ontap_kerberos_realm.py318
-rw-r--r--lib/ansible/modules/storage/netapp/na_ontap_ldap.py228
-rw-r--r--lib/ansible/modules/storage/netapp/na_ontap_ldap_client.py359
-rw-r--r--lib/ansible/modules/storage/netapp/na_ontap_license.py326
-rw-r--r--lib/ansible/modules/storage/netapp/na_ontap_lun.py406
-rw-r--r--lib/ansible/modules/storage/netapp/na_ontap_lun_copy.py182
-rw-r--r--lib/ansible/modules/storage/netapp/na_ontap_lun_map.py282
-rw-r--r--lib/ansible/modules/storage/netapp/na_ontap_motd.py209
-rw-r--r--lib/ansible/modules/storage/netapp/na_ontap_ndmp.py342
-rw-r--r--lib/ansible/modules/storage/netapp/na_ontap_net_ifgrp.py307
-rw-r--r--lib/ansible/modules/storage/netapp/na_ontap_net_port.py226
-rw-r--r--lib/ansible/modules/storage/netapp/na_ontap_net_routes.py324
-rw-r--r--lib/ansible/modules/storage/netapp/na_ontap_net_subnet.py326
-rw-r--r--lib/ansible/modules/storage/netapp/na_ontap_net_vlan.py186
-rw-r--r--lib/ansible/modules/storage/netapp/na_ontap_nfs.py576
-rw-r--r--lib/ansible/modules/storage/netapp/na_ontap_node.py144
-rw-r--r--lib/ansible/modules/storage/netapp/na_ontap_ntp.py226
-rw-r--r--lib/ansible/modules/storage/netapp/na_ontap_nvme.py209
-rw-r--r--lib/ansible/modules/storage/netapp/na_ontap_nvme_namespace.py195
-rw-r--r--lib/ansible/modules/storage/netapp/na_ontap_nvme_subsystem.py355
-rw-r--r--lib/ansible/modules/storage/netapp/na_ontap_object_store.py237
-rw-r--r--lib/ansible/modules/storage/netapp/na_ontap_ports.py380
-rw-r--r--lib/ansible/modules/storage/netapp/na_ontap_portset.py278
-rw-r--r--lib/ansible/modules/storage/netapp/na_ontap_qos_adaptive_policy_group.py335
-rw-r--r--lib/ansible/modules/storage/netapp/na_ontap_qos_policy_group.py290
-rw-r--r--lib/ansible/modules/storage/netapp/na_ontap_qtree.py303
-rw-r--r--lib/ansible/modules/storage/netapp/na_ontap_quotas.py345
-rw-r--r--lib/ansible/modules/storage/netapp/na_ontap_security_key_manager.py229
-rw-r--r--lib/ansible/modules/storage/netapp/na_ontap_service_processor_network.py284
-rw-r--r--lib/ansible/modules/storage/netapp/na_ontap_snapmirror.py716
-rw-r--r--lib/ansible/modules/storage/netapp/na_ontap_snapshot.py326
-rw-r--r--lib/ansible/modules/storage/netapp/na_ontap_snapshot_policy.py453
-rw-r--r--lib/ansible/modules/storage/netapp/na_ontap_snmp.py152
-rw-r--r--lib/ansible/modules/storage/netapp/na_ontap_software_update.py301
-rw-r--r--lib/ansible/modules/storage/netapp/na_ontap_svm.py444
-rw-r--r--lib/ansible/modules/storage/netapp/na_ontap_svm_options.py156
-rw-r--r--lib/ansible/modules/storage/netapp/na_ontap_ucadapter.py224
-rw-r--r--lib/ansible/modules/storage/netapp/na_ontap_unix_group.py348
-rw-r--r--lib/ansible/modules/storage/netapp/na_ontap_unix_user.py253
-rw-r--r--lib/ansible/modules/storage/netapp/na_ontap_user.py389
-rw-r--r--lib/ansible/modules/storage/netapp/na_ontap_user_role.py268
-rw-r--r--lib/ansible/modules/storage/netapp/na_ontap_volume.py1283
-rw-r--r--lib/ansible/modules/storage/netapp/na_ontap_volume_autosize.py361
-rw-r--r--lib/ansible/modules/storage/netapp/na_ontap_volume_clone.py228
-rw-r--r--lib/ansible/modules/storage/netapp/na_ontap_vscan.py178
-rw-r--r--lib/ansible/modules/storage/netapp/na_ontap_vscan_on_access_policy.py366
-rw-r--r--lib/ansible/modules/storage/netapp/na_ontap_vscan_on_demand_task.py313
-rw-r--r--lib/ansible/modules/storage/netapp/na_ontap_vscan_scanner_pool.py240
-rw-r--r--lib/ansible/modules/storage/netapp/na_ontap_vserver_cifs_security.py282
-rw-r--r--lib/ansible/modules/storage/netapp/na_ontap_vserver_peer.py276
-rw-r--r--lib/ansible/plugins/doc_fragments/netapp.py226
-rw-r--r--test/sanity/ignore.txt260
-rw-r--r--test/units/module_utils/test_netapp.py163
-rw-r--r--test/units/modules/storage/netapp/test_na_ontap_aggregate.py217
-rw-r--r--test/units/modules/storage/netapp/test_na_ontap_autosupport.py244
-rw-r--r--test/units/modules/storage/netapp/test_na_ontap_broadcast_domain.py308
-rw-r--r--test/units/modules/storage/netapp/test_na_ontap_cifs.py227
-rw-r--r--test/units/modules/storage/netapp/test_na_ontap_cifs_server.py221
-rw-r--r--test/units/modules/storage/netapp/test_na_ontap_cluster.py198
-rw-r--r--test/units/modules/storage/netapp/test_na_ontap_cluster_peer.py211
-rw-r--r--test/units/modules/storage/netapp/test_na_ontap_command.py169
-rw-r--r--test/units/modules/storage/netapp/test_na_ontap_dns.py276
-rw-r--r--test/units/modules/storage/netapp/test_na_ontap_export_policy_rule.py264
-rw-r--r--test/units/modules/storage/netapp/test_na_ontap_firewall_policy.py286
-rw-r--r--test/units/modules/storage/netapp/test_na_ontap_firmware_upgrade.py291
-rw-r--r--test/units/modules/storage/netapp/test_na_ontap_flexcache.py529
-rw-r--r--test/units/modules/storage/netapp/test_na_ontap_igroup.py259
-rw-r--r--test/units/modules/storage/netapp/test_na_ontap_igroup_initiator.py217
-rw-r--r--test/units/modules/storage/netapp/test_na_ontap_info.py313
-rw-r--r--test/units/modules/storage/netapp/test_na_ontap_interface.py272
-rw-r--r--test/units/modules/storage/netapp/test_na_ontap_ipspace.py138
-rw-r--r--test/units/modules/storage/netapp/test_na_ontap_job_schedule.py246
-rw-r--r--test/units/modules/storage/netapp/test_na_ontap_kerberos_realm.py239
-rw-r--r--test/units/modules/storage/netapp/test_na_ontap_lun_copy.py159
-rw-r--r--test/units/modules/storage/netapp/test_na_ontap_lun_map.py191
-rw-r--r--test/units/modules/storage/netapp/test_na_ontap_motd.py181
-rw-r--r--test/units/modules/storage/netapp/test_na_ontap_ndmp.py166
-rw-r--r--test/units/modules/storage/netapp/test_na_ontap_net_ifgrp.py298
-rw-r--r--test/units/modules/storage/netapp/test_na_ontap_net_port.py179
-rw-r--r--test/units/modules/storage/netapp/test_na_ontap_net_routes.py309
-rw-r--r--test/units/modules/storage/netapp/test_na_ontap_net_subnet.py264
-rw-r--r--test/units/modules/storage/netapp/test_na_ontap_nfs.py308
-rw-r--r--test/units/modules/storage/netapp/test_na_ontap_nvme.py216
-rw-r--r--test/units/modules/storage/netapp/test_na_ontap_nvme_namespace.py197
-rw-r--r--test/units/modules/storage/netapp/test_na_ontap_nvme_subsystem.py241
-rw-r--r--test/units/modules/storage/netapp/test_na_ontap_object_store.py200
-rw-r--r--test/units/modules/storage/netapp/test_na_ontap_ports.py173
-rw-r--r--test/units/modules/storage/netapp/test_na_ontap_portset.py189
-rw-r--r--test/units/modules/storage/netapp/test_na_ontap_qos_adaptive_policy_group.py347
-rw-r--r--test/units/modules/storage/netapp/test_na_ontap_qos_policy_group.py331
-rw-r--r--test/units/modules/storage/netapp/test_na_ontap_quotas.py238
-rw-r--r--test/units/modules/storage/netapp/test_na_ontap_security_key_manager.py173
-rw-r--r--test/units/modules/storage/netapp/test_na_ontap_service_processor_network.py232
-rw-r--r--test/units/modules/storage/netapp/test_na_ontap_snapmirror.py574
-rw-r--r--test/units/modules/storage/netapp/test_na_ontap_snapshot.py226
-rw-r--r--test/units/modules/storage/netapp/test_na_ontap_snapshot_policy.py654
-rw-r--r--test/units/modules/storage/netapp/test_na_ontap_software_update.py163
-rw-r--r--test/units/modules/storage/netapp/test_na_ontap_svm.py267
-rw-r--r--test/units/modules/storage/netapp/test_na_ontap_ucadapter.py159
-rw-r--r--test/units/modules/storage/netapp/test_na_ontap_unix_group.py288
-rw-r--r--test/units/modules/storage/netapp/test_na_ontap_unix_user.py282
-rw-r--r--test/units/modules/storage/netapp/test_na_ontap_user.py320
-rw-r--r--test/units/modules/storage/netapp/test_na_ontap_user_role.py238
-rw-r--r--test/units/modules/storage/netapp/test_na_ontap_volume.py970
-rw-r--r--test/units/modules/storage/netapp/test_na_ontap_volume_autosize.py242
-rw-r--r--test/units/modules/storage/netapp/test_na_ontap_volume_clone.py179
-rw-r--r--test/units/modules/storage/netapp/test_na_ontap_vscan.py231
-rw-r--r--test/units/modules/storage/netapp/test_na_ontap_vscan_on_access_policy.py156
-rw-r--r--test/units/modules/storage/netapp/test_na_ontap_vscan_on_demand_task.py167
-rw-r--r--test/units/modules/storage/netapp/test_na_ontap_vscan_scanner_pool.py179
-rw-r--r--test/units/modules/storage/netapp/test_na_ontap_vserver_cifs_security.py161
141 files changed, 0 insertions, 41472 deletions
diff --git a/lib/ansible/module_utils/netapp.py b/lib/ansible/module_utils/netapp.py
deleted file mode 100644
index 5c4d0abd9f..0000000000
--- a/lib/ansible/module_utils/netapp.py
+++ /dev/null
@@ -1,744 +0,0 @@
-# This code is part of Ansible, but is an independent component.
-# This particular file snippet, and this file snippet only, is BSD licensed.
-# Modules you write using this snippet, which is embedded dynamically by Ansible
-# still belong to the author of the module, and may assign their own license
-# to the complete work.
-#
-# Copyright (c) 2017, Sumit Kumar <sumit4@netapp.com>
-# Copyright (c) 2017, Michael Price <michael.price@netapp.com>
-# All rights reserved.
-#
-# Redistribution and use in source and binary forms, with or without modification,
-# are permitted provided that the following conditions are met:
-#
-# * Redistributions of source code must retain the above copyright
-# notice, this list of conditions and the following disclaimer.
-# * Redistributions in binary form must reproduce the above copyright notice,
-# this list of conditions and the following disclaimer in the documentation
-# and/or other materials provided with the distribution.
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
-# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
-# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
-# IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
-# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
-# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
-# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
-# USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-import json
-import os
-import random
-import mimetypes
-
-from pprint import pformat
-from ansible.module_utils import six
-from ansible.module_utils.basic import AnsibleModule, missing_required_lib
-from ansible.module_utils.six.moves.urllib.error import HTTPError, URLError
-from ansible.module_utils.urls import open_url
-from ansible.module_utils.api import basic_auth_argument_spec
-from ansible.module_utils._text import to_native
-
-try:
- from ansible.module_utils.ansible_release import __version__ as ansible_version
-except ImportError:
- ansible_version = 'unknown'
-
-try:
- from netapp_lib.api.zapi import zapi
- HAS_NETAPP_LIB = True
-except ImportError:
- HAS_NETAPP_LIB = False
-
-try:
- import requests
- HAS_REQUESTS = True
-except ImportError:
- HAS_REQUESTS = False
-
-import ssl
-try:
- from urlparse import urlparse, urlunparse
-except ImportError:
- from urllib.parse import urlparse, urlunparse
-
-
-HAS_SF_SDK = False
-SF_BYTE_MAP = dict(
- # Management GUI displays 1024 ** 3 as 1.1 GB, thus use 1000.
- bytes=1,
- b=1,
- kb=1000,
- mb=1000 ** 2,
- gb=1000 ** 3,
- tb=1000 ** 4,
- pb=1000 ** 5,
- eb=1000 ** 6,
- zb=1000 ** 7,
- yb=1000 ** 8
-)
-
-POW2_BYTE_MAP = dict(
- # Here, 1 kb = 1024
- bytes=1,
- b=1,
- kb=1024,
- mb=1024 ** 2,
- gb=1024 ** 3,
- tb=1024 ** 4,
- pb=1024 ** 5,
- eb=1024 ** 6,
- zb=1024 ** 7,
- yb=1024 ** 8
-)
-
-try:
- from solidfire.factory import ElementFactory
- from solidfire.custom.models import TimeIntervalFrequency
- from solidfire.models import Schedule, ScheduleInfo
-
- HAS_SF_SDK = True
-except Exception:
- HAS_SF_SDK = False
-
-
-def has_netapp_lib():
- return HAS_NETAPP_LIB
-
-
-def has_sf_sdk():
- return HAS_SF_SDK
-
-
-def na_ontap_host_argument_spec():
-
- return dict(
- hostname=dict(required=True, type='str'),
- username=dict(required=True, type='str', aliases=['user']),
- password=dict(required=True, type='str', aliases=['pass'], no_log=True),
- https=dict(required=False, type='bool', default=False),
- validate_certs=dict(required=False, type='bool', default=True),
- http_port=dict(required=False, type='int'),
- ontapi=dict(required=False, type='int'),
- use_rest=dict(required=False, type='str', default='Auto', choices=['Never', 'Always', 'Auto'])
- )
-
-
-def ontap_sf_host_argument_spec():
-
- return dict(
- hostname=dict(required=True, type='str'),
- username=dict(required=True, type='str', aliases=['user']),
- password=dict(required=True, type='str', aliases=['pass'], no_log=True)
- )
-
-
-def aws_cvs_host_argument_spec():
-
- return dict(
- api_url=dict(required=True, type='str'),
- validate_certs=dict(required=False, type='bool', default=True),
- api_key=dict(required=True, type='str'),
- secret_key=dict(required=True, type='str')
- )
-
-
-def create_sf_connection(module, port=None):
- hostname = module.params['hostname']
- username = module.params['username']
- password = module.params['password']
-
- if HAS_SF_SDK and hostname and username and password:
- try:
- return_val = ElementFactory.create(hostname, username, password, port=port)
- return return_val
- except Exception:
- raise Exception("Unable to create SF connection")
- else:
- module.fail_json(msg="the python SolidFire SDK module is required")
-
-
-def setup_na_ontap_zapi(module, vserver=None):
- hostname = module.params['hostname']
- username = module.params['username']
- password = module.params['password']
- https = module.params['https']
- validate_certs = module.params['validate_certs']
- port = module.params['http_port']
- version = module.params['ontapi']
-
- if HAS_NETAPP_LIB:
- # set up zapi
- server = zapi.NaServer(hostname)
- server.set_username(username)
- server.set_password(password)
- if vserver:
- server.set_vserver(vserver)
- if version:
- minor = version
- else:
- minor = 110
- server.set_api_version(major=1, minor=minor)
- # default is HTTP
- if https:
- if port is None:
- port = 443
- transport_type = 'HTTPS'
- # HACK to bypass certificate verification
- if validate_certs is False:
- if not os.environ.get('PYTHONHTTPSVERIFY', '') and getattr(ssl, '_create_unverified_context', None):
- ssl._create_default_https_context = ssl._create_unverified_context
- else:
- if port is None:
- port = 80
- transport_type = 'HTTP'
- server.set_transport_type(transport_type)
- server.set_port(port)
- server.set_server_type('FILER')
- return server
- else:
- module.fail_json(msg="the python NetApp-Lib module is required")
-
-
-def setup_ontap_zapi(module, vserver=None):
- hostname = module.params['hostname']
- username = module.params['username']
- password = module.params['password']
-
- if HAS_NETAPP_LIB:
- # set up zapi
- server = zapi.NaServer(hostname)
- server.set_username(username)
- server.set_password(password)
- if vserver:
- server.set_vserver(vserver)
- # Todo : Replace hard-coded values with configurable parameters.
- server.set_api_version(major=1, minor=110)
- server.set_port(80)
- server.set_server_type('FILER')
- server.set_transport_type('HTTP')
- return server
- else:
- module.fail_json(msg="the python NetApp-Lib module is required")
-
-
-def eseries_host_argument_spec():
- """Retrieve a base argument specification common to all NetApp E-Series modules"""
- argument_spec = basic_auth_argument_spec()
- argument_spec.update(dict(
- api_username=dict(type='str', required=True),
- api_password=dict(type='str', required=True, no_log=True),
- api_url=dict(type='str', required=True),
- ssid=dict(type='str', required=False, default='1'),
- validate_certs=dict(type='bool', required=False, default=True)
- ))
- return argument_spec
-
-
-class NetAppESeriesModule(object):
- """Base class for all NetApp E-Series modules.
-
- Provides a set of common methods for NetApp E-Series modules, including version checking, mode (proxy, embedded)
- verification, http requests, secure http redirection for embedded web services, and logging setup.
-
- Be sure to add the following lines in the module's documentation section:
- extends_documentation_fragment:
- - netapp.eseries
-
- :param dict(dict) ansible_options: dictionary of ansible option definitions
- :param str web_services_version: minimally required web services rest api version (default value: "02.00.0000.0000")
- :param bool supports_check_mode: whether the module will support the check_mode capabilities (default=False)
- :param list(list) mutually_exclusive: list containing list(s) of mutually exclusive options (optional)
- :param list(list) required_if: list containing list(s) containing the option, the option value, and then
- a list of required options. (optional)
- :param list(list) required_one_of: list containing list(s) of options for which at least one is required. (optional)
- :param list(list) required_together: list containing list(s) of options that are required together. (optional)
- :param bool log_requests: controls whether to log each request (default: True)
- """
- DEFAULT_TIMEOUT = 60
- DEFAULT_SECURE_PORT = "8443"
- DEFAULT_REST_API_PATH = "devmgr/v2/"
- DEFAULT_REST_API_ABOUT_PATH = "devmgr/utils/about"
- DEFAULT_HEADERS = {"Content-Type": "application/json", "Accept": "application/json",
- "netapp-client-type": "Ansible-%s" % ansible_version}
- HTTP_AGENT = "Ansible / %s" % ansible_version
- SIZE_UNIT_MAP = dict(bytes=1, b=1, kb=1024, mb=1024**2, gb=1024**3, tb=1024**4,
- pb=1024**5, eb=1024**6, zb=1024**7, yb=1024**8)
-
- def __init__(self, ansible_options, web_services_version=None, supports_check_mode=False,
- mutually_exclusive=None, required_if=None, required_one_of=None, required_together=None,
- log_requests=True):
- argument_spec = eseries_host_argument_spec()
- argument_spec.update(ansible_options)
-
- self.module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=supports_check_mode,
- mutually_exclusive=mutually_exclusive, required_if=required_if,
- required_one_of=required_one_of, required_together=required_together)
-
- args = self.module.params
- self.web_services_version = web_services_version if web_services_version else "02.00.0000.0000"
- self.ssid = args["ssid"]
- self.url = args["api_url"]
- self.log_requests = log_requests
- self.creds = dict(url_username=args["api_username"],
- url_password=args["api_password"],
- validate_certs=args["validate_certs"])
-
- if not self.url.endswith("/"):
- self.url += "/"
-
- self.is_embedded_mode = None
- self.is_web_services_valid_cache = None
-
- def _check_web_services_version(self):
- """Verify proxy or embedded web services meets minimum version required for module.
-
- The minimum required web services version is evaluated against version supplied through the web services rest
- api. AnsibleFailJson exception will be raised when the minimum is not met or exceeded.
-
- This helper function will update the supplied api url if secure http is not used for embedded web services
-
- :raise AnsibleFailJson: raised when the contacted api service does not meet the minimum required version.
- """
- if not self.is_web_services_valid_cache:
-
- url_parts = urlparse(self.url)
- if not url_parts.scheme or not url_parts.netloc:
- self.module.fail_json(msg="Failed to provide valid API URL. Example: https://192.168.1.100:8443/devmgr/v2. URL [%s]." % self.url)
-
- if url_parts.scheme not in ["http", "https"]:
- self.module.fail_json(msg="Protocol must be http or https. URL [%s]." % self.url)
-
- self.url = "%s://%s/" % (url_parts.scheme, url_parts.netloc)
- about_url = self.url + self.DEFAULT_REST_API_ABOUT_PATH
- rc, data = request(about_url, timeout=self.DEFAULT_TIMEOUT, headers=self.DEFAULT_HEADERS, ignore_errors=True, **self.creds)
-
- if rc != 200:
- self.module.warn("Failed to retrieve web services about information! Retrying with secure ports. Array Id [%s]." % self.ssid)
- self.url = "https://%s:8443/" % url_parts.netloc.split(":")[0]
- about_url = self.url + self.DEFAULT_REST_API_ABOUT_PATH
- try:
- rc, data = request(about_url, timeout=self.DEFAULT_TIMEOUT, headers=self.DEFAULT_HEADERS, **self.creds)
- except Exception as error:
- self.module.fail_json(msg="Failed to retrieve the webservices about information! Array Id [%s]. Error [%s]."
- % (self.ssid, to_native(error)))
-
- major, minor, other, revision = data["version"].split(".")
- minimum_major, minimum_minor, other, minimum_revision = self.web_services_version.split(".")
-
- if not (major > minimum_major or
- (major == minimum_major and minor > minimum_minor) or
- (major == minimum_major and minor == minimum_minor and revision >= minimum_revision)):
- self.module.fail_json(msg="Web services version does not meet minimum version required. Current version: [%s]."
- " Version required: [%s]." % (data["version"], self.web_services_version))
-
- self.module.log("Web services rest api version met the minimum required version.")
- self.is_web_services_valid_cache = True
-
- def is_embedded(self):
- """Determine whether web services server is the embedded web services.
-
- If web services about endpoint fails based on an URLError then the request will be attempted again using
- secure http.
-
- :raise AnsibleFailJson: raised when web services about endpoint failed to be contacted.
- :return bool: whether contacted web services is running from storage array (embedded) or from a proxy.
- """
- self._check_web_services_version()
-
- if self.is_embedded_mode is None:
- about_url = self.url + self.DEFAULT_REST_API_ABOUT_PATH
- try:
- rc, data = request(about_url, timeout=self.DEFAULT_TIMEOUT, headers=self.DEFAULT_HEADERS, **self.creds)
- self.is_embedded_mode = not data["runningAsProxy"]
- except Exception as error:
- self.module.fail_json(msg="Failed to retrieve the webservices about information! Array Id [%s]. Error [%s]."
- % (self.ssid, to_native(error)))
-
- return self.is_embedded_mode
-
- def request(self, path, data=None, method='GET', headers=None, ignore_errors=False):
- """Issue an HTTP request to a url, retrieving an optional JSON response.
-
- :param str path: web services rest api endpoint path (Example: storage-systems/1/graph). Note that when the
- full url path is specified then that will be used without supplying the protocol, hostname, port and rest path.
- :param data: data required for the request (data may be json or any python structured data)
- :param str method: request method such as GET, POST, DELETE.
- :param dict headers: dictionary containing request headers.
- :param bool ignore_errors: forces the request to ignore any raised exceptions.
- """
- self._check_web_services_version()
-
- if headers is None:
- headers = self.DEFAULT_HEADERS
-
- if not isinstance(data, str) and headers["Content-Type"] == "application/json":
- data = json.dumps(data)
-
- if path.startswith("/"):
- path = path[1:]
- request_url = self.url + self.DEFAULT_REST_API_PATH + path
-
- if self.log_requests or True:
- self.module.log(pformat(dict(url=request_url, data=data, method=method)))
-
- return request(url=request_url, data=data, method=method, headers=headers, use_proxy=True, force=False, last_mod_time=None,
- timeout=self.DEFAULT_TIMEOUT, http_agent=self.HTTP_AGENT, force_basic_auth=True, ignore_errors=ignore_errors, **self.creds)
-
-
-def create_multipart_formdata(files, fields=None, send_8kb=False):
- """Create the data for a multipart/form request.
-
- :param list(list) files: list of lists each containing (name, filename, path).
- :param list(list) fields: list of lists each containing (key, value).
- :param bool send_8kb: only sends the first 8kb of the files (default: False).
- """
- boundary = "---------------------------" + "".join([str(random.randint(0, 9)) for x in range(27)])
- data_parts = list()
- data = None
-
- if six.PY2: # Generate payload for Python 2
- newline = "\r\n"
- if fields is not None:
- for key, value in fields:
- data_parts.extend(["--%s" % boundary,
- 'Content-Disposition: form-data; name="%s"' % key,
- "",
- value])
-
- for name, filename, path in files:
- with open(path, "rb") as fh:
- value = fh.read(8192) if send_8kb else fh.read()
-
- data_parts.extend(["--%s" % boundary,
- 'Content-Disposition: form-data; name="%s"; filename="%s"' % (name, filename),
- "Content-Type: %s" % (mimetypes.guess_type(path)[0] or "application/octet-stream"),
- "",
- value])
- data_parts.extend(["--%s--" % boundary, ""])
- data = newline.join(data_parts)
-
- else:
- newline = six.b("\r\n")
- if fields is not None:
- for key, value in fields:
- data_parts.extend([six.b("--%s" % boundary),
- six.b('Content-Disposition: form-data; name="%s"' % key),
- six.b(""),
- six.b(value)])
-
- for name, filename, path in files:
- with open(path, "rb") as fh:
- value = fh.read(8192) if send_8kb else fh.read()
-
- data_parts.extend([six.b("--%s" % boundary),
- six.b('Content-Disposition: form-data; name="%s"; filename="%s"' % (name, filename)),
- six.b("Content-Type: %s" % (mimetypes.guess_type(path)[0] or "application/octet-stream")),
- six.b(""),
- value])
- data_parts.extend([six.b("--%s--" % boundary), b""])
- data = newline.join(data_parts)
-
- headers = {
- "Content-Type": "multipart/form-data; boundary=%s" % boundary,
- "Content-Length": str(len(data))}
-
- return headers, data
-
-
-def request(url, data=None, headers=None, method='GET', use_proxy=True,
- force=False, last_mod_time=None, timeout=10, validate_certs=True,
- url_username=None, url_password=None, http_agent=None, force_basic_auth=True, ignore_errors=False):
- """Issue an HTTP request to a url, retrieving an optional JSON response."""
-
- if headers is None:
- headers = {"Content-Type": "application/json", "Accept": "application/json"}
- headers.update({"netapp-client-type": "Ansible-%s" % ansible_version})
-
- if not http_agent:
- http_agent = "Ansible / %s" % ansible_version
-
- try:
- r = open_url(url=url, data=data, headers=headers, method=method, use_proxy=use_proxy,
- force=force, last_mod_time=last_mod_time, timeout=timeout, validate_certs=validate_certs,
- url_username=url_username, url_password=url_password, http_agent=http_agent,
- force_basic_auth=force_basic_auth)
- except HTTPError as err:
- r = err.fp
-
- try:
- raw_data = r.read()
- if raw_data:
- data = json.loads(raw_data)
- else:
- raw_data = None
- except Exception:
- if ignore_errors:
- pass
- else:
- raise Exception(raw_data)
-
- resp_code = r.getcode()
-
- if resp_code >= 400 and not ignore_errors:
- raise Exception(resp_code, data)
- else:
- return resp_code, data
-
-
-def ems_log_event(source, server, name="Ansible", id="12345", version=ansible_version,
- category="Information", event="setup", autosupport="false"):
- ems_log = zapi.NaElement('ems-autosupport-log')
- # Host name invoking the API.
- ems_log.add_new_child("computer-name", name)
- # ID of event. A user defined event-id, range [0..2^32-2].
- ems_log.add_new_child("event-id", id)
- # Name of the application invoking the API.
- ems_log.add_new_child("event-source", source)
- # Version of application invoking the API.
- ems_log.add_new_child("app-version", version)
- # Application defined category of the event.
- ems_log.add_new_child("category", category)
- # Description of event to log. An application defined message to log.
- ems_log.add_new_child("event-description", event)
- ems_log.add_new_child("log-level", "6")
- ems_log.add_new_child("auto-support", autosupport)
- server.invoke_successfully(ems_log, True)
-
-
-def get_cserver_zapi(server):
- vserver_info = zapi.NaElement('vserver-get-iter')
- query_details = zapi.NaElement.create_node_with_children('vserver-info', **{'vserver-type': 'admin'})
- query = zapi.NaElement('query')
- query.add_child_elem(query_details)
- vserver_info.add_child_elem(query)
- result = server.invoke_successfully(vserver_info,
- enable_tunneling=False)
- attribute_list = result.get_child_by_name('attributes-list')
- vserver_list = attribute_list.get_child_by_name('vserver-info')
- return vserver_list.get_child_content('vserver-name')
-
-
-def get_cserver(connection, is_rest=False):
- if not is_rest:
- return get_cserver_zapi(connection)
-
- params = {'fields': 'type'}
- api = "private/cli/vserver"
- json, error = connection.get(api, params)
- if json is None or error is not None:
- # exit if there is an error or no data
- return None
- vservers = json.get('records')
- if vservers is not None:
- for vserver in vservers:
- if vserver['type'] == 'admin': # cluster admin
- return vserver['vserver']
- if len(vservers) == 1: # assume vserver admin
- return vservers[0]['vserver']
-
- return None
-
-
-class OntapRestAPI(object):
- def __init__(self, module, timeout=60):
- self.module = module
- self.username = self.module.params['username']
- self.password = self.module.params['password']
- self.hostname = self.module.params['hostname']
- self.use_rest = self.module.params['use_rest']
- self.verify = self.module.params['validate_certs']
- self.timeout = timeout
- self.url = 'https://' + self.hostname + '/api/'
- self.errors = list()
- self.debug_logs = list()
- self.check_required_library()
-
- def check_required_library(self):
- if not HAS_REQUESTS:
- self.module.fail_json(msg=missing_required_lib('requests'))
-
- def send_request(self, method, api, params, json=None, return_status_code=False):
- ''' send http request and process reponse, including error conditions '''
- url = self.url + api
- status_code = None
- content = None
- json_dict = None
- json_error = None
- error_details = None
-
- def get_json(response):
- ''' extract json, and error message if present '''
- try:
- json = response.json()
- except ValueError:
- return None, None
- error = json.get('error')
- return json, error
-
- try:
- response = requests.request(method, url, verify=self.verify, auth=(self.username, self.password), params=params, timeout=self.timeout, json=json)
- content = response.content # for debug purposes
- status_code = response.status_code
- # If the response was successful, no Exception will be raised
- response.raise_for_status()
- json_dict, json_error = get_json(response)
- except requests.exceptions.HTTPError as err:
- __, json_error = get_json(response)
- if json_error is None:
- self.log_error(status_code, 'HTTP error: %s' % err)
- error_details = str(err)
- # If an error was reported in the json payload, it is handled below
- except requests.exceptions.ConnectionError as err:
- self.log_error(status_code, 'Connection error: %s' % err)
- error_details = str(err)
- except Exception as err:
- self.log_error(status_code, 'Other error: %s' % err)
- error_details = str(err)
- if json_error is not None:
- self.log_error(status_code, 'Endpoint error: %d: %s' % (status_code, json_error))
- error_details = json_error
- self.log_debug(status_code, content)
- if return_status_code:
- return status_code, error_details
- return json_dict, error_details
-
- def get(self, api, params):
- method = 'GET'
- return self.send_request(method, api, params)
-
- def post(self, api, data, params=None):
- method = 'POST'
- return self.send_request(method, api, params, json=data)
-
- def patch(self, api, data, params=None):
- method = 'PATCH'
- return self.send_request(method, api, params, json=data)
-
- def delete(self, api, data, params=None):
- method = 'DELETE'
- return self.send_request(method, api, params, json=data)
-
- def _is_rest(self, used_unsupported_rest_properties=None):
- if self.use_rest == "Always":
- if used_unsupported_rest_properties:
- error = "REST API currently does not support '%s'" % \
- ', '.join(used_unsupported_rest_properties)
- return True, error
- else:
- return True, None
- if self.use_rest == 'Never' or used_unsupported_rest_properties:
- # force ZAPI if requested or if some parameter requires it
- return False, None
- method = 'HEAD'
- api = 'cluster/software'
- status_code, __ = self.send_request(method, api, params=None, return_status_code=True)
- if status_code == 200:
- return True, None
- return False, None
-
- def is_rest(self, used_unsupported_rest_properties=None):
- ''' only return error if there is a reason to '''
- use_rest, error = self._is_rest(used_unsupported_rest_properties)
- if used_unsupported_rest_properties is None:
- return use_rest
- return use_rest, error
-
- def log_error(self, status_code, message):
- self.errors.append(message)
- self.debug_logs.append((status_code, message))
-
- def log_debug(self, status_code, content):
- self.debug_logs.append((status_code, content))
-
-
-class AwsCvsRestAPI(object):
- def __init__(self, module, timeout=60):
- self.module = module
- self.api_key = self.module.params['api_key']
- self.secret_key = self.module.params['secret_key']
- self.api_url = self.module.params['api_url']
- self.verify = self.module.params['validate_certs']
- self.timeout = timeout
- self.url = 'https://' + self.api_url + '/v1/'
- self.check_required_library()
-
- def check_required_library(self):
- if not HAS_REQUESTS:
- self.module.fail_json(msg=missing_required_lib('requests'))
-
- def send_request(self, method, api, params, json=None):
- ''' send http request and process reponse, including error conditions '''
- url = self.url + api
- status_code = None
- content = None
- json_dict = None
- json_error = None
- error_details = None
- headers = {
- 'Content-type': "application/json",
- 'api-key': self.api_key,
- 'secret-key': self.secret_key,
- 'Cache-Control': "no-cache",
- }
-
- def get_json(response):
- ''' extract json, and error message if present '''
- try:
- json = response.json()
-
- except ValueError:
- return None, None
- success_code = [200, 201, 202]
- if response.status_code not in success_code:
- error = json.get('message')
- else:
- error = None
- return json, error
- try:
- response = requests.request(method, url, headers=headers, timeout=self.timeout, json=json)
- status_code = response.status_code
- # If the response was successful, no Exception will be raised
- json_dict, json_error = get_json(response)
- except requests.exceptions.HTTPError as err:
- __, json_error = get_json(response)
- if json_error is None:
- error_details = str(err)
- except requests.exceptions.ConnectionError as err:
- error_details = str(err)
- except Exception as err:
- error_details = str(err)
- if json_error is not None:
- error_details = json_error
-
- return json_dict, error_details
-
- # If an error was reported in the json payload, it is handled below
- def get(self, api, params=None):
- method = 'GET'
- return self.send_request(method, api, params)
-
- def post(self, api, data, params=None):
- method = 'POST'
- return self.send_request(method, api, params, json=data)
-
- def patch(self, api, data, params=None):
- method = 'PATCH'
- return self.send_request(method, api, params, json=data)
-
- def put(self, api, data, params=None):
- method = 'PUT'
- return self.send_request(method, api, params, json=data)
-
- def delete(self, api, data, params=None):
- method = 'DELETE'
- return self.send_request(method, api, params, json=data)
-
- def get_state(self, jobId):
- """ Method to get the state of the job """
- method = 'GET'
- response, status_code = self.get('Jobs/%s' % jobId)
- while str(response['state']) not in 'done':
- response, status_code = self.get('Jobs/%s' % jobId)
- return 'done'
diff --git a/lib/ansible/module_utils/netapp_elementsw_module.py b/lib/ansible/module_utils/netapp_elementsw_module.py
deleted file mode 100644
index c9baa0aeb9..0000000000
--- a/lib/ansible/module_utils/netapp_elementsw_module.py
+++ /dev/null
@@ -1,156 +0,0 @@
-# This code is part of Ansible, but is an independent component.
-# This particular file snippet, and this file snippet only, is BSD licensed.
-
-HAS_SF_SDK = False
-try:
- import solidfire.common
-
- HAS_SF_SDK = True
-except Exception:
- HAS_SF_SDK = False
-
-
-def has_sf_sdk():
- return HAS_SF_SDK
-
-
-class NaElementSWModule(object):
-
- def __init__(self, elem):
- self.elem_connect = elem
- self.parameters = dict()
-
- def get_volume(self, volume_id):
- """
- Return volume details if volume exists for given volume_id
-
- :param volume_id: volume ID
- :type volume_id: int
- :return: Volume dict if found, None if not found
- :rtype: dict
- """
- volume_list = self.elem_connect.list_volumes(volume_ids=[volume_id])
- for volume in volume_list.volumes:
- if volume.volume_id == volume_id:
- if str(volume.delete_time) == "":
- return volume
- return None
-
- def get_volume_id(self, vol_name, account_id):
- """
- Return volume id from the given (valid) account_id if found
- Return None if not found
-
- :param vol_name: Name of the volume
- :type vol_name: str
- :param account_id: Account ID
- :type account_id: int
-
- :return: Volume ID of the first matching volume if found. None if not found.
- :rtype: int
- """
- volume_list = self.elem_connect.list_volumes_for_account(account_id=account_id)
- for volume in volume_list.volumes:
- if volume.name == vol_name:
- # return volume_id
- if str(volume.delete_time) == "":
- return volume.volume_id
- return None
-
- def volume_id_exists(self, volume_id):
- """
- Return volume_id if volume exists for given volume_id
-
- :param volume_id: volume ID
- :type volume_id: int
- :return: Volume ID if found, None if not found
- :rtype: int
- """
- volume_list = self.elem_connect.list_volumes(volume_ids=[volume_id])
- for volume in volume_list.volumes:
- if volume.volume_id == volume_id:
- if str(volume.delete_time) == "":
- return volume.volume_id
- return None
-
- def volume_exists(self, volume, account_id):
- """
- Return volume_id if exists, None if not found
-
- :param volume: Volume ID or Name
- :type volume: str
- :param account_id: Account ID (valid)
- :type account_id: int
- :return: Volume ID if found, None if not found
- """
- # If volume is an integer, get_by_id
- if str(volume).isdigit():
- volume_id = int(volume)
- try:
- if self.volume_id_exists(volume_id):
- return volume_id
- except solidfire.common.ApiServerError:
- # don't fail, continue and try get_by_name
- pass
- # get volume by name
- volume_id = self.get_volume_id(volume, account_id)
- return volume_id
-
- def get_snapshot(self, snapshot_id, volume_id):
- """
- Return snapshot details if found
-
- :param snapshot_id: Snapshot ID or Name
- :type snapshot_id: str
- :param volume_id: Account ID (valid)
- :type volume_id: int
- :return: Snapshot dict if found, None if not found
- :rtype: dict
- """
- # mandate src_volume_id although not needed by sdk
- snapshot_list = self.elem_connect.list_snapshots(
- volume_id=volume_id)
- for snapshot in snapshot_list.snapshots:
- # if actual id is provided
- if str(snapshot_id).isdigit() and snapshot.snapshot_id == int(snapshot_id):
- return snapshot
- # if snapshot name is provided
- elif snapshot.name == snapshot_id:
- return snapshot
- return None
-
- def account_exists(self, account):
- """
- Return account_id if account exists for given account id or name
- Raises an exception if account does not exist
-
- :param account: Account ID or Name
- :type account: str
- :return: Account ID if found, None if not found
- """
- # If account is an integer, get_by_id
- if account.isdigit():
- account_id = int(account)
- try:
- result = self.elem_connect.get_account_by_id(account_id=account_id)
- if result.account.account_id == account_id:
- return account_id
- except solidfire.common.ApiServerError:
- # don't fail, continue and try get_by_name
- pass
- # get account by name, the method returns an Exception if account doesn't exist
- result = self.elem_connect.get_account_by_name(username=account)
- return result.account.account_id
-
- def set_element_attributes(self, source):
- """
- Return telemetry attributes for the current execution
-
- :param source: name of the module
- :type source: str
- :return: a dict containing telemetry attributes
- """
- attributes = {}
- attributes['config-mgmt'] = 'ansible'
- attributes['event-source'] = source
- return(attributes)
diff --git a/lib/ansible/module_utils/netapp_module.py b/lib/ansible/module_utils/netapp_module.py
deleted file mode 100644
index 914236b349..0000000000
--- a/lib/ansible/module_utils/netapp_module.py
+++ /dev/null
@@ -1,270 +0,0 @@
-# This code is part of Ansible, but is an independent component.
-# This particular file snippet, and this file snippet only, is BSD licensed.
-# Modules you write using this snippet, which is embedded dynamically by Ansible
-# still belong to the author of the module, and may assign their own license
-# to the complete work.
-#
-# Copyright (c) 2018, Laurent Nicolas <laurentn@netapp.com>
-# All rights reserved.
-#
-# Redistribution and use in source and binary forms, with or without modification,
-# are permitted provided that the following conditions are met:
-#
-# * Redistributions of source code must retain the above copyright
-# notice, this list of conditions and the following disclaimer.
-# * Redistributions in binary form must reproduce the above copyright notice,
-# this list of conditions and the following disclaimer in the documentation
-# and/or other materials provided with the distribution.
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
-# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
-# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
-# IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
-# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
-# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
-# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
-# USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-''' Support class for NetApp ansible modules '''
-
-import ansible.module_utils.netapp as netapp_utils
-
-
-def cmp(a, b):
- """
- Python 3 does not have a cmp function, this will do the cmp.
- :param a: first object to check
- :param b: second object to check
- :return:
- """
- # convert to lower case for string comparison.
- if a is None:
- return -1
- if type(a) is str and type(b) is str:
- a = a.lower()
- b = b.lower()
- # if list has string element, convert string to lower case.
- if type(a) is list and type(b) is list:
- a = [x.lower() if type(x) is str else x for x in a]
- b = [x.lower() if type(x) is str else x for x in b]
- a.sort()
- b.sort()
- return (a > b) - (a < b)
-
-
-class NetAppModule(object):
- '''
- Common class for NetApp modules
- set of support functions to derive actions based
- on the current state of the system, and a desired state
- '''
-
- def __init__(self):
- self.log = list()
- self.changed = False
- self.parameters = {'name': 'not intialized'}
- self.zapi_string_keys = dict()
- self.zapi_bool_keys = dict()
- self.zapi_list_keys = dict()
- self.zapi_int_keys = dict()
- self.zapi_required = dict()
-
- def set_parameters(self, ansible_params):
- self.parameters = dict()
- for param in ansible_params:
- if ansible_params[param] is not None:
- self.parameters[param] = ansible_params[param]
- return self.parameters
-
- def get_value_for_bool(self, from_zapi, value):
- """
- Convert boolean values to string or vice-versa
- If from_zapi = True, value is converted from string (as it appears in ZAPI) to boolean
- If from_zapi = False, value is converted from boolean to string
- For get() method, from_zapi = True
- For modify(), create(), from_zapi = False
- :param from_zapi: convert the value from ZAPI or to ZAPI acceptable type
- :param value: value of the boolean attribute
- :return: string or boolean
- """
- if value is None:
- return None
- if from_zapi:
- return True if value == 'true' else False
- else:
- return 'true' if value else 'false'
-
- def get_value_for_int(self, from_zapi, value):
- """
- Convert integer values to string or vice-versa
- If from_zapi = True, value is converted from string (as it appears in ZAPI) to integer
- If from_zapi = False, value is converted from integer to string
- For get() method, from_zapi = True
- For modify(), create(), from_zapi = False
- :param from_zapi: convert the value from ZAPI or to ZAPI acceptable type
- :param value: value of the integer attribute
- :return: string or integer
- """
- if value is None:
- return None
- if from_zapi:
- return int(value)
- else:
- return str(value)
-
- def get_value_for_list(self, from_zapi, zapi_parent, zapi_child=None, data=None):
- """
- Convert a python list() to NaElement or vice-versa
- If from_zapi = True, value is converted from NaElement (parent-children structure) to list()
- If from_zapi = False, value is converted from list() to NaElement
- :param zapi_parent: ZAPI parent key or the ZAPI parent NaElement
- :param zapi_child: ZAPI child key
- :param data: list() to be converted to NaElement parent-children object
- :param from_zapi: convert the value from ZAPI or to ZAPI acceptable type
- :return: list() or NaElement
- """
- if from_zapi:
- if zapi_parent is None:
- return []
- else:
- return [zapi_child.get_content() for zapi_child in zapi_parent.get_children()]
- else:
- zapi_parent = netapp_utils.zapi.NaElement(zapi_parent)
- for item in data:
- zapi_parent.add_new_child(zapi_child, item)
- return zapi_parent
-
- def get_cd_action(self, current, desired):
- ''' takes a desired state and a current state, and return an action:
- create, delete, None
- eg:
- is_present = 'absent'
- some_object = self.get_object(source)
- if some_object is not None:
- is_present = 'present'
- action = cd_action(current=is_present, desired = self.desired.state())
- '''
- if 'state' in desired:
- desired_state = desired['state']
- else:
- desired_state = 'present'
-
- if current is None and desired_state == 'absent':
- return None
- if current is not None and desired_state == 'present':
- return None
- # change in state
- self.changed = True
- if current is not None:
- return 'delete'
- return 'create'
-
- def compare_and_update_values(self, current, desired, keys_to_compare):
- updated_values = dict()
- is_changed = False
- for key in keys_to_compare:
- if key in current:
- if key in desired and desired[key] is not None:
- if current[key] != desired[key]:
- updated_values[key] = desired[key]
- is_changed = True
- else:
- updated_values[key] = current[key]
- else:
- updated_values[key] = current[key]
-
- return updated_values, is_changed
-
- @staticmethod
- def check_keys(current, desired):
- ''' TODO: raise an error if keys do not match
- with the exception of:
- new_name, state in desired
- '''
- pass
-
- @staticmethod
- def compare_lists(current, desired, get_list_diff):
- ''' compares two lists and return a list of elements that are either the desired elements or elements that are
- modified from the current state depending on the get_list_diff flag
- :param: current: current item attribute in ONTAP
- :param: desired: attributes from playbook
- :param: get_list_diff: specifies whether to have a diff of desired list w.r.t current list for an attribute
- :return: list of attributes to be modified
- :rtype: list
- '''
- desired_diff_list = [item for item in desired if item not in current] # get what in desired and not in current
- current_diff_list = [item for item in current if item not in desired] # get what in current but not in desired
-
- if desired_diff_list or current_diff_list:
- # there are changes
- if get_list_diff:
- return desired_diff_list
- else:
- return desired
- else:
- return []
-
- def get_modified_attributes(self, current, desired, get_list_diff=False):
- ''' takes two dicts of attributes and return a dict of attributes that are
- not in the current state
- It is expected that all attributes of interest are listed in current and
- desired.
- :param: current: current attributes in ONTAP
- :param: desired: attributes from playbook
- :param: get_list_diff: specifies whether to have a diff of desired list w.r.t current list for an attribute
- :return: dict of attributes to be modified
- :rtype: dict
-
- NOTE: depending on the attribute, the caller may need to do a modify or a
- different operation (eg move volume if the modified attribute is an
- aggregate name)
- '''
- # if the object does not exist, we can't modify it
- modified = dict()
- if current is None:
- return modified
-
- # error out if keys do not match
- self.check_keys(current, desired)
-
- # collect changed attributes
- for key, value in current.items():
- if key in desired and desired[key] is not None:
- if type(value) is list:
- modified_list = self.compare_lists(value, desired[key], get_list_diff) # get modified list from current and desired
- if modified_list:
- modified[key] = modified_list
- elif cmp(value, desired[key]) != 0:
- modified[key] = desired[key]
- if modified:
- self.changed = True
- return modified
-
- def is_rename_action(self, source, target):
- ''' takes a source and target object, and returns True
- if a rename is required
- eg:
- source = self.get_object(source_name)
- target = self.get_object(target_name)
- action = is_rename_action(source, target)
- :return: None for error, True for rename action, False otherwise
- '''
- if source is None and target is None:
- # error, do nothing
- # cannot rename an non existent resource
- # alternatively we could create B
- return None
- if source is not None and target is not None:
- # error, do nothing
- # idempotency (or) new_name_is_already_in_use
- # alternatively we could delete B and rename A to B
- return False
- if source is None and target is not None:
- # do nothing, maybe the rename was already done
- return False
- # source is not None and target is None:
- # rename is in order
- self.changed = True
- return True
diff --git a/lib/ansible/modules/storage/netapp/na_ontap_aggregate.py b/lib/ansible/modules/storage/netapp/na_ontap_aggregate.py
deleted file mode 100644
index ac76e77abd..0000000000
--- a/lib/ansible/modules/storage/netapp/na_ontap_aggregate.py
+++ /dev/null
@@ -1,474 +0,0 @@
-#!/usr/bin/python
-
-# (c) 2018-2019, NetApp, Inc
-# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-
-from __future__ import absolute_import, division, print_function
-__metaclass__ = type
-
-
-ANSIBLE_METADATA = {'metadata_version': '1.1',
- 'status': ['preview'],
- 'supported_by': 'certified'}
-
-
-DOCUMENTATION = '''
-
-module: na_ontap_aggregate
-short_description: NetApp ONTAP manage aggregates.
-extends_documentation_fragment:
- - netapp.na_ontap
-version_added: '2.6'
-author: NetApp Ansible Team (@carchi8py) <ng-ansibleteam@netapp.com>
-
-description:
-- Create, delete, or manage aggregates on ONTAP.
-
-options:
-
- state:
- description:
- - Whether the specified aggregate should exist or not.
- choices: ['present', 'absent']
- default: 'present'
-
- service_state:
- description:
- - Whether the specified aggregate should be enabled or disabled. Creates aggregate if doesnt exist.
- choices: ['online', 'offline']
-
- name:
- required: true
- description:
- - The name of the aggregate to manage.
-
- from_name:
- description:
- - Name of the aggregate to be renamed.
- version_added: '2.7'
-
- nodes:
- description:
- - Node(s) for the aggregate to be created on. If no node specified, mgmt lif home will be used.
- - If multiple nodes specified an aggr stripe will be made.
-
- disk_type:
- description:
- - Type of disk to use to build aggregate
- choices: ['ATA', 'BSAS', 'FCAL', 'FSAS', 'LUN', 'MSATA', 'SAS', 'SSD', 'VMDISK']
- version_added: '2.7'
-
- disk_count:
- description:
- - Number of disks to place into the aggregate, including parity disks.
- - The disks in this newly-created aggregate come from the spare disk pool.
- - The smallest disks in this pool join the aggregate first, unless the C(disk-size) argument is provided.
- - Either C(disk-count) or C(disks) must be supplied. Range [0..2^31-1].
- - Required when C(state=present).
-
- disk_size:
- description:
- - Disk size to use in 4K block size. Disks within 10% of specified size will be used.
- version_added: '2.7'
-
- raid_size:
- description:
- - Sets the maximum number of drives per raid group.
- version_added: '2.7'
-
- raid_type:
- description:
- - Specifies the type of RAID groups to use in the new aggregate.
- choices: ['raid4', 'raid_dp', 'raid_tec']
- version_added: '2.7'
-
- unmount_volumes:
- type: bool
- description:
- - If set to "TRUE", this option specifies that all of the volumes hosted by the given aggregate are to be unmounted
- - before the offline operation is executed.
- - By default, the system will reject any attempt to offline an aggregate that hosts one or more online volumes.
-
- disks:
- type: list
- description:
- - Specific list of disks to use for the new aggregate.
- - To create a "mirrored" aggregate with a specific list of disks, both 'disks' and 'mirror_disks' options must be supplied.
- Additionally, the same number of disks must be supplied in both lists.
- version_added: '2.8'
-
- is_mirrored:
- type: bool
- description:
- - Specifies that the new aggregate be mirrored (have two plexes).
- - If set to true, then the indicated disks will be split across the two plexes. By default, the new aggregate will not be mirrored.
- - This option cannot be used when a specific list of disks is supplied with either the 'disks' or 'mirror_disks' options.
- version_added: '2.8'
-
- mirror_disks:
- type: list
- description:
- - List of mirror disks to use. It must contain the same number of disks specified in 'disks'.
- version_added: '2.8'
-
- spare_pool:
- description:
- - Specifies the spare pool from which to select spare disks to use in creation of a new aggregate.
- choices: ['Pool0', 'Pool1']
- version_added: '2.8'
-
- wait_for_online:
- description:
- - Set this parameter to 'true' for synchronous execution during create (wait until aggregate status is online)
- - Set this parameter to 'false' for asynchronous execution
- - For asynchronous, execution exits as soon as the request is sent, without checking aggregate status
- type: bool
- default: false
- version_added: '2.8'
-
- time_out:
- description:
- - time to wait for aggregate creation in seconds
- - default is set to 100 seconds
- default: 100
- version_added: "2.8"
-'''
-
-EXAMPLES = """
-- name: Create Aggregates and wait 5 minutes until aggregate is online
- na_ontap_aggregate:
- state: present
- service_state: online
- name: ansibleAggr
- disk_count: 1
- wait_for_online: True
- time_out: 300
- hostname: "{{ netapp_hostname }}"
- username: "{{ netapp_username }}"
- password: "{{ netapp_password }}"
-
-- name: Manage Aggregates
- na_ontap_aggregate:
- state: present
- service_state: offline
- unmount_volumes: true
- name: ansibleAggr
- disk_count: 1
- hostname: "{{ netapp_hostname }}"
- username: "{{ netapp_username }}"
- password: "{{ netapp_password }}"
-
-- name: Rename Aggregates
- na_ontap_aggregate:
- state: present
- service_state: online
- from_name: ansibleAggr
- name: ansibleAggr2
- disk_count: 1
- hostname: "{{ netapp_hostname }}"
- username: "{{ netapp_username }}"
- password: "{{ netapp_password }}"
-
-- name: Delete Aggregates
- na_ontap_aggregate:
- state: absent
- service_state: offline
- unmount_volumes: true
- name: ansibleAggr
- hostname: "{{ netapp_hostname }}"
- username: "{{ netapp_username }}"
- password: "{{ netapp_password }}"
-"""
-
-RETURN = """
-
-"""
-import time
-import traceback
-
-from ansible.module_utils.basic import AnsibleModule
-from ansible.module_utils._text import to_native
-import ansible.module_utils.netapp as netapp_utils
-from ansible.module_utils.netapp_module import NetAppModule
-
-HAS_NETAPP_LIB = netapp_utils.has_netapp_lib()
-
-
-class NetAppOntapAggregate(object):
- ''' object initialize and class methods '''
-
- def __init__(self):
- self.argument_spec = netapp_utils.na_ontap_host_argument_spec()
- self.argument_spec.update(dict(
- name=dict(required=True, type='str'),
- disks=dict(required=False, type='list'),
- disk_count=dict(required=False, type='int', default=None),
- disk_size=dict(required=False, type='int'),
- disk_type=dict(required=False, choices=['ATA', 'BSAS', 'FCAL', 'FSAS', 'LUN', 'MSATA', 'SAS', 'SSD', 'VMDISK']),
- from_name=dict(required=False, type='str'),
- mirror_disks=dict(required=False, type='list'),
- nodes=dict(required=False, type='list'),
- is_mirrored=dict(required=False, type='bool'),
- raid_size=dict(required=False, type='int'),
- raid_type=dict(required=False, choices=['raid4', 'raid_dp', 'raid_tec']),
- service_state=dict(required=False, choices=['online', 'offline']),
- spare_pool=dict(required=False, choices=['Pool0', 'Pool1']),
- state=dict(required=False, choices=['present', 'absent'], default='present'),
- unmount_volumes=dict(required=False, type='bool'),
- wait_for_online=dict(required=False, type='bool', default=False),
- time_out=dict(required=False, type='int', default=100)
- ))
-
- self.module = AnsibleModule(
- argument_spec=self.argument_spec,
- required_if=[
- ('service_state', 'offline', ['unmount_volumes']),
- ],
- mutually_exclusive=[
- ('is_mirrored', 'disks'),
- ('is_mirrored', 'mirror_disks'),
- ('is_mirrored', 'spare_pool'),
- ('spare_pool', 'disks')
- ],
- supports_check_mode=True
- )
-
- self.na_helper = NetAppModule()
- self.parameters = self.na_helper.set_parameters(self.module.params)
- if self.parameters.get('mirror_disks') is not None and self.parameters.get('disks') is None:
- self.module.fail_json(mgs="mirror_disks require disks options to be set")
- if HAS_NETAPP_LIB is False:
- self.module.fail_json(msg="the python NetApp-Lib module is required")
- else:
- self.server = netapp_utils.setup_na_ontap_zapi(module=self.module)
-
- def aggr_get_iter(self, name):
- """
- Return aggr-get-iter query results
- :param name: Name of the aggregate
- :return: NaElement if aggregate found, None otherwise
- """
-
- aggr_get_iter = netapp_utils.zapi.NaElement('aggr-get-iter')
- query_details = netapp_utils.zapi.NaElement.create_node_with_children(
- 'aggr-attributes', **{'aggregate-name': name})
- query = netapp_utils.zapi.NaElement('query')
- query.add_child_elem(query_details)
- aggr_get_iter.add_child_elem(query)
- result = None
- try:
- result = self.server.invoke_successfully(aggr_get_iter, enable_tunneling=False)
- except netapp_utils.zapi.NaApiError as error:
- # Error 13040 denotes an aggregate not being found.
- if to_native(error.code) == "13040":
- pass
- else:
- self.module.fail_json(msg=to_native(error), exception=traceback.format_exc())
- return result
-
- def get_aggr(self, name=None):
- """
- Fetch details if aggregate exists.
- :param name: Name of the aggregate to be fetched
- :return:
- Dictionary of current details if aggregate found
- None if aggregate is not found
- """
- if name is None:
- name = self.parameters['name']
- aggr_get = self.aggr_get_iter(name)
- if (aggr_get and aggr_get.get_child_by_name('num-records') and
- int(aggr_get.get_child_content('num-records')) >= 1):
- current_aggr = dict()
- attr = aggr_get.get_child_by_name('attributes-list').get_child_by_name('aggr-attributes')
- current_aggr['service_state'] = attr.get_child_by_name('aggr-raid-attributes').get_child_content('state')
- return current_aggr
- return None
-
- def aggregate_online(self):
- """
- Set state of an offline aggregate to online
- :return: None
- """
- online_aggr = netapp_utils.zapi.NaElement.create_node_with_children(
- 'aggr-online', **{'aggregate': self.parameters['name'],
- 'force-online': 'true'})
- try:
- self.server.invoke_successfully(online_aggr,
- enable_tunneling=True)
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg='Error changing the state of aggregate %s to %s: %s' %
- (self.parameters['name'], self.parameters['service_state'], to_native(error)),
- exception=traceback.format_exc())
-
- def aggregate_offline(self):
- """
- Set state of an online aggregate to offline
- :return: None
- """
- offline_aggr = netapp_utils.zapi.NaElement.create_node_with_children(
- 'aggr-offline', **{'aggregate': self.parameters['name'],
- 'force-offline': 'false',
- 'unmount-volumes': str(self.parameters['unmount_volumes'])})
- try:
- self.server.invoke_successfully(offline_aggr, enable_tunneling=True)
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg='Error changing the state of aggregate %s to %s: %s' %
- (self.parameters['name'], self.parameters['service_state'], to_native(error)),
- exception=traceback.format_exc())
-
- def create_aggr(self):
- """
- Create aggregate
- :return: None
- """
- if not self.parameters.get('disk_count'):
- self.module.fail_json(msg='Error provisioning aggregate %s: \
- disk_count is required' % self.parameters['name'])
- options = {'aggregate': self.parameters['name'],
- 'disk-count': str(self.parameters['disk_count'])
- }
- if self.parameters.get('disk_type'):
- options['disk-type'] = self.parameters['disk_type']
- if self.parameters.get('raid_size'):
- options['raid-size'] = str(self.parameters['raid_size'])
- if self.parameters.get('raid_type'):
- options['raid-type'] = self.parameters['raid_type']
- if self.parameters.get('disk_size'):
- options['disk-size'] = str(self.parameters['disk_size'])
- if self.parameters.get('is_mirrored'):
- options['is-mirrored'] = str(self.parameters['is_mirrored'])
- if self.parameters.get('spare_pool'):
- options['spare-pool'] = self.parameters['spare_pool']
- if self.parameters.get('raid_type'):
- options['raid-type'] = self.parameters['raid_type']
- aggr_create = netapp_utils.zapi.NaElement.create_node_with_children('aggr-create', **options)
- if self.parameters.get('nodes'):
- nodes_obj = netapp_utils.zapi.NaElement('nodes')
- aggr_create.add_child_elem(nodes_obj)
- for node in self.parameters['nodes']:
- nodes_obj.add_new_child('node-name', node)
- if self.parameters.get('disks'):
- disks_obj = netapp_utils.zapi.NaElement('disk-info')
- for disk in self.parameters.get('disks'):
- disks_obj.add_new_child('name', disk)
- aggr_create.add_child_elem(disks_obj)
- if self.parameters.get('mirror_disks'):
- mirror_disks_obj = netapp_utils.zapi.NaElement('disk-info')
- for disk in self.parameters.get('mirror_disks'):
- mirror_disks_obj.add_new_child('name', disk)
- aggr_create.add_child_elem(mirror_disks_obj)
-
- try:
- self.server.invoke_successfully(aggr_create, enable_tunneling=False)
- if self.parameters.get('wait_for_online'):
- # round off time_out
- retries = (self.parameters['time_out'] + 5) / 10
- current = self.get_aggr()
- status = None if current is None else current['service_state']
- while status != 'online' and retries > 0:
- time.sleep(10)
- retries = retries - 1
- current = self.get_aggr()
- status = None if current is None else current['service_state']
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg="Error provisioning aggregate %s: %s"
- % (self.parameters['name'], to_native(error)),
- exception=traceback.format_exc())
-
- def delete_aggr(self):
- """
- Delete aggregate.
- :return: None
- """
- aggr_destroy = netapp_utils.zapi.NaElement.create_node_with_children(
- 'aggr-destroy', **{'aggregate': self.parameters['name']})
-
- try:
- self.server.invoke_successfully(aggr_destroy,
- enable_tunneling=False)
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg="Error removing aggregate %s: %s" % (self.parameters['name'], to_native(error)),
- exception=traceback.format_exc())
-
- def rename_aggregate(self):
- """
- Rename aggregate.
- """
- aggr_rename = netapp_utils.zapi.NaElement.create_node_with_children(
- 'aggr-rename', **{'aggregate': self.parameters['from_name'],
- 'new-aggregate-name': self.parameters['name']})
-
- try:
- self.server.invoke_successfully(aggr_rename, enable_tunneling=False)
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg="Error renaming aggregate %s: %s"
- % (self.parameters['from_name'], to_native(error)),
- exception=traceback.format_exc())
-
- def modify_aggr(self, modify):
- """
- Modify state of the aggregate
- :param modify: dictionary of parameters to be modified
- :return: None
- """
- if modify['service_state'] == 'offline':
- self.aggregate_offline()
- elif modify['service_state'] == 'online':
- self.aggregate_online()
-
- def asup_log_for_cserver(self, event_name):
- """
- Fetch admin vserver for the given cluster
- Create and Autosupport log event with the given module name
- :param event_name: Name of the event log
- :return: None
- """
- results = netapp_utils.get_cserver(self.server)
- cserver = netapp_utils.setup_na_ontap_zapi(module=self.module, vserver=results)
- netapp_utils.ems_log_event(event_name, cserver)
-
- def apply(self):
- """
- Apply action to the aggregate
- :return: None
- """
- self.asup_log_for_cserver("na_ontap_aggregate")
-
- current = self.get_aggr()
- # rename and create are mutually exclusive
- rename, cd_action = None, None
- if self.parameters.get('from_name'):
- rename = self.na_helper.is_rename_action(self.get_aggr(self.parameters['from_name']), current)
- if rename is None:
- self.module.fail_json(msg="Error renaming: aggregate %s does not exist" % self.parameters['from_name'])
- else:
- cd_action = self.na_helper.get_cd_action(current, self.parameters)
- modify = self.na_helper.get_modified_attributes(current, self.parameters)
-
- if self.na_helper.changed:
- if self.module.check_mode:
- pass
- else:
- if rename:
- self.rename_aggregate()
- elif cd_action == 'create':
- self.create_aggr()
- elif cd_action == 'delete':
- self.delete_aggr()
- elif modify:
- self.modify_aggr(modify)
- self.module.exit_json(changed=self.na_helper.changed)
-
-
-def main():
- """
- Create Aggregate class instance and invoke apply
- :return: None
- """
- obj_aggr = NetAppOntapAggregate()
- obj_aggr.apply()
-
-
-if __name__ == '__main__':
- main()
diff --git a/lib/ansible/modules/storage/netapp/na_ontap_autosupport.py b/lib/ansible/modules/storage/netapp/na_ontap_autosupport.py
deleted file mode 100644
index c77096ee97..0000000000
--- a/lib/ansible/modules/storage/netapp/na_ontap_autosupport.py
+++ /dev/null
@@ -1,275 +0,0 @@
-#!/usr/bin/python
-"""
-create Autosupport module to enable, disable or modify
-"""
-
-# (c) 2018-2019, NetApp, Inc
-# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-from __future__ import absolute_import, division, print_function
-__metaclass__ = type
-
-
-ANSIBLE_METADATA = {'metadata_version': '1.1',
- 'status': ['preview'],
- 'supported_by': 'certified'}
-
-
-DOCUMENTATION = """
-author: NetApp Ansible Team (@carchi8py) <ng-ansibleteam@netapp.com>
-description:
- - "Enable/Disable Autosupport"
-extends_documentation_fragment:
- - netapp.na_ontap
-module: na_ontap_autosupport
-options:
- state:
- description:
- - Specifies whether the AutoSupport daemon is present or absent.
- - When this setting is absent, delivery of all AutoSupport messages is turned off.
- choices: ['present', 'absent']
- default: present
- node_name:
- description:
- - The name of the filer that owns the AutoSupport Configuration.
- required: true
- transport:
- description:
- - The name of the transport protocol used to deliver AutoSupport messages
- choices: ['http', 'https', 'smtp']
- noteto:
- description:
- - Specifies up to five recipients of short AutoSupport e-mail messages.
- post_url:
- description:
- - The URL used to deliver AutoSupport messages via HTTP POST
- mail_hosts:
- description:
- - List of mail server(s) used to deliver AutoSupport messages via SMTP.
- - Both host names and IP addresses may be used as valid input.
- support:
- description:
- - Specifies whether AutoSupport notification to technical support is enabled.
- type: bool
- from_address:
- description:
- - specify the e-mail address from which the node sends AutoSupport messages
- version_added: 2.8
- partner_addresses:
- description:
- - Specifies up to five partner vendor recipients of full AutoSupport e-mail messages.
- version_added: 2.8
- to_addresses:
- description:
- - Specifies up to five recipients of full AutoSupport e-mail messages.
- version_added: 2.8
- proxy_url:
- description:
- - specify an HTTP or HTTPS proxy if the 'transport' parameter is set to HTTP or HTTPS and your organization uses a proxy.
- - If authentication is required, use the format "username:password@host:port".
- version_added: 2.8
- hostname_in_subject:
- description:
- - Specify whether the hostname of the node is included in the subject line of the AutoSupport message.
- type: bool
- version_added: 2.8
-short_description: NetApp ONTAP Autosupport
-version_added: "2.7"
-
-"""
-
-EXAMPLES = """
- - name: Enable autosupport
- na_ontap_autosupport:
- hostname: "{{ hostname }}"
- username: "{{ username }}"
- password: "{{ password }}"
- state: present
- node_name: test
- transport: https
- noteto: abc@def.com,def@ghi.com
- mail_hosts: 1.2.3.4,5.6.7.8
- support: False
- post_url: url/1.0/post
-
- - name: Modify autosupport proxy_url with password
- na_ontap_autosupport:
- hostname: "{{ hostname }}"
- username: "{{ username }}"
- password: "{{ password }}"
- state: present
- node_name: test
- transport: https
- proxy_url: username:password@host.com:8000
-
- - name: Modify autosupport proxy_url without password
- na_ontap_autosupport:
- hostname: "{{ hostname }}"
- username: "{{ username }}"
- password: "{{ password }}"
- state: present
- node_name: test
- transport: https
- proxy_url: username@host.com:8000
-
- - name: Disable autosupport
- na_ontap_autosupport:
- hostname: "{{ hostname }}"
- username: "{{ username }}"
- password: "{{ password }}"
- state: absent
- node_name: test
-
-"""
-
-RETURN = """
-"""
-import traceback
-
-from ansible.module_utils.basic import AnsibleModule
-from ansible.module_utils._text import to_native
-import ansible.module_utils.netapp as netapp_utils
-from ansible.module_utils.netapp_module import NetAppModule
-
-HAS_NETAPP_LIB = netapp_utils.has_netapp_lib()
-
-
-class NetAppONTAPasup(object):
- """Class with autosupport methods"""
-
- def __init__(self):
-
- self.argument_spec = netapp_utils.na_ontap_host_argument_spec()
- self.argument_spec.update(dict(
- state=dict(required=False, choices=['present', 'absent'], default='present'),
- node_name=dict(required=True, type='str'),
- transport=dict(required=False, type='str', choices=['smtp', 'http', 'https']),
- noteto=dict(required=False, type='list'),
- post_url=dict(required=False, type='str'),
- support=dict(required=False, type='bool'),
- mail_hosts=dict(required=False, type='list'),
- from_address=dict(required=False, type='str'),
- partner_addresses=dict(required=False, type='list'),
- to_addresses=dict(required=False, type='list'),
- proxy_url=dict(required=False, type='str'),
- hostname_in_subject=dict(required=False, type='bool'),
- ))
-
- self.module = AnsibleModule(
- argument_spec=self.argument_spec,
- supports_check_mode=False
- )
-
- self.na_helper = NetAppModule()
- self.parameters = self.na_helper.set_parameters(self.module.params)
- # present or absent requires modifying state to enabled or disabled
- self.parameters['service_state'] = 'started' if self.parameters['state'] == 'present' else 'stopped'
- self.set_playbook_zapi_key_map()
-
- if HAS_NETAPP_LIB is False:
- self.module.fail_json(msg="the python NetApp-Lib module is required")
- else:
- self.server = netapp_utils.setup_na_ontap_zapi(module=self.module)
-
- def set_playbook_zapi_key_map(self):
- self.na_helper.zapi_string_keys = {
- 'node_name': 'node-name',
- 'transport': 'transport',
- 'post_url': 'post-url',
- 'from_address': 'from',
- 'proxy_url': 'proxy-url'
- }
- self.na_helper.zapi_list_keys = {
- 'noteto': ('noteto', 'mail-address'),
- 'mail_hosts': ('mail-hosts', 'string'),
- 'partner_addresses': ('partner-address', 'mail-address'),
- 'to_addresses': ('to', 'mail-address'),
- }
- self.na_helper.zapi_bool_keys = {
- 'support': 'is-support-enabled',
- 'hostname_in_subject': 'is-node-in-subject'
- }
-
- def get_autosupport_config(self):
- """
- Invoke zapi - get current autosupport details
- :return: dict()
- """
- asup_details = netapp_utils.zapi.NaElement('autosupport-config-get')
- asup_details.add_new_child('node-name', self.parameters['node_name'])
- asup_info = dict()
- try:
- result = self.server.invoke_successfully(asup_details, enable_tunneling=True)
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg='%s' % to_native(error),
- exception=traceback.format_exc())
- # zapi invoke successful
- asup_attr_info = result.get_child_by_name('attributes').get_child_by_name('autosupport-config-info')
- asup_info['service_state'] = 'started' if asup_attr_info['is-enabled'] == 'true' else 'stopped'
- for item_key, zapi_key in self.na_helper.zapi_string_keys.items():
- asup_info[item_key] = asup_attr_info[zapi_key]
- for item_key, zapi_key in self.na_helper.zapi_bool_keys.items():
- asup_info[item_key] = self.na_helper.get_value_for_bool(from_zapi=True,
- value=asup_attr_info[zapi_key])
- for item_key, zapi_key in self.na_helper.zapi_list_keys.items():
- parent, dummy = zapi_key
- asup_info[item_key] = self.na_helper.get_value_for_list(from_zapi=True,
- zapi_parent=asup_attr_info.get_child_by_name(parent)
- )
- return asup_info
-
- def modify_autosupport_config(self, modify):
- """
- Invoke zapi - modify autosupport config
- @return: NaElement object / FAILURE with an error_message
- """
- asup_details = {'node-name': self.parameters['node_name']}
- if modify.get('service_state'):
- asup_details['is-enabled'] = 'true' if modify.get('service_state') == 'started' else 'false'
- asup_config = netapp_utils.zapi.NaElement('autosupport-config-modify')
- for item_key in modify:
- if item_key in self.na_helper.zapi_string_keys:
- zapi_key = self.na_helper.zapi_string_keys.get(item_key)
- asup_details[zapi_key] = modify[item_key]
- elif item_key in self.na_helper.zapi_bool_keys:
- zapi_key = self.na_helper.zapi_bool_keys.get(item_key)
- asup_details[zapi_key] = self.na_helper.get_value_for_bool(from_zapi=False,
- value=modify[item_key])
- elif item_key in self.na_helper.zapi_list_keys:
- parent_key, child_key = self.na_helper.zapi_list_keys.get(item_key)
- asup_config.add_child_elem(self.na_helper.get_value_for_list(from_zapi=False,
- zapi_parent=parent_key,
- zapi_child=child_key,
- data=modify.get(item_key)))
- asup_config.translate_struct(asup_details)
- try:
- return self.server.invoke_successfully(asup_config, enable_tunneling=True)
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg='%s' % to_native(error), exception=traceback.format_exc())
-
- def autosupport_log(self):
- results = netapp_utils.get_cserver(self.server)
- cserver = netapp_utils.setup_na_ontap_zapi(module=self.module, vserver=results)
- netapp_utils.ems_log_event("na_ontap_autosupport", cserver)
-
- def apply(self):
- """
- Apply action to autosupport
- """
- current = self.get_autosupport_config()
- modify = self.na_helper.get_modified_attributes(current, self.parameters)
- if self.na_helper.changed:
- if self.module.check_mode:
- pass
- else:
- self.modify_autosupport_config(modify)
- self.module.exit_json(changed=self.na_helper.changed)
-
-
-def main():
- """Execute action"""
- asup_obj = NetAppONTAPasup()
- asup_obj.apply()
-
-
-if __name__ == '__main__':
- main()
diff --git a/lib/ansible/modules/storage/netapp/na_ontap_broadcast_domain.py b/lib/ansible/modules/storage/netapp/na_ontap_broadcast_domain.py
deleted file mode 100644
index 561efc5f9a..0000000000
--- a/lib/ansible/modules/storage/netapp/na_ontap_broadcast_domain.py
+++ /dev/null
@@ -1,436 +0,0 @@
-#!/usr/bin/python
-
-# (c) 2018-2019, NetApp, Inc
-# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-
-from __future__ import absolute_import, division, print_function
-__metaclass__ = type
-
-
-ANSIBLE_METADATA = {'metadata_version': '1.1',
- 'status': ['preview'],
- 'supported_by': 'certified'}
-
-DOCUMENTATION = '''
-module: na_ontap_broadcast_domain
-short_description: NetApp ONTAP manage broadcast domains.
-extends_documentation_fragment:
- - netapp.na_ontap
-version_added: '2.6'
-author: NetApp Ansible Team (@carchi8py) <ng-ansibleteam@netapp.com>
-description:
-- Modify a ONTAP broadcast domain.
-options:
- state:
- description:
- - Whether the specified broadcast domain should exist or not.
- choices: ['present', 'absent']
- default: present
- name:
- description:
- - Specify the broadcast domain name.
- required: true
- aliases:
- - broadcast_domain
- from_name:
- description:
- - Specify the broadcast domain name to be split into new broadcast domain.
- version_added: "2.8"
- mtu:
- description:
- - Specify the required mtu for the broadcast domain.
- ipspace:
- description:
- - Specify the required ipspace for the broadcast domain.
- - A domain ipspace can not be modified after the domain has been created.
- ports:
- description:
- - Specify the ports associated with this broadcast domain. Should be comma separated.
- - It represents the expected state of a list of ports at any time.
- - Add a port if it is specified in expected state but not in current state.
- - Delete a port if it is specified in current state but not in expected state.
- - For split action, it represents the ports to be split from current broadcast domain and added to the new broadcast domain.
- - if all ports are removed or split from a broadcast domain, the broadcast domain will be deleted automatically.
-'''
-
-EXAMPLES = """
- - name: create broadcast domain
- na_ontap_broadcast_domain:
- state: present
- username: "{{ netapp_username }}"
- password: "{{ netapp_password }}"
- hostname: "{{ netapp_hostname }}"
- name: ansible_domain
- mtu: 1000
- ipspace: Default
- ports: ["khutton-vsim1:e0d-12", "khutton-vsim1:e0d-13"]
- - name: modify broadcast domain
- na_ontap_broadcast_domain:
- state: present
- username: "{{ netapp_username }}"
- password: "{{ netapp_password }}"
- hostname: "{{ netapp_hostname }}"
- name: ansible_domain
- mtu: 1100
- ipspace: Default
- ports: ["khutton-vsim1:e0d-12", "khutton-vsim1:e0d-13"]
- - name: split broadcast domain
- na_ontap_broadcast_domain:
- state: present
- username: "{{ netapp_username }}"
- password: "{{ netapp_password }}"
- hostname: "{{ netapp_hostname }}"
- from_name: ansible_domain
- name: new_ansible_domain
- mtu: 1200
- ipspace: Default
- ports: khutton-vsim1:e0d-12
- - name: delete broadcast domain
- na_ontap_broadcast_domain:
- state: absent
- username: "{{ netapp_username }}"
- password: "{{ netapp_password }}"
- hostname: "{{ netapp_hostname }}"
- name: ansible_domain
- ipspace: Default
-"""
-
-RETURN = """
-
-
-"""
-import traceback
-
-from ansible.module_utils.basic import AnsibleModule
-from ansible.module_utils._text import to_native
-import ansible.module_utils.netapp as netapp_utils
-from ansible.module_utils.netapp_module import NetAppModule
-
-HAS_NETAPP_LIB = netapp_utils.has_netapp_lib()
-
-
-class NetAppOntapBroadcastDomain(object):
- """
- Create, Modifies and Destroys a Broadcast domain
- """
- def __init__(self):
- """
- Initialize the ONTAP Broadcast Domain class
- """
- self.argument_spec = netapp_utils.na_ontap_host_argument_spec()
- self.argument_spec.update(dict(
- state=dict(required=False, choices=['present', 'absent'], default='present'),
- name=dict(required=True, type='str', aliases=["broadcast_domain"]),
- ipspace=dict(required=False, type='str'),
- mtu=dict(required=False, type='str'),
- ports=dict(required=False, type='list'),
- from_name=dict(required=False, type='str'),
- ))
-
- self.module = AnsibleModule(
- argument_spec=self.argument_spec,
- supports_check_mode=True
- )
-
- self.na_helper = NetAppModule()
- self.parameters = self.na_helper.set_parameters(self.module.params)
-
- if HAS_NETAPP_LIB is False:
- self.module.fail_json(msg="the python NetApp-Lib module is required")
- else:
- self.server = netapp_utils.setup_na_ontap_zapi(module=self.module)
- return
-
- def get_broadcast_domain(self, broadcast_domain=None):
- """
- Return details about the broadcast domain
- :param broadcast_domain: specific broadcast domain to get.
- :return: Details about the broadcast domain. None if not found.
- :rtype: dict
- """
- if broadcast_domain is None:
- broadcast_domain = self.parameters['name']
- domain_get_iter = netapp_utils.zapi.NaElement('net-port-broadcast-domain-get-iter')
- broadcast_domain_info = netapp_utils.zapi.NaElement('net-port-broadcast-domain-info')
- broadcast_domain_info.add_new_child('broadcast-domain', broadcast_domain)
- query = netapp_utils.zapi.NaElement('query')
- query.add_child_elem(broadcast_domain_info)
- domain_get_iter.add_child_elem(query)
- result = self.server.invoke_successfully(domain_get_iter, True)
- domain_exists = None
- # check if broadcast_domain exists
- if result.get_child_by_name('num-records') and \
- int(result.get_child_content('num-records')) == 1:
- domain_info = result.get_child_by_name('attributes-list').\
- get_child_by_name('net-port-broadcast-domain-info')
- domain_name = domain_info.get_child_content('broadcast-domain')
- domain_mtu = domain_info.get_child_content('mtu')
- domain_ipspace = domain_info.get_child_content('ipspace')
- domain_ports = domain_info.get_child_by_name('ports')
- if domain_ports is not None:
- ports = [port.get_child_content('port') for port in domain_ports.get_children()]
- else:
- ports = []
- domain_exists = {
- 'domain-name': domain_name,
- 'mtu': domain_mtu,
- 'ipspace': domain_ipspace,
- 'ports': ports
- }
- return domain_exists
-
- def create_broadcast_domain(self):
- """
- Creates a new broadcast domain
- """
- domain_obj = netapp_utils.zapi.NaElement('net-port-broadcast-domain-create')
- domain_obj.add_new_child("broadcast-domain", self.parameters['name'])
- if self.parameters.get('ipspace'):
- domain_obj.add_new_child("ipspace", self.parameters['ipspace'])
- if self.parameters.get('mtu'):
- domain_obj.add_new_child("mtu", self.parameters['mtu'])
- if self.parameters.get('ports'):
- ports_obj = netapp_utils.zapi.NaElement('ports')
- domain_obj.add_child_elem(ports_obj)
- for port in self.parameters['ports']:
- ports_obj.add_new_child('net-qualified-port-name', port)
- try:
- self.server.invoke_successfully(domain_obj, True)
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg='Error creating broadcast domain %s: %s' %
- (self.parameters['name'], to_native(error)),
- exception=traceback.format_exc())
-
- def delete_broadcast_domain(self, broadcast_domain=None):
- """
- Deletes a broadcast domain
- """
- if broadcast_domain is None:
- broadcast_domain = self.parameters['name']
- domain_obj = netapp_utils.zapi.NaElement('net-port-broadcast-domain-destroy')
- domain_obj.add_new_child("broadcast-domain", broadcast_domain)
- if self.parameters.get('ipspace'):
- domain_obj.add_new_child("ipspace", self.parameters['ipspace'])
- try:
- self.server.invoke_successfully(domain_obj, True)
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg='Error deleting broadcast domain %s: %s' %
- (broadcast_domain, to_native(error)),
- exception=traceback.format_exc())
-
- def modify_broadcast_domain(self):
- """
- Modifies ipspace and mtu options of a broadcast domain
- """
- domain_obj = netapp_utils.zapi.NaElement('net-port-broadcast-domain-modify')
- domain_obj.add_new_child("broadcast-domain", self.parameters['name'])
- if self.parameters.get('mtu'):
- domain_obj.add_new_child("mtu", self.parameters['mtu'])
- if self.parameters.get('ipspace'):
- domain_obj.add_new_child("ipspace", self.parameters['ipspace'])
- try:
- self.server.invoke_successfully(domain_obj, True)
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg='Error modifying broadcast domain %s: %s' %
- (self.parameters['name'], to_native(error)),
- exception=traceback.format_exc())
-
- def split_broadcast_domain(self):
- """
- split broadcast domain
- """
- domain_obj = netapp_utils.zapi.NaElement('net-port-broadcast-domain-split')
- domain_obj.add_new_child("broadcast-domain", self.parameters['from_name'])
- domain_obj.add_new_child("new-broadcast-domain", self.parameters['name'])
- if self.parameters.get('ports'):
- ports_obj = netapp_utils.zapi.NaElement('ports')
- domain_obj.add_child_elem(ports_obj)
- for port in self.parameters['ports']:
- ports_obj.add_new_child('net-qualified-port-name', port)
- if self.parameters.get('ipspace'):
- domain_obj.add_new_child("ipspace", self.parameters['ipspace'])
- try:
- self.server.invoke_successfully(domain_obj, True)
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg='Error splitting broadcast domain %s: %s' %
- (self.parameters['name'], to_native(error)),
- exception=traceback.format_exc())
- if len(self.get_broadcast_domain_ports(self.parameters['from_name'])) == 0:
- self.delete_broadcast_domain(self.parameters['from_name'])
-
- def modify_redirect(self, modify):
- """
- :param modify: modify attributes.
- """
- for attribute in modify.keys():
- if attribute == 'mtu':
- self.modify_broadcast_domain()
- if attribute == 'ports':
- self.modify_broadcast_domain_ports()
-
- def get_modify_attributes(self, current, split):
- """
- :param current: current state.
- :param split: True or False of split action.
- :return: list of modified attributes.
- """
- modify = None
- if self.parameters['state'] == 'present':
- # split already handled ipspace and ports.
- if self.parameters.get('from_name'):
- current = self.get_broadcast_domain(self.parameters['from_name'])
- if split:
- modify = self.na_helper.get_modified_attributes(current, self.parameters)
- if modify.get('ipspace'):
- del modify['ipspace']
- if modify.get('ports'):
- del modify['ports']
- # ipspace can not be modified.
- else:
- modify = self.na_helper.get_modified_attributes(current, self.parameters)
- if modify.get('ipspace'):
- self.module.fail_json(msg='A domain ipspace can not be modified after the domain has been created.',
- exception=traceback.format_exc())
- return modify
-
- def modify_broadcast_domain_ports(self):
- """
- compare current and desire ports. Call add or remove ports methods if needed.
- :return: None.
- """
- current_ports = self.get_broadcast_domain_ports()
- expect_ports = self.parameters['ports']
- # if want to remove all ports, simply delete the broadcast domain.
- if len(expect_ports) == 0:
- self.delete_broadcast_domain()
- return
- ports_to_remove = list(set(current_ports) - set(expect_ports))
- ports_to_add = list(set(expect_ports) - set(current_ports))
-
- if len(ports_to_add) > 0:
- self.add_broadcast_domain_ports(ports_to_add)
-
- if len(ports_to_remove) > 0:
- self.delete_broadcast_domain_ports(ports_to_remove)
-
- def add_broadcast_domain_ports(self, ports):
- """
- Creates new broadcast domain ports
- """
- domain_obj = netapp_utils.zapi.NaElement('net-port-broadcast-domain-add-ports')
- domain_obj.add_new_child("broadcast-domain", self.parameters['name'])
- if self.parameters.get('ipspace'):
- domain_obj.add_new_child("ipspace", self.parameters['ipspace'])
- if ports:
- ports_obj = netapp_utils.zapi.NaElement('ports')
- domain_obj.add_child_elem(ports_obj)
- for port in ports:
- ports_obj.add_new_child('net-qualified-port-name', port)
- try:
- self.server.invoke_successfully(domain_obj, True)
- return True
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg='Error creating port for broadcast domain %s: %s' %
- (self.parameters['name'], to_native(error)),
- exception=traceback.format_exc())
-
- def delete_broadcast_domain_ports(self, ports):
- """
- Deletes broadcast domain ports
- :param: ports to be deleted.
- """
- domain_obj = netapp_utils.zapi.NaElement('net-port-broadcast-domain-remove-ports')
- domain_obj.add_new_child("broadcast-domain", self.parameters['name'])
- if self.parameters.get('ipspace'):
- domain_obj.add_new_child("ipspace", self.parameters['ipspace'])
- if ports:
- ports_obj = netapp_utils.zapi.NaElement('ports')
- domain_obj.add_child_elem(ports_obj)
- for port in ports:
- ports_obj.add_new_child('net-qualified-port-name', port)
- try:
- self.server.invoke_successfully(domain_obj, True)
- return True
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg='Error deleting port for broadcast domain %s: %s' %
- (self.parameters['name'], to_native(error)),
- exception=traceback.format_exc())
-
- def get_broadcast_domain_ports(self, broadcast_domain=None):
- """
- Return details about the broadcast domain ports.
- :return: Details about the broadcast domain ports. None if not found.
- :rtype: list
- """
- if broadcast_domain is None:
- broadcast_domain = self.parameters['name']
- domain_get_iter = netapp_utils.zapi.NaElement('net-port-broadcast-domain-get-iter')
- broadcast_domain_info = netapp_utils.zapi.NaElement('net-port-broadcast-domain-info')
- broadcast_domain_info.add_new_child('broadcast-domain', broadcast_domain)
- query = netapp_utils.zapi.NaElement('query')
- query.add_child_elem(broadcast_domain_info)
- domain_get_iter.add_child_elem(query)
- result = self.server.invoke_successfully(domain_get_iter, True)
- ports = []
- if result.get_child_by_name('num-records') and \
- int(result.get_child_content('num-records')) == 1:
- domain_info = result.get_child_by_name('attributes-list').get_child_by_name('net-port-broadcast-domain-info')
- domain_ports = domain_info.get_child_by_name('ports')
- if domain_ports is not None:
- ports = [port.get_child_content('port') for port in domain_ports.get_children()]
- return ports
-
- def apply(self):
- """
- Run Module based on play book
- """
- self.asup_log_for_cserver("na_ontap_broadcast_domain")
- current = self.get_broadcast_domain()
- cd_action, split = None, None
- cd_action = self.na_helper.get_cd_action(current, self.parameters)
- if cd_action == 'create':
- # either create new domain or split domain.
- if self.parameters.get('from_name'):
- split = self.na_helper.is_rename_action(self.get_broadcast_domain(self.parameters['from_name']), current)
- if split is None:
- self.module.fail_json(msg='A domain can not be split if it does not exist.',
- exception=traceback.format_exc())
- if split:
- cd_action = None
- modify = self.get_modify_attributes(current, split)
- if self.na_helper.changed:
- if self.module.check_mode:
- pass
- else:
- if split:
- self.split_broadcast_domain()
- if cd_action == 'create':
- self.create_broadcast_domain()
- elif cd_action == 'delete':
- self.delete_broadcast_domain()
- elif modify:
- self.modify_redirect(modify)
- self.module.exit_json(changed=self.na_helper.changed)
-
- def asup_log_for_cserver(self, event_name):
- """
- Fetch admin vserver for the given cluster
- Create and Autosupport log event with the given module name
- :param event_name: Name of the event log
- :return: None
- """
- results = netapp_utils.get_cserver(self.server)
- cserver = netapp_utils.setup_na_ontap_zapi(module=self.module, vserver=results)
- netapp_utils.ems_log_event(event_name, cserver)
-
-
-def main():
- """
- Creates the NetApp ONTAP Broadcast Domain Object that can be created, deleted and modified.
- """
- obj = NetAppOntapBroadcastDomain()
- obj.apply()
-
-
-if __name__ == '__main__':
- main()
diff --git a/lib/ansible/modules/storage/netapp/na_ontap_broadcast_domain_ports.py b/lib/ansible/modules/storage/netapp/na_ontap_broadcast_domain_ports.py
deleted file mode 100644
index 8b37e49590..0000000000
--- a/lib/ansible/modules/storage/netapp/na_ontap_broadcast_domain_ports.py
+++ /dev/null
@@ -1,215 +0,0 @@
-#!/usr/bin/python
-
-# (c) 2018, NetApp, Inc
-# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-
-from __future__ import absolute_import, division, print_function
-__metaclass__ = type
-
-ANSIBLE_METADATA = {'metadata_version': '1.1',
- 'status': ['preview'],
- 'supported_by': 'certified'}
-
-DOCUMENTATION = '''
-module: na_ontap_broadcast_domain_ports
-short_description: NetApp ONTAP manage broadcast domain ports
-extends_documentation_fragment:
- - netapp.na_ontap
-version_added: '2.6'
-author: NetApp Ansible Team (@carchi8py) <ng-ansibleteam@netapp.com>
-description:
-- Add or remove ONTAP broadcast domain ports. Existing ports that are not listed are kept.
-options:
- state:
- description:
- - Whether the specified broadcast domain should exist or not.
- choices: ['present', 'absent']
- default: present
- broadcast_domain:
- description:
- - Specify the broadcast_domain name
- required: true
- ipspace:
- description:
- - Specify the ipspace for the broadcast domain
- ports:
- description:
- - Specify the list of ports to add to or remove from this broadcast domain.
-
-'''
-
-EXAMPLES = """
- - name: create broadcast domain ports
- na_ontap_broadcast_domain_ports:
- state=present
- username={{ netapp_username }}
- password={{ netapp_password }}
- hostname={{ netapp_hostname }}
- broadcast_domain=123kevin
- ports=khutton-vsim1:e0d-13
- - name: delete broadcast domain ports
- na_ontap_broadcast_domain_ports:
- state=absent
- username={{ netapp_username }}
- password={{ netapp_password }}
- hostname={{ netapp_hostname }}
- broadcast_domain=123kevin
- ports=khutton-vsim1:e0d-13
-"""
-
-RETURN = """
-
-
-"""
-
-import traceback
-from ansible.module_utils.basic import AnsibleModule
-from ansible.module_utils._text import to_native
-import ansible.module_utils.netapp as netapp_utils
-
-HAS_NETAPP_LIB = netapp_utils.has_netapp_lib()
-
-
-class NetAppOntapBroadcastDomainPorts(object):
- """
- Create and Destroys Broadcast Domain Ports
- """
- def __init__(self):
- """
- Initialize the Ontap Net Route class
- """
- self.argument_spec = netapp_utils.na_ontap_host_argument_spec()
- self.argument_spec.update(dict(
- state=dict(required=False, choices=['present', 'absent'], default='present'),
- broadcast_domain=dict(required=True, type='str'),
- ipspace=dict(required=False, type='str', default=None),
- ports=dict(required=True, type='list'),
- ))
-
- self.module = AnsibleModule(
- argument_spec=self.argument_spec,
- supports_check_mode=True
- )
- parameters = self.module.params
- # set up state variables
- self.state = parameters['state']
- self.broadcast_domain = parameters['broadcast_domain']
- self.ipspace = parameters['ipspace']
- self.ports = parameters['ports']
-
- if HAS_NETAPP_LIB is False:
- self.module.fail_json(msg="the python NetApp-Lib module is required")
- else:
- self.server = netapp_utils.setup_na_ontap_zapi(module=self.module)
- return
-
- def get_broadcast_domain_ports(self):
- """
- Return details about the broadcast domain ports
- :param:
- name : broadcast domain name
- :return: Details about the broadcast domain. None if not found.
- :rtype: dict
- """
- domain_get_iter = netapp_utils.zapi.NaElement('net-port-broadcast-domain-get-iter')
- broadcast_domain_info = netapp_utils.zapi.NaElement('net-port-broadcast-domain-info')
- broadcast_domain_info.add_new_child('broadcast-domain', self.broadcast_domain)
- query = netapp_utils.zapi.NaElement('query')
- query.add_child_elem(broadcast_domain_info)
- domain_get_iter.add_child_elem(query)
- result = self.server.invoke_successfully(domain_get_iter, True)
- domain_exists = None
- # check if broadcast domain exists
- if result.get_child_by_name('num-records') and \
- int(result.get_child_content('num-records')) == 1:
- domain_info = result.get_child_by_name('attributes-list').get_child_by_name('net-port-broadcast-domain-info')
- domain_name = domain_info.get_child_content('broadcast-domain')
- domain_ports = domain_info.get_child_by_name('ports')
- if domain_ports is not None:
- ports = [port.get_child_content('port') for port in domain_ports.get_children()]
- else:
- ports = []
- domain_exists = {
- 'domain-name': domain_name,
- 'ports': ports
- }
- return domain_exists
-
- def create_broadcast_domain_ports(self, ports):
- """
- Creates new broadcast domain ports
- """
- domain_obj = netapp_utils.zapi.NaElement('net-port-broadcast-domain-add-ports')
- domain_obj.add_new_child("broadcast-domain", self.broadcast_domain)
- if self.ipspace:
- domain_obj.add_new_child("ipspace", self.ipspace)
- if ports:
- ports_obj = netapp_utils.zapi.NaElement('ports')
- domain_obj.add_child_elem(ports_obj)
- for port in ports:
- ports_obj.add_new_child('net-qualified-port-name', port)
- try:
- self.server.invoke_successfully(domain_obj, True)
- return True
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg='Error creating port for broadcast domain %s: %s' %
- (self.broadcast_domain, to_native(error)),
- exception=traceback.format_exc())
-
- def delete_broadcast_domain_ports(self, ports):
- """
- Deletes broadcast domain ports
- """
- domain_obj = netapp_utils.zapi.NaElement('net-port-broadcast-domain-remove-ports')
- domain_obj.add_new_child("broadcast-domain", self.broadcast_domain)
- if self.ipspace:
- domain_obj.add_new_child("ipspace", self.ipspace)
- if ports:
- ports_obj = netapp_utils.zapi.NaElement('ports')
- domain_obj.add_child_elem(ports_obj)
- for port in ports:
- ports_obj.add_new_child('net-qualified-port-name', port)
- try:
- self.server.invoke_successfully(domain_obj, True)
- return True
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg='Error deleting port for broadcast domain %s: %s' %
- (self.broadcast_domain, to_native(error)),
- exception=traceback.format_exc())
-
- def apply(self):
- """
- Run Module based on play book
- """
- changed = False
- broadcast_domain_details = self.get_broadcast_domain_ports()
- results = netapp_utils.get_cserver(self.server)
- cserver = netapp_utils.setup_na_ontap_zapi(module=self.module, vserver=results)
- netapp_utils.ems_log_event("na_ontap_broadcast_domain_ports", cserver)
- if broadcast_domain_details is None:
- self.module.fail_json(msg='Error broadcast domain not found: %s' % self.broadcast_domain)
- if self.module.check_mode:
- pass
- else:
- if self.state == 'present': # execute create
- ports_to_add = [port for port in self.ports if port not in broadcast_domain_details['ports']]
- if len(ports_to_add) > 0:
- changed = self.create_broadcast_domain_ports(ports_to_add)
- elif self.state == 'absent': # execute delete
- ports_to_delete = [port for port in self.ports if port in broadcast_domain_details['ports']]
- if len(ports_to_delete) > 0:
- changed = self.delete_broadcast_domain_ports(ports_to_delete)
-
- self.module.exit_json(changed=changed)
-
-
-def main():
- """
- Creates the NetApp Ontap Net Route object and runs the correct play task
- """
- obj = NetAppOntapBroadcastDomainPorts()
- obj.apply()
-
-
-if __name__ == '__main__':
- main()
diff --git a/lib/ansible/modules/storage/netapp/na_ontap_cg_snapshot.py b/lib/ansible/modules/storage/netapp/na_ontap_cg_snapshot.py
deleted file mode 100644
index 2500509011..0000000000
--- a/lib/ansible/modules/storage/netapp/na_ontap_cg_snapshot.py
+++ /dev/null
@@ -1,219 +0,0 @@
-#!/usr/bin/python
-
-# (c) 2018-2019, NetApp, Inc
-# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-
-from __future__ import absolute_import, division, print_function
-__metaclass__ = type
-
-ANSIBLE_METADATA = {'metadata_version': '1.1',
- 'status': ['preview'],
- 'supported_by': 'certified'}
-
-DOCUMENTATION = '''
-short_description: NetApp ONTAP manage consistency group snapshot
-author: NetApp Ansible Team (@carchi8py) <ng-ansibleteam@netapp.com>
-description:
- - Create consistency group snapshot for ONTAP volumes.
-extends_documentation_fragment:
- - netapp.na_ontap
-module: na_ontap_cg_snapshot
-options:
- state:
- description:
- - If you want to create a snapshot.
- default: present
- vserver:
- required: true
- description:
- - Name of the vserver.
- volumes:
- required: true
- description:
- - A list of volumes in this filer that is part of this CG operation.
- snapshot:
- required: true
- description:
- - The provided name of the snapshot that is created in each volume.
- timeout:
- description:
- - Timeout selector.
- choices: ['urgent', 'medium', 'relaxed']
- default: medium
- snapmirror_label:
- description:
- - A human readable SnapMirror label to be attached with the consistency group snapshot copies.
-version_added: "2.7"
-
-'''
-
-EXAMPLES = """
- - name:
- na_ontap_cg_snapshot:
- state: present
- vserver: vserver_name
- snapshot: snapshot name
- volumes: vol_name
- username: "{{ netapp username }}"
- password: "{{ netapp password }}"
- hostname: "{{ netapp hostname }}"
-"""
-
-RETURN = """
-"""
-
-import traceback
-
-from ansible.module_utils.basic import AnsibleModule
-from ansible.module_utils._text import to_native
-import ansible.module_utils.netapp as netapp_utils
-
-HAS_NETAPP_LIB = netapp_utils.has_netapp_lib()
-
-
-class NetAppONTAPCGSnapshot(object):
- """
- Methods to create CG snapshots
- """
-
- def __init__(self):
- self.argument_spec = netapp_utils.na_ontap_host_argument_spec()
- self.argument_spec.update(dict(
- state=dict(required=False, default='present'),
- vserver=dict(required=True, type='str'),
- volumes=dict(required=True, type='list'),
- snapshot=dict(required=True, type='str'),
- timeout=dict(required=False, type='str', choices=[
- 'urgent', 'medium', 'relaxed'], default='medium'),
- snapmirror_label=dict(required=False, type='str')
- ))
-
- self.module = AnsibleModule(
- argument_spec=self.argument_spec,
- supports_check_mode=True
- )
-
- parameters = self.module.params
-
- # set up variables
- self.state = parameters['state']
- self.vserver = parameters['vserver']
- self.volumes = parameters['volumes']
- self.snapshot = parameters['snapshot']
- self.timeout = parameters['timeout']
- self.snapmirror_label = parameters['snapmirror_label']
- self.cgid = None
-
- if HAS_NETAPP_LIB is False:
- self.module.fail_json(
- msg="the python NetApp-Lib module is required")
- else:
- self.server = netapp_utils.setup_na_ontap_zapi(
- module=self.module, vserver=self.vserver)
-
- def does_snapshot_exist(self, volume):
- """
- This is duplicated from na_ontap_snapshot
- Checks to see if a snapshot exists or not
- :return: Return True if a snapshot exists, false if it doesn't
- """
- # TODO: Remove this method and import snapshot module and
- # call get after re-factoring __init__ across all the modules
- # we aren't importing now, since __init__ does a lot of Ansible setup
- snapshot_obj = netapp_utils.zapi.NaElement("snapshot-get-iter")
- desired_attr = netapp_utils.zapi.NaElement("desired-attributes")
- snapshot_info = netapp_utils.zapi.NaElement('snapshot-info')
- comment = netapp_utils.zapi.NaElement('comment')
- # add more desired attributes that are allowed to be modified
- snapshot_info.add_child_elem(comment)
- desired_attr.add_child_elem(snapshot_info)
- snapshot_obj.add_child_elem(desired_attr)
- # compose query
- query = netapp_utils.zapi.NaElement("query")
- snapshot_info_obj = netapp_utils.zapi.NaElement("snapshot-info")
- snapshot_info_obj.add_new_child("name", self.snapshot)
- snapshot_info_obj.add_new_child("volume", volume)
- snapshot_info_obj.add_new_child("vserver", self.vserver)
- query.add_child_elem(snapshot_info_obj)
- snapshot_obj.add_child_elem(query)
- result = self.server.invoke_successfully(snapshot_obj, True)
- return_value = None
- if result.get_child_by_name('num-records') and \
- int(result.get_child_content('num-records')) == 1:
- attributes_list = result.get_child_by_name('attributes-list')
- snap_info = attributes_list.get_child_by_name('snapshot-info')
- return_value = {'comment': snap_info.get_child_content('comment')}
- return return_value
-
- def cgcreate(self):
- """
- Calls cg-start and cg-commit (when cg-start succeeds)
- """
- started = self.cg_start()
- if started:
- if self.cgid is not None:
- self.cg_commit()
- else:
- self.module.fail_json(msg="Error fetching CG ID for CG commit %s" % self.snapshot,
- exception=traceback.format_exc())
- return started
-
- def cg_start(self):
- """
- For the given list of volumes, creates cg-snapshot
- """
- snapshot_started = False
- cgstart = netapp_utils.zapi.NaElement("cg-start")
- cgstart.add_new_child("snapshot", self.snapshot)
- cgstart.add_new_child("timeout", self.timeout)
- volume_list = netapp_utils.zapi.NaElement("volumes")
- cgstart.add_child_elem(volume_list)
- for vol in self.volumes:
- snapshot_exists = self.does_snapshot_exist(vol)
- if snapshot_exists is None:
- snapshot_started = True
- volume_list.add_new_child("volume-name", vol)
- if snapshot_started:
- if self.snapmirror_label:
- cgstart.add_new_child("snapmirror-label",
- self.snapmirror_label)
- try:
- cgresult = self.server.invoke_successfully(
- cgstart, enable_tunneling=True)
- if cgresult.get_child_by_name('cg-id'):
- self.cgid = cgresult['cg-id']
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg="Error creating CG snapshot %s: %s" %
- (self.snapshot, to_native(error)),
- exception=traceback.format_exc())
- return snapshot_started
-
- def cg_commit(self):
- """
- When cg-start is successful, performs a cg-commit with the cg-id
- """
- cgcommit = netapp_utils.zapi.NaElement.create_node_with_children(
- 'cg-commit', **{'cg-id': self.cgid})
- try:
- self.server.invoke_successfully(cgcommit,
- enable_tunneling=True)
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg="Error committing CG snapshot %s: %s" %
- (self.snapshot, to_native(error)),
- exception=traceback.format_exc())
-
- def apply(self):
- '''Applies action from playbook'''
- netapp_utils.ems_log_event("na_ontap_cg_snapshot", self.server)
- changed = self.cgcreate()
- self.module.exit_json(changed=changed)
-
-
-def main():
- '''Execute action from playbook'''
- cg_obj = NetAppONTAPCGSnapshot()
- cg_obj.apply()
-
-
-if __name__ == '__main__':
- main()
diff --git a/lib/ansible/modules/storage/netapp/na_ontap_cifs.py b/lib/ansible/modules/storage/netapp/na_ontap_cifs.py
deleted file mode 100644
index 160e7ee1da..0000000000
--- a/lib/ansible/modules/storage/netapp/na_ontap_cifs.py
+++ /dev/null
@@ -1,306 +0,0 @@
-#!/usr/bin/python
-
-# (c) 2018-2019, NetApp, Inc
-# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-# import untangle
-
-from __future__ import absolute_import, division, print_function
-__metaclass__ = type
-
-ANSIBLE_METADATA = {'metadata_version': '1.1',
- 'status': ['preview'],
- 'supported_by': 'certified'}
-
-DOCUMENTATION = '''
-author: NetApp Ansible Team (@carchi8py) <ng-ansibleteam@netapp.com>
-description:
- - "Create or destroy or modify(path) cifs-share on ONTAP"
-extends_documentation_fragment:
- - netapp.na_ontap
-module: na_ontap_cifs
-
-options:
-
- path:
- description:
- The file system path that is shared through this CIFS share. The path is the full, user visible path relative
- to the vserver root, and it might be crossing junction mount points. The path is in UTF8 and uses forward
- slash as directory separator
- required: false
-
- vserver:
- description:
- - "Vserver containing the CIFS share."
- required: true
-
- share_name:
- description:
- The name of the CIFS share. The CIFS share name is a UTF-8 string with the following characters being
- illegal; control characters from 0x00 to 0x1F, both inclusive, 0x22 (double quotes)
- required: true
-
- share_properties:
- description:
- - The list of properties for the CIFS share
- required: false
- version_added: '2.8'
-
- symlink_properties:
- description:
- - The list of symlink properties for this CIFS share
- required: false
- version_added: '2.8'
-
- state:
- choices: ['present', 'absent']
- description:
- - "Whether the specified CIFS share should exist or not."
- required: false
- default: present
-
- vscan_fileop_profile:
- choices: ['no_scan', 'standard', 'strict', 'writes_only']
- description:
- - Profile_set of file_ops to which vscan on access scanning is applicable.
- required: false
- version_added: '2.9'
-
-short_description: NetApp ONTAP Manage cifs-share
-version_added: "2.6"
-
-'''
-
-EXAMPLES = """
- - name: Create CIFS share
- na_ontap_cifs:
- state: present
- share_name: cifsShareName
- path: /
- vserver: vserverName
- share_properties: browsable,oplocks
- symlink_properties: read_only,enable
- hostname: "{{ netapp_hostname }}"
- username: "{{ netapp_username }}"
- password: "{{ netapp_password }}"
- - name: Delete CIFS share
- na_ontap_cifs:
- state: absent
- share_name: cifsShareName
- vserver: vserverName
- hostname: "{{ netapp_hostname }}"
- username: "{{ netapp_username }}"
- password: "{{ netapp_password }}"
- - name: Modify path CIFS share
- na_ontap_cifs:
- state: present
- share_name: pb_test
- vserver: vserverName
- path: /
- share_properties: show_previous_versions
- symlink_properties: disable
- vscan_fileop_profile: no_scan
- hostname: "{{ netapp_hostname }}"
- username: "{{ netapp_username }}"
- password: "{{ netapp_password }}"
-"""
-
-RETURN = """
-"""
-
-
-import traceback
-
-from ansible.module_utils.basic import AnsibleModule
-from ansible.module_utils._text import to_native
-import ansible.module_utils.netapp as netapp_utils
-from ansible.module_utils.netapp_module import NetAppModule
-
-HAS_NETAPP_LIB = netapp_utils.has_netapp_lib()
-
-
-class NetAppONTAPCifsShare(object):
- """
- Methods to create/delete/modify(path) CIFS share
- """
-
- def __init__(self):
- self.argument_spec = netapp_utils.na_ontap_host_argument_spec()
- self.argument_spec.update(dict(
- state=dict(required=False, type='str', choices=[
- 'present', 'absent'], default='present'),
- share_name=dict(required=True, type='str'),
- path=dict(required=False, type='str'),
- vserver=dict(required=True, type='str'),
- share_properties=dict(required=False, type='list'),
- symlink_properties=dict(required=False, type='list'),
- vscan_fileop_profile=dict(required=False, type='str', choices=['no_scan', 'standard', 'strict', 'writes_only'])
- ))
-
- self.module = AnsibleModule(
- argument_spec=self.argument_spec,
- supports_check_mode=True
- )
-
- self.na_helper = NetAppModule()
- self.parameters = self.na_helper.set_parameters(self.module.params)
-
- if HAS_NETAPP_LIB is False:
- self.module.fail_json(
- msg="the python NetApp-Lib module is required")
- else:
- self.server = netapp_utils.setup_na_ontap_zapi(
- module=self.module, vserver=self.parameters.get('vserver'))
-
- def get_cifs_share(self):
- """
- Return details about the cifs-share
- :param:
- name : Name of the cifs-share
- :return: Details about the cifs-share. None if not found.
- :rtype: dict
- """
- cifs_iter = netapp_utils.zapi.NaElement('cifs-share-get-iter')
- cifs_info = netapp_utils.zapi.NaElement('cifs-share')
- cifs_info.add_new_child('share-name', self.parameters.get('share_name'))
- cifs_info.add_new_child('vserver', self.parameters.get('vserver'))
-
- query = netapp_utils.zapi.NaElement('query')
- query.add_child_elem(cifs_info)
-
- cifs_iter.add_child_elem(query)
-
- result = self.server.invoke_successfully(cifs_iter, True)
-
- return_value = None
- # check if query returns the expected cifs-share
- if result.get_child_by_name('num-records') and \
- int(result.get_child_content('num-records')) == 1:
- properties_list = []
- symlink_list = []
- cifs_attrs = result.get_child_by_name('attributes-list').\
- get_child_by_name('cifs-share')
- if cifs_attrs.get_child_by_name('share-properties'):
- properties_attrs = cifs_attrs['share-properties']
- if properties_attrs is not None:
- properties_list = [property.get_content() for property in properties_attrs.get_children()]
- if cifs_attrs.get_child_by_name('symlink-properties'):
- symlink_attrs = cifs_attrs['symlink-properties']
- if symlink_attrs is not None:
- symlink_list = [symlink.get_content() for symlink in symlink_attrs.get_children()]
- return_value = {
- 'share': cifs_attrs.get_child_content('share-name'),
- 'path': cifs_attrs.get_child_content('path'),
- 'share_properties': properties_list,
- 'symlink_properties': symlink_list
- }
- if cifs_attrs.get_child_by_name('vscan-fileop-profile'):
- return_value['vscan_fileop_profile'] = cifs_attrs['vscan-fileop-profile']
-
- return return_value
-
- def create_cifs_share(self):
- """
- Create CIFS share
- """
- options = {'share-name': self.parameters.get('share_name'),
- 'path': self.parameters.get('path')}
- cifs_create = netapp_utils.zapi.NaElement.create_node_with_children(
- 'cifs-share-create', **options)
- if self.parameters.get('share_properties'):
- property_attrs = netapp_utils.zapi.NaElement('share-properties')
- cifs_create.add_child_elem(property_attrs)
- for property in self.parameters.get('share_properties'):
- property_attrs.add_new_child('cifs-share-properties', property)
- if self.parameters.get('symlink_properties'):
- symlink_attrs = netapp_utils.zapi.NaElement('symlink-properties')
- cifs_create.add_child_elem(symlink_attrs)
- for symlink in self.parameters.get('symlink_properties'):
- symlink_attrs.add_new_child('cifs-share-symlink-properties', symlink)
- if self.parameters.get('vscan_fileop_profile'):
- fileop_attrs = netapp_utils.zapi.NaElement('vscan-fileop-profile')
- fileop_attrs.set_content(self.parameters['vscan_fileop_profile'])
- cifs_create.add_child_elem(fileop_attrs)
-
- try:
- self.server.invoke_successfully(cifs_create,
- enable_tunneling=True)
- except netapp_utils.zapi.NaApiError as error:
-
- self.module.fail_json(msg='Error creating cifs-share %s: %s'
- % (self.parameters.get('share_name'), to_native(error)),
- exception=traceback.format_exc())
-
- def delete_cifs_share(self):
- """
- Delete CIFS share
- """
- cifs_delete = netapp_utils.zapi.NaElement.create_node_with_children(
- 'cifs-share-delete', **{'share-name': self.parameters.get('share_name')})
-
- try:
- self.server.invoke_successfully(cifs_delete,
- enable_tunneling=True)
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg='Error deleting cifs-share %s: %s'
- % (self.parameters.get('share_name'), to_native(error)),
- exception=traceback.format_exc())
-
- def modify_cifs_share(self):
- """
- modify path for the given CIFS share
- """
- options = {'share-name': self.parameters.get('share_name')}
- cifs_modify = netapp_utils.zapi.NaElement.create_node_with_children(
- 'cifs-share-modify', **options)
- if self.parameters.get('path'):
- cifs_modify.add_new_child('path', self.parameters.get('path'))
- if self.parameters.get('share_properties'):
- property_attrs = netapp_utils.zapi.NaElement('share-properties')
- cifs_modify.add_child_elem(property_attrs)
- for property in self.parameters.get('share_properties'):
- property_attrs.add_new_child('cifs-share-properties', property)
- if self.parameters.get('symlink_properties'):
- symlink_attrs = netapp_utils.zapi.NaElement('symlink-properties')
- cifs_modify.add_child_elem(symlink_attrs)
- for property in self.parameters.get('symlink_properties'):
- symlink_attrs.add_new_child('cifs-share-symlink-properties', property)
- if self.parameters.get('vscan_fileop_profile'):
- fileop_attrs = netapp_utils.zapi.NaElement('vscan-fileop-profile')
- fileop_attrs.set_content(self.parameters['vscan_fileop_profile'])
- cifs_modify.add_child_elem(fileop_attrs)
- try:
- self.server.invoke_successfully(cifs_modify,
- enable_tunneling=True)
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg='Error modifying cifs-share %s:%s'
- % (self.parameters.get('share_name'), to_native(error)),
- exception=traceback.format_exc())
-
- def apply(self):
- '''Apply action to cifs share'''
- netapp_utils.ems_log_event("na_ontap_cifs", self.server)
- current = self.get_cifs_share()
- cd_action = self.na_helper.get_cd_action(current, self.parameters)
- if cd_action is None:
- modify = self.na_helper.get_modified_attributes(current, self.parameters)
- if self.na_helper.changed:
- if self.module.check_mode:
- pass
- else:
- if cd_action == 'create':
- self.create_cifs_share()
- elif cd_action == 'delete':
- self.delete_cifs_share()
- elif modify:
- self.modify_cifs_share()
- self.module.exit_json(changed=self.na_helper.changed)
-
-
-def main():
- '''Execute action from playbook'''
- cifs_obj = NetAppONTAPCifsShare()
- cifs_obj.apply()
-
-
-if __name__ == '__main__':
- main()
diff --git a/lib/ansible/modules/storage/netapp/na_ontap_cifs_acl.py b/lib/ansible/modules/storage/netapp/na_ontap_cifs_acl.py
deleted file mode 100644
index f159c33752..0000000000
--- a/lib/ansible/modules/storage/netapp/na_ontap_cifs_acl.py
+++ /dev/null
@@ -1,238 +0,0 @@
-#!/usr/bin/python
-
-# (c) 2018-2019, NetApp, Inc
-# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-
-from __future__ import absolute_import, division, print_function
-__metaclass__ = type
-
-ANSIBLE_METADATA = {'metadata_version': '1.1',
- 'status': ['preview'],
- 'supported_by': 'certified'}
-
-
-DOCUMENTATION = '''
-author: NetApp Ansible Team (@carchi8py) <ng-ansibleteam@netapp.com>
-description:
- - "Create or destroy or modify cifs-share-access-controls on ONTAP"
-extends_documentation_fragment:
- - netapp.na_ontap
-module: na_ontap_cifs_acl
-options:
- permission:
- choices: ['no_access', 'read', 'change', 'full_control']
- description:
- -"The access rights that the user or group has on the defined CIFS share."
- share_name:
- description:
- - "The name of the cifs-share-access-control to manage."
- required: true
- state:
- choices: ['present', 'absent']
- description:
- - "Whether the specified CIFS share acl should exist or not."
- default: present
- vserver:
- description:
- - Name of the vserver to use.
- required: true
- user_or_group:
- description:
- - "The user or group name for which the permissions are listed."
- required: true
-short_description: NetApp ONTAP manage cifs-share-access-control
-version_added: "2.6"
-
-'''
-
-EXAMPLES = """
- - name: Create CIFS share acl
- na_ontap_cifs_acl:
- state: present
- share_name: cifsShareName
- user_or_group: Everyone
- permission: read
- vserver: "{{ netapp_vserver }}"
- hostname: "{{ netapp_hostname }}"
- username: "{{ netapp_username }}"
- password: "{{ netapp_password }}"
- - name: Modify CIFS share acl permission
- na_ontap_cifs_acl:
- state: present
- share_name: cifsShareName
- user_or_group: Everyone
- permission: change
- vserver: "{{ netapp_vserver }}"
- hostname: "{{ netapp_hostname }}"
- username: "{{ netapp_username }}"
- password: "{{ netapp_password }}"
-"""
-
-RETURN = """
-"""
-
-
-import traceback
-from ansible.module_utils.basic import AnsibleModule
-from ansible.module_utils._text import to_native
-import ansible.module_utils.netapp as netapp_utils
-
-HAS_NETAPP_LIB = netapp_utils.has_netapp_lib()
-
-
-class NetAppONTAPCifsAcl(object):
- """
- Methods to create/delete/modify CIFS share/user access-control
- """
-
- def __init__(self):
- self.argument_spec = netapp_utils.na_ontap_host_argument_spec()
- self.argument_spec.update(dict(
- state=dict(required=False, type='str', choices=['present', 'absent'], default='present'),
- vserver=dict(required=True, type='str'),
- share_name=dict(required=True, type='str'),
- user_or_group=dict(required=True, type='str'),
- permission=dict(required=False, type='str', choices=['no_access', 'read', 'change', 'full_control'])
- ))
- self.module = AnsibleModule(
- argument_spec=self.argument_spec,
- required_if=[
- ('state', 'present', ['share_name', 'user_or_group', 'permission'])
- ],
- supports_check_mode=True
- )
- parameters = self.module.params
- # set up state variables
- self.state = parameters['state']
- self.vserver = parameters['vserver']
- self.share_name = parameters['share_name']
- self.user_or_group = parameters['user_or_group']
- self.permission = parameters['permission']
-
- if HAS_NETAPP_LIB is False:
- self.module.fail_json(msg="the python NetApp-Lib module is required")
- else:
- self.server = netapp_utils.setup_na_ontap_zapi(module=self.module, vserver=self.vserver)
-
- def get_cifs_acl(self):
- """
- Return details about the cifs-share-access-control
- :param:
- name : Name of the cifs-share-access-control
- :return: Details about the cifs-share-access-control. None if not found.
- :rtype: dict
- """
- cifs_acl_iter = netapp_utils.zapi.NaElement('cifs-share-access-control-get-iter')
- cifs_acl_info = netapp_utils.zapi.NaElement('cifs-share-access-control')
- cifs_acl_info.add_new_child('share', self.share_name)
- cifs_acl_info.add_new_child('user-or-group', self.user_or_group)
- cifs_acl_info.add_new_child('vserver', self.vserver)
- query = netapp_utils.zapi.NaElement('query')
- query.add_child_elem(cifs_acl_info)
- cifs_acl_iter.add_child_elem(query)
- result = self.server.invoke_successfully(cifs_acl_iter, True)
- return_value = None
- # check if query returns the expected cifs-share-access-control
- if result.get_child_by_name('num-records') and \
- int(result.get_child_content('num-records')) == 1:
-
- cifs_acl = result.get_child_by_name('attributes-list').get_child_by_name('cifs-share-access-control')
- return_value = {
- 'share': cifs_acl.get_child_content('share'),
- 'user-or-group': cifs_acl.get_child_content('user-or-group'),
- 'permission': cifs_acl.get_child_content('permission')
- }
-
- return return_value
-
- def create_cifs_acl(self):
- """
- Create access control for the given CIFS share/user-group
- """
- cifs_acl_create = netapp_utils.zapi.NaElement.create_node_with_children(
- 'cifs-share-access-control-create', **{'share': self.share_name,
- 'user-or-group': self.user_or_group,
- 'permission': self.permission})
- try:
- self.server.invoke_successfully(cifs_acl_create,
- enable_tunneling=True)
- except netapp_utils.zapi.NaApiError as error:
-
- self.module.fail_json(msg='Error creating cifs-share-access-control %s: %s'
- % (self.share_name, to_native(error)),
- exception=traceback.format_exc())
-
- def delete_cifs_acl(self):
- """
- Delete access control for the given CIFS share/user-group
- """
- cifs_acl_delete = netapp_utils.zapi.NaElement.create_node_with_children(
- 'cifs-share-access-control-delete', **{'share': self.share_name,
- 'user-or-group': self.user_or_group})
- try:
- self.server.invoke_successfully(cifs_acl_delete,
- enable_tunneling=True)
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg='Error deleting cifs-share-access-control %s: %s'
- % (self.share_name, to_native(error)),
- exception=traceback.format_exc())
-
- def modify_cifs_acl_permission(self):
- """
- Change permission for the given CIFS share/user-group
- """
- cifs_acl_modify = netapp_utils.zapi.NaElement.create_node_with_children(
- 'cifs-share-access-control-modify', **{'share': self.share_name,
- 'user-or-group': self.user_or_group,
- 'permission': self.permission})
- try:
- self.server.invoke_successfully(cifs_acl_modify,
- enable_tunneling=True)
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg='Error modifying cifs-share-access-control permission %s:%s'
- % (self.share_name, to_native(error)),
- exception=traceback.format_exc())
-
- def apply(self):
- """
- Apply action to cifs-share-access-control
- """
- changed = False
- cifs_acl_exists = False
- netapp_utils.ems_log_event("na_ontap_cifs_acl", self.server)
- cifs_acl_details = self.get_cifs_acl()
- if cifs_acl_details:
- cifs_acl_exists = True
- if self.state == 'absent': # delete
- changed = True
- elif self.state == 'present':
- if cifs_acl_details['permission'] != self.permission: # rename
- changed = True
- else:
- if self.state == 'present': # create
- changed = True
- if changed:
- if self.module.check_mode:
- pass
- else:
- if self.state == 'present': # execute create
- if not cifs_acl_exists:
- self.create_cifs_acl()
- else: # execute modify
- self.modify_cifs_acl_permission()
- elif self.state == 'absent': # execute delete
- self.delete_cifs_acl()
-
- self.module.exit_json(changed=changed)
-
-
-def main():
- """
- Execute action from playbook
- """
- cifs_acl = NetAppONTAPCifsAcl()
- cifs_acl.apply()
-
-
-if __name__ == '__main__':
- main()
diff --git a/lib/ansible/modules/storage/netapp/na_ontap_cifs_server.py b/lib/ansible/modules/storage/netapp/na_ontap_cifs_server.py
deleted file mode 100644
index b4fc9774e7..0000000000
--- a/lib/ansible/modules/storage/netapp/na_ontap_cifs_server.py
+++ /dev/null
@@ -1,329 +0,0 @@
-#!/usr/bin/python
-""" this is cifs_server module
-
- (c) 2018-2019, NetApp, Inc
- # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-"""
-
-from __future__ import absolute_import, division, print_function
-__metaclass__ = type
-
-ANSIBLE_METADATA = {
- 'metadata_version': '1.1',
- 'status': ['preview'],
- 'supported_by': 'certified'
-}
-
-DOCUMENTATION = '''
----
-module: na_ontap_cifs_server
-short_description: NetApp ONTAP CIFS server configuration
-extends_documentation_fragment:
- - netapp.na_ontap
-version_added: '2.6'
-author: NetApp Ansible Team (@carchi8py) <ng-ansibleteam@netapp.com>
-
-description:
- - Creating / deleting and modifying the CIFS server .
-
-options:
-
- state:
- description:
- - Whether the specified cifs_server should exist or not.
- default: present
- choices: ['present', 'absent']
-
- service_state:
- description:
- - CIFS Server Administrative Status.
- choices: ['stopped', 'started']
-
- name:
- description:
- - Specifies the cifs_server name.
- required: true
- aliases: ['cifs_server_name']
-
- admin_user_name:
- description:
- - Specifies the cifs server admin username.
-
- admin_password:
- description:
- - Specifies the cifs server admin password.
-
- domain:
- description:
- - The Fully Qualified Domain Name of the Windows Active Directory this CIFS server belongs to.
-
- workgroup:
- description:
- - The NetBIOS name of the domain or workgroup this CIFS server belongs to.
-
- ou:
- description:
- - The Organizational Unit (OU) within the Windows Active Directory
- this CIFS server belongs to.
- version_added: '2.7'
-
- force:
- type: bool
- description:
- - If this is set and a machine account with the same name as
- specified in 'name' exists in the Active Directory, it
- will be overwritten and reused.
- version_added: '2.7'
-
- vserver:
- description:
- - The name of the vserver to use.
- required: true
-
-'''
-
-EXAMPLES = '''
- - name: Create cifs_server
- na_ontap_cifs_server:
- state: present
- vserver: svm1
- hostname: "{{ netapp_hostname }}"
- username: "{{ netapp_username }}"
- password: "{{ netapp_password }}"
-
- - name: Delete cifs_server
- na_ontap_cifs_server:
- state: absent
- name: data2
- vserver: svm1
- hostname: "{{ netapp_hostname }}"
- username: "{{ netapp_username }}"
- password: "{{ netapp_password }}"
-
-'''
-
-RETURN = '''
-'''
-
-import traceback
-from ansible.module_utils.basic import AnsibleModule
-from ansible.module_utils._text import to_native
-import ansible.module_utils.netapp as netapp_utils
-
-HAS_NETAPP_LIB = netapp_utils.has_netapp_lib()
-
-
-class NetAppOntapcifsServer(object):
- """
- object to describe cifs_server info
- """
-
- def __init__(self):
-
- self.argument_spec = netapp_utils.na_ontap_host_argument_spec()
- self.argument_spec.update(dict(
- state=dict(required=False, choices=['present', 'absent'], default='present'),
- service_state=dict(required=False, choices=['stopped', 'started']),
- name=dict(required=True, type='str', aliases=['cifs_server_name']),
- workgroup=dict(required=False, type='str', default=None),
- domain=dict(required=False, type='str'),
- admin_user_name=dict(required=False, type='str'),
- admin_password=dict(required=False, type='str', no_log=True),
- ou=dict(required=False, type='str'),
- force=dict(required=False, type='bool'),
- vserver=dict(required=True, type='str'),
- ))
-
- self.module = AnsibleModule(
- argument_spec=self.argument_spec,
- supports_check_mode=True
- )
-
- params = self.module.params
-
- # set up state variables
- self.state = params['state']
- self.cifs_server_name = params['cifs_server_name']
- self.workgroup = params['workgroup']
- self.domain = params['domain']
- self.vserver = params['vserver']
- self.service_state = params['service_state']
- self.admin_user_name = params['admin_user_name']
- self.admin_password = params['admin_password']
- self.ou = params['ou']
- self.force = params['force']
-
- if HAS_NETAPP_LIB is False:
- self.module.fail_json(msg="the python NetApp-Lib module is required")
- else:
- self.server = netapp_utils.setup_na_ontap_zapi(module=self.module, vserver=self.vserver)
-
- def get_cifs_server(self):
- """
- Return details about the CIFS-server
- :param:
- name : Name of the name of the cifs_server
-
- :return: Details about the cifs_server. None if not found.
- :rtype: dict
- """
- cifs_server_info = netapp_utils.zapi.NaElement('cifs-server-get-iter')
- cifs_server_attributes = netapp_utils.zapi.NaElement('cifs-server-config')
- cifs_server_attributes.add_new_child('cifs-server', self.cifs_server_name)
- cifs_server_attributes.add_new_child('vserver', self.vserver)
- query = netapp_utils.zapi.NaElement('query')
- query.add_child_elem(cifs_server_attributes)
- cifs_server_info.add_child_elem(query)
- result = self.server.invoke_successfully(cifs_server_info, True)
- return_value = None
-
- if result.get_child_by_name('num-records') and \
- int(result.get_child_content('num-records')) >= 1:
-
- cifs_server_attributes = result.get_child_by_name('attributes-list').\
- get_child_by_name('cifs-server-config')
- return_value = {
- 'cifs_server_name': self.cifs_server_name,
- 'administrative-status': cifs_server_attributes.get_child_content('administrative-status')
- }
-
- return return_value
-
- def create_cifs_server(self):
- """
- calling zapi to create cifs_server
- """
- options = {'cifs-server': self.cifs_server_name, 'administrative-status': 'up'
- if self.service_state == 'started' else 'down'}
- if self.workgroup is not None:
- options['workgroup'] = self.workgroup
- if self.domain is not None:
- options['domain'] = self.domain
- if self.admin_user_name is not None:
- options['admin-username'] = self.admin_user_name
- if self.admin_password is not None:
- options['admin-password'] = self.admin_password
- if self.ou is not None:
- options['organizational-unit'] = self.ou
- if self.force is not None:
- options['force-account-overwrite'] = str(self.force).lower()
-
- cifs_server_create = netapp_utils.zapi.NaElement.create_node_with_children(
- 'cifs-server-create', **options)
-
- try:
- self.server.invoke_successfully(cifs_server_create,
- enable_tunneling=True)
- except netapp_utils.zapi.NaApiError as exc:
- self.module.fail_json(msg='Error Creating cifs_server %s: %s' %
- (self.cifs_server_name, to_native(exc)), exception=traceback.format_exc())
-
- def delete_cifs_server(self):
- """
- calling zapi to create cifs_server
- """
- if self.cifs_server_name == 'up':
- self.modify_cifs_server(admin_status='down')
-
- cifs_server_delete = netapp_utils.zapi.NaElement.create_node_with_children('cifs-server-delete')
-
- try:
- self.server.invoke_successfully(cifs_server_delete,
- enable_tunneling=True)
- except netapp_utils.zapi.NaApiError as exc:
- self.module.fail_json(msg='Error deleting cifs_server %s: %s' % (self.cifs_server_name, to_native(exc)),
- exception=traceback.format_exc())
-
- def modify_cifs_server(self, admin_status):
- """
- RModify the cifs_server.
- """
- cifs_server_modify = netapp_utils.zapi.NaElement.create_node_with_children(
- 'cifs-server-modify', **{'cifs-server': self.cifs_server_name,
- 'administrative-status': admin_status, 'vserver': self.vserver})
- try:
- self.server.invoke_successfully(cifs_server_modify,
- enable_tunneling=True)
- except netapp_utils.zapi.NaApiError as e:
- self.module.fail_json(msg='Error modifying cifs_server %s: %s' % (self.cifs_server_name, to_native(e)),
- exception=traceback.format_exc())
-
- def start_cifs_server(self):
- """
- RModify the cifs_server.
- """
- cifs_server_modify = netapp_utils.zapi.NaElement.create_node_with_children(
- 'cifs-server-start')
- try:
- self.server.invoke_successfully(cifs_server_modify,
- enable_tunneling=True)
- except netapp_utils.zapi.NaApiError as e:
- self.module.fail_json(msg='Error modifying cifs_server %s: %s' % (self.cifs_server_name, to_native(e)),
- exception=traceback.format_exc())
-
- def stop_cifs_server(self):
- """
- RModify the cifs_server.
- """
- cifs_server_modify = netapp_utils.zapi.NaElement.create_node_with_children(
- 'cifs-server-stop')
- try:
- self.server.invoke_successfully(cifs_server_modify,
- enable_tunneling=True)
- except netapp_utils.zapi.NaApiError as e:
- self.module.fail_json(msg='Error modifying cifs_server %s: %s' % (self.cifs_server_name, to_native(e)),
- exception=traceback.format_exc())
-
- def apply(self):
- """
- calling all cifs_server features
- """
-
- changed = False
- cifs_server_exists = False
- netapp_utils.ems_log_event("na_ontap_cifs_server", self.server)
- cifs_server_detail = self.get_cifs_server()
-
- if cifs_server_detail:
- cifs_server_exists = True
-
- if self.state == 'present':
- administrative_status = cifs_server_detail['administrative-status']
- if self.service_state == 'started' and administrative_status == 'down':
- changed = True
- if self.service_state == 'stopped' and administrative_status == 'up':
- changed = True
- else:
- # we will delete the CIFs server
- changed = True
- else:
- if self.state == 'present':
- changed = True
-
- if changed:
- if self.module.check_mode:
- pass
- else:
- if self.state == 'present':
- if not cifs_server_exists:
- self.create_cifs_server()
-
- elif self.service_state == 'stopped':
- self.stop_cifs_server()
-
- elif self.service_state == 'started':
- self.start_cifs_server()
-
- elif self.state == 'absent':
- self.delete_cifs_server()
-
- self.module.exit_json(changed=changed)
-
-
-def main():
- cifs_server = NetAppOntapcifsServer()
- cifs_server.apply()
-
-
-if __name__ == '__main__':
- main()
diff --git a/lib/ansible/modules/storage/netapp/na_ontap_cluster.py b/lib/ansible/modules/storage/netapp/na_ontap_cluster.py
deleted file mode 100644
index 2b3b21d3aa..0000000000
--- a/lib/ansible/modules/storage/netapp/na_ontap_cluster.py
+++ /dev/null
@@ -1,287 +0,0 @@
-#!/usr/bin/python
-
-# (c) 2017-2019, NetApp, Inc
-# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-
-from __future__ import absolute_import, division, print_function
-__metaclass__ = type
-
-ANSIBLE_METADATA = {'metadata_version': '1.1',
- 'status': ['preview'],
- 'supported_by': 'certified'}
-
-
-DOCUMENTATION = '''
-module: na_ontap_cluster
-short_description: NetApp ONTAP cluster - create, join, add license
-extends_documentation_fragment:
- - netapp.na_ontap
-version_added: '2.6'
-author: NetApp Ansible Team (@carchi8py) <ng-ansibleteam@netapp.com>
-description:
-- Create or join or apply licenses to ONTAP clusters
-- Cluster join can be performed using only one of the parameters, either cluster_name or cluster_ip_address
-options:
- state:
- description:
- - Whether the specified cluster should exist or not.
- choices: ['present']
- default: present
- cluster_name:
- description:
- - The name of the cluster to manage.
- cluster_ip_address:
- description:
- - IP address of cluster to be joined
- license_code:
- description:
- - License code to be applied to the cluster
- license_package:
- description:
- - License package name of the license to be removed
- node_serial_number:
- description:
- - Serial number of the cluster node
-
-'''
-
-EXAMPLES = """
- - name: Create cluster
- na_ontap_cluster:
- state: present
- cluster_name: new_cluster
- hostname: "{{ netapp_hostname }}"
- username: "{{ netapp_username }}"
- password: "{{ netapp_password }}"
- - name: Add license from cluster
- na_ontap_cluster:
- state: present
- cluster_name: FPaaS-A300-01
- license_code: SGHLQDBBVAAAAAAAAAAAAAAAAAAA
- hostname: "{{ netapp_hostname }}"
- username: "{{ netapp_username }}"
- password: "{{ netapp_password }}"
- - name: Join cluster
- na_ontap_cluster:
- state: present
- cluster_ip_address: 10.61.184.181
- hostname: "{{ netapp_hostname }}"
- username: "{{ netapp_username }}"
- password: "{{ netapp_password }}"
- - name: Join cluster
- na_ontap_cluster:
- state: present
- cluster_name: FPaaS-A300-01
- hostname: "{{ netapp_hostname }}"
- username: "{{ netapp_username }}"
- password: "{{ netapp_password }}"
-"""
-
-RETURN = """
-"""
-
-import traceback
-
-from ansible.module_utils.basic import AnsibleModule
-from ansible.module_utils._text import to_native
-import ansible.module_utils.netapp as netapp_utils
-from ansible.module_utils.netapp_module import NetAppModule
-
-HAS_NETAPP_LIB = netapp_utils.has_netapp_lib()
-
-
-def local_cmp(a, b):
- """
- compares with only values and not keys, keys should be the same for both dicts
- :param a: dict 1
- :param b: dict 2
- :return: difference of values in both dicts
- """
- diff = [key for key in a if a[key] != b[key]]
- return len(diff)
-
-
-class NetAppONTAPCluster(object):
- """
- object initialize and class methods
- """
- def __init__(self):
- self.argument_spec = netapp_utils.na_ontap_host_argument_spec()
- self.argument_spec.update(dict(
- state=dict(required=False, choices=['present'], default='present'),
- cluster_name=dict(required=False, type='str'),
- cluster_ip_address=dict(required=False, type='str'),
- license_code=dict(required=False, type='str'),
- license_package=dict(required=False, type='str'),
- node_serial_number=dict(required=False, type='str')
- ))
-
- self.module = AnsibleModule(
- argument_spec=self.argument_spec,
- supports_check_mode=True,
- required_together=[
- ['license_package', 'node_serial_number']
- ]
- )
-
- self.na_helper = NetAppModule()
- self.parameters = self.na_helper.set_parameters(self.module.params)
-
- if HAS_NETAPP_LIB is False:
- self.module.fail_json(msg="the python NetApp-Lib module is required")
- else:
- self.server = netapp_utils.setup_na_ontap_zapi(module=self.module)
-
- def get_licensing_status(self):
- """
- Check licensing status
-
- :return: package (key) and licensing status (value)
- :rtype: dict
- """
- license_status = netapp_utils.zapi.NaElement(
- 'license-v2-status-list-info')
- try:
- result = self.server.invoke_successfully(license_status,
- enable_tunneling=True)
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg="Error checking license status: %s" %
- to_native(error), exception=traceback.format_exc())
-
- return_dictionary = {}
- license_v2_status = result.get_child_by_name('license-v2-status')
- if license_v2_status:
- for license_v2_status_info in license_v2_status.get_children():
- package = license_v2_status_info.get_child_content('package')
- status = license_v2_status_info.get_child_content('method')
- return_dictionary[package] = status
-
- return return_dictionary
-
- def create_cluster(self):
- """
- Create a cluster
- """
- cluster_create = netapp_utils.zapi.NaElement.create_node_with_children(
- 'cluster-create', **{'cluster-name': self.parameters['cluster_name']})
-
- try:
- self.server.invoke_successfully(cluster_create,
- enable_tunneling=True)
- except netapp_utils.zapi.NaApiError as error:
- # Error 36503 denotes node already being used.
- if to_native(error.code) == "36503":
- return False
- else:
- self.module.fail_json(msg='Error creating cluster %s: %s'
- % (self.parameters['cluster_name'], to_native(error)),
- exception=traceback.format_exc())
- return True
-
- def cluster_join(self):
- """
- Add a node to an existing cluster
- """
- if self.parameters.get('cluster_ip_address') is not None:
- cluster_add_node = netapp_utils.zapi.NaElement.create_node_with_children(
- 'cluster-join', **{'cluster-ip-address': self.parameters['cluster_ip_address']})
- for_fail_attribute = self.parameters.get('cluster_ip_address')
- elif self.parameters.get('cluster_name') is not None:
- cluster_add_node = netapp_utils.zapi.NaElement.create_node_with_children(
- 'cluster-join', **{'cluster-name': self.parameters['cluster_name']})
- for_fail_attribute = self.parameters.get('cluster_name')
- else:
- return False
- try:
- self.server.invoke_successfully(cluster_add_node, enable_tunneling=True)
- except netapp_utils.zapi.NaApiError as error:
- # Error 36503 denotes node already being used.
- if to_native(error.code) == "36503":
- return False
- else:
- self.module.fail_json(msg='Error adding node to cluster %s: %s'
- % (for_fail_attribute, to_native(error)),
- exception=traceback.format_exc())
- return True
-
- def license_v2_add(self):
- """
- Apply a license to cluster
- """
- license_add = netapp_utils.zapi.NaElement.create_node_with_children('license-v2-add')
- license_add.add_node_with_children('codes', **{'license-code-v2': self.parameters['license_code']})
- try:
- self.server.invoke_successfully(license_add, enable_tunneling=True)
-
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg='Error adding license %s: %s'
- % (self.parameters['license_code'], to_native(error)),
- exception=traceback.format_exc())
-
- def license_v2_delete(self):
- """
- Delete license from cluster
- """
- license_delete = netapp_utils.zapi.NaElement.create_node_with_children(
- 'license-v2-delete', **{'package': self.parameters['license_package'],
- 'serial-number': self.parameters['node_serial_number']})
- try:
- self.server.invoke_successfully(license_delete, enable_tunneling=True)
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg='Error deleting license : %s' % (to_native(error)),
- exception=traceback.format_exc())
-
- def autosupport_log(self):
- """
- Autosupport log for cluster
- :return:
- """
- results = netapp_utils.get_cserver(self.server)
- cserver = netapp_utils.setup_na_ontap_zapi(module=self.module, vserver=results)
- netapp_utils.ems_log_event("na_ontap_cluster", cserver)
-
- def apply(self):
- """
- Apply action to cluster
- """
- property_changed = False
- create_flag = False
- join_flag = False
-
- self.autosupport_log()
- license_status = self.get_licensing_status()
-
- if self.module.check_mode:
- pass
- else:
- if self.parameters.get('state') == 'present':
- if self.parameters.get('cluster_name') is not None:
- create_flag = self.create_cluster()
- if not create_flag:
- join_flag = self.cluster_join()
- if self.parameters.get('license_code') is not None:
- self.license_v2_add()
- property_changed = True
- if self.parameters.get('license_package') is not None and\
- self.parameters.get('node_serial_number') is not None:
- if license_status.get(str(self.parameters.get('license_package')).lower()) != 'none':
- self.license_v2_delete()
- property_changed = True
- if property_changed:
- new_license_status = self.get_licensing_status()
- if local_cmp(license_status, new_license_status) == 0:
- property_changed = False
- changed = property_changed or create_flag or join_flag
- self.module.exit_json(changed=changed)
-
-
-def main():
- """
- Create object and call apply
- """
- cluster_obj = NetAppONTAPCluster()
- cluster_obj.apply()
-
-
-if __name__ == '__main__':
- main()
diff --git a/lib/ansible/modules/storage/netapp/na_ontap_cluster_ha.py b/lib/ansible/modules/storage/netapp/na_ontap_cluster_ha.py
deleted file mode 100644
index 2d29b0f860..0000000000
--- a/lib/ansible/modules/storage/netapp/na_ontap_cluster_ha.py
+++ /dev/null
@@ -1,133 +0,0 @@
-#!/usr/bin/python
-
-# (c) 2018-2019, NetApp, Inc
-# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-
-from __future__ import absolute_import, division, print_function
-__metaclass__ = type
-
-ANSIBLE_METADATA = {'metadata_version': '1.1',
- 'status': ['preview'],
- 'supported_by': 'certified'}
-
-
-DOCUMENTATION = '''
-author: NetApp Ansible Team (@carchi8py) <ng-ansibleteam@netapp.com>
-description:
- - "Enable or disable HA on a cluster"
-extends_documentation_fragment:
- - netapp.na_ontap
-module: na_ontap_cluster_ha
-options:
- state:
- choices: ['present', 'absent']
- description:
- - "Whether HA on cluster should be enabled or disabled."
- default: present
-short_description: NetApp ONTAP Manage HA status for cluster
-version_added: "2.6"
-'''
-
-EXAMPLES = """
- - name: "Enable HA status for cluster"
- na_ontap_cluster_ha:
- state: present
- hostname: "{{ netapp_hostname }}"
- username: "{{ netapp_username }}"
- password: "{{ netapp_password }}"
-"""
-
-RETURN = """
-"""
-
-import traceback
-from ansible.module_utils.basic import AnsibleModule
-from ansible.module_utils._text import to_native
-import ansible.module_utils.netapp as netapp_utils
-from ansible.module_utils.netapp_module import NetAppModule
-
-HAS_NETAPP_LIB = netapp_utils.has_netapp_lib()
-
-
-class NetAppOntapClusterHA(object):
- """
- object initialize and class methods
- """
- def __init__(self):
-
- self.argument_spec = netapp_utils.na_ontap_host_argument_spec()
- self.argument_spec.update(dict(
- state=dict(required=False, choices=['present', 'absent'], default='present'),
- ))
- self.module = AnsibleModule(
- argument_spec=self.argument_spec,
- supports_check_mode=True
- )
-
- self.na_helper = NetAppModule()
- self.parameters = self.na_helper.set_parameters(self.module.params)
-
- if HAS_NETAPP_LIB is False:
- self.module.fail_json(msg="the python NetApp-Lib module is required")
- else:
- self.server = netapp_utils.setup_na_ontap_zapi(module=self.module)
-
- def modify_cluster_ha(self, configure):
- """
- Enable or disable HA on cluster
- :return: None
- """
- cluster_ha_modify = netapp_utils.zapi.NaElement.create_node_with_children(
- 'cluster-ha-modify', **{'ha-configured': configure})
- try:
- self.server.invoke_successfully(cluster_ha_modify,
- enable_tunneling=True)
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg='Error modifying cluster HA to %s: %s'
- % (configure, to_native(error)),
- exception=traceback.format_exc())
-
- def get_cluster_ha_enabled(self):
- """
- Get current cluster HA details
- :return: dict if enabled, None if disabled
- """
- cluster_ha_get = netapp_utils.zapi.NaElement('cluster-ha-get')
- try:
- result = self.server.invoke_successfully(cluster_ha_get,
- enable_tunneling=True)
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg='Error fetching cluster HA details',
- exception=traceback.format_exc())
- cluster_ha_info = result.get_child_by_name('attributes').get_child_by_name('cluster-ha-info')
- if cluster_ha_info.get_child_content('ha-configured') == 'true':
- return {'ha-configured': True}
- return None
-
- def apply(self):
- """
- Apply action to cluster HA
- """
- results = netapp_utils.get_cserver(self.server)
- cserver = netapp_utils.setup_na_ontap_zapi(module=self.module, vserver=results)
- netapp_utils.ems_log_event("na_ontap_cluster_ha", cserver)
- current = self.get_cluster_ha_enabled()
- cd_action = self.na_helper.get_cd_action(current, self.parameters)
- if cd_action == 'create':
- self.modify_cluster_ha("true")
- elif cd_action == 'delete':
- self.modify_cluster_ha("false")
-
- self.module.exit_json(changed=self.na_helper.changed)
-
-
-def main():
- """
- Create object and call apply
- """
- ha_obj = NetAppOntapClusterHA()
- ha_obj.apply()
-
-
-if __name__ == '__main__':
- main()
diff --git a/lib/ansible/modules/storage/netapp/na_ontap_cluster_peer.py b/lib/ansible/modules/storage/netapp/na_ontap_cluster_peer.py
deleted file mode 100644
index 4bf1598208..0000000000
--- a/lib/ansible/modules/storage/netapp/na_ontap_cluster_peer.py
+++ /dev/null
@@ -1,295 +0,0 @@
-#!/usr/bin/python
-
-# (c) 2018-2019, NetApp, Inc
-# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-from __future__ import absolute_import, division, print_function
-__metaclass__ = type
-
-ANSIBLE_METADATA = {'metadata_version': '1.1',
- 'status': ['preview'],
- 'supported_by': 'certified'}
-
-
-DOCUMENTATION = '''
-author: NetApp Ansible Team (@carchi8py) <ng-ansibleteam@netapp.com>
-description:
- - Create/Delete cluster peer relations on ONTAP
-extends_documentation_fragment:
- - netapp.na_ontap
-module: na_ontap_cluster_peer
-options:
- state:
- choices: ['present', 'absent']
- description:
- - Whether the specified cluster peer should exist or not.
- default: present
- source_intercluster_lifs:
- description:
- - List of intercluster addresses of the source cluster.
- - Used as peer-addresses in destination cluster.
- - All these intercluster lifs should belong to the source cluster.
- version_added: "2.8"
- aliases:
- - source_intercluster_lif
- dest_intercluster_lifs:
- description:
- - List of intercluster addresses of the destination cluster.
- - Used as peer-addresses in source cluster.
- - All these intercluster lifs should belong to the destination cluster.
- version_added: "2.8"
- aliases:
- - dest_intercluster_lif
- passphrase:
- description:
- - The arbitrary passphrase that matches the one given to the peer cluster.
- source_cluster_name:
- description:
- - The name of the source cluster name in the peer relation to be deleted.
- dest_cluster_name:
- description:
- - The name of the destination cluster name in the peer relation to be deleted.
- - Required for delete
- dest_hostname:
- description:
- - Destination cluster IP or hostname which needs to be peered
- - Required to complete the peering process at destination cluster.
- required: True
- dest_username:
- description:
- - Destination username.
- - Optional if this is same as source username.
- dest_password:
- description:
- - Destination password.
- - Optional if this is same as source password.
-short_description: NetApp ONTAP Manage Cluster peering
-version_added: "2.7"
-'''
-
-EXAMPLES = """
-
- - name: Create cluster peer
- na_ontap_cluster_peer:
- state: present
- source_intercluster_lifs: 1.2.3.4,1.2.3.5
- dest_intercluster_lifs: 1.2.3.6,1.2.3.7
- passphrase: XXXX
- hostname: "{{ netapp_hostname }}"
- username: "{{ netapp_username }}"
- password: "{{ netapp_password }}"
- dest_hostname: "{{ dest_netapp_hostname }}"
-
- - name: Delete cluster peer
- na_ontap_cluster_peer:
- state: absent
- source_cluster_name: test-source-cluster
- dest_cluster_name: test-dest-cluster
- hostname: "{{ netapp_hostname }}"
- username: "{{ netapp_username }}"
- password: "{{ netapp_password }}"
- dest_hostname: "{{ dest_netapp_hostname }}"
-"""
-
-RETURN = """
-"""
-
-import traceback
-from ansible.module_utils.basic import AnsibleModule
-from ansible.module_utils._text import to_native
-import ansible.module_utils.netapp as netapp_utils
-from ansible.module_utils.netapp_module import NetAppModule
-
-HAS_NETAPP_LIB = netapp_utils.has_netapp_lib()
-
-
-class NetAppONTAPClusterPeer(object):
- """
- Class with cluster peer methods
- """
-
- def __init__(self):
-
- self.argument_spec = netapp_utils.na_ontap_host_argument_spec()
- self.argument_spec.update(dict(
- state=dict(required=False, type='str', choices=['present', 'absent'], default='present'),
- source_intercluster_lifs=dict(required=False, type='list', aliases=['source_intercluster_lif']),
- dest_intercluster_lifs=dict(required=False, type='list', aliases=['dest_intercluster_lif']),
- passphrase=dict(required=False, type='str', no_log=True),
- dest_hostname=dict(required=True, type='str'),
- dest_username=dict(required=False, type='str'),
- dest_password=dict(required=False, type='str', no_log=True),
- source_cluster_name=dict(required=False, type='str'),
- dest_cluster_name=dict(required=False, type='str')
- ))
-
- self.module = AnsibleModule(
- argument_spec=self.argument_spec,
- required_together=[['source_intercluster_lifs', 'dest_intercluster_lifs']],
- required_if=[('state', 'absent', ['source_cluster_name', 'dest_cluster_name'])],
- supports_check_mode=True
- )
-
- self.na_helper = NetAppModule()
- self.parameters = self.na_helper.set_parameters(self.module.params)
-
- if HAS_NETAPP_LIB is False:
- self.module.fail_json(msg="the python NetApp-Lib module is required")
- else:
- self.server = netapp_utils.setup_na_ontap_zapi(module=self.module)
- # set destination server connection
- self.module.params['hostname'] = self.parameters['dest_hostname']
- if self.parameters.get('dest_username'):
- self.module.params['username'] = self.parameters['dest_username']
- if self.parameters.get('dest_password'):
- self.module.params['password'] = self.parameters['dest_password']
- self.dest_server = netapp_utils.setup_na_ontap_zapi(module=self.module)
- # reset to source host connection for asup logs
- self.module.params['hostname'] = self.parameters['hostname']
-
- def cluster_peer_get_iter(self, cluster):
- """
- Compose NaElement object to query current source cluster using peer-cluster-name and peer-addresses parameters
- :param cluster: type of cluster (source or destination)
- :return: NaElement object for cluster-get-iter with query
- """
- cluster_peer_get = netapp_utils.zapi.NaElement('cluster-peer-get-iter')
- query = netapp_utils.zapi.NaElement('query')
- cluster_peer_info = netapp_utils.zapi.NaElement('cluster-peer-info')
- if cluster == 'source':
- peer_lifs, peer_cluster = 'dest_intercluster_lifs', 'dest_cluster_name'
- else:
- peer_lifs, peer_cluster = 'source_intercluster_lifs', 'source_cluster_name'
- if self.parameters.get(peer_lifs):
- peer_addresses = netapp_utils.zapi.NaElement('peer-addresses')
- for peer in self.parameters.get(peer_lifs):
- peer_addresses.add_new_child('remote-inet-address', peer)
- cluster_peer_info.add_child_elem(peer_addresses)
- if self.parameters.get(peer_cluster):
- cluster_peer_info.add_new_child('cluster-name', self.parameters[peer_cluster])
- query.add_child_elem(cluster_peer_info)
- cluster_peer_get.add_child_elem(query)
- return cluster_peer_get
-
- def cluster_peer_get(self, cluster):
- """
- Get current cluster peer info
- :param cluster: type of cluster (source or destination)
- :return: Dictionary of current cluster peer details if query successful, else return None
- """
- cluster_peer_get_iter = self.cluster_peer_get_iter(cluster)
- result, cluster_info = None, dict()
- if cluster == 'source':
- server = self.server
- else:
- server = self.dest_server
- try:
- result = server.invoke_successfully(cluster_peer_get_iter, enable_tunneling=True)
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg='Error fetching cluster peer %s: %s'
- % (self.parameters['dest_cluster_name'], to_native(error)),
- exception=traceback.format_exc())
- # return cluster peer details
- if result.get_child_by_name('num-records') and \
- int(result.get_child_content('num-records')) >= 1:
- cluster_peer_info = result.get_child_by_name('attributes-list').get_child_by_name('cluster-peer-info')
- cluster_info['cluster_name'] = cluster_peer_info.get_child_content('cluster-name')
- peers = cluster_peer_info.get_child_by_name('peer-addresses')
- cluster_info['peer-addresses'] = [peer.get_content() for peer in peers.get_children()]
- return cluster_info
- return None
-
- def cluster_peer_delete(self, cluster):
- """
- Delete a cluster peer on source or destination
- For source cluster, peer cluster-name = destination cluster name and vice-versa
- :param cluster: type of cluster (source or destination)
- :return:
- """
- if cluster == 'source':
- server, peer_cluster_name = self.server, self.parameters['dest_cluster_name']
- else:
- server, peer_cluster_name = self.dest_server, self.parameters['source_cluster_name']
- cluster_peer_delete = netapp_utils.zapi.NaElement.create_node_with_children(
- 'cluster-peer-delete', **{'cluster-name': peer_cluster_name})
- try:
- server.invoke_successfully(cluster_peer_delete, enable_tunneling=True)
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg='Error deleting cluster peer %s: %s'
- % (peer_cluster_name, to_native(error)),
- exception=traceback.format_exc())
-
- def cluster_peer_create(self, cluster):
- """
- Create a cluster peer on source or destination
- For source cluster, peer addresses = destination inter-cluster LIFs and vice-versa
- :param cluster: type of cluster (source or destination)
- :return: None
- """
- cluster_peer_create = netapp_utils.zapi.NaElement.create_node_with_children('cluster-peer-create')
- if self.parameters.get('passphrase') is not None:
- cluster_peer_create.add_new_child('passphrase', self.parameters['passphrase'])
- peer_addresses = netapp_utils.zapi.NaElement('peer-addresses')
- if cluster == 'source':
- server, peer_address = self.server, self.parameters['dest_intercluster_lifs']
- else:
- server, peer_address = self.dest_server, self.parameters['source_intercluster_lifs']
- for each in peer_address:
- peer_addresses.add_new_child('remote-inet-address', each)
- cluster_peer_create.add_child_elem(peer_addresses)
- try:
- server.invoke_successfully(cluster_peer_create, enable_tunneling=True)
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg='Error creating cluster peer %s: %s'
- % (peer_address, to_native(error)),
- exception=traceback.format_exc())
-
- def apply(self):
- """
- Apply action to cluster peer
- :return: None
- """
- self.asup_log_for_cserver("na_ontap_cluster_peer")
- source = self.cluster_peer_get('source')
- destination = self.cluster_peer_get('destination')
- source_action = self.na_helper.get_cd_action(source, self.parameters)
- destination_action = self.na_helper.get_cd_action(destination, self.parameters)
- self.na_helper.changed = False
- # create only if expected cluster peer relation is not present on both source and destination clusters
- if source_action == 'create' and destination_action == 'create':
- self.cluster_peer_create('source')
- self.cluster_peer_create('destination')
- self.na_helper.changed = True
- # delete peer relation in cluster where relation is present
- else:
- if source_action == 'delete':
- self.cluster_peer_delete('source')
- self.na_helper.changed = True
- if destination_action == 'delete':
- self.cluster_peer_delete('destination')
- self.na_helper.changed = True
-
- self.module.exit_json(changed=self.na_helper.changed)
-
- def asup_log_for_cserver(self, event_name):
- """
- Fetch admin vserver for the given cluster
- Create and Autosupport log event with the given module name
- :param event_name: Name of the event log
- :return: None
- """
- results = netapp_utils.get_cserver(self.server)
- cserver = netapp_utils.setup_na_ontap_zapi(module=self.module, vserver=results)
- netapp_utils.ems_log_event(event_name, cserver)
-
-
-def main():
- """
- Execute action
- :return: None
- """
- community_obj = NetAppONTAPClusterPeer()
- community_obj.apply()
-
-
-if __name__ == '__main__':
- main()
diff --git a/lib/ansible/modules/storage/netapp/na_ontap_command.py b/lib/ansible/modules/storage/netapp/na_ontap_command.py
deleted file mode 100644
index 60ff63b86a..0000000000
--- a/lib/ansible/modules/storage/netapp/na_ontap_command.py
+++ /dev/null
@@ -1,228 +0,0 @@
-#!/usr/bin/python
-'''
-# (c) 2018, NetApp, Inc
-# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-'''
-
-from __future__ import absolute_import, division, print_function
-__metaclass__ = type
-
-ANSIBLE_METADATA = {'metadata_version': '1.1',
- 'status': ['preview'],
- 'supported_by': 'certified'}
-
-DOCUMENTATION = '''
-author: NetApp Ansible Team (@carchi8py) <ng-ansibleteam@netapp.com>
-description:
- - "Run system-cli commands on ONTAP"
-extends_documentation_fragment:
- - netapp.na_ontap
-module: na_ontap_command
-short_description: NetApp ONTAP Run any cli command, the username provided needs to have console login permission.
-version_added: "2.7"
-options:
- command:
- description:
- - a comma separated list containing the command and arguments.
- required: true
- privilege:
- description:
- - privilege level at which to run the command.
- choices: ['admin', 'advanced']
- default: admin
- version_added: "2.8"
- return_dict:
- description:
- - returns a parsesable dictionary instead of raw XML output
- type: bool
- default: false
- version_added: "2.9"
-'''
-
-EXAMPLES = """
- - name: run ontap cli command
- na_ontap_command:
- hostname: "{{ hostname }}"
- username: "{{ admin username }}"
- password: "{{ admin password }}"
- command: ['version']
-
- - name: run ontap cli command
- na_ontap_command:
- hostname: "{{ hostname }}"
- username: "{{ admin username }}"
- password: "{{ admin password }}"
- command: ['network', 'interface', 'show']
- privilege: 'admin'
- return_dict: true
-"""
-
-RETURN = """
-"""
-
-import traceback
-from ansible.module_utils.basic import AnsibleModule
-from ansible.module_utils._text import to_native
-import ansible.module_utils.netapp as netapp_utils
-
-HAS_NETAPP_LIB = netapp_utils.has_netapp_lib()
-
-
-class NetAppONTAPCommand(object):
- ''' calls a CLI command '''
-
- def __init__(self):
- self.argument_spec = netapp_utils.na_ontap_host_argument_spec()
- self.argument_spec.update(dict(
- command=dict(required=True, type='list'),
- privilege=dict(required=False, type='str', choices=['admin', 'advanced'], default='admin'),
- return_dict=dict(required=False, type='bool', default=False)
- ))
- self.module = AnsibleModule(
- argument_spec=self.argument_spec,
- supports_check_mode=True
- )
- parameters = self.module.params
- # set up state variables
- self.command = parameters['command']
- self.privilege = parameters['privilege']
- self.return_dict = parameters['return_dict']
-
- self.result_dict = dict()
- self.result_dict['status'] = ""
- self.result_dict['result_value'] = 0
- self.result_dict['invoked_command'] = " ".join(self.command)
- self.result_dict['stdout'] = ""
- self.result_dict['stdout_lines'] = []
- self.result_dict['xml_dict'] = dict()
-
- if HAS_NETAPP_LIB is False:
- self.module.fail_json(msg="the python NetApp-Lib module is required")
- else:
- self.server = netapp_utils.setup_na_ontap_zapi(module=self.module)
-
- def asup_log_for_cserver(self, event_name):
- """
- Fetch admin vserver for the given cluster
- Create and Autosupport log event with the given module name
- :param event_name: Name of the event log
- :return: None
- """
- results = netapp_utils.get_cserver(self.server)
- cserver = netapp_utils.setup_na_ontap_zapi(module=self.module, vserver=results)
- netapp_utils.ems_log_event(event_name, cserver)
-
- def run_command(self):
- ''' calls the ZAPI '''
- self.asup_log_for_cserver("na_ontap_command: " + str(self.command))
- command_obj = netapp_utils.zapi.NaElement("system-cli")
-
- args_obj = netapp_utils.zapi.NaElement("args")
- if self.return_dict:
- args_obj.add_new_child('arg', 'set')
- args_obj.add_new_child('arg', '-showseparator')
- args_obj.add_new_child('arg', '"###"')
- args_obj.add_new_child('arg', ';')
- for arg in self.command:
- args_obj.add_new_child('arg', arg)
- command_obj.add_child_elem(args_obj)
- command_obj.add_new_child('priv', self.privilege)
-
- try:
- output = self.server.invoke_successfully(command_obj, True)
- if self.return_dict:
- # Parseable dict output
- retval = self.parse_xml_to_dict(output.to_string())
- else:
- # Raw XML output
- retval = output.to_string()
-
- return retval
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg='Error running command %s: %s' %
- (self.command, to_native(error)),
- exception=traceback.format_exc())
-
- def apply(self):
- ''' calls the command and returns raw output '''
- changed = True
- output = self.run_command()
- self.module.exit_json(changed=changed, msg=output)
-
- def parse_xml_to_dict(self, xmldata):
- '''Parse raw XML from system-cli and create an Ansible parseable dictionary'''
- xml_import_ok = True
- xml_parse_ok = True
-
- try:
- import xml.parsers.expat
- except ImportError:
- self.result_dict['status'] = "XML parsing failed. Cannot import xml.parsers.expat!"
- self.result_dict['stdout'] = str(xmldata)
- xml_import_ok = False
-
- if xml_import_ok:
- xml_str = xmldata.decode('utf-8').replace('\n', '---')
- xml_parser = xml.parsers.expat.ParserCreate()
- xml_parser.StartElementHandler = self._start_element
- xml_parser.CharacterDataHandler = self._char_data
- xml_parser.EndElementHandler = self._end_element
-
- try:
- xml_parser.Parse(xml_str)
- except xml.parsers.expat.ExpatError as errcode:
- self.result_dict['status'] = "XML parsing failed: " + str(errcode)
- self.result_dict['stdout'] = str(xmldata)
- xml_parse_ok = False
-
- if xml_parse_ok:
- self.result_dict['status'] = self.result_dict['xml_dict']['results']['attrs']['status']
- stdout_string = self._format_escaped_data(self.result_dict['xml_dict']['cli-output']['data'])
- self.result_dict['stdout'] = stdout_string
- for line in stdout_string.split('\n'):
- stripped_line = line.strip()
- if len(stripped_line) > 1:
- self.result_dict['stdout_lines'].append(stripped_line)
- self.result_dict['xml_dict']['cli-output']['data'] = stdout_string
- self.result_dict['result_value'] = int(str(self.result_dict['xml_dict']['cli-result-value']['data']).replace("'", ""))
-
- return self.result_dict
-
- def _start_element(self, name, attrs):
- ''' Start XML element '''
- self.result_dict['xml_dict'][name] = dict()
- self.result_dict['xml_dict'][name]['attrs'] = attrs
- self.result_dict['xml_dict'][name]['data'] = ""
- self.result_dict['xml_dict']['active_element'] = name
- self.result_dict['xml_dict']['last_element'] = ""
-
- def _char_data(self, data):
- ''' Dump XML element data '''
- self.result_dict['xml_dict'][str(self.result_dict['xml_dict']['active_element'])]['data'] = repr(data)
-
- def _end_element(self, name):
- self.result_dict['xml_dict']['last_element'] = name
- self.result_dict['xml_dict']['active_element'] = ""
-
- @classmethod
- def _format_escaped_data(cls, datastring):
- ''' replace helper escape sequences '''
- formatted_string = datastring.replace('------', '---').replace('---', '\n').replace("###", " ").strip()
- retval_string = ""
- for line in formatted_string.split('\n'):
- stripped_line = line.strip()
- if len(stripped_line) > 1:
- retval_string += stripped_line + "\n"
- return retval_string
-
-
-def main():
- """
- Execute action from playbook
- """
- command = NetAppONTAPCommand()
- command.apply()
-
-
-if __name__ == '__main__':
- main()
diff --git a/lib/ansible/modules/storage/netapp/na_ontap_disks.py b/lib/ansible/modules/storage/netapp/na_ontap_disks.py
deleted file mode 100644
index 5e719fa3ea..0000000000
--- a/lib/ansible/modules/storage/netapp/na_ontap_disks.py
+++ /dev/null
@@ -1,205 +0,0 @@
-#!/usr/bin/python
-
-# (c) 2018-2019, NetApp, Inc
-# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-
-from __future__ import absolute_import, division, print_function
-__metaclass__ = type
-
-
-ANSIBLE_METADATA = {'metadata_version': '1.1',
- 'status': ['preview'],
- 'supported_by': 'certified'}
-
-
-DOCUMENTATION = '''
-
-module: na_ontap_disks
-
-short_description: NetApp ONTAP Assign disks to nodes
-extends_documentation_fragment:
- - netapp.na_ontap
-version_added: '2.7'
-author: NetApp Ansible Team (@carchi8py) <ng-ansibleteam@netapp.com>
-
-description:
-- Assign all or part of disks to nodes.
-
-options:
-
- node:
- required: true
- description:
- - It specifies the node to assign all visible unowned disks.
-
- disk_count:
- required: false
- type: int
- description:
- - Total number of disks a node should own
- version_added: '2.9'
-'''
-
-EXAMPLES = """
-- name: Assign unowned disks
- na_ontap_disks:
- node: cluster-01
- hostname: "{{ hostname }}"
- username: "{{ admin username }}"
- password: "{{ admin password }}"
-
-- name: Assign specified total disks
- na_ontap_disks:
- node: cluster-01
- disk_count: 56
- hostname: "{{ hostname }}"
- username: "{{ admin username }}"
- password: "{{ admin password }}"
-"""
-
-RETURN = """
-
-"""
-import traceback
-
-from ansible.module_utils.basic import AnsibleModule
-from ansible.module_utils._text import to_native
-import ansible.module_utils.netapp as netapp_utils
-from ansible.module_utils.netapp_module import NetAppModule
-
-
-HAS_NETAPP_LIB = netapp_utils.has_netapp_lib()
-
-
-class NetAppOntapDisks(object):
- ''' object initialize and class methods '''
-
- def __init__(self):
- self.argument_spec = netapp_utils.na_ontap_host_argument_spec()
- self.argument_spec.update(dict(
- node=dict(required=True, type='str'),
- disk_count=dict(required=False, type='int')
- ))
-
- self.module = AnsibleModule(
- argument_spec=self.argument_spec,
- supports_check_mode=True
- )
-
- self.na_helper = NetAppModule()
- self.parameters = self.na_helper.set_parameters(self.module.params)
-
- if HAS_NETAPP_LIB is False:
- self.module.fail_json(msg="the python NetApp-Lib module is required")
- else:
- self.server = netapp_utils.setup_na_ontap_zapi(module=self.module)
-
- def get_unassigned_disk_count(self):
- """
- Check for free disks
- """
- disk_iter = netapp_utils.zapi.NaElement('storage-disk-get-iter')
- disk_storage_info = netapp_utils.zapi.NaElement('storage-disk-info')
- disk_raid_info = netapp_utils.zapi.NaElement('disk-raid-info')
- disk_raid_info.add_new_child('container-type', 'unassigned')
- disk_storage_info.add_child_elem(disk_raid_info)
-
- disk_query = netapp_utils.zapi.NaElement('query')
- disk_query.add_child_elem(disk_storage_info)
-
- disk_iter.add_child_elem(disk_query)
-
- try:
- result = self.server.invoke_successfully(disk_iter, True)
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg='Error getting disk information: %s'
- % (to_native(error)),
- exception=traceback.format_exc())
- return int(result.get_child_content('num-records'))
-
- def get_owned_disk_count(self):
- """
- Check for owned disks
- """
- disk_iter = netapp_utils.zapi.NaElement('storage-disk-get-iter')
- disk_storage_info = netapp_utils.zapi.NaElement('storage-disk-info')
- disk_ownership_info = netapp_utils.zapi.NaElement('disk-ownership-info')
- disk_ownership_info.add_new_child('home-node-name', self.parameters['node'])
- disk_storage_info.add_child_elem(disk_ownership_info)
-
- disk_query = netapp_utils.zapi.NaElement('query')
- disk_query.add_child_elem(disk_storage_info)
-
- disk_iter.add_child_elem(disk_query)
-
- try:
- result = self.server.invoke_successfully(disk_iter, True)
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg='Error getting disk information: %s'
- % (to_native(error)),
- exception=traceback.format_exc())
- return int(result.get_child_content('num-records'))
-
- def disk_assign(self, needed_disks):
- """
- Set node as disk owner.
- """
- if needed_disks > 0:
- assign_disk = netapp_utils.zapi.NaElement.create_node_with_children(
- 'disk-sanown-assign', **{'owner': self.parameters['node'],
- 'disk-count': str(needed_disks)})
- else:
- assign_disk = netapp_utils.zapi.NaElement.create_node_with_children(
- 'disk-sanown-assign', **{'node-name': self.parameters['node'],
- 'all': 'true'})
- try:
- self.server.invoke_successfully(assign_disk,
- enable_tunneling=True)
- return True
- except netapp_utils.zapi.NaApiError as error:
- if to_native(error.code) == "13001":
- # Error 13060 denotes aggregate is already online
- return False
- else:
- self.module.fail_json(msg='Error assigning disks %s' %
- (to_native(error)),
- exception=traceback.format_exc())
-
- def apply(self):
- '''Apply action to disks'''
- changed = False
- results = netapp_utils.get_cserver(self.server)
- cserver = netapp_utils.setup_na_ontap_zapi(
- module=self.module, vserver=results)
- netapp_utils.ems_log_event("na_ontap_disks", cserver)
-
- # check if anything needs to be changed (add/delete/update)
- unowned_disks = self.get_unassigned_disk_count()
- owned_disks = self.get_owned_disk_count()
- if 'disk_count' in self.parameters:
- if self.parameters['disk_count'] < owned_disks:
- self.module.fail_json(msg="Fewer disks than are currently owned was requested. "
- "This module does not do any disk removing. "
- "All disk removing will need to be done manually.")
- if self.parameters['disk_count'] > owned_disks + unowned_disks:
- self.module.fail_json(msg="Not enough unowned disks remain to fulfill request")
- if unowned_disks >= 1:
- if 'disk_count' in self.parameters:
- if self.parameters['disk_count'] > owned_disks:
- needed_disks = self.parameters['disk_count'] - owned_disks
- self.disk_assign(needed_disks)
- changed = True
- else:
- self.disk_assign(0)
- changed = True
- self.module.exit_json(changed=changed)
-
-
-def main():
- ''' Create object and call apply '''
- obj_aggr = NetAppOntapDisks()
- obj_aggr.apply()
-
-
-if __name__ == '__main__':
- main()
diff --git a/lib/ansible/modules/storage/netapp/na_ontap_dns.py b/lib/ansible/modules/storage/netapp/na_ontap_dns.py
deleted file mode 100644
index afadcaec03..0000000000
--- a/lib/ansible/modules/storage/netapp/na_ontap_dns.py
+++ /dev/null
@@ -1,294 +0,0 @@
-#!/usr/bin/python
-
-# (c) 2018, NetApp, Inc
-# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-
-from __future__ import absolute_import, division, print_function
-__metaclass__ = type
-
-
-ANSIBLE_METADATA = {'metadata_version': '1.1',
- 'status': ['preview'],
- 'supported_by': 'certified'}
-
-DOCUMENTATION = '''
-module: na_ontap_dns
-short_description: NetApp ONTAP Create, delete, modify DNS servers.
-extends_documentation_fragment:
- - netapp.na_ontap
-version_added: '2.7'
-author: NetApp Ansible Team (@carchi8py) <ng-ansibleteam@netapp.com>
-description:
-- Create, delete, modify DNS servers.
-options:
- state:
- description:
- - Whether the DNS servers should be enabled for the given vserver.
- choices: ['present', 'absent']
- default: present
-
- vserver:
- description:
- - The name of the vserver to use.
- required: true
-
- domains:
- description:
- - List of DNS domains such as 'sales.bar.com'. The first domain is the one that the Vserver belongs to.
-
- nameservers:
- description:
- - List of IPv4 addresses of name servers such as '123.123.123.123'.
-
- skip_validation:
- type: bool
- description:
- - By default, all nameservers are checked to validate they are available to resolve.
- - If you DNS servers are not yet installed or momentarily not available, you can set this option to 'true'
- - to bypass the check for all servers specified in nameservers field.
- version_added: '2.8'
-'''
-
-EXAMPLES = """
- - name: create DNS
- na_ontap_dns:
- state: present
- hostname: "{{hostname}}"
- username: "{{username}}"
- password: "{{password}}"
- vserver: "{{vservername}}"
- domains: sales.bar.com
- nameservers: 10.193.0.250,10.192.0.250
- skip_validation: true
-"""
-
-RETURN = """
-
-"""
-import traceback
-from ansible.module_utils.basic import AnsibleModule
-from ansible.module_utils._text import to_native
-import ansible.module_utils.netapp as netapp_utils
-from ansible.module_utils.netapp_module import NetAppModule
-from ansible.module_utils.netapp import OntapRestAPI
-
-HAS_NETAPP_LIB = netapp_utils.has_netapp_lib()
-
-
-class NetAppOntapDns(object):
- """
- Enable and Disable dns
- """
-
- def __init__(self):
- self.use_rest = False
- self.argument_spec = netapp_utils.na_ontap_host_argument_spec()
- self.argument_spec.update(dict(
- state=dict(required=False, choices=['present', 'absent'], default='present'),
- vserver=dict(required=True, type='str'),
- domains=dict(required=False, type='list'),
- nameservers=dict(required=False, type='list'),
- skip_validation=dict(required=False, type='bool')
- ))
-
- self.module = AnsibleModule(
- argument_spec=self.argument_spec,
- required_if=[('state', 'present', ['domains', 'nameservers'])],
- supports_check_mode=True
- )
-
- self.na_helper = NetAppModule()
- self.parameters = self.na_helper.set_parameters(self.module.params)
-
- # REST API should be used for ONTAP 9.6 or higher, ZAPI for lower version
- self.restApi = OntapRestAPI(self.module)
- # some attributes are not supported in earlier REST implementation
- unsupported_rest_properties = ['skip_validation']
- used_unsupported_rest_properties = [x for x in unsupported_rest_properties if x in self.parameters]
- self.use_rest, error = self.restApi.is_rest(used_unsupported_rest_properties)
-
- if error is not None:
- self.module.fail_json(msg=error)
-
- if not self.use_rest:
- if HAS_NETAPP_LIB is False:
- self.module.fail_json(msg="the python NetApp-Lib module is required")
- else:
- self.server = netapp_utils.setup_na_ontap_zapi(module=self.module, vserver=self.parameters['vserver'])
- return
-
- def create_dns(self):
- """
- Create DNS server
- :return: none
- """
- if self.use_rest:
- api = 'name-services/dns'
- params = {}
- params['domains'] = self.parameters['domains']
- params['servers'] = self.parameters['nameservers']
- params['svm'] = {'name': self.parameters['vserver']}
- message, error = self.restApi.post(api, params)
- if error:
- self.module.fail_json(msg=error)
- else:
- dns = netapp_utils.zapi.NaElement('net-dns-create')
- nameservers = netapp_utils.zapi.NaElement('name-servers')
- domains = netapp_utils.zapi.NaElement('domains')
- for each in self.parameters['nameservers']:
- ip_address = netapp_utils.zapi.NaElement('ip-address')
- ip_address.set_content(each)
- nameservers.add_child_elem(ip_address)
- dns.add_child_elem(nameservers)
- for each in self.parameters['domains']:
- domain = netapp_utils.zapi.NaElement('string')
- domain.set_content(each)
- domains.add_child_elem(domain)
- dns.add_child_elem(domains)
- if self.parameters.get('skip_validation'):
- validation = netapp_utils.zapi.NaElement('skip-config-validation')
- validation.set_content(str(self.parameters['skip_validation']))
- dns.add_child_elem(validation)
- try:
- self.server.invoke_successfully(dns, True)
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg='Error creating dns: %s' %
- (to_native(error)),
- exception=traceback.format_exc())
-
- def destroy_dns(self, dns_attrs):
- """
- Destroys an already created dns
- :return:
- """
- if self.use_rest:
- uuid = dns_attrs['records'][0]['svm']['uuid']
- api = 'name-services/dns/' + uuid
- data = None
- message, error = self.restApi.delete(api, data)
- if error:
- self.module.fail_json(msg=error)
- else:
- try:
- self.server.invoke_successfully(netapp_utils.zapi.NaElement('net-dns-destroy'), True)
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg='Error destroying dns %s' %
- (to_native(error)),
- exception=traceback.format_exc())
-
- def get_dns(self):
- if self.use_rest:
- api = "name-services/dns"
- params = {'fields': 'domains,servers,svm',
- "svm.name": self.parameters['vserver']}
- message, error = self.restApi.get(api, params)
- if error:
- self.module.fail_json(msg=error)
- if len(message.keys()) == 0:
- message = None
- elif 'records' in message and len(message['records']) == 0:
- message = None
- elif 'records' not in message or len(message['records']) != 1:
- error = "Unexpected response from %s: %s" % (api, repr(message))
- self.module.fail_json(msg=error)
- return message
- else:
- dns_obj = netapp_utils.zapi.NaElement('net-dns-get')
- try:
- result = self.server.invoke_successfully(dns_obj, True)
- except netapp_utils.zapi.NaApiError as error:
- if to_native(error.code) == "15661":
- # 15661 is object not found
- return None
- else:
- self.module.fail_json(msg=to_native(
- error), exception=traceback.format_exc())
-
- # read data for modify
- attrs = dict()
- attributes = result.get_child_by_name('attributes')
- dns_info = attributes.get_child_by_name('net-dns-info')
- nameservers = dns_info.get_child_by_name('name-servers')
- attrs['nameservers'] = [each.get_content() for each in nameservers.get_children()]
- domains = dns_info.get_child_by_name('domains')
- attrs['domains'] = [each.get_content() for each in domains.get_children()]
- attrs['skip_validation'] = dns_info.get_child_by_name('skip-config-validation')
- return attrs
-
- def modify_dns(self, dns_attrs):
- if self.use_rest:
- changed = False
- params = {}
- if dns_attrs['records'][0]['servers'] != self.parameters['nameservers']:
- changed = True
- params['servers'] = self.parameters['nameservers']
- if dns_attrs['records'][0]['domains'] != self.parameters['domains']:
- changed = True
- params['domains'] = self.parameters['domains']
- if changed:
- uuid = dns_attrs['records'][0]['svm']['uuid']
- api = "name-services/dns/" + uuid
- message, error = self.restApi.patch(api, params)
- if error:
- self.module.fail_json(msg=error)
-
- else:
- changed = False
- dns = netapp_utils.zapi.NaElement('net-dns-modify')
- if dns_attrs['nameservers'] != self.parameters['nameservers']:
- changed = True
- nameservers = netapp_utils.zapi.NaElement('name-servers')
- for each in self.parameters['nameservers']:
- ip_address = netapp_utils.zapi.NaElement('ip-address')
- ip_address.set_content(each)
- nameservers.add_child_elem(ip_address)
- dns.add_child_elem(nameservers)
- if dns_attrs['domains'] != self.parameters['domains']:
- changed = True
- domains = netapp_utils.zapi.NaElement('domains')
- for each in self.parameters['domains']:
- domain = netapp_utils.zapi.NaElement('string')
- domain.set_content(each)
- domains.add_child_elem(domain)
- dns.add_child_elem(domains)
- if changed:
- if self.parameters.get('skip_validation'):
- validation = netapp_utils.zapi.NaElement('skip-config-validation')
- validation.set_content(str(self.parameters['skip_validation']))
- dns.add_child_elem(validation)
- try:
- self.server.invoke_successfully(dns, True)
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg='Error modifying dns %s' %
- (to_native(error)), exception=traceback.format_exc())
- return changed
-
- def apply(self):
- # asup logging
- if not self.use_rest:
- netapp_utils.ems_log_event("na_ontap_dns", self.server)
- dns_attrs = self.get_dns()
- changed = False
- if self.parameters['state'] == 'present':
- if dns_attrs is not None:
- changed = self.modify_dns(dns_attrs)
- else:
- self.create_dns()
- changed = True
- else:
- if dns_attrs is not None:
- self.destroy_dns(dns_attrs)
- changed = True
- self.module.exit_json(changed=changed)
-
-
-def main():
- """
- Create, Delete, Modify DNS servers.
- """
- obj = NetAppOntapDns()
- obj.apply()
-
-
-if __name__ == '__main__':
- main()
diff --git a/lib/ansible/modules/storage/netapp/na_ontap_export_policy.py b/lib/ansible/modules/storage/netapp/na_ontap_export_policy.py
deleted file mode 100644
index cc6b7d613e..0000000000
--- a/lib/ansible/modules/storage/netapp/na_ontap_export_policy.py
+++ /dev/null
@@ -1,231 +0,0 @@
-#!/usr/bin/python
-
-# (c) 2018, NetApp, Inc
-# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-
-from __future__ import absolute_import, division, print_function
-__metaclass__ = type
-
-ANSIBLE_METADATA = {'metadata_version': '1.1',
- 'status': ['preview'],
- 'supported_by': 'certified'}
-
-
-DOCUMENTATION = '''
-module: na_ontap_export_policy
-short_description: NetApp ONTAP manage export-policy
-extends_documentation_fragment:
- - netapp.na_ontap
-version_added: '2.6'
-author: NetApp Ansible Team (@carchi8py) <ng-ansibleteam@netapp.com>
-description:
-- Create or destroy or rename export-policies on ONTAP
-options:
- state:
- description:
- - Whether the specified export policy should exist or not.
- choices: ['present', 'absent']
- default: present
- name:
- description:
- - The name of the export-policy to manage.
- required: true
- from_name:
- description:
- - The name of the export-policy to be renamed.
- version_added: '2.7'
- vserver:
- description:
- - Name of the vserver to use.
-'''
-
-EXAMPLES = """
- - name: Create Export Policy
- na_ontap_export_policy:
- state: present
- name: ansiblePolicyName
- vserver: vs_hack
- hostname: "{{ netapp_hostname }}"
- username: "{{ netapp_username }}"
- password: "{{ netapp_password }}"
- - name: Rename Export Policy
- na_ontap_export_policy:
- action: present
- from_name: ansiblePolicyName
- vserver: vs_hack
- name: newPolicyName
- hostname: "{{ netapp_hostname }}"
- username: "{{ netapp_username }}"
- password: "{{ netapp_password }}"
- - name: Delete Export Policy
- na_ontap_export_policy:
- state: absent
- name: ansiblePolicyName
- vserver: vs_hack
- hostname: "{{ netapp_hostname }}"
- username: "{{ netapp_username }}"
- password: "{{ netapp_password }}"
-"""
-
-RETURN = """
-"""
-
-import traceback
-from ansible.module_utils.basic import AnsibleModule
-from ansible.module_utils._text import to_native
-import ansible.module_utils.netapp as netapp_utils
-
-HAS_NETAPP_LIB = netapp_utils.has_netapp_lib()
-
-
-class NetAppONTAPExportPolicy(object):
- """
- Class with export policy methods
- """
-
- def __init__(self):
-
- self.argument_spec = netapp_utils.na_ontap_host_argument_spec()
- self.argument_spec.update(dict(
- state=dict(required=False, type='str', choices=['present', 'absent'], default='present'),
- name=dict(required=True, type='str'),
- from_name=dict(required=False, type='str', default=None),
- vserver=dict(required=False, type='str')
- ))
- self.module = AnsibleModule(
- argument_spec=self.argument_spec,
- required_if=[
- ('state', 'present', ['vserver'])
- ],
- supports_check_mode=True
- )
- parameters = self.module.params
- # set up state variables
- self.state = parameters['state']
- self.name = parameters['name']
- self.from_name = parameters['from_name']
- self.vserver = parameters['vserver']
-
- if HAS_NETAPP_LIB is False:
- self.module.fail_json(msg="the python NetApp-Lib module is required")
- else:
- self.server = netapp_utils.setup_na_ontap_zapi(module=self.module, vserver=self.vserver)
-
- def get_export_policy(self, name=None):
- """
- Return details about the export-policy
- :param:
- name : Name of the export-policy
- :return: Details about the export-policy. None if not found.
- :rtype: dict
- """
- if name is None:
- name = self.name
- export_policy_iter = netapp_utils.zapi.NaElement('export-policy-get-iter')
- export_policy_info = netapp_utils.zapi.NaElement('export-policy-info')
- export_policy_info.add_new_child('policy-name', name)
- query = netapp_utils.zapi.NaElement('query')
- query.add_child_elem(export_policy_info)
- export_policy_iter.add_child_elem(query)
- result = self.server.invoke_successfully(export_policy_iter, True)
- return_value = None
- # check if query returns the expected export-policy
- if result.get_child_by_name('num-records') and \
- int(result.get_child_content('num-records')) == 1:
-
- export_policy = result.get_child_by_name('attributes-list').get_child_by_name('export-policy-info').get_child_by_name('policy-name')
- return_value = {
- 'policy-name': export_policy
- }
- return return_value
-
- def create_export_policy(self):
- """
- Creates an export policy
- """
- export_policy_create = netapp_utils.zapi.NaElement.create_node_with_children(
- 'export-policy-create', **{'policy-name': self.name})
- try:
- self.server.invoke_successfully(export_policy_create,
- enable_tunneling=True)
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg='Error creating export-policy %s: %s'
- % (self.name, to_native(error)),
- exception=traceback.format_exc())
-
- def delete_export_policy(self):
- """
- Delete export-policy
- """
- export_policy_delete = netapp_utils.zapi.NaElement.create_node_with_children(
- 'export-policy-destroy', **{'policy-name': self.name, })
- try:
- self.server.invoke_successfully(export_policy_delete,
- enable_tunneling=True)
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg='Error deleting export-policy %s: %s'
- % (self.name,
- to_native(error)), exception=traceback.format_exc())
-
- def rename_export_policy(self):
- """
- Rename the export-policy.
- """
- export_policy_rename = netapp_utils.zapi.NaElement.create_node_with_children(
- 'export-policy-rename', **{'policy-name': self.from_name,
- 'new-policy-name': self.name})
- try:
- self.server.invoke_successfully(export_policy_rename,
- enable_tunneling=True)
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg='Error renaming export-policy %s:%s'
- % (self.name, to_native(error)),
- exception=traceback.format_exc())
-
- def apply(self):
- """
- Apply action to export-policy
- """
- changed = False
- export_policy_exists = False
- netapp_utils.ems_log_event("na_ontap_export_policy", self.server)
- rename_flag = False
- export_policy_details = self.get_export_policy()
- if export_policy_details:
- export_policy_exists = True
- if self.state == 'absent': # delete
- changed = True
- else:
- if self.state == 'present': # create or rename
- if self.from_name is not None:
- if self.get_export_policy(self.from_name):
- changed = True
- rename_flag = True
- else:
- self.module.fail_json(msg='Error renaming export-policy %s: does not exists' % self.from_name)
- else: # create
- changed = True
- if changed:
- if self.module.check_mode:
- pass
- else:
- if self.state == 'present': # execute create or rename_export_policy
- if rename_flag:
- self.rename_export_policy()
- else:
- self.create_export_policy()
- elif self.state == 'absent': # execute delete
- self.delete_export_policy()
- self.module.exit_json(changed=changed)
-
-
-def main():
- """
- Execute action
- """
- export_policy = NetAppONTAPExportPolicy()
- export_policy.apply()
-
-
-if __name__ == '__main__':
- main()
diff --git a/lib/ansible/modules/storage/netapp/na_ontap_export_policy_rule.py b/lib/ansible/modules/storage/netapp/na_ontap_export_policy_rule.py
deleted file mode 100644
index 831975f288..0000000000
--- a/lib/ansible/modules/storage/netapp/na_ontap_export_policy_rule.py
+++ /dev/null
@@ -1,431 +0,0 @@
-#!/usr/bin/python
-
-# (c) 2018-2019, NetApp, Inc
-# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-
-from __future__ import absolute_import, division, print_function
-__metaclass__ = type
-
-
-ANSIBLE_METADATA = {'metadata_version': '1.1',
- 'status': ['preview'],
- 'supported_by': 'certified'}
-
-
-DOCUMENTATION = '''
-
-module: na_ontap_export_policy_rule
-
-short_description: NetApp ONTAP manage export policy rules
-extends_documentation_fragment:
- - netapp.na_ontap
-version_added: '2.6'
-author: NetApp Ansible Team (@carchi8py) <ng-ansibleteam@netapp.com>
-
-description:
-- Create or delete or modify export rules in ONTAP
-
-options:
- state:
- description:
- - Whether the specified export policy rule should exist or not.
- required: false
- choices: ['present', 'absent']
- default: present
-
- name:
- description:
- - The name of the export rule to manage.
- required: True
- aliases:
- - policy_name
-
- client_match:
- description:
- - List of Client Match host names, IP Addresses, Netgroups, or Domains
- - If rule_index is not provided, client_match is used as a key to fetch current rule to determine create,delete,modify actions.
- If a rule with provided client_match exists, a new rule will not be created, but the existing rule will be modified or deleted.
- If a rule with provided client_match doesn't exist, a new rule will be created if state is present.
-
- ro_rule:
- description:
- - List of Read only access specifications for the rule
- choices: ['any','none','never','krb5','krb5i','krb5p','ntlm','sys']
-
- rw_rule:
- description:
- - List of Read Write access specifications for the rule
- choices: ['any','none','never','krb5','krb5i','krb5p','ntlm','sys']
-
- super_user_security:
- description:
- - List of Read Write access specifications for the rule
- choices: ['any','none','never','krb5','krb5i','krb5p','ntlm','sys']
-
- allow_suid:
- description:
- - If 'true', NFS server will honor SetUID bits in SETATTR operation. Default value on creation is 'true'
- type: bool
-
- protocol:
- description:
- - List of Client access protocols.
- - Default value is set to 'any' during create.
- choices: [any,nfs,nfs3,nfs4,cifs,flexcache]
-
- rule_index:
- description:
- - rule index of the export policy
-
- vserver:
- description:
- - Name of the vserver to use.
- required: true
-
-'''
-
-EXAMPLES = """
- - name: Create ExportPolicyRule
- na_ontap_export_policy_rule:
- state: present
- name: default123
- vserver: ci_dev
- client_match: 0.0.0.0/0,1.1.1.0/24
- ro_rule: krb5,krb5i
- rw_rule: any
- protocol: nfs,nfs3
- super_user_security: any
- allow_suid: true
- hostname: "{{ netapp_hostname }}"
- username: "{{ netapp_username }}"
- password: "{{ netapp_password }}"
-
- - name: Modify ExportPolicyRule
- na_ontap_export_policy_rule:
- state: present
- name: default123
- rule_index: 100
- client_match: 0.0.0.0/0
- ro_rule: ntlm
- rw_rule: any
- protocol: any
- allow_suid: false
- hostname: "{{ netapp_hostname }}"
- username: "{{ netapp_username }}"
- password: "{{ netapp_password }}"
-
- - name: Delete ExportPolicyRule
- na_ontap_export_policy_rule:
- state: absent
- name: default123
- rule_index: 100
- vserver: ci_dev
- hostname: "{{ netapp_hostname }}"
- username: "{{ netapp_username }}"
- password: "{{ netapp_password }}"
-
-"""
-
-RETURN = """
-
-
-"""
-import traceback
-
-import json
-from ansible.module_utils.basic import AnsibleModule
-from ansible.module_utils._text import to_native
-import ansible.module_utils.netapp as netapp_utils
-from ansible.module_utils.netapp_module import NetAppModule
-
-
-HAS_NETAPP_LIB = netapp_utils.has_netapp_lib()
-
-
-class NetAppontapExportRule(object):
- ''' object initialize and class methods '''
-
- def __init__(self):
-
- self.argument_spec = netapp_utils.na_ontap_host_argument_spec()
- self.argument_spec.update(dict(
- state=dict(required=False, type='str', choices=['present', 'absent'], default='present'),
- name=dict(required=True, type='str', aliases=['policy_name']),
- protocol=dict(required=False,
- type='list', default=None,
- choices=['any', 'nfs', 'nfs3', 'nfs4', 'cifs', 'flexcache']),
- client_match=dict(required=False, type='list'),
- ro_rule=dict(required=False,
- type='list', default=None,
- choices=['any', 'none', 'never', 'krb5', 'krb5i', 'krb5p', 'ntlm', 'sys']),
- rw_rule=dict(required=False,
- type='list', default=None,
- choices=['any', 'none', 'never', 'krb5', 'krb5i', 'krb5p', 'ntlm', 'sys']),
- super_user_security=dict(required=False,
- type='list', default=None,
- choices=['any', 'none', 'never', 'krb5', 'krb5i', 'krb5p', 'ntlm', 'sys']),
- allow_suid=dict(required=False, type='bool'),
- rule_index=dict(required=False, type='int'),
- vserver=dict(required=True, type='str'),
- ))
-
- self.module = AnsibleModule(
- argument_spec=self.argument_spec,
- supports_check_mode=True
- )
-
- self.na_helper = NetAppModule()
- self.parameters = self.na_helper.set_parameters(self.module.params)
- self.set_playbook_zapi_key_map()
-
- if HAS_NETAPP_LIB is False:
- self.module.fail_json(
- msg="the python NetApp-Lib module is required")
- else:
- self.server = netapp_utils.setup_na_ontap_zapi(
- module=self.module, vserver=self.parameters['vserver'])
-
- def set_playbook_zapi_key_map(self):
- self.na_helper.zapi_string_keys = {
- 'client_match': 'client-match',
- 'name': 'policy-name'
- }
- self.na_helper.zapi_list_keys = {
- 'protocol': ('protocol', 'access-protocol'),
- 'ro_rule': ('ro-rule', 'security-flavor'),
- 'rw_rule': ('rw-rule', 'security-flavor'),
- 'super_user_security': ('super-user-security', 'security-flavor'),
- }
- self.na_helper.zapi_bool_keys = {
- 'allow_suid': 'is-allow-set-uid-enabled'
- }
- self.na_helper.zapi_int_keys = {
- 'rule_index': 'rule-index'
- }
-
- def set_query_parameters(self):
- """
- Return dictionary of query parameters and
- :return:
- """
- query = {
- 'policy-name': self.parameters['name'],
- 'vserver': self.parameters['vserver']
- }
-
- if self.parameters.get('rule_index'):
- query['rule-index'] = self.parameters['rule_index']
- elif self.parameters.get('client_match'):
- query['client-match'] = self.parameters['client_match']
- else:
- self.module.fail_json(
- msg="Need to specify at least one of the rule_index and client_match option.")
-
- attributes = {
- 'query': {
- 'export-rule-info': query
- }
- }
- return attributes
-
- def get_export_policy_rule(self):
- """
- Return details about the export policy rule
- :param:
- name : Name of the export_policy
- :return: Details about the export_policy. None if not found.
- :rtype: dict
- """
- current, result = None, None
- rule_iter = netapp_utils.zapi.NaElement('export-rule-get-iter')
- rule_iter.translate_struct(self.set_query_parameters())
- try:
- result = self.server.invoke_successfully(rule_iter, True)
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg='Error getting export policy rule %s: %s'
- % (self.parameters['name'], to_native(error)),
- exception=traceback.format_exc())
- if result is not None and \
- result.get_child_by_name('num-records') and int(result.get_child_content('num-records')) >= 1:
- current = dict()
- rule_info = result.get_child_by_name('attributes-list').get_child_by_name('export-rule-info')
- for item_key, zapi_key in self.na_helper.zapi_string_keys.items():
- current[item_key] = rule_info.get_child_content(zapi_key)
- for item_key, zapi_key in self.na_helper.zapi_bool_keys.items():
- current[item_key] = self.na_helper.get_value_for_bool(from_zapi=True,
- value=rule_info[zapi_key])
- for item_key, zapi_key in self.na_helper.zapi_int_keys.items():
- current[item_key] = self.na_helper.get_value_for_int(from_zapi=True,
- value=rule_info[zapi_key])
- for item_key, zapi_key in self.na_helper.zapi_list_keys.items():
- parent, dummy = zapi_key
- current[item_key] = self.na_helper.get_value_for_list(from_zapi=True,
- zapi_parent=rule_info.get_child_by_name(parent))
- current['num_records'] = int(result.get_child_content('num-records'))
- if not self.parameters.get('rule_index'):
- self.parameters['rule_index'] = current['rule_index']
- return current
-
- def get_export_policy(self):
- """
- Return details about the export-policy
- :param:
- name : Name of the export-policy
-
- :return: Details about the export-policy. None if not found.
- :rtype: dict
- """
- export_policy_iter = netapp_utils.zapi.NaElement('export-policy-get-iter')
- attributes = {
- 'query': {
- 'export-policy-info': {
- 'policy-name': self.parameters['name'],
- 'vserver': self.parameters['vserver']
- }
- }
- }
-
- export_policy_iter.translate_struct(attributes)
- try:
- result = self.server.invoke_successfully(export_policy_iter, True)
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg='Error getting export policy %s: %s'
- % (self.parameters['name'], to_native(error)),
- exception=traceback.format_exc())
-
- if result.get_child_by_name('num-records') and int(result.get_child_content('num-records')) == 1:
- return result
-
- return None
-
- def add_parameters_for_create_or_modify(self, na_element_object, values):
- """
- Add children node for create or modify NaElement object
- :param na_element_object: modify or create NaElement object
- :param values: dictionary of cron values to be added
- :return: None
- """
- for key in values:
- if key in self.na_helper.zapi_string_keys:
- zapi_key = self.na_helper.zapi_string_keys.get(key)
- na_element_object[zapi_key] = values[key]
- elif key in self.na_helper.zapi_list_keys:
- parent_key, child_key = self.na_helper.zapi_list_keys.get(key)
- na_element_object.add_child_elem(self.na_helper.get_value_for_list(from_zapi=False,
- zapi_parent=parent_key,
- zapi_child=child_key,
- data=values[key]))
- elif key in self.na_helper.zapi_int_keys:
- zapi_key = self.na_helper.zapi_int_keys.get(key)
- na_element_object[zapi_key] = self.na_helper.get_value_for_int(from_zapi=False,
- value=values[key])
- elif key in self.na_helper.zapi_bool_keys:
- zapi_key = self.na_helper.zapi_bool_keys.get(key)
- na_element_object[zapi_key] = self.na_helper.get_value_for_bool(from_zapi=False,
- value=values[key])
-
- def create_export_policy_rule(self):
- """
- create rule for the export policy.
- """
- for key in ['client_match', 'ro_rule', 'rw_rule']:
- if self.parameters.get(key) is None:
- self.module.fail_json(msg='Error: Missing required param for creating export policy rule %s' % key)
- export_rule_create = netapp_utils.zapi.NaElement('export-rule-create')
- self.add_parameters_for_create_or_modify(export_rule_create, self.parameters)
- try:
- self.server.invoke_successfully(export_rule_create, enable_tunneling=True)
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg='Error creating export policy rule %s: %s'
- % (self.parameters['name'], to_native(error)), exception=traceback.format_exc())
-
- def create_export_policy(self):
- """
- Creates an export policy
- """
- export_policy_create = netapp_utils.zapi.NaElement.create_node_with_children(
- 'export-policy-create', **{'policy-name': self.parameters['name']})
- try:
- self.server.invoke_successfully(export_policy_create,
- enable_tunneling=True)
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg='Error creating export-policy %s: %s'
- % (self.parameters['name'], to_native(error)),
- exception=traceback.format_exc())
-
- def delete_export_policy_rule(self, rule_index):
- """
- delete rule for the export policy.
- """
- export_rule_delete = netapp_utils.zapi.NaElement.create_node_with_children(
- 'export-rule-destroy', **{'policy-name': self.parameters['name'],
- 'rule-index': str(rule_index)})
-
- try:
- self.server.invoke_successfully(export_rule_delete,
- enable_tunneling=True)
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg='Error deleting export policy rule %s: %s'
- % (self.parameters['name'], to_native(error)),
- exception=traceback.format_exc())
-
- def modify_export_policy_rule(self, params):
- '''
- Modify an existing export policy rule
- :param params: dict() of attributes with desired values
- :return: None
- '''
- export_rule_modify = netapp_utils.zapi.NaElement.create_node_with_children(
- 'export-rule-modify', **{'policy-name': self.parameters['name'],
- 'rule-index': str(self.parameters['rule_index'])})
- self.add_parameters_for_create_or_modify(export_rule_modify, params)
- try:
- self.server.invoke_successfully(export_rule_modify, enable_tunneling=True)
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg='Error modifying allow_suid %s: %s'
- % (self.parameters['allow_suid'], to_native(error)),
- exception=traceback.format_exc())
-
- def autosupport_log(self):
- netapp_utils.ems_log_event("na_ontap_export_policy_rules", self.server)
-
- def apply(self):
- ''' Apply required action from the play'''
- self.autosupport_log()
- # convert client_match list to comma-separated string
- if self.parameters.get('client_match') is not None:
- self.parameters['client_match'] = ','.join(self.parameters['client_match'])
- self.parameters['client_match'] = self.parameters['client_match'].replace(' ', '')
-
- current, modify = self.get_export_policy_rule(), None
- action = self.na_helper.get_cd_action(current, self.parameters)
- if action is None and self.parameters['state'] == 'present':
- modify = self.na_helper.get_modified_attributes(current, self.parameters)
-
- if self.na_helper.changed:
- if self.module.check_mode:
- pass
- else:
- # create export policy (if policy doesn't exist) only when changed=True
- if not self.get_export_policy():
- self.create_export_policy()
- if action == 'create':
- self.create_export_policy_rule()
- elif action == 'delete':
- if current['num_records'] > 1:
- self.module.fail_json(msg='Multiple export policy rules exist.'
- 'Please specify a rule_index to delete')
- self.delete_export_policy_rule(current['rule_index'])
- elif modify:
- self.modify_export_policy_rule(modify)
- self.module.exit_json(changed=self.na_helper.changed)
-
-
-def main():
- ''' Create object and call apply '''
- rule_obj = NetAppontapExportRule()
- rule_obj.apply()
-
-
-if __name__ == '__main__':
- main()
diff --git a/lib/ansible/modules/storage/netapp/na_ontap_fcp.py b/lib/ansible/modules/storage/netapp/na_ontap_fcp.py
deleted file mode 100644
index 0969158385..0000000000
--- a/lib/ansible/modules/storage/netapp/na_ontap_fcp.py
+++ /dev/null
@@ -1,210 +0,0 @@
-#!/usr/bin/python
-
-# (c) 2018-2019, NetApp, Inc
-# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-
-from __future__ import absolute_import, division, print_function
-__metaclass__ = type
-
-
-ANSIBLE_METADATA = {'metadata_version': '1.1',
- 'status': ['preview'],
- 'supported_by': 'certified'}
-
-DOCUMENTATION = '''
-module: na_ontap_fcp
-short_description: NetApp ONTAP Start, Stop and Enable FCP services.
-extends_documentation_fragment:
- - netapp.na_ontap
-version_added: '2.7'
-author: NetApp Ansible Team (@carchi8py) <ng-ansibleteam@netapp.com>
-description:
-- Start, Stop and Enable FCP services.
-options:
- state:
- description:
- - Whether the FCP should be enabled or not.
- choices: ['present', 'absent']
- default: present
-
- status:
- description:
- - Whether the FCP should be up or down
- choices: ['up', 'down']
- default: up
-
- vserver:
- description:
- - The name of the vserver to use.
- required: true
-
-'''
-
-EXAMPLES = """
- - name: create FCP
- na_ontap_fcp:
- state: present
- status: down
- hostname: "{{hostname}}"
- username: "{{username}}"
- password: "{{password}}"
- vserver: "{{vservername}}"
-"""
-
-RETURN = """
-
-"""
-import traceback
-
-from ansible.module_utils.basic import AnsibleModule
-from ansible.module_utils._text import to_native
-import ansible.module_utils.netapp as netapp_utils
-from ansible.module_utils.netapp_module import NetAppModule
-
-HAS_NETAPP_LIB = netapp_utils.has_netapp_lib()
-
-
-class NetAppOntapFCP(object):
- """
- Enable and Disable FCP
- """
-
- def __init__(self):
- self.argument_spec = netapp_utils.na_ontap_host_argument_spec()
- self.argument_spec.update(dict(
- state=dict(required=False, choices=['present', 'absent'], default='present'),
- vserver=dict(required=True, type='str'),
- status=dict(required=False, choices=['up', 'down'], default='up')
- ))
-
- self.module = AnsibleModule(
- argument_spec=self.argument_spec,
- supports_check_mode=True
- )
-
- self.na_helper = NetAppModule()
- self.parameters = self.na_helper.set_parameters(self.module.params)
-
- if HAS_NETAPP_LIB is False:
- self.module.fail_json(msg="the python NetApp-Lib module is required")
- else:
- self.server = netapp_utils.setup_na_ontap_zapi(module=self.module, vserver=self.parameters['vserver'])
- return
-
- def create_fcp(self):
- """
- Create's and Starts an FCP
- :return: none
- """
- try:
- self.server.invoke_successfully(netapp_utils.zapi.NaElement('fcp-service-create'), True)
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg='Error creating FCP: %s' %
- (to_native(error)),
- exception=traceback.format_exc())
-
- def start_fcp(self):
- """
- Starts an existing FCP
- :return: none
- """
- try:
- self.server.invoke_successfully(netapp_utils.zapi.NaElement('fcp-service-start'), True)
- except netapp_utils.zapi.NaApiError as error:
- # Error 13013 denotes fcp service already started.
- if to_native(error.code) == "13013":
- return None
- else:
- self.module.fail_json(msg='Error starting FCP %s' % (to_native(error)),
- exception=traceback.format_exc())
-
- def stop_fcp(self):
- """
- Steps an Existing FCP
- :return: none
- """
- try:
- self.server.invoke_successfully(netapp_utils.zapi.NaElement('fcp-service-stop'), True)
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg='Error Stopping FCP %s' %
- (to_native(error)),
- exception=traceback.format_exc())
-
- def destroy_fcp(self):
- """
- Destroys an already stopped FCP
- :return:
- """
- try:
- self.server.invoke_successfully(netapp_utils.zapi.NaElement('fcp-service-destroy'), True)
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg='Error destroying FCP %s' %
- (to_native(error)),
- exception=traceback.format_exc())
-
- def get_fcp(self):
- fcp_obj = netapp_utils.zapi.NaElement('fcp-service-get-iter')
- fcp_info = netapp_utils.zapi.NaElement('fcp-service-info')
- fcp_info.add_new_child('vserver', self.parameters['vserver'])
- query = netapp_utils.zapi.NaElement('query')
- query.add_child_elem(fcp_info)
- fcp_obj.add_child_elem(query)
- result = self.server.invoke_successfully(fcp_obj, True)
- # There can only be 1 FCP per vserver. If true, one is set up, else one isn't set up
- if result.get_child_by_name('num-records') and \
- int(result.get_child_content('num-records')) >= 1:
- return True
- else:
- return False
-
- def current_status(self):
- try:
- status = self.server.invoke_successfully(netapp_utils.zapi.NaElement('fcp-service-status'), True)
- return status.get_child_content('is-available') == 'true'
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg='Error destroying FCP: %s' %
- (to_native(error)),
- exception=traceback.format_exc())
-
- def apply(self):
- results = netapp_utils.get_cserver(self.server)
- cserver = netapp_utils.setup_na_ontap_zapi(module=self.module, vserver=results)
- netapp_utils.ems_log_event("na_ontap_fcp", cserver)
- exists = self.get_fcp()
- changed = False
- if self.parameters['state'] == 'present':
- if exists:
- if self.parameters['status'] == 'up':
- if not self.current_status():
- self.start_fcp()
- changed = True
- else:
- if self.current_status():
- self.stop_fcp()
- changed = True
- else:
- self.create_fcp()
- if self.parameters['status'] == 'up':
- self.start_fcp()
- elif self.parameters['status'] == 'down':
- self.stop_fcp()
- changed = True
- else:
- if exists:
- if self.current_status():
- self.stop_fcp()
- self.destroy_fcp()
- changed = True
- self.module.exit_json(changed=changed)
-
-
-def main():
- """
- Start, Stop and Enable FCP services.
- """
- obj = NetAppOntapFCP()
- obj.apply()
-
-
-if __name__ == '__main__':
- main()
diff --git a/lib/ansible/modules/storage/netapp/na_ontap_firewall_policy.py b/lib/ansible/modules/storage/netapp/na_ontap_firewall_policy.py
deleted file mode 100644
index ca58750881..0000000000
--- a/lib/ansible/modules/storage/netapp/na_ontap_firewall_policy.py
+++ /dev/null
@@ -1,351 +0,0 @@
-#!/usr/bin/python
-
-# (c) 2018-2019, NetApp, Inc
-# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-
-from __future__ import absolute_import, division, print_function
-__metaclass__ = type
-
-ANSIBLE_METADATA = {'metadata_version': '1.1',
- 'status': ['preview'],
- 'supported_by': 'certified'}
-
-DOCUMENTATION = '''
-module: na_ontap_firewall_policy
-short_description: NetApp ONTAP Manage a firewall policy
-version_added: '2.7'
-author: NetApp Ansible Team (@carchi8py) <ng-ansibleteam@netapp.com>
-description:
- - Configure firewall on an ONTAP node and manage firewall policy for an ONTAP SVM
-extends_documentation_fragment:
- - netapp.na_ontap
-requirements:
- - Python package ipaddress. Install using 'pip install ipaddress'
-options:
- state:
- description:
- - Whether to set up a firewall policy or not
- choices: ['present', 'absent']
- default: present
- allow_list:
- description:
- - A list of IPs and masks to use.
- - The host bits of the IP addresses used in this list must be set to 0.
- policy:
- description:
- - A policy name for the firewall policy
- service:
- description:
- - The service to apply the policy to
- choices: ['dns', 'http', 'https', 'ndmp', 'ndmps', 'ntp', 'rsh', 'snmp', 'ssh', 'telnet']
- vserver:
- description:
- - The Vserver to apply the policy to.
- enable:
- description:
- - enable firewall on a node
- choices: ['enable', 'disable']
- logging:
- description:
- - enable logging for firewall on a node
- choices: ['enable', 'disable']
- node:
- description:
- - The node to run the firewall configuration on
-'''
-
-EXAMPLES = """
- - name: create firewall Policy
- na_ontap_firewall_policy:
- state: present
- allow_list: [1.2.3.0/24,1.3.0.0/16]
- policy: pizza
- service: http
- vserver: ci_dev
- hostname: "{{ netapp hostname }}"
- username: "{{ netapp username }}"
- password: "{{ netapp password }}"
-
- - name: Modify firewall Policy
- na_ontap_firewall_policy:
- state: present
- allow_list: [1.5.3.0/24]
- policy: pizza
- service: http
- vserver: ci_dev
- hostname: "{{ netapp hostname }}"
- username: "{{ netapp username }}"
- password: "{{ netapp password }}"
-
- - name: Destroy firewall Policy
- na_ontap_firewall_policy:
- state: absent
- policy: pizza
- service: http
- vserver: ci_dev
- hostname: "{{ netapp hostname }}"
- username: "{{ netapp username }}"
- password: "{{ netapp password }}"
-
- - name: Enable firewall and logging on a node
- na_ontap_firewall_policy:
- node: test-vsim1
- enable: enable
- logging: enable
- hostname: "{{ netapp hostname }}"
- username: "{{ netapp username }}"
- password: "{{ netapp password }}"
-
-"""
-
-RETURN = """
-"""
-
-import traceback
-
-from ansible.module_utils.basic import AnsibleModule
-from ansible.module_utils._text import to_native
-import ansible.module_utils.netapp as netapp_utils
-from ansible.module_utils.netapp_module import NetAppModule
-try:
- import ipaddress
- HAS_IPADDRESS_LIB = True
-except ImportError:
- HAS_IPADDRESS_LIB = False
-
-import sys
-
-HAS_NETAPP_LIB = netapp_utils.has_netapp_lib()
-
-
-class NetAppONTAPFirewallPolicy(object):
- def __init__(self):
- self.argument_spec = netapp_utils.na_ontap_host_argument_spec()
- self.argument_spec.update(dict(
- state=dict(required=False, choices=['present', 'absent'], default='present'),
- allow_list=dict(required=False, type="list"),
- policy=dict(required=False, type='str'),
- service=dict(required=False, type='str', choices=['dns', 'http', 'https', 'ndmp',
- 'ndmps', 'ntp', 'rsh', 'snmp', 'ssh', 'telnet']),
- vserver=dict(required=False, type="str"),
- enable=dict(required=False, type="str", choices=['enable', 'disable']),
- logging=dict(required=False, type="str", choices=['enable', 'disable']),
- node=dict(required=False, type="str")
- ))
-
- self.module = AnsibleModule(
- argument_spec=self.argument_spec,
- required_together=(['policy', 'service', 'vserver'],
- ['enable', 'node']
- ),
- supports_check_mode=True
- )
-
- self.na_helper = NetAppModule()
- self.parameters = self.na_helper.set_parameters(self.module.params)
-
- if HAS_NETAPP_LIB is False:
- self.module.fail_json(msg="the python NetApp-Lib module is required")
- else:
- self.server = netapp_utils.setup_na_ontap_zapi(module=self.module)
-
- if HAS_IPADDRESS_LIB is False:
- self.module.fail_json(msg="the python ipaddress lib is required for this module")
- return
-
- def validate_ip_addresses(self):
- '''
- Validate if the given IP address is a network address (i.e. it's host bits are set to 0)
- ONTAP doesn't validate if the host bits are set,
- and hence doesn't add a new address unless the IP is from a different network.
- So this validation allows the module to be idempotent.
- :return: None
- '''
- for ip in self.parameters['allow_list']:
- # create an IPv4 object for current IP address
- if sys.version_info[0] >= 3:
- ip_addr = str(ip)
- else:
- ip_addr = unicode(ip) # pylint: disable=undefined-variable
- # get network address from netmask, throw exception if address is not a network address
- try:
- ipaddress.ip_network(ip_addr)
- except ValueError as exc:
- self.module.fail_json(msg='Error: Invalid IP address value for allow_list parameter.'
- 'Please specify a network address without host bits set: %s'
- % (to_native(exc)))
-
- def get_firewall_policy(self):
- """
- Get a firewall policy
- :return: returns a firewall policy object, or returns False if there are none
- """
- net_firewall_policy_obj = netapp_utils.zapi.NaElement("net-firewall-policy-get-iter")
- attributes = {
- 'query': {
- 'net-firewall-policy-info': self.firewall_policy_attributes()
- }
- }
- net_firewall_policy_obj.translate_struct(attributes)
-
- try:
- result = self.server.invoke_successfully(net_firewall_policy_obj, True)
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg="Error getting firewall policy %s:%s" % (self.parameters['policy'],
- to_native(error)),
- exception=traceback.format_exc())
-
- if result.get_child_by_name('num-records') and int(result.get_child_content('num-records')) >= 1:
- attributes_list = result.get_child_by_name('attributes-list')
- policy_info = attributes_list.get_child_by_name('net-firewall-policy-info')
- ips = self.na_helper.get_value_for_list(from_zapi=True,
- zapi_parent=policy_info.get_child_by_name('allow-list'))
- return {
- 'service': policy_info['service'],
- 'allow_list': ips}
- return None
-
- def create_firewall_policy(self):
- """
- Create a firewall policy for given vserver
- :return: None
- """
- net_firewall_policy_obj = netapp_utils.zapi.NaElement("net-firewall-policy-create")
- net_firewall_policy_obj.translate_struct(self.firewall_policy_attributes())
- if self.parameters.get('allow_list'):
- self.validate_ip_addresses()
- net_firewall_policy_obj.add_child_elem(self.na_helper.get_value_for_list(from_zapi=False,
- zapi_parent='allow-list',
- zapi_child='ip-and-mask',
- data=self.parameters['allow_list'])
- )
- try:
- self.server.invoke_successfully(net_firewall_policy_obj, enable_tunneling=True)
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg="Error creating Firewall Policy: %s" % (to_native(error)), exception=traceback.format_exc())
-
- def destroy_firewall_policy(self):
- """
- Destroy a Firewall Policy from a vserver
- :return: None
- """
- net_firewall_policy_obj = netapp_utils.zapi.NaElement("net-firewall-policy-destroy")
- net_firewall_policy_obj.translate_struct(self.firewall_policy_attributes())
- try:
- self.server.invoke_successfully(net_firewall_policy_obj, enable_tunneling=True)
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg="Error destroying Firewall Policy: %s" % (to_native(error)), exception=traceback.format_exc())
-
- def modify_firewall_policy(self, modify):
- """
- Modify a firewall Policy on a vserver
- :return: none
- """
- self.validate_ip_addresses()
- net_firewall_policy_obj = netapp_utils.zapi.NaElement("net-firewall-policy-modify")
- net_firewall_policy_obj.translate_struct(self.firewall_policy_attributes())
- net_firewall_policy_obj.add_child_elem(self.na_helper.get_value_for_list(from_zapi=False,
- zapi_parent='allow-list',
- zapi_child='ip-and-mask',
- data=modify['allow_list']))
- try:
- self.server.invoke_successfully(net_firewall_policy_obj, enable_tunneling=True)
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg="Error modifying Firewall Policy: %s" % (to_native(error)), exception=traceback.format_exc())
-
- def firewall_policy_attributes(self):
- return {
- 'policy': self.parameters['policy'],
- 'service': self.parameters['service'],
- 'vserver': self.parameters['vserver'],
- }
-
- def get_firewall_config_for_node(self):
- """
- Get firewall configuration on the node
- :return: dict() with firewall config details
- """
- if self.parameters.get('logging'):
- if self.parameters.get('node') is None:
- self.module.fail_json(msg='Error: Missing parameter \'node\' to modify firewall logging')
- net_firewall_config_obj = netapp_utils.zapi.NaElement("net-firewall-config-get")
- net_firewall_config_obj.add_new_child('node-name', self.parameters['node'])
- try:
- result = self.server.invoke_successfully(net_firewall_config_obj, True)
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg="Error getting Firewall Configuration: %s" % (to_native(error)),
- exception=traceback.format_exc())
- if result.get_child_by_name('attributes'):
- firewall_info = result['attributes'].get_child_by_name('net-firewall-config-info')
- return {'enable': self.change_status_to_bool(firewall_info.get_child_content('is-enabled'), to_zapi=False),
- 'logging': self.change_status_to_bool(firewall_info.get_child_content('is-logging'), to_zapi=False)}
- return None
-
- def modify_firewall_config(self, modify):
- """
- Modify the configuration of a firewall on node
- :return: None
- """
- net_firewall_config_obj = netapp_utils.zapi.NaElement("net-firewall-config-modify")
- net_firewall_config_obj.add_new_child('node-name', self.parameters['node'])
- if modify.get('enable'):
- net_firewall_config_obj.add_new_child('is-enabled', self.change_status_to_bool(self.parameters['enable']))
- if modify.get('logging'):
- net_firewall_config_obj.add_new_child('is-logging', self.change_status_to_bool(self.parameters['logging']))
- try:
- self.server.invoke_successfully(net_firewall_config_obj, enable_tunneling=True)
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg="Error modifying Firewall Config: %s" % (to_native(error)),
- exception=traceback.format_exc())
-
- def change_status_to_bool(self, input, to_zapi=True):
- if to_zapi:
- return 'true' if input == 'enable' else 'false'
- else:
- return 'enable' if input == 'true' else 'disable'
-
- def autosupport_log(self):
- results = netapp_utils.get_cserver(self.server)
- cserver = netapp_utils.setup_na_ontap_zapi(module=self.module, vserver=results)
- netapp_utils.ems_log_event("na_ontap_firewall_policy", cserver)
-
- def apply(self):
- self.autosupport_log()
- cd_action, modify, modify_config = None, None, None
- if self.parameters.get('policy'):
- current = self.get_firewall_policy()
- cd_action = self.na_helper.get_cd_action(current, self.parameters)
- if cd_action is None and self.parameters['state'] == 'present':
- modify = self.na_helper.get_modified_attributes(current, self.parameters)
- if self.parameters.get('node'):
- current_config = self.get_firewall_config_for_node()
- # firewall config for a node is always present, we cannot create or delete a firewall on a node
- modify_config = self.na_helper.get_modified_attributes(current_config, self.parameters)
-
- if self.na_helper.changed:
- if self.module.check_mode:
- pass
- else:
- if cd_action == 'create':
- self.create_firewall_policy()
- elif cd_action == 'delete':
- self.destroy_firewall_policy()
- else:
- if modify:
- self.modify_firewall_policy(modify)
- if modify_config:
- self.modify_firewall_config(modify_config)
- self.module.exit_json(changed=self.na_helper.changed)
-
-
-def main():
- """
- Execute action from playbook
- :return: nothing
- """
- cg_obj = NetAppONTAPFirewallPolicy()
- cg_obj.apply()
-
-
-if __name__ == '__main__':
- main()
diff --git a/lib/ansible/modules/storage/netapp/na_ontap_firmware_upgrade.py b/lib/ansible/modules/storage/netapp/na_ontap_firmware_upgrade.py
deleted file mode 100644
index dd50060c54..0000000000
--- a/lib/ansible/modules/storage/netapp/na_ontap_firmware_upgrade.py
+++ /dev/null
@@ -1,461 +0,0 @@
-#!/usr/bin/python
-
-# (c) 2019, NetApp, Inc
-# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-from __future__ import absolute_import, division, print_function
-__metaclass__ = type
-
-ANSIBLE_METADATA = {'metadata_version': '1.1',
- 'status': ['preview'],
- 'supported_by': 'community'}
-
-
-DOCUMENTATION = '''
-author: NetApp Ansible Team (@carchi8py) <ng-ansibleteam@netapp.com>
-description:
- - Update ONTAP service-prosessor firmware
-extends_documentation_fragment:
- - netapp.na_ontap
-module: na_ontap_firmware_upgrade
-options:
- state:
- description:
- - Whether the specified ONTAP firmware should be upgraded or not.
- default: present
- type: str
- node:
- description:
- - Node on which the device is located.
- type: str
- required: true
- clear_logs:
- description:
- - Clear logs on the device after update. Default value is true
- type: bool
- default: true
- package:
- description:
- - Name of the package file containing the firmware to be installed. Not required when -baseline is true.
- type: str
- shelf_module_fw:
- description:
- - Shelf module firmware to be updated to.
- type: str
- disk_fw:
- description:
- - disk firmware to be updated to.
- type: str
- update_type:
- description:
- - Type of firmware update to be performed. Options include serial_full, serial_differential, network_full.
- type: str
- install_baseline_image:
- description:
- - Install the version packaged with ONTAP if this parameter is set to true. Otherwise, package must be used to specify the package to install.
- type: bool
- default: false
- firmware_type:
- description:
- - Type of firmware to be upgraded. Options include shelf, ACP, service-processor, and disk.
- - For shelf firmware upgrade the operation is asynchronous, and therefore returns no errors that might occur during the download process.
- - Shelf firmware upgrade is idempotent if shelf_module_fw is provided .
- - disk firmware upgrade is idempotent if disk_fw is provided .
- - With check mode, SP, ACP, disk, and shelf firmware upgrade is not idempotent.
- - This operation will only update firmware on shelves/disk that do not have the latest firmware-revision.
- choices: ['service-processor', 'shelf', 'acp', 'disk']
- type: str
-short_description: NetApp ONTAP firmware upgrade for SP, shelf, ACP, and disk.
-version_added: "2.9"
-'''
-
-EXAMPLES = """
-
- - name: SP firmware upgrade
- na_ontap_firmware_upgrade:
- state: present
- node: vsim1
- package: "{{ file name }}"
- clear_logs: True
- install_baseline_image: False
- update_type: serial_full
- firmware_type: service-processor
- hostname: "{{ netapp_hostname }}"
- username: "{{ netapp_username }}"
- password: "{{ netapp_password }}"
- - name: ACP firmware upgrade
- na_ontap_firmware_upgrade:
- state: present
- node: vsim1
- firmware_type: acp
- hostname: "{{ netapp_hostname }}"
- username: "{{ netapp_username }}"
- password: "{{ netapp_password }}"
- - name: shelf firmware upgrade
- na_ontap_firmware_upgrade:
- state: present
- firmware_type: shelf
- shelf_module_fw: 1221
- hostname: "{{ netapp_hostname }}"
- username: "{{ netapp_username }}"
- password: "{{ netapp_password }}"
- - name: disk firmware upgrade
- na_ontap_firmware_upgrade:
- state: present
- firmware_type: disk
- disk_fw: NA02
- hostname: "{{ netapp_hostname }}"
- username: "{{ netapp_username }}"
- password: "{{ netapp_password }}"
-"""
-
-RETURN = """
-"""
-
-import traceback
-from ansible.module_utils.basic import AnsibleModule
-from ansible.module_utils._text import to_native
-import ansible.module_utils.netapp as netapp_utils
-from ansible.module_utils.netapp_module import NetAppModule
-import time
-
-HAS_NETAPP_LIB = netapp_utils.has_netapp_lib()
-
-
-class NetAppONTAPFirmwareUpgrade(object):
- """
- Class with ONTAP firmware upgrade methods
- """
-
- def __init__(self):
- self.argument_spec = netapp_utils.na_ontap_host_argument_spec()
- self.argument_spec.update(dict(
- state=dict(required=False, type='str', default='present'),
- node=dict(required=False, type='str'),
- firmware_type=dict(required=True, type='str', choices=['service-processor', 'shelf', 'acp', 'disk']),
- clear_logs=dict(required=False, type='bool', default=True),
- package=dict(required=False, type='str'),
- install_baseline_image=dict(required=False, type='bool', default=False),
- update_type=dict(required=False, type='str'),
- shelf_module_fw=dict(required=False, type='str'),
- disk_fw=dict(required=False, type='str')
-
- ))
-
- self.module = AnsibleModule(
- argument_spec=self.argument_spec,
- required_if=[
- ('firmware_type', 'acp', ['node']),
- ('firmware_type', 'disk', ['node']),
- ('firmware_type', 'service-processor', ['node', 'update_type']),
- ],
- supports_check_mode=True
- )
-
- self.na_helper = NetAppModule()
- self.parameters = self.na_helper.set_parameters(self.module.params)
- if self.parameters.get('firmware_type') == 'service-processor':
- if self.parameters.get('install_baseline_image') and self.parameters.get('package') is not None:
- self.module.fail_json(msg='Do not specify both package and install_baseline_image: true')
- if not self.parameters.get('package') and self.parameters.get('install_baseline_image') == 'False':
- self.module.fail_json(msg='Specify at least one of package or install_baseline_image')
- if HAS_NETAPP_LIB is False:
- self.module.fail_json(msg="the python NetApp-Lib module is required")
- else:
- self.server = netapp_utils.setup_na_ontap_zapi(module=self.module)
-
- def firmware_image_get_iter(self):
- """
- Compose NaElement object to query current firmware version
- :return: NaElement object for firmware_image_get_iter with query
- """
- firmware_image_get = netapp_utils.zapi.NaElement('service-processor-get-iter')
- query = netapp_utils.zapi.NaElement('query')
- firmware_image_info = netapp_utils.zapi.NaElement('service-processor-info')
- firmware_image_info.add_new_child('node', self.parameters['node'])
- query.add_child_elem(firmware_image_info)
- firmware_image_get.add_child_elem(query)
- return firmware_image_get
-
- def firmware_image_get(self, node_name):
- """
- Get current firmware image info
- :return: True if query successful, else return None
- """
- firmware_image_get_iter = self.firmware_image_get_iter()
- try:
- result = self.server.invoke_successfully(firmware_image_get_iter, enable_tunneling=True)
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg='Error fetching firmware image details: %s: %s'
- % (self.parameters['node'], to_native(error)),
- exception=traceback.format_exc())
- # return firmware image details
- if result.get_child_by_name('num-records') and int(result.get_child_content('num-records')) > 0:
- sp_info = result.get_child_by_name('attributes-list').get_child_by_name('service-processor-info')
- firmware_version = sp_info.get_child_content('firmware-version')
- return firmware_version
- return None
-
- def acp_firmware_required_get(self):
- """
- where acp firmware upgrade is required
- :return: True is firmware upgrade is required else return None
- """
- acp_firmware_get_iter = netapp_utils.zapi.NaElement('storage-shelf-acp-module-get-iter')
- query = netapp_utils.zapi.NaElement('query')
- acp_info = netapp_utils.zapi.NaElement('storage-shelf-acp-module')
- query.add_child_elem(acp_info)
- acp_firmware_get_iter.add_child_elem(query)
- try:
- result = self.server.invoke_successfully(acp_firmware_get_iter, enable_tunneling=True)
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg='Error fetching acp firmware details details: %s'
- % (to_native(error)), exception=traceback.format_exc())
- if result.get_child_by_name('attributes-list').get_child_by_name('storage-shelf-acp-module'):
- acp_module_info = result.get_child_by_name('attributes-list').get_child_by_name(
- 'storage-shelf-acp-module')
- state = acp_module_info.get_child_content('state')
- if state == 'firmware_update_required':
- # acp firmware version upgrade required
- return True
- return False
-
- def sp_firmware_image_update_progress_get(self, node_name):
- """
- Get current firmware image update progress info
- :return: Dictionary of firmware image update progress if query successful, else return None
- """
- firmware_update_progress_get = netapp_utils.zapi.NaElement('service-processor-image-update-progress-get')
- firmware_update_progress_get.add_new_child('node', self.parameters['node'])
-
- firmware_update_progress_info = dict()
- try:
- result = self.server.invoke_successfully(firmware_update_progress_get, enable_tunneling=True)
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg='Error fetching firmware image upgrade progress details: %s'
- % (to_native(error)), exception=traceback.format_exc())
- # return firmware image update progress details
- if result.get_child_by_name('attributes').get_child_by_name('service-processor-image-update-progress-info'):
- update_progress_info = result.get_child_by_name('attributes').get_child_by_name('service-processor-image-update-progress-info')
- firmware_update_progress_info['is-in-progress'] = update_progress_info.get_child_content('is-in-progress')
- firmware_update_progress_info['node'] = update_progress_info.get_child_content('node')
- return firmware_update_progress_info
-
- def shelf_firmware_info_get(self):
- """
- Get the current firmware of shelf module
- :return:dict with module id and firmware info
- """
- shelf_id_fw_info = dict()
- shelf_firmware_info_get = netapp_utils.zapi.NaElement('storage-shelf-info-get-iter')
- desired_attributes = netapp_utils.zapi.NaElement('desired-attributes')
- storage_shelf_info = netapp_utils.zapi.NaElement('storage-shelf-info')
- shelf_module = netapp_utils.zapi.NaElement('shelf-modules')
- shelf_module_info = netapp_utils.zapi.NaElement('storage-shelf-module-info')
- shelf_module.add_child_elem(shelf_module_info)
- storage_shelf_info.add_child_elem(shelf_module)
- desired_attributes.add_child_elem(storage_shelf_info)
- shelf_firmware_info_get.add_child_elem(desired_attributes)
-
- try:
- result = self.server.invoke_successfully(shelf_firmware_info_get, enable_tunneling=True)
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg='Error fetching shelf module firmware details: %s'
- % (to_native(error)), exception=traceback.format_exc())
- if result.get_child_by_name('num-records') and int(result.get_child_content('num-records')) > 0:
- shelf_info = result.get_child_by_name('attributes-list').get_child_by_name('storage-shelf-info')
- if (shelf_info.get_child_by_name('shelf-modules') and
- shelf_info.get_child_by_name('shelf-modules').get_child_by_name('storage-shelf-module-info')):
- shelves = shelf_info['shelf-modules'].get_children()
- for shelf in shelves:
- shelf_id_fw_info[shelf.get_child_content('module-id')] = shelf.get_child_content('module-fw-revision')
- return shelf_id_fw_info
-
- def disk_firmware_info_get(self):
- """
- Get the current firmware of disks module
- :return:
- """
- disk_id_fw_info = dict()
- disk_firmware_info_get = netapp_utils.zapi.NaElement('storage-disk-get-iter')
- desired_attributes = netapp_utils.zapi.NaElement('desired-attributes')
- storage_disk_info = netapp_utils.zapi.NaElement('storage-disk-info')
- disk_inv = netapp_utils.zapi.NaElement('disk-inventory-info')
- storage_disk_info.add_child_elem(disk_inv)
- desired_attributes.add_child_elem(storage_disk_info)
- disk_firmware_info_get.add_child_elem(desired_attributes)
- try:
- result = self.server.invoke_successfully(disk_firmware_info_get, enable_tunneling=True)
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg='Error fetching disk module firmware details: %s'
- % (to_native(error)), exception=traceback.format_exc())
- if result.get_child_by_name('num-records') and int(result.get_child_content('num-records')) > 0:
- disk_info = result.get_child_by_name('attributes-list')
- disks = disk_info.get_children()
- for disk in disks:
- disk_id_fw_info[disk.get_child_content('disk-uid')] = disk.get_child_by_name('disk-inventory-info').get_child_content('firmware-revision')
- return disk_id_fw_info
-
- def disk_firmware_required_get(self):
- """
- Check weather disk firmware upgrade is required or not
- :return: True if the firmware upgrade is required
- """
- disk_firmware_info = self.disk_firmware_info_get()
- for disk in disk_firmware_info:
- if (disk_firmware_info[disk]) != self.parameters['disk_fw']:
- return True
- return False
-
- def shelf_firmware_required_get(self):
- """
- Check weather shelf firmware upgrade is required or not
- :return: True if the firmware upgrade is required
- """
- shelf_firmware_info = self.shelf_firmware_info_get()
- for module in shelf_firmware_info:
- if (shelf_firmware_info[module]) != self.parameters['shelf_module_fw']:
- return True
- return False
-
- def sp_firmware_image_update(self):
- """
- Update current firmware image
- """
- firmware_update_info = netapp_utils.zapi.NaElement('service-processor-image-update')
- if self.parameters.get('package') is not None:
- firmware_update_info.add_new_child('package', self.parameters['package'])
- if self.parameters.get('clear_logs') is not None:
- firmware_update_info.add_new_child('clear-logs', str(self.parameters['clear_logs']))
- if self.parameters.get('install_baseline_image') is not None:
- firmware_update_info.add_new_child('install-baseline-image', str(self.parameters['install_baseline_image']))
- firmware_update_info.add_new_child('node', self.parameters['node'])
- firmware_update_info.add_new_child('update-type', self.parameters['update_type'])
-
- try:
- self.server.invoke_successfully(firmware_update_info, enable_tunneling=True)
- except netapp_utils.zapi.NaApiError as error:
- # Current firmware version matches the version to be installed
- if to_native(error.code) == '13001' and (error.message.startswith('Service Processor update skipped')):
- return False
- self.module.fail_json(msg='Error updating firmware image for %s: %s'
- % (self.parameters['node'], to_native(error)),
- exception=traceback.format_exc())
- return True
-
- def shelf_firmware_upgrade(self):
- """
- Upgrade shelf firmware image
- """
- shelf_firmware_update_info = netapp_utils.zapi.NaElement('storage-shelf-firmware-update')
- try:
- self.server.invoke_successfully(shelf_firmware_update_info, enable_tunneling=True)
- return True
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg='Error updating shelf firmware image : %s'
- % (to_native(error)), exception=traceback.format_exc())
-
- def acp_firmware_upgrade(self):
-
- """
- Upgrade shelf firmware image
- """
- acp_firmware_update_info = netapp_utils.zapi.NaElement('storage-shelf-acp-firmware-update')
- acp_firmware_update_info.add_new_child('node-name', self.parameters['node'])
- try:
- self.server.invoke_successfully(acp_firmware_update_info, enable_tunneling=True)
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg='Error updating acp firmware image : %s'
- % (to_native(error)), exception=traceback.format_exc())
-
- def disk_firmware_upgrade(self):
-
- """
- Upgrade disk firmware
- """
- disk_firmware_update_info = netapp_utils.zapi.NaElement('disk-update-disk-fw')
- disk_firmware_update_info.add_new_child('node-name', self.parameters['node'])
- try:
- self.server.invoke_successfully(disk_firmware_update_info, enable_tunneling=True)
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg='Error updating disk firmware image : %s'
- % (to_native(error)), exception=traceback.format_exc())
- return True
-
- def autosupport_log(self):
- """
- Autosupport log for software_update
- :return:
- """
- results = netapp_utils.get_cserver(self.server)
- cserver = netapp_utils.setup_na_ontap_zapi(module=self.module, vserver=results)
- netapp_utils.ems_log_event("na_ontap_firmware_upgrade", cserver)
-
- def apply(self):
- """
- Apply action to upgrade firmware
- """
- changed = False
- self.autosupport_log()
- firmware_update_progress = dict()
- if self.parameters.get('firmware_type') == 'service-processor':
- # service-processor firmware upgrade
- current = self.firmware_image_get(self.parameters['node'])
-
- if self.parameters.get('state') == 'present' and current:
- if not self.module.check_mode:
- if self.sp_firmware_image_update():
- changed = True
- firmware_update_progress = self.sp_firmware_image_update_progress_get(self.parameters['node'])
- while firmware_update_progress.get('is-in-progress') == 'true':
- time.sleep(25)
- firmware_update_progress = self.sp_firmware_image_update_progress_get(self.parameters['node'])
- else:
- # we don't know until we try the upgrade
- changed = True
-
- elif self.parameters.get('firmware_type') == 'shelf':
- # shelf firmware upgrade
- if self.parameters.get('shelf_module_fw'):
- if self.shelf_firmware_required_get():
- if not self.module.check_mode:
- changed = self.shelf_firmware_upgrade()
- else:
- changed = True
- else:
- if not self.module.check_mode:
- changed = self.shelf_firmware_upgrade()
- else:
- # we don't know until we try the upgrade -- assuming the worst
- changed = True
- elif self.parameters.get('firmware_type') == 'acp':
- # acp firmware upgrade
- if self.acp_firmware_required_get():
- if not self.module.check_mode:
- self.acp_firmware_upgrade()
- changed = True
- elif self.parameters.get('firmware_type') == 'disk':
- # Disk firmware upgrade
- if self.parameters.get('disk_fw'):
- if self.disk_firmware_required_get():
- if not self.module.check_mode:
- changed = self.disk_firmware_upgrade()
- else:
- changed = True
- else:
- if not self.module.check_mode:
- changed = self.disk_firmware_upgrade()
- else:
- # we don't know until we try the upgrade -- assuming the worst
- changed = True
-
- self.module.exit_json(changed=changed)
-
-
-def main():
- """Execute action"""
- community_obj = NetAppONTAPFirmwareUpgrade()
- community_obj.apply()
-
-
-if __name__ == '__main__':
- main()
diff --git a/lib/ansible/modules/storage/netapp/na_ontap_flexcache.py b/lib/ansible/modules/storage/netapp/na_ontap_flexcache.py
deleted file mode 100644
index 3b72e7d4d2..0000000000
--- a/lib/ansible/modules/storage/netapp/na_ontap_flexcache.py
+++ /dev/null
@@ -1,474 +0,0 @@
-#!/usr/bin/python
-
-# (c) 2019, NetApp, Inc
-# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-from __future__ import absolute_import, division, print_function
-__metaclass__ = type
-
-ANSIBLE_METADATA = {'metadata_version': '1.1',
- 'status': ['preview'],
- 'supported_by': 'certified'}
-
-
-DOCUMENTATION = '''
-short_description: NetApp ONTAP FlexCache - create/delete relationship
-author: NetApp Ansible Team (@carchi8py) <ng-ansibleteam@netapp.com>
-description:
- - Create/Delete FlexCache volume relationships
-extends_documentation_fragment:
- - netapp.na_ontap
-module: na_ontap_flexcache
-options:
- state:
- choices: ['present', 'absent']
- description:
- - Whether the specified relationship should exist or not.
- default: present
- origin_volume:
- description:
- - Name of the origin volume for the FlexCache.
- - Required for creation.
- origin_vserver:
- description:
- - Name of the origin vserver for the FlexCache.
- - Required for creation.
- origin_cluster:
- description:
- - Name of the origin cluster for the FlexCache.
- - Defaults to cluster associated with target vserver if absent.
- - Not used for creation.
- volume:
- description:
- - Name of the target volume for the FlexCache.
- required: true
- junction_path:
- description:
- - Junction path of the cache volume.
- auto_provision_as:
- description:
- - Use this parameter to automatically select existing aggregates for volume provisioning.Eg flexgroup
- - Note that the fastest aggregate type with at least one aggregate on each node of the cluster will be selected.
- size:
- description:
- - Size of cache volume.
- size_unit:
- description:
- - The unit used to interpret the size parameter.
- choices: ['bytes', 'b', 'kb', 'mb', 'gb', 'tb', 'pb', 'eb', 'zb', 'yb']
- default: gb
- vserver:
- description:
- - Name of the target vserver for the FlexCache.
- - Note that hostname, username, password are intended for the target vserver.
- required: true
- aggr_list:
- description:
- - List of aggregates to host target FlexCache volume.
- aggr_list_multiplier:
- description:
- - Aggregate list repeat count.
- force_unmount:
- description:
- - Unmount FlexCache volume. Delete the junction path at which the volume is mounted before deleting the FlexCache relationship.
- type: bool
- default: false
- force_offline:
- description:
- - Offline FlexCache volume before deleting the FlexCache relationship.
- - The volume will be destroyed and data can be lost.
- type: bool
- default: false
- time_out:
- description:
- - time to wait for flexcache creation or deletion in seconds
- - if 0, the request is asynchronous
- - default is set to 3 minutes
- default: 180
-version_added: "2.8"
-'''
-
-EXAMPLES = """
-
- - name: Create FlexCache
- na_ontap_FlexCache:
- state: present
- origin_volume: test_src
- volume: test_dest
- origin_vserver: ansible_src
- vserver: ansible_dest
- hostname: "{{ netapp_hostname }}"
- username: "{{ netapp_username }}"
- password: "{{ netapp_password }}"
-
- - name: Delete FlexCache
- na_ontap_FlexCache:
- state: absent
- volume: test_dest
- vserver: ansible_dest
- hostname: "{{ netapp_hostname }}"
- username: "{{ netapp_username }}"
- password: "{{ netapp_password }}"
-
-"""
-
-RETURN = """
-"""
-
-import time
-import traceback
-from ansible.module_utils.basic import AnsibleModule
-from ansible.module_utils._text import to_native
-import ansible.module_utils.netapp as netapp_utils
-from ansible.module_utils.netapp_module import NetAppModule
-
-HAS_NETAPP_LIB = netapp_utils.has_netapp_lib()
-
-
-class NetAppONTAPFlexCache(object):
- """
- Class with FlexCache methods
- """
-
- def __init__(self):
-
- self.argument_spec = netapp_utils.na_ontap_host_argument_spec()
- self.argument_spec.update(dict(
- state=dict(required=False, type='str', choices=['present', 'absent'],
- default='present'),
- origin_volume=dict(required=False, type='str'),
- origin_vserver=dict(required=False, type='str'),
- origin_cluster=dict(required=False, type='str'),
- auto_provision_as=dict(required=False, type='str'),
- volume=dict(required=True, type='str'),
- junction_path=dict(required=False, type='str'),
- size=dict(required=False, type='int'),
- size_unit=dict(default='gb',
- choices=['bytes', 'b', 'kb', 'mb', 'gb', 'tb',
- 'pb', 'eb', 'zb', 'yb'], type='str'),
- vserver=dict(required=True, type='str'),
- aggr_list=dict(required=False, type='list'),
- aggr_list_multiplier=dict(required=False, type='int'),
- force_offline=dict(required=False, type='bool', default=False),
- force_unmount=dict(required=False, type='bool', default=False),
- time_out=dict(required=False, type='int', default=180),
- ))
-
- self.module = AnsibleModule(
- argument_spec=self.argument_spec,
- mutually_exclusive=[
- ('aggr_list', 'auto_provision_as'),
- ],
- supports_check_mode=True
- )
-
- self.na_helper = NetAppModule()
- self.parameters = self.na_helper.set_parameters(self.module.params)
- if self.parameters.get('size'):
- self.parameters['size'] = self.parameters['size'] * \
- netapp_utils.POW2_BYTE_MAP[self.parameters['size_unit']]
- # setup later if required
- self.origin_server = None
- if HAS_NETAPP_LIB is False:
- self.module.fail_json(msg="the python NetApp-Lib module is required")
- else:
- self.server = netapp_utils.setup_na_ontap_zapi(module=self.module, vserver=self.parameters['vserver'])
-
- def add_parameter_to_dict(self, adict, name, key=None, tostr=False):
- ''' add defined parameter (not None) to adict using key '''
- if key is None:
- key = name
- if self.parameters.get(name) is not None:
- if tostr:
- adict[key] = str(self.parameters.get(name))
- else:
- adict[key] = self.parameters.get(name)
-
- def get_job(self, jobid, server):
- """
- Get job details by id
- """
- job_get = netapp_utils.zapi.NaElement('job-get')
- job_get.add_new_child('job-id', jobid)
- try:
- result = server.invoke_successfully(job_get, enable_tunneling=True)
- except netapp_utils.zapi.NaApiError as error:
- if to_native(error.code) == "15661":
- # Not found
- return None
- self.module.fail_json(msg='Error fetching job info: %s' % to_native(error),
- exception=traceback.format_exc())
- results = dict()
- job_info = result.get_child_by_name('attributes').get_child_by_name('job-info')
- results = {
- 'job-progress': job_info['job-progress'],
- 'job-state': job_info['job-state']
- }
- if job_info.get_child_by_name('job-completion') is not None:
- results['job-completion'] = job_info['job-completion']
- else:
- results['job-completion'] = None
- return results
-
- def check_job_status(self, jobid):
- """
- Loop until job is complete
- """
- server = self.server
- sleep_time = 5
- time_out = self.parameters['time_out']
- while time_out > 0:
- results = self.get_job(jobid, server)
- # If running as cluster admin, the job is owned by cluster vserver
- # rather than the target vserver.
- if results is None and server == self.server:
- results = netapp_utils.get_cserver(self.server)
- server = netapp_utils.setup_na_ontap_zapi(module=self.module, vserver=results)
- continue
- if results is None:
- error = 'cannot locate job with id: %d' % jobid
- break
- if results['job-state'] in ('queued', 'running'):
- time.sleep(sleep_time)
- time_out -= sleep_time
- continue
- if results['job-state'] in ('success', 'failure'):
- break
- else:
- self.module.fail_json(msg='Unexpected job status in: %s' % repr(results))
-
- if results is not None:
- if results['job-state'] == 'success':
- error = None
- elif results['job-state'] in ('queued', 'running'):
- error = 'job completion exceeded expected timer of: %s seconds' % \
- self.parameters['time_out']
- else:
- if results['job-completion'] is not None:
- error = results['job-completion']
- else:
- error = results['job-progress']
- return error
-
- def flexcache_get_iter(self):
- """
- Compose NaElement object to query current FlexCache relation
- """
- options = {'volume': self.parameters['volume']}
- self.add_parameter_to_dict(options, 'origin_volume', 'origin-volume')
- self.add_parameter_to_dict(options, 'origin_vserver', 'origin-vserver')
- self.add_parameter_to_dict(options, 'origin_cluster', 'origin-cluster')
- flexcache_info = netapp_utils.zapi.NaElement.create_node_with_children(
- 'flexcache-info', **options)
- query = netapp_utils.zapi.NaElement('query')
- query.add_child_elem(flexcache_info)
- flexcache_get_iter = netapp_utils.zapi.NaElement('flexcache-get-iter')
- flexcache_get_iter.add_child_elem(query)
- return flexcache_get_iter
-
- def flexcache_get(self):
- """
- Get current FlexCache relations
- :return: Dictionary of current FlexCache details if query successful, else None
- """
- flexcache_get_iter = self.flexcache_get_iter()
- flex_info = dict()
- try:
- result = self.server.invoke_successfully(flexcache_get_iter, enable_tunneling=True)
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg='Error fetching FlexCache info: %s' % to_native(error),
- exception=traceback.format_exc())
- if result.get_child_by_name('num-records') and \
- int(result.get_child_content('num-records')) == 1:
- flexcache_info = result.get_child_by_name('attributes-list') \
- .get_child_by_name('flexcache-info')
- flex_info['origin_cluster'] = flexcache_info.get_child_content('origin-cluster')
- flex_info['origin_volume'] = flexcache_info.get_child_content('origin-volume')
- flex_info['origin_vserver'] = flexcache_info.get_child_content('origin-vserver')
- flex_info['size'] = flexcache_info.get_child_content('size')
- flex_info['volume'] = flexcache_info.get_child_content('volume')
- flex_info['vserver'] = flexcache_info.get_child_content('vserver')
- flex_info['auto_provision_as'] = flexcache_info.get_child_content('auto-provision-as')
-
- return flex_info
- if result.get_child_by_name('num-records') and \
- int(result.get_child_content('num-records')) > 1:
- msg = 'Multiple records found for %s:' % self.parameters['volume']
- self.module.fail_json(msg='Error fetching FlexCache info: %s' % msg)
- return None
-
- def flexcache_create_async(self):
- """
- Create a FlexCache relationship
- """
- options = {'origin-volume': self.parameters['origin_volume'],
- 'origin-vserver': self.parameters['origin_vserver'],
- 'volume': self.parameters['volume']}
- self.add_parameter_to_dict(options, 'junction_path', 'junction-path')
- self.add_parameter_to_dict(options, 'auto_provision_as', 'auto-provision-as')
- self.add_parameter_to_dict(options, 'size', 'size', tostr=True)
- if self.parameters.get('aggr_list'):
- if self.parameters.get('aggr_list_multiplier'):
- self.tobytes_aggr_list_multiplier = bytes(self.parameters['aggr_list_multiplier'])
- self.add_parameter_to_dict(options, 'tobytes_aggr_list_multiplier', 'aggr-list-multiplier')
- flexcache_create = netapp_utils.zapi.NaElement.create_node_with_children(
- 'flexcache-create-async', **options)
- if self.parameters.get('aggr_list'):
- aggregates = netapp_utils.zapi.NaElement('aggr-list')
- for aggregate in self.parameters['aggr_list']:
- aggregates.add_new_child('aggr-name', aggregate)
- flexcache_create.add_child_elem(aggregates)
- try:
- result = self.server.invoke_successfully(flexcache_create, enable_tunneling=True)
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg='Error creating FlexCache %s' % to_native(error),
- exception=traceback.format_exc())
- results = dict()
- for key in ('result-status', 'result-jobid'):
- if result.get_child_by_name(key):
- results[key] = result[key]
- return results
-
- def flexcache_create(self):
- """
- Create a FlexCache relationship
- Check job status
- """
- results = self.flexcache_create_async()
- status = results.get('result-status')
- if status == 'in_progress' and 'result-jobid' in results:
- if self.parameters['time_out'] == 0:
- # asynchronous call, assuming success!
- return
- error = self.check_job_status(results['result-jobid'])
- if error is None:
- return
- else:
- self.module.fail_json(msg='Error when creating flexcache: %s' % error)
- self.module.fail_json(msg='Unexpected error when creating flexcache: results is: %s' % repr(results))
-
- def flexcache_delete_async(self):
- """
- Delete FlexCache relationship at destination cluster
- """
- options = {'volume': self.parameters['volume']}
- flexcache_delete = netapp_utils.zapi.NaElement.create_node_with_children(
- 'flexcache-destroy-async', **options)
- try:
- result = self.server.invoke_successfully(flexcache_delete, enable_tunneling=True)
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg='Error deleting FlexCache : %s'
- % (to_native(error)),
- exception=traceback.format_exc())
- results = dict()
- for key in ('result-status', 'result-jobid'):
- if result.get_child_by_name(key):
- results[key] = result[key]
- return results
-
- def volume_offline(self):
- """
- Offline FlexCache volume at destination cluster
- """
- options = {'name': self.parameters['volume']}
- xml = netapp_utils.zapi.NaElement.create_node_with_children(
- 'volume-offline', **options)
- try:
- self.server.invoke_successfully(xml, enable_tunneling=True)
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg='Error offlining FlexCache volume: %s'
- % (to_native(error)),
- exception=traceback.format_exc())
-
- def volume_unmount(self):
- """
- Unmount FlexCache volume at destination cluster
- """
- options = {'volume-name': self.parameters['volume']}
- xml = netapp_utils.zapi.NaElement.create_node_with_children(
- 'volume-unmount', **options)
- try:
- self.server.invoke_successfully(xml, enable_tunneling=True)
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg='Error unmounting FlexCache volume: %s'
- % (to_native(error)),
- exception=traceback.format_exc())
-
- def flexcache_delete_async(self):
- """
- Delete FlexCache relationship at destination cluster
- """
- options = {'volume': self.parameters['volume']}
- flexcache_delete = netapp_utils.zapi.NaElement.create_node_with_children(
- 'flexcache-destroy-async', **options)
- try:
- result = self.server.invoke_successfully(flexcache_delete, enable_tunneling=True)
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg='Error deleting FlexCache : %s'
- % (to_native(error)),
- exception=traceback.format_exc())
- results = dict()
- for key in ('result-status', 'result-jobid'):
- if result.get_child_by_name(key):
- results[key] = result[key]
- return results
-
- def flexcache_delete(self):
- """
- Delete FlexCache relationship at destination cluster
- Check job status
- """
- if self.parameters['force_unmount']:
- self.volume_unmount()
- if self.parameters['force_offline']:
- self.volume_offline()
- results = self.flexcache_delete_async()
- status = results.get('result-status')
- if status == 'in_progress' and 'result-jobid' in results:
- if self.parameters['time_out'] == 0:
- # asynchronous call, assuming success!
- return
- error = self.check_job_status(results['result-jobid'])
- if error is None:
- return
- else:
- self.module.fail_json(msg='Error when deleting flexcache: %s' % error)
- self.module.fail_json(msg='Unexpected error when deleting flexcache: results is: %s' % repr(results))
-
- def check_parameters(self):
- """
- Validate parameters and fail if one or more required params are missing
- """
- missings = list()
- expected = ('origin_volume', 'origin_vserver')
- if self.parameters['state'] == 'present':
- for param in expected:
- if not self.parameters.get(param):
- missings.append(param)
- if missings:
- plural = 's' if len(missings) > 1 else ''
- msg = 'Missing parameter%s: %s' % (plural, ', '.join(missings))
- self.module.fail_json(msg=msg)
-
- def apply(self):
- """
- Apply action to FlexCache
- """
- netapp_utils.ems_log_event("na_ontap_flexcache", self.server)
- current = self.flexcache_get()
- cd_action = self.na_helper.get_cd_action(current, self.parameters)
- if cd_action == 'create':
- self.check_parameters()
- self.flexcache_create()
- elif cd_action == 'delete':
- self.flexcache_delete()
- self.module.exit_json(changed=self.na_helper.changed)
-
-
-def main():
- """Execute action"""
- community_obj = NetAppONTAPFlexCache()
- community_obj.apply()
-
-
-if __name__ == '__main__':
- main()
diff --git a/lib/ansible/modules/storage/netapp/na_ontap_igroup.py b/lib/ansible/modules/storage/netapp/na_ontap_igroup.py
deleted file mode 100644
index 57e808d687..0000000000
--- a/lib/ansible/modules/storage/netapp/na_ontap_igroup.py
+++ /dev/null
@@ -1,346 +0,0 @@
-#!/usr/bin/python
-''' this is igroup module
-
- (c) 2018-2019, NetApp, Inc
- # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-'''
-
-from __future__ import absolute_import, division, print_function
-
-__metaclass__ = type
-
-ANSIBLE_METADATA = {
- 'metadata_version': '1.1',
- 'status': ['preview'],
- 'supported_by': 'certified'
-}
-
-DOCUMENTATION = '''
-
-module: na_ontap_igroup
-short_description: NetApp ONTAP iSCSI or FC igroup configuration
-extends_documentation_fragment:
- - netapp.na_ontap
-version_added: '2.6'
-author: NetApp Ansible Team (@carchi8py) <ng-ansibleteam@netapp.com>
-
-description:
- - Create/Delete/Rename Igroups and Modify initiators belonging to an igroup
-
-options:
- state:
- description:
- - Whether the specified Igroup should exist or not.
- choices: ['present', 'absent']
- default: present
-
- name:
- description:
- - The name of the igroup to manage.
- required: true
-
- initiator_group_type:
- description:
- - Type of the initiator group.
- - Required when C(state=present).
- choices: ['fcp', 'iscsi', 'mixed']
-
- from_name:
- description:
- - Name of igroup to rename to name.
- version_added: '2.7'
-
- ostype:
- description:
- - OS type of the initiators within the group.
-
- initiators:
- description:
- - List of initiators to be mapped to the igroup.
- - WWPN, WWPN Alias, or iSCSI name of Initiator to add or remove.
- - For a modify operation, this list replaces the existing initiators
- - This module does not add or remove specific initiator(s) in an igroup
- aliases:
- - initiator
-
- bind_portset:
- description:
- - Name of a current portset to bind to the newly created igroup.
-
- force_remove_initiator:
- description:
- - Forcibly remove the initiator even if there are existing LUNs mapped to this initiator group.
- type: bool
-
- vserver:
- description:
- - The name of the vserver to use.
- required: true
-
-'''
-
-EXAMPLES = '''
- - name: Create iSCSI Igroup
- na_ontap_igroup:
- state: present
- name: ansibleIgroup3
- initiator_group_type: iscsi
- ostype: linux
- initiators: iqn.1994-05.com.redhat:scspa0395855001.rtp.openenglab.netapp.com,abc.com:redhat.com
- vserver: ansibleVServer
- hostname: "{{ netapp_hostname }}"
- username: "{{ netapp_username }}"
- password: "{{ netapp_password }}"
-
- - name: Create FC Igroup
- na_ontap_igroup:
- state: present
- name: ansibleIgroup4
- initiator_group_type: fcp
- ostype: linux
- initiators: 20:00:00:50:56:9f:19:82
- vserver: ansibleVServer
- hostname: "{{ netapp_hostname }}"
- username: "{{ netapp_username }}"
- password: "{{ netapp_password }}"
-
- - name: rename Igroup
- na_ontap_igroup:
- state: present
- from_name: ansibleIgroup3
- name: testexamplenewname
- initiator_group_type: iscsi
- ostype: linux
- initiators: iqn.1994-05.com.redhat:scspa0395855001.rtp.openenglab.netapp.com
- vserver: ansibleVServer
- hostname: "{{ netapp_hostname }}"
- username: "{{ netapp_username }}"
- password: "{{ netapp_password }}"
-
- - name: Modify Igroup Initiators (replaces existing initiators)
- na_ontap_igroup:
- state: present
- name: ansibleIgroup3
- initiator_group_type: iscsi
- ostype: linux
- initiator: iqn.1994-05.com.redhat:scspa0395855001.rtp.openenglab.netapp.com
- vserver: ansibleVServer
- hostname: "{{ netapp_hostname }}"
- username: "{{ netapp_username }}"
- password: "{{ netapp_password }}"
-
- - name: Delete Igroup
- na_ontap_igroup:
- state: absent
- name: ansibleIgroup3
- vserver: ansibleVServer
- hostname: "{{ netapp_hostname }}"
- username: "{{ netapp_username }}"
- password: "{{ netapp_password }}"
-'''
-
-RETURN = '''
-'''
-
-import traceback
-
-from ansible.module_utils.basic import AnsibleModule
-from ansible.module_utils._text import to_native
-import ansible.module_utils.netapp as netapp_utils
-from ansible.module_utils.netapp_module import NetAppModule
-
-HAS_NETAPP_LIB = netapp_utils.has_netapp_lib()
-
-
-class NetAppOntapIgroup(object):
-
- def __init__(self):
-
- self.argument_spec = netapp_utils.na_ontap_host_argument_spec()
- self.argument_spec.update(dict(
- state=dict(required=False, choices=[
- 'present', 'absent'], default='present'),
- name=dict(required=True, type='str'),
- from_name=dict(required=False, type='str', default=None),
- ostype=dict(required=False, type='str'),
- initiator_group_type=dict(required=False, type='str',
- choices=['fcp', 'iscsi', 'mixed']),
- initiators=dict(required=False, type='list', aliases=['initiator']),
- vserver=dict(required=True, type='str'),
- force_remove_initiator=dict(required=False, type='bool', default=False),
- bind_portset=dict(required=False, type='str')
- ))
-
- self.module = AnsibleModule(
- argument_spec=self.argument_spec,
- supports_check_mode=True
- )
-
- self.na_helper = NetAppModule()
- self.parameters = self.na_helper.set_parameters(self.module.params)
-
- if HAS_NETAPP_LIB is False:
- self.module.fail_json(
- msg="the python NetApp-Lib module is required")
- else:
- self.server = netapp_utils.setup_na_ontap_zapi(module=self.module, vserver=self.parameters['vserver'])
-
- def get_igroup(self, name):
- """
- Return details about the igroup
- :param:
- name : Name of the igroup
-
- :return: Details about the igroup. None if not found.
- :rtype: dict
- """
- igroup_info = netapp_utils.zapi.NaElement('igroup-get-iter')
- attributes = dict(query={'initiator-group-info': {'initiator-group-name': name,
- 'vserver': self.parameters['vserver']}})
- igroup_info.translate_struct(attributes)
- result, current = None, None
-
- try:
- result = self.server.invoke_successfully(igroup_info, True)
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg='Error fetching igroup info %s: %s' % (self.parameters['name'], to_native(error)),
- exception=traceback.format_exc())
- if result.get_child_by_name('num-records') and int(result.get_child_content('num-records')) >= 1:
- igroup = result.get_child_by_name('attributes-list').get_child_by_name('initiator-group-info')
- initiators = []
- if igroup.get_child_by_name('initiators'):
- current_initiators = igroup['initiators'].get_children()
- for initiator in current_initiators:
- initiators.append(initiator['initiator-name'])
- current = {
- 'initiators': initiators
- }
-
- return current
-
- def add_initiators(self):
- """
- Add the list of initiators to igroup
- :return: None
- """
- # don't add if initiators is empty string
- if self.parameters.get('initiators') == [''] or self.parameters.get('initiators') is None:
- return
- for initiator in self.parameters['initiators']:
- self.modify_initiator(initiator, 'igroup-add')
-
- def remove_initiators(self, initiators):
- """
- Removes all existing initiators from igroup
- :return: None
- """
- for initiator in initiators:
- self.modify_initiator(initiator, 'igroup-remove')
-
- def modify_initiator(self, initiator, zapi):
- """
- Add or remove an initiator to/from an igroup
- """
- initiator.strip() # remove leading spaces if any (eg: if user types a space after comma in initiators list)
- options = {'initiator-group-name': self.parameters['name'],
- 'initiator': initiator}
-
- igroup_modify = netapp_utils.zapi.NaElement.create_node_with_children(zapi, **options)
-
- try:
- self.server.invoke_successfully(igroup_modify, enable_tunneling=True)
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg='Error modifying igroup initiator %s: %s' % (self.parameters['name'],
- to_native(error)),
- exception=traceback.format_exc())
-
- def create_igroup(self):
- """
- Create the igroup.
- """
- options = {'initiator-group-name': self.parameters['name']}
- if self.parameters.get('ostype') is not None:
- options['os-type'] = self.parameters['ostype']
- if self.parameters.get('initiator_group_type') is not None:
- options['initiator-group-type'] = self.parameters['initiator_group_type']
- if self.parameters.get('bind_portset') is not None:
- options['bind-portset'] = self.parameters['bind_portset']
-
- igroup_create = netapp_utils.zapi.NaElement.create_node_with_children(
- 'igroup-create', **options)
-
- try:
- self.server.invoke_successfully(igroup_create,
- enable_tunneling=True)
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg='Error provisioning igroup %s: %s' % (self.parameters['name'], to_native(error)),
- exception=traceback.format_exc())
- self.add_initiators()
-
- def delete_igroup(self):
- """
- Delete the igroup.
- """
- igroup_delete = netapp_utils.zapi.NaElement.create_node_with_children(
- 'igroup-destroy', **{'initiator-group-name': self.parameters['name'],
- 'force': 'true' if self.parameters['force_remove_initiator'] else 'false'})
-
- try:
- self.server.invoke_successfully(igroup_delete,
- enable_tunneling=True)
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg='Error deleting igroup %s: %s' % (self.parameters['name'], to_native(error)),
- exception=traceback.format_exc())
-
- def rename_igroup(self):
- """
- Rename the igroup.
- """
- igroup_rename = netapp_utils.zapi.NaElement.create_node_with_children(
- 'igroup-rename', **{'initiator-group-name': self.parameters['from_name'],
- 'initiator-group-new-name': str(self.parameters['name'])})
- try:
- self.server.invoke_successfully(igroup_rename,
- enable_tunneling=True)
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg='Error renaming igroup %s: %s' % (self.parameters['name'], to_native(error)),
- exception=traceback.format_exc())
-
- def autosupport_log(self):
- netapp_utils.ems_log_event("na_ontap_igroup", self.server)
-
- def apply(self):
- self.autosupport_log()
- current = self.get_igroup(self.parameters['name'])
- # rename and create are mutually exclusive
- rename, cd_action, modify = None, None, None
- if self.parameters.get('from_name'):
- rename = self.na_helper.is_rename_action(self.get_igroup(self.parameters['from_name']), current)
- else:
- cd_action = self.na_helper.get_cd_action(current, self.parameters)
- if cd_action is None and self.parameters['state'] == 'present':
- modify = self.na_helper.get_modified_attributes(current, self.parameters)
-
- if self.na_helper.changed:
- if self.module.check_mode:
- pass
- else:
- if rename:
- self.rename_igroup()
- elif cd_action == 'create':
- self.create_igroup()
- elif cd_action == 'delete':
- self.delete_igroup()
- if modify:
- self.remove_initiators(current['initiators'])
- self.add_initiators()
- self.module.exit_json(changed=self.na_helper.changed)
-
-
-def main():
- obj = NetAppOntapIgroup()
- obj.apply()
-
-
-if __name__ == '__main__':
- main()
diff --git a/lib/ansible/modules/storage/netapp/na_ontap_igroup_initiator.py b/lib/ansible/modules/storage/netapp/na_ontap_igroup_initiator.py
deleted file mode 100644
index 1f784767e7..0000000000
--- a/lib/ansible/modules/storage/netapp/na_ontap_igroup_initiator.py
+++ /dev/null
@@ -1,183 +0,0 @@
-#!/usr/bin/python
-''' This is an Ansible module for ONTAP, to manage initiators in an Igroup
-
- (c) 2019, NetApp, Inc
- # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-'''
-
-from __future__ import absolute_import, division, print_function
-__metaclass__ = type
-
-ANSIBLE_METADATA = {
- 'metadata_version': '1.1',
- 'status': ['preview'],
- 'supported_by': 'community'
-}
-
-DOCUMENTATION = '''
-
-module: na_ontap_igroup_initiator
-short_description: NetApp ONTAP igroup initiator configuration
-extends_documentation_fragment:
- - netapp.na_ontap
-version_added: '2.8'
-author: NetApp Ansible Team (@carchi8py) <ng-ansibleteam@netapp.com>
-
-description:
- - Add/Remove initiators from an igroup
-
-options:
- state:
- description:
- - Whether the specified initiator should exist or not in an igroup.
- choices: ['present', 'absent']
- default: present
-
- names:
- description:
- - List of initiators to manage.
- required: true
- aliases:
- - name
-
- initiator_group:
- description:
- - Name of the initiator group to which the initiator belongs.
- required: true
-
- vserver:
- description:
- - The name of the vserver to use.
- required: true
-
-'''
-
-EXAMPLES = '''
- - name: Add initiators to an igroup
- na_ontap_igroup_initiator:
- names: abc.test:def.com,def.test:efg.com
- initiator_group: test_group
- vserver: ansibleVServer
- hostname: "{{ netapp_hostname }}"
- username: "{{ netapp_username }}"
- password: "{{ netapp_password }}"
-
- - name: Remove an initiator from an igroup
- na_ontap_igroup_initiator:
- state: absent
- names: abc.test:def.com
- initiator_group: test_group
- vserver: ansibleVServer
- hostname: "{{ netapp_hostname }}"
- username: "{{ netapp_username }}"
- password: "{{ netapp_password }}"
-
-'''
-
-RETURN = '''
-'''
-
-import traceback
-
-from ansible.module_utils.basic import AnsibleModule
-from ansible.module_utils._text import to_native
-import ansible.module_utils.netapp as netapp_utils
-from ansible.module_utils.netapp_module import NetAppModule
-
-
-HAS_NETAPP_LIB = netapp_utils.has_netapp_lib()
-
-
-class NetAppOntapIgroupInitiator(object):
-
- def __init__(self):
-
- self.argument_spec = netapp_utils.na_ontap_host_argument_spec()
- self.argument_spec.update(dict(
- state=dict(required=False, choices=['present', 'absent'], default='present'),
- names=dict(required=True, type='list', aliases=['name']),
- initiator_group=dict(required=True, type='str'),
- vserver=dict(required=True, type='str'),
- ))
-
- self.module = AnsibleModule(
- argument_spec=self.argument_spec,
- supports_check_mode=True
- )
-
- self.na_helper = NetAppModule()
- self.parameters = self.na_helper.set_parameters(self.module.params)
-
- if HAS_NETAPP_LIB is False:
- self.module.fail_json(msg="the python NetApp-Lib module is required")
- else:
- self.server = netapp_utils.setup_na_ontap_zapi(module=self.module, vserver=self.parameters['vserver'])
-
- def get_initiators(self):
- """
- Get the existing list of initiators from an igroup
- :rtype: list() or None
- """
- igroup_info = netapp_utils.zapi.NaElement('igroup-get-iter')
- attributes = dict(query={'initiator-group-info': {'initiator-group-name': self.parameters['initiator_group'],
- 'vserver': self.parameters['vserver']}})
- igroup_info.translate_struct(attributes)
- result, current = None, []
-
- try:
- result = self.server.invoke_successfully(igroup_info, True)
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg='Error fetching igroup info %s: %s' % (self.parameters['initiator_group'],
- to_native(error)),
- exception=traceback.format_exc())
-
- if result.get_child_by_name('num-records') and int(result.get_child_content('num-records')) >= 1:
- igroup_info = result.get_child_by_name('attributes-list').get_child_by_name('initiator-group-info')
- if igroup_info.get_child_by_name('initiators') is not None:
- current = [initiator['initiator-name'] for initiator in igroup_info['initiators'].get_children()]
- return current
-
- def modify_initiator(self, initiator_name, zapi):
- """
- Add or remove an initiator to/from an igroup
- """
- options = {'initiator-group-name': self.parameters['initiator_group'],
- 'initiator': initiator_name}
- initiator_modify = netapp_utils.zapi.NaElement.create_node_with_children(zapi, **options)
-
- try:
- self.server.invoke_successfully(initiator_modify, enable_tunneling=True)
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg='Error modifying igroup initiator %s: %s' % (initiator_name,
- to_native(error)),
- exception=traceback.format_exc())
-
- def autosupport_log(self):
- netapp_utils.ems_log_event("na_ontap_igroup_initiator", self.server)
-
- def apply(self):
- self.autosupport_log()
- initiators = self.get_initiators()
- for initiator in self.parameters['names']:
- present = None
- if initiator in initiators:
- present = True
- cd_action = self.na_helper.get_cd_action(present, self.parameters)
- if self.na_helper.changed:
- if self.module.check_mode:
- pass
- else:
- if cd_action == 'create':
- self.modify_initiator(initiator, 'igroup-add')
- elif cd_action == 'delete':
- self.modify_initiator(initiator, 'igroup-remove')
- self.module.exit_json(changed=self.na_helper.changed)
-
-
-def main():
- obj = NetAppOntapIgroupInitiator()
- obj.apply()
-
-
-if __name__ == '__main__':
- main()
diff --git a/lib/ansible/modules/storage/netapp/na_ontap_info.py b/lib/ansible/modules/storage/netapp/na_ontap_info.py
deleted file mode 100644
index 6945f68d83..0000000000
--- a/lib/ansible/modules/storage/netapp/na_ontap_info.py
+++ /dev/null
@@ -1,619 +0,0 @@
-#!/usr/bin/python
-
-# (c) 2018 Piotr Olczak <piotr.olczak@redhat.com>
-# (c) 2018-2019, NetApp, Inc
-# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-
-from __future__ import absolute_import, division, print_function
-__metaclass__ = type
-
-ANSIBLE_METADATA = {'metadata_version': '1.1',
- 'status': ['preview'],
- 'supported_by': 'certified'}
-
-DOCUMENTATION = '''
-module: na_ontap_info
-author: Piotr Olczak (@dprts) <polczak@redhat.com>
-extends_documentation_fragment:
- - netapp.na_ontap
-short_description: NetApp information gatherer
-description:
- - This module allows you to gather various information about ONTAP configuration
-version_added: "2.9"
-requirements:
- - netapp_lib
-options:
- state:
- type: str
- description:
- - Returns "info"
- default: "info"
- choices: ['info']
- gather_subset:
- type: list
- description:
- - When supplied, this argument will restrict the information collected
- to a given subset. Possible values for this argument include
- "aggregate_info", "cluster_node_info", "igroup_info", "lun_info", "net_dns_info",
- "net_ifgrp_info",
- "net_interface_info", "net_port_info", "nvme_info", "nvme_interface_info",
- "nvme_namespace_info", "nvme_subsystem_info", "ontap_version",
- "qos_adaptive_policy_info", "qos_policy_info", "security_key_manager_key_info",
- "security_login_account_info", "storage_failover_info", "volume_info",
- "vserver_info", "vserver_login_banner_info", "vserver_motd_info", "vserver_nfs_info"
- Can specify a list of values to include a larger subset. Values can also be used
- with an initial C(M(!)) to specify that a specific subset should
- not be collected.
- - nvme is supported with ONTAP 9.4 onwards.
- - use "help" to get a list of supported information for your system.
- default: "all"
-'''
-
-EXAMPLES = '''
-- name: Get NetApp info (Password Authentication)
- na_ontap_info:
- state: info
- hostname: "na-vsim"
- username: "admin"
- password: "admins_password"
- register: ontap_info
-- debug:
- msg: "{{ ontap_info.ontap_info }}"
-
-- name: Limit Info Gathering to Aggregate Information
- na_ontap_info:
- state: info
- hostname: "na-vsim"
- username: "admin"
- password: "admins_password"
- gather_subset: "aggregate_info"
- register: ontap_info
-
-- name: Limit Info Gathering to Volume and Lun Information
- na_ontap_info:
- state: info
- hostname: "na-vsim"
- username: "admin"
- password: "admins_password"
- gather_subset:
- - volume_info
- - lun_info
- register: ontap_info
-
-- name: Gather all info except for volume and lun information
- na_ontap_info:
- state: info
- hostname: "na-vsim"
- username: "admin"
- password: "admins_password"
- gather_subset:
- - "!volume_info"
- - "!lun_info"
- register: ontap_info
-'''
-
-RETURN = '''
-ontap_info:
- description: Returns various information about NetApp cluster configuration
- returned: always
- type: dict
- sample: '{
- "ontap_info": {
- "aggregate_info": {...},
- "cluster_node_info": {...},
- "net_dns_info": {...},
- "net_ifgrp_info": {...},
- "net_interface_info": {...},
- "net_port_info": {...},
- "security_key_manager_key_info": {...},
- "security_login_account_info": {...},
- "volume_info": {...},
- "lun_info": {...},
- "storage_failover_info": {...},
- "vserver_login_banner_info": {...},
- "vserver_motd_info": {...},
- "vserver_info": {...},
- "vserver_nfs_info": {...},
- "ontap_version": {...},
- "igroup_info": {...},
- "qos_policy_info": {...},
- "qos_adaptive_policy_info": {...}
- }'
-'''
-
-import traceback
-from ansible.module_utils.basic import AnsibleModule
-from ansible.module_utils._text import to_native
-import ansible.module_utils.netapp as netapp_utils
-
-try:
- import xmltodict
- HAS_XMLTODICT = True
-except ImportError:
- HAS_XMLTODICT = False
-
-try:
- import json
- HAS_JSON = True
-except ImportError:
- HAS_JSON = False
-
-HAS_NETAPP_LIB = netapp_utils.has_netapp_lib()
-
-
-class NetAppONTAPGatherInfo(object):
- '''Class with gather info methods'''
-
- def __init__(self, module):
- self.module = module
- self.netapp_info = dict()
-
- # thanks to coreywan (https://github.com/ansible/ansible/pull/47016)
- # for starting this
- # min_version identifies the ontapi version which supports this ZAPI
- # use 0 if it is supported since 9.1
- self.info_subsets = {
- 'net_dns_info': {
- 'method': self.get_generic_get_iter,
- 'kwargs': {
- 'call': 'net-dns-get-iter',
- 'attribute': 'net-dns-info',
- 'field': 'vserver-name',
- 'query': {'max-records': '1024'},
- },
- 'min_version': '0',
- },
- 'net_interface_info': {
- 'method': self.get_generic_get_iter,
- 'kwargs': {
- 'call': 'net-interface-get-iter',
- 'attribute': 'net-interface-info',
- 'field': 'interface-name',
- 'query': {'max-records': '1024'},
- },
- 'min_version': '0',
- },
- 'net_port_info': {
- 'method': self.get_generic_get_iter,
- 'kwargs': {
- 'call': 'net-port-get-iter',
- 'attribute': 'net-port-info',
- 'field': ('node', 'port'),
- 'query': {'max-records': '1024'},
- },
- 'min_version': '0',
- },
- 'cluster_node_info': {
- 'method': self.get_generic_get_iter,
- 'kwargs': {
- 'call': 'cluster-node-get-iter',
- 'attribute': 'cluster-node-info',
- 'field': 'node-name',
- 'query': {'max-records': '1024'},
- },
- 'min_version': '0',
- },
- 'security_login_account_info': {
- 'method': self.get_generic_get_iter,
- 'kwargs': {
- 'call': 'security-login-get-iter',
- 'attribute': 'security-login-account-info',
- 'field': ('vserver', 'user-name', 'application', 'authentication-method'),
- 'query': {'max-records': '1024'},
- },
- 'min_version': '0',
- },
- 'aggregate_info': {
- 'method': self.get_generic_get_iter,
- 'kwargs': {
- 'call': 'aggr-get-iter',
- 'attribute': 'aggr-attributes',
- 'field': 'aggregate-name',
- 'query': {'max-records': '1024'},
- },
- 'min_version': '0',
- },
- 'volume_info': {
- 'method': self.get_generic_get_iter,
- 'kwargs': {
- 'call': 'volume-get-iter',
- 'attribute': 'volume-attributes',
- 'field': ('name', 'owning-vserver-name'),
- 'query': {'max-records': '1024'},
- },
- 'min_version': '0',
- },
- 'lun_info': {
- 'method': self.get_generic_get_iter,
- 'kwargs': {
- 'call': 'lun-get-iter',
- 'attribute': 'lun-info',
- 'field': ('vserver', 'path'),
- 'query': {'max-records': '1024'},
- },
- 'min_version': '0',
- },
- 'storage_failover_info': {
- 'method': self.get_generic_get_iter,
- 'kwargs': {
- 'call': 'cf-get-iter',
- 'attribute': 'storage-failover-info',
- 'field': 'node',
- 'query': {'max-records': '1024'},
- },
- 'min_version': '0',
- },
- 'vserver_motd_info': {
- 'method': self.get_generic_get_iter,
- 'kwargs': {
- 'call': 'vserver-motd-get-iter',
- 'attribute': 'vserver-motd-info',
- 'field': 'vserver',
- 'query': {'max-records': '1024'},
- },
- 'min_version': '0',
- },
- 'vserver_login_banner_info': {
- 'method': self.get_generic_get_iter,
- 'kwargs': {
- 'call': 'vserver-login-banner-get-iter',
- 'attribute': 'vserver-login-banner-info',
- 'field': 'vserver',
- 'query': {'max-records': '1024'},
- },
- 'min_version': '0',
- },
- 'security_key_manager_key_info': {
- 'method': self.get_generic_get_iter,
- 'kwargs': {
- 'call': 'security-key-manager-key-get-iter',
- 'attribute': 'security-key-manager-key-info',
- 'field': ('node', 'key-id'),
- 'query': {'max-records': '1024'},
- },
- 'min_version': '0',
- },
- 'vserver_info': {
- 'method': self.get_generic_get_iter,
- 'kwargs': {
- 'call': 'vserver-get-iter',
- 'attribute': 'vserver-info',
- 'field': 'vserver-name',
- 'query': {'max-records': '1024'},
- },
- 'min_version': '0',
- },
- 'vserver_nfs_info': {
- 'method': self.get_generic_get_iter,
- 'kwargs': {
- 'call': 'nfs-service-get-iter',
- 'attribute': 'nfs-info',
- 'field': 'vserver',
- 'query': {'max-records': '1024'},
- },
- 'min_version': '0',
- },
- 'net_ifgrp_info': {
- 'method': self.get_ifgrp_info,
- 'kwargs': {},
- 'min_version': '0',
- },
- 'ontap_version': {
- 'method': self.ontapi,
- 'kwargs': {},
- 'min_version': '0',
- },
- 'system_node_info': {
- 'method': self.get_generic_get_iter,
- 'kwargs': {
- 'call': 'system-node-get-iter',
- 'attribute': 'node-details-info',
- 'field': 'node',
- 'query': {'max-records': '1024'},
- },
- 'min_version': '0',
- },
- 'igroup_info': {
- 'method': self.get_generic_get_iter,
- 'kwargs': {
- 'call': 'igroup-get-iter',
- 'attribute': 'initiator-group-info',
- 'field': ('vserver', 'initiator-group-name'),
- 'query': {'max-records': '1024'},
- },
- 'min_version': '0',
- },
- 'qos_policy_info': {
- 'method': self.get_generic_get_iter,
- 'kwargs': {
- 'call': 'qos-policy-group-get-iter',
- 'attribute': 'qos-policy-group-info',
- 'field': 'policy-group',
- 'query': {'max-records': '1024'},
- },
- 'min_version': '0',
- },
- # supported in ONTAP 9.3 and onwards
- 'qos_adaptive_policy_info': {
- 'method': self.get_generic_get_iter,
- 'kwargs': {
- 'call': 'qos-adaptive-policy-group-get-iter',
- 'attribute': 'qos-adaptive-policy-group-info',
- 'field': 'policy-group',
- 'query': {'max-records': '1024'},
- },
- 'min_version': '130',
- },
- # supported in ONTAP 9.4 and onwards
- 'nvme_info': {
- 'method': self.get_generic_get_iter,
- 'kwargs': {
- 'call': 'nvme-get-iter',
- 'attribute': 'nvme-target-service-info',
- 'field': 'vserver',
- 'query': {'max-records': '1024'},
- },
- 'min_version': '140',
- },
- 'nvme_interface_info': {
- 'method': self.get_generic_get_iter,
- 'kwargs': {
- 'call': 'nvme-interface-get-iter',
- 'attribute': 'nvme-interface-info',
- 'field': 'vserver',
- 'query': {'max-records': '1024'},
- },
- 'min_version': '140',
- },
- 'nvme_subsystem_info': {
- 'method': self.get_generic_get_iter,
- 'kwargs': {
- 'call': 'nvme-subsystem-get-iter',
- 'attribute': 'nvme-subsystem-info',
- 'field': 'subsystem',
- 'query': {'max-records': '1024'},
- },
- 'min_version': '140',
- },
- 'nvme_namespace_info': {
- 'method': self.get_generic_get_iter,
- 'kwargs': {
- 'call': 'nvme-namespace-get-iter',
- 'attribute': 'nvme-namespace-info',
- 'field': 'path',
- 'query': {'max-records': '1024'},
- },
- 'min_version': '140',
- },
- }
-
- if HAS_NETAPP_LIB is False:
- self.module.fail_json(msg="the python NetApp-Lib module is required")
- else:
- self.server = netapp_utils.setup_na_ontap_zapi(module=self.module)
-
- def ontapi(self):
- '''Method to get ontapi version'''
-
- api = 'system-get-ontapi-version'
- api_call = netapp_utils.zapi.NaElement(api)
- try:
- results = self.server.invoke_successfully(api_call, enable_tunneling=False)
- ontapi_version = results.get_child_content('minor-version')
- return ontapi_version if ontapi_version is not None else '0'
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg="Error calling API %s: %s" %
- (api, to_native(error)), exception=traceback.format_exc())
-
- def call_api(self, call, query=None):
- '''Main method to run an API call'''
-
- api_call = netapp_utils.zapi.NaElement(call)
- result = None
-
- if query:
- for key, val in query.items():
- # Can val be nested?
- api_call.add_new_child(key, val)
- try:
- result = self.server.invoke_successfully(api_call, enable_tunneling=False)
- return result
- except netapp_utils.zapi.NaApiError as error:
- if call in ['security-key-manager-key-get-iter']:
- return result
- else:
- self.module.fail_json(msg="Error calling API %s: %s"
- % (call, to_native(error)), exception=traceback.format_exc())
-
- def get_ifgrp_info(self):
- '''Method to get network port ifgroups info'''
-
- try:
- net_port_info = self.netapp_info['net_port_info']
- except KeyError:
- net_port_info_calls = self.info_subsets['net_port_info']
- net_port_info = net_port_info_calls['method'](**net_port_info_calls['kwargs'])
- interfaces = net_port_info.keys()
-
- ifgrps = []
- for ifn in interfaces:
- if net_port_info[ifn]['port_type'] == 'if_group':
- ifgrps.append(ifn)
-
- net_ifgrp_info = dict()
- for ifgrp in ifgrps:
- query = dict()
- query['node'], query['ifgrp-name'] = ifgrp.split(':')
-
- tmp = self.get_generic_get_iter('net-port-ifgrp-get', field=('node', 'ifgrp-name'),
- attribute='net-ifgrp-info', query=query)
- net_ifgrp_info = net_ifgrp_info.copy()
- net_ifgrp_info.update(tmp)
- return net_ifgrp_info
-
- def get_generic_get_iter(self, call, attribute=None, field=None, query=None):
- '''Method to run a generic get-iter call'''
-
- generic_call = self.call_api(call, query)
-
- if call == 'net-port-ifgrp-get':
- children = 'attributes'
- else:
- children = 'attributes-list'
-
- if generic_call is None:
- return None
-
- if field is None:
- out = []
- else:
- out = {}
-
- attributes_list = generic_call.get_child_by_name(children)
-
- if attributes_list is None:
- return None
-
- for child in attributes_list.get_children():
- dic = xmltodict.parse(child.to_string(), xml_attribs=False)
-
- if attribute is not None:
- dic = dic[attribute]
-
- if isinstance(field, str):
- unique_key = _finditem(dic, field)
- out = out.copy()
- out.update({unique_key: convert_keys(json.loads(json.dumps(dic)))})
- elif isinstance(field, tuple):
- unique_key = ':'.join([_finditem(dic, el) for el in field])
- out = out.copy()
- out.update({unique_key: convert_keys(json.loads(json.dumps(dic)))})
- else:
- out.append(convert_keys(json.loads(json.dumps(dic))))
-
- return out
-
- def get_all(self, gather_subset):
- '''Method to get all subsets'''
-
- results = netapp_utils.get_cserver(self.server)
- cserver = netapp_utils.setup_na_ontap_zapi(module=self.module, vserver=results)
- netapp_utils.ems_log_event("na_ontap_info", cserver)
-
- self.netapp_info['ontap_version'] = self.ontapi()
-
- run_subset = self.get_subset(gather_subset, self.netapp_info['ontap_version'])
- if 'help' in gather_subset:
- self.netapp_info['help'] = sorted(run_subset)
- else:
- for subset in run_subset:
- call = self.info_subsets[subset]
- self.netapp_info[subset] = call['method'](**call['kwargs'])
-
- return self.netapp_info
-
- def get_subset(self, gather_subset, version):
- '''Method to get a single subset'''
-
- runable_subsets = set()
- exclude_subsets = set()
- usable_subsets = [key for key in self.info_subsets.keys() if version >= self.info_subsets[key]['min_version']]
- if 'help' in gather_subset:
- return usable_subsets
- for subset in gather_subset:
- if subset == 'all':
- runable_subsets.update(usable_subsets)
- return runable_subsets
- if subset.startswith('!'):
- subset = subset[1:]
- if subset == 'all':
- return set()
- exclude = True
- else:
- exclude = False
-
- if subset not in usable_subsets:
- if subset not in self.info_subsets.keys():
- self.module.fail_json(msg='Bad subset: %s' % subset)
- self.module.fail_json(msg='Remote system at version %s does not support %s' %
- (version, subset))
-
- if exclude:
- exclude_subsets.add(subset)
- else:
- runable_subsets.add(subset)
-
- if not runable_subsets:
- runable_subsets.update(usable_subsets)
-
- runable_subsets.difference_update(exclude_subsets)
-
- return runable_subsets
-
-
-# https://stackoverflow.com/questions/14962485/finding-a-key-recursively-in-a-dictionary
-def __finditem(obj, key):
-
- if key in obj:
- return obj[key]
- for dummy, val in obj.items():
- if isinstance(val, dict):
- item = __finditem(val, key)
- if item is not None:
- return item
- return None
-
-
-def _finditem(obj, key):
-
- value = __finditem(obj, key)
- if value is not None:
- return value
- raise KeyError(key)
-
-
-def convert_keys(d_param):
- '''Method to convert hyphen to underscore'''
-
- out = {}
- if isinstance(d_param, dict):
- for key, val in d_param.items():
- val = convert_keys(val)
- out[key.replace('-', '_')] = val
- else:
- return d_param
- return out
-
-
-def main():
- '''Execute action'''
-
- argument_spec = netapp_utils.na_ontap_host_argument_spec()
- argument_spec.update(dict(
- state=dict(type='str', default='info', choices=['info']),
- gather_subset=dict(default=['all'], type='list'),
- ))
-
- module = AnsibleModule(
- argument_spec=argument_spec,
- supports_check_mode=True
- )
-
- if not HAS_XMLTODICT:
- module.fail_json(msg="xmltodict missing")
-
- if not HAS_JSON:
- module.fail_json(msg="json missing")
-
- state = module.params['state']
- gather_subset = module.params['gather_subset']
- if gather_subset is None:
- gather_subset = ['all']
- gf_obj = NetAppONTAPGatherInfo(module)
- gf_all = gf_obj.get_all(gather_subset)
- result = {'state': state, 'changed': False}
- module.exit_json(ontap_info=gf_all, **result)
-
-
-if __name__ == '__main__':
- main()
diff --git a/lib/ansible/modules/storage/netapp/na_ontap_interface.py b/lib/ansible/modules/storage/netapp/na_ontap_interface.py
deleted file mode 100644
index 0b1abea6b3..0000000000
--- a/lib/ansible/modules/storage/netapp/na_ontap_interface.py
+++ /dev/null
@@ -1,449 +0,0 @@
-#!/usr/bin/python
-""" this is interface module
-
- (c) 2018-2019, NetApp, Inc
- # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-"""
-
-from __future__ import absolute_import, division, print_function
-__metaclass__ = type
-
-ANSIBLE_METADATA = {
- 'metadata_version': '1.1',
- 'status': ['preview'],
- 'supported_by': 'certified'
-}
-
-DOCUMENTATION = '''
----
-
-module: na_ontap_interface
-short_description: NetApp ONTAP LIF configuration
-
-extends_documentation_fragment:
- - netapp.na_ontap
-version_added: '2.6'
-author: NetApp Ansible Team (@carchi8py) <ng-ansibleteam@netapp.com>
-
-description:
- - Creating / deleting and modifying the LIF.
-
-options:
- state:
- description:
- - Whether the specified interface should exist or not.
- choices: ['present', 'absent']
- default: present
-
- interface_name:
- description:
- - Specifies the logical interface (LIF) name.
- required: true
-
- home_node:
- description:
- - Specifies the LIF's home node.
- - By default, the first node from the cluster is considered as home node
-
- home_port:
- description:
- - Specifies the LIF's home port.
- - Required when C(state=present)
-
- role:
- description:
- - Specifies the role of the LIF.
- - When setting role as "intercluster", setting protocol is not supported.
- - Required when C(state=present).
-
- address:
- description:
- - Specifies the LIF's IP address.
- - Required when C(state=present)
-
- netmask:
- description:
- - Specifies the LIF's netmask.
- - Required when C(state=present).
-
- vserver:
- description:
- - The name of the vserver to use.
- required: true
-
- firewall_policy:
- description:
- - Specifies the firewall policy for the LIF.
-
- failover_policy:
- choices: ['disabled', 'system-defined', 'local-only', 'sfo-partner-only', 'broadcast-domain-wide']
- description:
- - Specifies the failover policy for the LIF.
-
- subnet_name:
- description:
- - Subnet where the interface address is allocated from.
- If the option is not used, the IP address will need to be provided by
- the administrator during configuration.
- version_added: '2.8'
-
- admin_status:
- choices: ['up', 'down']
- description:
- - Specifies the administrative status of the LIF.
-
- is_auto_revert:
- description:
- If true, data LIF will revert to its home node under certain circumstances such as startup, and load balancing
- migration capability is disabled automatically
- type: bool
-
- force_subnet_association:
- description:
- Set this to true to acquire the address from the named subnet and assign the subnet to the LIF.
- type: bool
- version_added: '2.9'
-
- protocols:
- description:
- - Specifies the list of data protocols configured on the LIF. By default, the values in this element are nfs, cifs and fcache.
- - Other supported protocols are iscsi and fcp. A LIF can be configured to not support any data protocols by specifying 'none'.
- - Protocol values of none, iscsi, fc-nvme or fcp can't be combined with any other data protocol(s).
- - address, netmask and firewall_policy parameters are not supported for 'fc-nvme' option.
-
- dns_domain_name:
- description:
- - Specifies the unique, fully qualified domain name of the DNS zone of this LIF.
- type: str
- version_added: '2.9'
-
- listen_for_dns_query:
- description:
- - If True, this IP address will listen for DNS queries for the dnszone specified.
- type: bool
- version_added: '2.9'
-
- is_dns_update_enabled:
- description:
- - Specifies if DNS update is enabled for this LIF. Dynamic updates will be sent for this LIF if updates are enabled at Vserver level.
- type: bool
- version_added: '2.9'
-
-'''
-
-EXAMPLES = '''
- - name: Create interface
- na_ontap_interface:
- state: present
- interface_name: data2
- home_port: e0d
- home_node: laurentn-vsim1
- role: data
- protocols: nfs
- admin_status: up
- failover_policy: local-only
- firewall_policy: mgmt
- is_auto_revert: true
- address: 10.10.10.10
- netmask: 255.255.255.0
- force_subnet_association: false
- dns_domain_name: test.com
- listen_for_dns_query: true
- is_dns_update_enabled: true
- vserver: svm1
- hostname: "{{ netapp_hostname }}"
- username: "{{ netapp_username }}"
- password: "{{ netapp_password }}"
-
- - name: Delete interface
- na_ontap_interface:
- state: absent
- interface_name: data2
- vserver: svm1
- hostname: "{{ netapp_hostname }}"
- username: "{{ netapp_username }}"
- password: "{{ netapp_password }}"
-
-'''
-
-RETURN = """
-
-"""
-
-import traceback
-from ansible.module_utils.basic import AnsibleModule
-from ansible.module_utils.netapp_module import NetAppModule
-from ansible.module_utils._text import to_native
-import ansible.module_utils.netapp as netapp_utils
-
-HAS_NETAPP_LIB = netapp_utils.has_netapp_lib()
-
-
-class NetAppOntapInterface(object):
- ''' object to describe interface info '''
- def __init__(self):
-
- self.argument_spec = netapp_utils.na_ontap_host_argument_spec()
- self.argument_spec.update(dict(
- state=dict(required=False, choices=[
- 'present', 'absent'], default='present'),
- interface_name=dict(required=True, type='str'),
- home_node=dict(required=False, type='str', default=None),
- home_port=dict(required=False, type='str'),
- role=dict(required=False, type='str'),
- address=dict(required=False, type='str'),
- netmask=dict(required=False, type='str'),
- vserver=dict(required=True, type='str'),
- firewall_policy=dict(required=False, type='str', default=None),
- failover_policy=dict(required=False, type='str', default=None,
- choices=['disabled', 'system-defined',
- 'local-only', 'sfo-partner-only', 'broadcast-domain-wide']),
- admin_status=dict(required=False, choices=['up', 'down']),
- subnet_name=dict(required=False, type='str'),
- is_auto_revert=dict(required=False, type='bool', default=None),
- protocols=dict(required=False, type='list'),
- force_subnet_association=dict(required=False, type='bool', default=None),
- dns_domain_name=dict(required=False, type='str'),
- listen_for_dns_query=dict(required=False, type='bool'),
- is_dns_update_enabled=dict(required=False, type='bool')
- ))
-
- self.module = AnsibleModule(
- argument_spec=self.argument_spec,
- mutually_exclusive=[
- ['subnet_name', 'address'],
- ['subnet_name', 'netmask']
- ],
-
- supports_check_mode=True
- )
- self.na_helper = NetAppModule()
- self.parameters = self.na_helper.set_parameters(self.module.params)
-
- if HAS_NETAPP_LIB is False:
- self.module.fail_json(
- msg="the python NetApp-Lib module is required")
- else:
- self.server = netapp_utils.setup_na_ontap_zapi(module=self.module)
-
- def get_interface(self):
- """
- Return details about the interface
- :param:
- name : Name of the name of the interface
-
- :return: Details about the interface. None if not found.
- :rtype: dict
- """
- interface_info = netapp_utils.zapi.NaElement('net-interface-get-iter')
- interface_attributes = netapp_utils.zapi.NaElement('net-interface-info')
- interface_attributes.add_new_child('interface-name', self.parameters['interface_name'])
- interface_attributes.add_new_child('vserver', self.parameters['vserver'])
- query = netapp_utils.zapi.NaElement('query')
- query.add_child_elem(interface_attributes)
- interface_info.add_child_elem(query)
- result = self.server.invoke_successfully(interface_info, True)
- return_value = None
- if result.get_child_by_name('num-records') and \
- int(result.get_child_content('num-records')) >= 1:
-
- interface_attributes = result.get_child_by_name('attributes-list').\
- get_child_by_name('net-interface-info')
- return_value = {
- 'interface_name': self.parameters['interface_name'],
- 'admin_status': interface_attributes['administrative-status'],
- 'home_port': interface_attributes['home-port'],
- 'home_node': interface_attributes['home-node'],
- 'failover_policy': interface_attributes['failover-policy'].replace('_', '-'),
- 'is_auto_revert': True if interface_attributes['is-auto-revert'] == 'true' else False,
- }
- if interface_attributes.get_child_by_name('address'):
- return_value['address'] = interface_attributes['address']
- if interface_attributes.get_child_by_name('netmask'):
- return_value['netmask'] = interface_attributes['netmask']
- if interface_attributes.get_child_by_name('firewall-policy'):
- return_value['firewall_policy'] = interface_attributes['firewall-policy']
- if interface_attributes.get_child_by_name('dns-domain-name') != 'none':
- return_value['dns_domain_name'] = interface_attributes['dns-domain-name']
- else:
- return_value['dns_domain_name'] = None
- if interface_attributes.get_child_by_name('listen-for-dns-query'):
- return_value['listen_for_dns_query'] = self.na_helper.get_value_for_bool(True, interface_attributes['listen-for-dns-query'])
- if interface_attributes.get_child_by_name('is-dns-update-enabled'):
- return_value['is_dns_update_enabled'] = self.na_helper.get_value_for_bool(True, interface_attributes['is-dns-update-enabled'])
- return return_value
-
- @staticmethod
- def set_options(options, parameters):
- """ set attributes for create or modify """
- if parameters.get('home_port') is not None:
- options['home-port'] = parameters['home_port']
- if parameters.get('subnet_name') is not None:
- options['subnet-name'] = parameters['subnet_name']
- if parameters.get('address') is not None:
- options['address'] = parameters['address']
- if parameters.get('netmask') is not None:
- options['netmask'] = parameters['netmask']
- if parameters.get('failover_policy') is not None:
- options['failover-policy'] = parameters['failover_policy']
- if parameters.get('firewall_policy') is not None:
- options['firewall-policy'] = parameters['firewall_policy']
- if parameters.get('is_auto_revert') is not None:
- options['is-auto-revert'] = 'true' if parameters['is_auto_revert'] is True else 'false'
- if parameters.get('admin_status') is not None:
- options['administrative-status'] = parameters['admin_status']
- if parameters.get('force_subnet_association') is not None:
- options['force-subnet-association'] = 'true' if parameters['force_subnet_association'] else 'false'
- if parameters.get('dns_domain_name') is not None:
- options['dns-domain-name'] = parameters['dns_domain_name']
- if parameters.get('listen_for_dns_query') is not None:
- options['listen-for-dns-query'] = str(parameters['listen_for_dns_query'])
- if parameters.get('is_dns_update_enabled') is not None:
- options['is-dns-update-enabled'] = str(parameters['is_dns_update_enabled'])
-
- def set_protocol_option(self, required_keys):
- """ set protocols for create """
- if self.parameters.get('protocols') is not None:
- data_protocols_obj = netapp_utils.zapi.NaElement('data-protocols')
- for protocol in self.parameters.get('protocols'):
- if protocol.lower() in ['fc-nvme', 'fcp']:
- if 'address' in required_keys:
- required_keys.remove('address')
- if 'home_port' in required_keys:
- required_keys.remove('home_port')
- if 'netmask' in required_keys:
- required_keys.remove('netmask')
- not_required_params = set(['address', 'netmask', 'firewall_policy'])
- if not not_required_params.isdisjoint(set(self.parameters.keys())):
- self.module.fail_json(msg='Error: Following parameters for creating interface are not supported'
- ' for data-protocol fc-nvme: %s' % ', '.join(not_required_params))
- data_protocols_obj.add_new_child('data-protocol', protocol)
- return data_protocols_obj
- return None
-
- def get_home_node_for_cluster(self):
- ''' get the first node name from this cluster '''
- get_node = netapp_utils.zapi.NaElement('cluster-node-get-iter')
- attributes = {
- 'query': {
- 'cluster-node-info': {}
- }
- }
- get_node.translate_struct(attributes)
- try:
- result = self.server.invoke_successfully(get_node, enable_tunneling=True)
- except netapp_utils.zapi.NaApiError as exc:
- self.module.fail_json(msg='Error fetching node for interface %s: %s' %
- (self.parameters['interface_name'], to_native(exc)),
- exception=traceback.format_exc())
- if result.get_child_by_name('num-records') and int(result.get_child_content('num-records')) >= 1:
- attributes = result.get_child_by_name('attributes-list')
- return attributes.get_child_by_name('cluster-node-info').get_child_content('node-name')
- return None
-
- def validate_create_parameters(self, keys):
- '''
- Validate if required parameters for create are present.
- Parameter requirement might vary based on given data-protocol.
- :return: None
- '''
- if self.parameters.get('home_node') is None:
- node = self.get_home_node_for_cluster()
- if node is not None:
- self.parameters['home_node'] = node
- # validate if mandatory parameters are present for create
- if not keys.issubset(set(self.parameters.keys())) and self.parameters.get('subnet_name') is None:
- self.module.fail_json(msg='Error: Missing one or more required parameters for creating interface: %s'
- % ', '.join(keys))
- # if role is intercluster, protocol cannot be specified
- if self.parameters['role'] == "intercluster" and self.parameters.get('protocols') is not None:
- self.module.fail_json(msg='Error: Protocol cannot be specified for intercluster role,'
- 'failed to create interface')
-
- def create_interface(self):
- ''' calling zapi to create interface '''
- required_keys = set(['role', 'home_port'])
- data_protocols_obj = None
- if self.parameters.get('subnet_name') is None:
- required_keys.add('address')
- required_keys.add('netmask')
- data_protocols_obj = self.set_protocol_option(required_keys)
- self.validate_create_parameters(required_keys)
-
- options = {'interface-name': self.parameters['interface_name'],
- 'role': self.parameters['role'],
- 'home-node': self.parameters.get('home_node'),
- 'vserver': self.parameters['vserver']}
- NetAppOntapInterface.set_options(options, self.parameters)
- interface_create = netapp_utils.zapi.NaElement.create_node_with_children('net-interface-create', **options)
- if data_protocols_obj is not None:
- interface_create.add_child_elem(data_protocols_obj)
- try:
- self.server.invoke_successfully(interface_create, enable_tunneling=True)
- except netapp_utils.zapi.NaApiError as exc:
- self.module.fail_json(msg='Error Creating interface %s: %s' %
- (self.parameters['interface_name'], to_native(exc)), exception=traceback.format_exc())
-
- def delete_interface(self, current_status):
- ''' calling zapi to delete interface '''
- if current_status == 'up':
- self.parameters['admin_status'] = 'down'
- self.modify_interface({'admin_status': 'down'})
-
- interface_delete = netapp_utils.zapi.NaElement.create_node_with_children(
- 'net-interface-delete', **{'interface-name': self.parameters['interface_name'],
- 'vserver': self.parameters['vserver']})
- try:
- self.server.invoke_successfully(interface_delete, enable_tunneling=True)
- except netapp_utils.zapi.NaApiError as exc:
- self.module.fail_json(msg='Error deleting interface %s: %s' % (self.parameters['interface_name'], to_native(exc)),
- exception=traceback.format_exc())
-
- def modify_interface(self, modify):
- """
- Modify the interface.
- """
- options = {'interface-name': self.parameters['interface_name'],
- 'vserver': self.parameters['vserver']
- }
- NetAppOntapInterface.set_options(options, modify)
- interface_modify = netapp_utils.zapi.NaElement.create_node_with_children('net-interface-modify', **options)
- try:
- self.server.invoke_successfully(interface_modify, enable_tunneling=True)
- except netapp_utils.zapi.NaApiError as err:
- self.module.fail_json(msg='Error modifying interface %s: %s' % (self.parameters['interface_name'],
- to_native(err)), exception=traceback.format_exc())
-
- def autosupport_log(self):
- results = netapp_utils.get_cserver(self.server)
- cserver = netapp_utils.setup_na_ontap_zapi(
- module=self.module, vserver=results)
- netapp_utils.ems_log_event("na_ontap_interface", cserver)
-
- def apply(self):
- ''' calling all interface features '''
- self.autosupport_log()
- current = self.get_interface()
- # rename and create are mutually exclusive
- cd_action = self.na_helper.get_cd_action(current, self.parameters)
- modify = self.na_helper.get_modified_attributes(current, self.parameters)
- if self.na_helper.changed:
- if self.module.check_mode:
- pass
- else:
- if cd_action == 'create':
- self.create_interface()
- elif cd_action == 'delete':
- self.delete_interface(current['admin_status'])
- elif modify:
- self.modify_interface(modify)
- self.module.exit_json(changed=self.na_helper.changed)
-
-
-def main():
- interface = NetAppOntapInterface()
- interface.apply()
-
-
-if __name__ == '__main__':
- main()
diff --git a/lib/ansible/modules/storage/netapp/na_ontap_ipspace.py b/lib/ansible/modules/storage/netapp/na_ontap_ipspace.py
deleted file mode 100644
index 56f1e87692..0000000000
--- a/lib/ansible/modules/storage/netapp/na_ontap_ipspace.py
+++ /dev/null
@@ -1,258 +0,0 @@
-#!/usr/bin/python
-"""
-this is ipspace module
-
-# (c) 2018, NTT Europe Ltd.
-# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-"""
-
-from __future__ import absolute_import, division, print_function
-__metaclass__ = type
-
-ANSIBLE_METADATA = {
- 'metadata_version': '1.1',
- 'status': ['preview'],
- 'supported_by': 'community'
-}
-
-DOCUMENTATION = '''
----
-module: na_ontap_ipspace
-
-short_description: NetApp ONTAP Manage an ipspace
-
-version_added: '2.9'
-
-author:
- - NTTE Storage Engineering (@vicmunoz) <cl.eng.sto@ntt.eu>
-
-description:
- - Manage an ipspace for an Ontap Cluster
-
-extends_documentation_fragment:
- - netapp.na_ontap
-
-options:
- state:
- description:
- - Whether the specified ipspace should exist or not
- choices: ['present', 'absent']
- default: present
- name:
- description:
- - The name of the ipspace to manage
- required: true
- from_name:
- description:
- - Name of the existing ipspace to be renamed to name
-'''
-
-EXAMPLES = """
- - name: Create ipspace
- na_ontap_ipspace:
- state: present
- name: ansibleIpspace
- hostname: "{{ netapp_hostname }}"
- username: "{{ netapp_username }}"
- password: "{{ netapp_password }}"
-
- - name: Delete ipspace
- na_ontap_ipspace:
- state: absent
- name: ansibleIpspace
- hostname: "{{ netapp_hostname }}"
- username: "{{ netapp_username }}"
- password: "{{ netapp_password }}"
-
- - name: Rename ipspace
- na_ontap_ipspace:
- state: present
- name: ansibleIpspace_newname
- from_name: ansibleIpspace
- hostname: "{{ netapp_hostname }}"
- username: "{{ netapp_username }}"
- password: "{{ netapp_password }}"
-"""
-
-RETURN = """
-"""
-
-import traceback
-
-import ansible.module_utils.netapp as netapp_utils
-from ansible.module_utils.netapp_module import NetAppModule
-from ansible.module_utils.basic import AnsibleModule
-from ansible.module_utils._text import to_native
-
-HAS_NETAPP_LIB = netapp_utils.has_netapp_lib()
-
-
-class NetAppOntapIpspace(object):
- '''Class with ipspace operations'''
-
- def __init__(self):
- self.argument_spec = netapp_utils.na_ontap_host_argument_spec()
- self.argument_spec.update(dict(
- state=dict(
- required=False, choices=['present', 'absent'],
- default='present'),
- name=dict(required=True, type='str'),
- from_name=dict(required=False, type='str'),
- ))
- self.module = AnsibleModule(
- argument_spec=self.argument_spec,
- supports_check_mode=True
- )
-
- self.na_helper = NetAppModule()
- self.parameters = self.na_helper.set_parameters(self.module.params)
-
- if HAS_NETAPP_LIB is False:
- self.module.fail_json(
- msg="the python NetApp-Lib module is required")
- else:
- self.server = netapp_utils.setup_na_ontap_zapi(module=self.module)
- return
-
- def ipspace_get_iter(self, name):
- """
- Return net-ipspaces-get-iter query results
- :param name: Name of the ipspace
- :return: NaElement if ipspace found, None otherwise
- """
- ipspace_get_iter = netapp_utils.zapi.NaElement('net-ipspaces-get-iter')
- query_details = netapp_utils.zapi.NaElement.create_node_with_children(
- 'net-ipspaces-info', **{'ipspace': name})
- query = netapp_utils.zapi.NaElement('query')
- query.add_child_elem(query_details)
- ipspace_get_iter.add_child_elem(query)
- try:
- result = self.server.invoke_successfully(
- ipspace_get_iter, enable_tunneling=False)
- except netapp_utils.zapi.NaApiError as error:
- # Error 14636 denotes an ipspace does not exist
- # Error 13073 denotes an ipspace not found
- if to_native(error.code) == "14636" or to_native(error.code) == "13073":
- return None
- else:
- self.module.self.fail_json(
- msg=to_native(error),
- exception=traceback.format_exc())
- return result
-
- def get_ipspace(self, name=None):
- """
- Fetch details if ipspace exists
- :param name: Name of the ipspace to be fetched
- :return:
- Dictionary of current details if ipspace found
- None if ipspace is not found
- """
- if name is None:
- name = self.parameters['name']
- ipspace_get = self.ipspace_get_iter(name)
- if (ipspace_get and ipspace_get.get_child_by_name('num-records') and
- int(ipspace_get.get_child_content('num-records')) >= 1):
- current_ipspace = dict()
- attr_list = ipspace_get.get_child_by_name('attributes-list')
- attr = attr_list.get_child_by_name('net-ipspaces-info')
- current_ipspace['name'] = attr.get_child_content('ipspace')
- return current_ipspace
- return None
-
- def create_ipspace(self):
- """
- Create ipspace
- :return: None
- """
- ipspace_create = netapp_utils.zapi.NaElement.create_node_with_children(
- 'net-ipspaces-create', **{'ipspace': self.parameters['name']})
- try:
- self.server.invoke_successfully(ipspace_create,
- enable_tunneling=False)
- except netapp_utils.zapi.NaApiError as error:
- self.module.self.fail_json(
- msg="Error provisioning ipspace %s: %s" % (
- self.parameters['name'],
- to_native(error)),
- exception=traceback.format_exc())
-
- def delete_ipspace(self):
- """
- Destroy ipspace
- :return: None
- """
- ipspace_destroy = netapp_utils.zapi.NaElement.create_node_with_children(
- 'net-ipspaces-destroy',
- **{'ipspace': self.parameters['name']})
- try:
- self.server.invoke_successfully(
- ipspace_destroy, enable_tunneling=False)
- except netapp_utils.zapi.NaApiError as error:
- self.module.self.fail_json(
- msg="Error removing ipspace %s: %s" % (
- self.parameters['name'],
- to_native(error)),
- exception=traceback.format_exc())
-
- def rename_ipspace(self):
- """
- Rename an ipspace
- :return: Nothing
- """
- ipspace_rename = netapp_utils.zapi.NaElement.create_node_with_children(
- 'net-ipspaces-rename',
- **{'ipspace': self.parameters['from_name'],
- 'new-name': self.parameters['name']})
- try:
- self.server.invoke_successfully(ipspace_rename,
- enable_tunneling=False)
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(
- msg="Error renaming ipspace %s: %s" % (
- self.parameters['from_name'],
- to_native(error)),
- exception=traceback.format_exc())
-
- def apply(self):
- """
- Apply action to the ipspace
- :return: Nothing
- """
- current = self.get_ipspace()
- # rename and create are mutually exclusive
- rename, cd_action = None, None
- if self.parameters.get('from_name'):
- rename = self.na_helper.is_rename_action(
- self.get_ipspace(self.parameters['from_name']),
- current)
- if rename is None:
- self.module.fail_json(
- msg="Error renaming: ipspace %s does not exist" %
- self.parameters['from_name'])
- else:
- cd_action = self.na_helper.get_cd_action(current, self.parameters)
- if self.na_helper.changed:
- if self.module.check_mode:
- pass
- else:
- if rename:
- self.rename_ipspace()
- elif cd_action == 'create':
- self.create_ipspace()
- elif cd_action == 'delete':
- self.delete_ipspace()
- self.module.exit_json(changed=self.na_helper.changed)
-
-
-def main():
- """
- Execute action
- :return: nothing
- """
- obj = NetAppOntapIpspace()
- obj.apply()
-
-
-if __name__ == '__main__':
- main()
diff --git a/lib/ansible/modules/storage/netapp/na_ontap_iscsi.py b/lib/ansible/modules/storage/netapp/na_ontap_iscsi.py
deleted file mode 100644
index 07dcbed78e..0000000000
--- a/lib/ansible/modules/storage/netapp/na_ontap_iscsi.py
+++ /dev/null
@@ -1,272 +0,0 @@
-#!/usr/bin/python
-
-# (c) 2017-2019, NetApp, Inc
-# GNU General Public License v3.0+
-# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-
-from __future__ import absolute_import, division, print_function
-__metaclass__ = type
-
-
-ANSIBLE_METADATA = {'metadata_version': '1.1',
- 'status': ['preview'],
- 'supported_by': 'certified'}
-
-
-DOCUMENTATION = '''
-
-module: na_ontap_iscsi
-
-short_description: NetApp ONTAP manage iSCSI service
-extends_documentation_fragment:
- - netapp.na_ontap
-version_added: '2.6'
-author: NetApp Ansible Team (@carchi8py) <ng-ansibleteam@netapp.com>
-
-description:
-- create, delete, start, stop iSCSI service on SVM.
-
-options:
-
- state:
- description:
- - Whether the service should be present or deleted.
- choices: ['present', 'absent']
- default: present
-
- service_state:
- description:
- - Whether the specified service should running .
- choices: ['started', 'stopped']
-
- vserver:
- required: true
- description:
- - The name of the vserver to use.
-
-'''
-
-EXAMPLES = """
-- name: Create iscsi service
- na_ontap_iscsi:
- state: present
- service_state: started
- vserver: ansibleVServer
- hostname: "{{ netapp_hostname }}"
- username: "{{ netapp_username }}"
- password: "{{ netapp_password }}"
-
-- name: Stop Iscsi service
- na_ontap_iscsi:
- state: present
- service_state: stopped
- vserver: ansibleVServer
- hostname: "{{ netapp_hostname }}"
- username: "{{ netapp_username }}"
- password: "{{ netapp_password }}"
-
-- name: Delete Iscsi service
- na_ontap_iscsi:
- state: absent
- vserver: ansibleVServer
- hostname: "{{ netapp_hostname }}"
- username: "{{ netapp_username }}"
- password: "{{ netapp_password }}"
-"""
-
-RETURN = """
-
-"""
-
-import traceback
-
-from ansible.module_utils.basic import AnsibleModule
-from ansible.module_utils._text import to_native
-import ansible.module_utils.netapp as netapp_utils
-
-HAS_NETAPP_LIB = netapp_utils.has_netapp_lib()
-
-
-class NetAppOntapISCSI(object):
-
- def __init__(self):
-
- self.argument_spec = netapp_utils.na_ontap_host_argument_spec()
- self.argument_spec.update(dict(
- state=dict(required=False, choices=[
- 'present', 'absent'], default='present'),
- service_state=dict(required=False, choices=[
- 'started', 'stopped'], default=None),
- vserver=dict(required=True, type='str'),
- ))
-
- self.module = AnsibleModule(
- argument_spec=self.argument_spec,
- supports_check_mode=True
- )
-
- params = self.module.params
-
- # set up state variables
- self.state = params['state']
- self.service_state = params['service_state']
- if self.state == 'present' and self.service_state is None:
- self.service_state = 'started'
- self.vserver = params['vserver']
- self.is_started = None
-
- if HAS_NETAPP_LIB is False:
- self.module.fail_json(
- msg="the python NetApp-Lib module is required")
- else:
- self.server = netapp_utils.setup_na_ontap_zapi(
- module=self.module, vserver=self.vserver)
-
- def get_iscsi(self):
- """
- Return details about the iscsi service
-
- :return: Details about the iscsi service
- :rtype: dict
- """
- iscsi_info = netapp_utils.zapi.NaElement('iscsi-service-get-iter')
- iscsi_attributes = netapp_utils.zapi.NaElement('iscsi-service-info')
-
- iscsi_attributes.add_new_child('vserver', self.vserver)
-
- query = netapp_utils.zapi.NaElement('query')
- query.add_child_elem(iscsi_attributes)
-
- iscsi_info.add_child_elem(query)
-
- result = self.server.invoke_successfully(iscsi_info, True)
- return_value = None
-
- if result.get_child_by_name('num-records') and \
- int(result.get_child_content('num-records')) >= 1:
-
- iscsi = result.get_child_by_name(
- 'attributes-list').get_child_by_name('iscsi-service-info')
- if iscsi:
- is_started = iscsi.get_child_content('is-available') == 'true'
- return_value = {
- 'is_started': is_started
- }
-
- return return_value
-
- def create_iscsi_service(self):
- """
- Create iscsi service and start if requested
- """
- iscsi_service = netapp_utils.zapi.NaElement.create_node_with_children(
- 'iscsi-service-create',
- **{'start': 'true' if self.state == 'started' else 'false'
- })
-
- try:
- self.server.invoke_successfully(
- iscsi_service, enable_tunneling=True)
- except netapp_utils.zapi.NaApiError as e:
- self.module.fail_json(msg="Error creating iscsi service: % s"
- % (to_native(e)),
- exception=traceback.format_exc())
-
- def delete_iscsi_service(self):
- """
- Delete the iscsi service
- """
- if self.is_started:
- self.stop_iscsi_service()
-
- iscsi_delete = netapp_utils.zapi.NaElement.create_node_with_children(
- 'iscsi-service-destroy')
-
- try:
- self.server.invoke_successfully(
- iscsi_delete, enable_tunneling=True)
- except netapp_utils.zapi.NaApiError as e:
- self.module.fail_json(msg="Error deleting iscsi service \
- on vserver %s: %s"
- % (self.vserver, to_native(e)),
- exception=traceback.format_exc())
-
- def stop_iscsi_service(self):
- """
- Stop iscsi service
- """
-
- iscsi_stop = netapp_utils.zapi.NaElement.create_node_with_children(
- 'iscsi-service-stop')
-
- try:
- self.server.invoke_successfully(iscsi_stop, enable_tunneling=True)
- except netapp_utils.zapi.NaApiError as e:
- self.module.fail_json(msg="Error Stopping iscsi service \
- on vserver %s: %s"
- % (self.vserver, to_native(e)),
- exception=traceback.format_exc())
-
- def start_iscsi_service(self):
- """
- Start iscsi service
- """
- iscsi_start = netapp_utils.zapi.NaElement.create_node_with_children(
- 'iscsi-service-start')
-
- try:
- self.server.invoke_successfully(iscsi_start, enable_tunneling=True)
- except netapp_utils.zapi.NaApiError as e:
- self.module.fail_json(msg="Error starting iscsi service \
- on vserver %s: %s"
- % (self.vserver, to_native(e)),
- exception=traceback.format_exc())
-
- def apply(self):
- property_changed = False
- iscsi_service_exists = False
- netapp_utils.ems_log_event("na_ontap_iscsi", self.server)
- iscsi_service_detail = self.get_iscsi()
-
- if iscsi_service_detail:
- self.is_started = iscsi_service_detail['is_started']
- iscsi_service_exists = True
-
- if self.state == 'absent':
- property_changed = True
-
- elif self.state == 'present':
- is_started = 'started' if self.is_started else 'stopped'
- property_changed = is_started != self.service_state
-
- else:
- if self.state == 'present':
- property_changed = True
-
- if property_changed:
- if self.module.check_mode:
- pass
- else:
- if self.state == 'present':
- if not iscsi_service_exists:
- self.create_iscsi_service() # the service is stopped when initially created
- if self.service_state == 'started':
- self.start_iscsi_service()
- if iscsi_service_exists and self.service_state == 'stopped':
- self.stop_iscsi_service()
-
- elif self.state == 'absent':
- self.delete_iscsi_service()
-
- changed = property_changed
- # TODO: include other details about the lun (size, etc.)
- self.module.exit_json(changed=changed)
-
-
-def main():
- v = NetAppOntapISCSI()
- v.apply()
-
-
-if __name__ == '__main__':
- main()
diff --git a/lib/ansible/modules/storage/netapp/na_ontap_job_schedule.py b/lib/ansible/modules/storage/netapp/na_ontap_job_schedule.py
deleted file mode 100644
index 30b7f885b2..0000000000
--- a/lib/ansible/modules/storage/netapp/na_ontap_job_schedule.py
+++ /dev/null
@@ -1,297 +0,0 @@
-#!/usr/bin/python
-
-# (c) 2018-2019, NetApp, Inc
-# GNU General Public License v3.0+
-# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-from __future__ import absolute_import, division, print_function
-__metaclass__ = type
-
-
-ANSIBLE_METADATA = {'metadata_version': '1.1',
- 'status': ['preview'],
- 'supported_by': 'certified'}
-
-
-DOCUMENTATION = '''
-module: na_ontap_job_schedule
-short_description: NetApp ONTAP Job Schedule
-extends_documentation_fragment:
- - netapp.na_ontap
-version_added: '2.6'
-author: NetApp Ansible Team (@carchi8py) <ng-ansibleteam@netapp.com>
-description:
-- Create/Delete/Modify job-schedules on ONTAP
-options:
- state:
- description:
- - Whether the specified job schedule should exist or not.
- choices: ['present', 'absent']
- default: present
- name:
- description:
- - The name of the job-schedule to manage.
- required: true
- job_minutes:
- description:
- - The minute(s) of each hour when the job should be run.
- Job Manager cron scheduling minute.
- -1 represents all minutes and is
- only supported for cron schedule create and modify.
- Range is [-1..59]
- type: list
- job_hours:
- version_added: '2.8'
- description:
- - The hour(s) of the day when the job should be run.
- Job Manager cron scheduling hour.
- -1 represents all hours and is
- only supported for cron schedule create and modify.
- Range is [-1..23]
- type: list
- job_months:
- version_added: '2.8'
- description:
- - The month(s) when the job should be run.
- Job Manager cron scheduling month.
- -1 represents all months and is
- only supported for cron schedule create and modify.
- Range is [-1..11]
- type: list
- job_days_of_month:
- version_added: '2.8'
- description:
- - The day(s) of the month when the job should be run.
- Job Manager cron scheduling day of month.
- -1 represents all days of a month from 1 to 31, and is
- only supported for cron schedule create and modify.
- Range is [-1..31]
- type: list
- job_days_of_week:
- version_added: '2.8'
- description:
- - The day(s) in the week when the job should be run.
- Job Manager cron scheduling day of week.
- Zero represents Sunday. -1 represents all days of a week and is
- only supported for cron schedule create and modify.
- Range is [-1..6]
- type: list
-'''
-
-EXAMPLES = """
- - name: Create Job for 11.30PM at 10th of every month
- na_ontap_job_schedule:
- state: present
- name: jobName
- job_minutes: 30
- job_hours: 23
- job_days_of_month: 10
- job_months: -1
- hostname: "{{ netapp_hostname }}"
- username: "{{ netapp_username }}"
- password: "{{ netapp_password }}"
- - name: Delete Job
- na_ontap_job_schedule:
- state: absent
- name: jobName
- hostname: "{{ netapp_hostname }}"
- username: "{{ netapp_username }}"
- password: "{{ netapp_password }}"
-"""
-
-RETURN = """
-
-"""
-
-
-import traceback
-from ansible.module_utils.basic import AnsibleModule
-from ansible.module_utils._text import to_native
-import ansible.module_utils.netapp as netapp_utils
-from ansible.module_utils.netapp_module import NetAppModule
-
-HAS_NETAPP_LIB = netapp_utils.has_netapp_lib()
-
-
-class NetAppONTAPJob(object):
- '''Class with job schedule cron methods'''
-
- def __init__(self):
-
- self.argument_spec = netapp_utils.na_ontap_host_argument_spec()
- self.argument_spec.update(dict(
- state=dict(required=False, choices=[
- 'present', 'absent'], default='present'),
- name=dict(required=True, type='str'),
- job_minutes=dict(required=False, type='list'),
- job_months=dict(required=False, type='list'),
- job_hours=dict(required=False, type='list'),
- job_days_of_month=dict(required=False, type='list'),
- job_days_of_week=dict(required=False, type='list')
- ))
-
- self.module = AnsibleModule(
- argument_spec=self.argument_spec,
- supports_check_mode=True
- )
-
- self.na_helper = NetAppModule()
- self.parameters = self.na_helper.set_parameters(self.module.params)
- self.set_playbook_zapi_key_map()
-
- if HAS_NETAPP_LIB is False:
- self.module.fail_json(
- msg="the python NetApp-Lib module is required")
- else:
- self.server = netapp_utils.setup_na_ontap_zapi(module=self.module)
-
- def set_playbook_zapi_key_map(self):
- self.na_helper.zapi_string_keys = {
- 'name': 'job-schedule-name',
- }
- self.na_helper.zapi_list_keys = {
- 'job_minutes': ('job-schedule-cron-minute', 'cron-minute'),
- 'job_months': ('job-schedule-cron-month', 'cron-month'),
- 'job_hours': ('job-schedule-cron-hour', 'cron-hour'),
- 'job_days_of_month': ('job-schedule-cron-day', 'cron-day-of-month'),
- 'job_days_of_week': ('job-schedule-cron-day-of-week', 'cron-day-of-week')
- }
-
- def get_job_schedule(self):
- """
- Return details about the job
- :param:
- name : Job name
- :return: Details about the Job. None if not found.
- :rtype: dict
- """
- job_get_iter = netapp_utils.zapi.NaElement('job-schedule-cron-get-iter')
- job_get_iter.translate_struct({
- 'query': {
- 'job-schedule-cron-info': {
- 'job-schedule-name': self.parameters['name']
- }
- }
- })
- result = self.server.invoke_successfully(job_get_iter, True)
- job_details = None
- # check if job exists
- if result.get_child_by_name('num-records') and int(result['num-records']) >= 1:
- job_info = result['attributes-list']['job-schedule-cron-info']
- job_details = dict()
- for item_key, zapi_key in self.na_helper.zapi_string_keys.items():
- job_details[item_key] = job_info[zapi_key]
- for item_key, zapi_key in self.na_helper.zapi_list_keys.items():
- parent, dummy = zapi_key
- job_details[item_key] = self.na_helper.get_value_for_list(from_zapi=True,
- zapi_parent=job_info.get_child_by_name(parent)
- )
- # if any of the job_hours, job_minutes, job_months, job_days are empty:
- # it means the value is -1 for ZAPI
- if not job_details[item_key]:
- job_details[item_key] = ['-1']
- return job_details
-
- def add_job_details(self, na_element_object, values):
- """
- Add children node for create or modify NaElement object
- :param na_element_object: modify or create NaElement object
- :param values: dictionary of cron values to be added
- :return: None
- """
- for item_key in values:
- if item_key in self.na_helper.zapi_string_keys:
- zapi_key = self.na_helper.zapi_string_keys.get(item_key)
- na_element_object[zapi_key] = values[item_key]
- elif item_key in self.na_helper.zapi_list_keys:
- parent_key, child_key = self.na_helper.zapi_list_keys.get(item_key)
- na_element_object.add_child_elem(self.na_helper.get_value_for_list(from_zapi=False,
- zapi_parent=parent_key,
- zapi_child=child_key,
- data=values.get(item_key)))
-
- def create_job_schedule(self):
- """
- Creates a job schedule
- """
- # job_minutes is mandatory for create
- if self.parameters.get('job_minutes') is None:
- self.module.fail_json(msg='Error: missing required parameter job_minutes for create')
-
- job_schedule_create = netapp_utils.zapi.NaElement('job-schedule-cron-create')
- self.add_job_details(job_schedule_create, self.parameters)
- try:
- self.server.invoke_successfully(job_schedule_create,
- enable_tunneling=True)
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg='Error creating job schedule %s: %s'
- % (self.parameters['name'], to_native(error)),
- exception=traceback.format_exc())
-
- def delete_job_schedule(self):
- """
- Delete a job schedule
- """
- job_schedule_delete = netapp_utils.zapi.NaElement('job-schedule-cron-destroy')
- self.add_job_details(job_schedule_delete, self.parameters)
- try:
- self.server.invoke_successfully(job_schedule_delete,
- enable_tunneling=True)
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg='Error deleting job schedule %s: %s'
- % (self.parameters['name'], to_native(error)),
- exception=traceback.format_exc())
-
- def modify_job_schedule(self, params):
- """
- modify a job schedule
- """
- job_schedule_modify = netapp_utils.zapi.NaElement.create_node_with_children(
- 'job-schedule-cron-modify', **{'job-schedule-name': self.parameters['name']})
- self.add_job_details(job_schedule_modify, params)
- try:
- self.server.invoke_successfully(job_schedule_modify, enable_tunneling=True)
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg='Error modifying job schedule %s: %s'
- % (self.parameters['name'], to_native(error)),
- exception=traceback.format_exc())
-
- def autosupport_log(self):
- """
- Autosupport log for job_schedule
- :return: None
- """
- results = netapp_utils.get_cserver(self.server)
- cserver = netapp_utils.setup_na_ontap_zapi(module=self.module, vserver=results)
- netapp_utils.ems_log_event("na_ontap_job_schedule", cserver)
-
- def apply(self):
- """
- Apply action to job-schedule
- """
- self.autosupport_log()
- current = self.get_job_schedule()
- action = self.na_helper.get_cd_action(current, self.parameters)
- if action is None and self.parameters['state'] == 'present':
- modify = self.na_helper.get_modified_attributes(current, self.parameters)
-
- if self.na_helper.changed:
- if self.module.check_mode:
- pass
- else:
- if action == 'create':
- self.create_job_schedule()
- elif action == 'delete':
- self.delete_job_schedule()
- elif modify:
- self.modify_job_schedule(modify)
- self.module.exit_json(changed=self.na_helper.changed)
-
-
-def main():
- '''Execute action'''
- job_obj = NetAppONTAPJob()
- job_obj.apply()
-
-
-if __name__ == '__main__':
- main()
diff --git a/lib/ansible/modules/storage/netapp/na_ontap_kerberos_realm.py b/lib/ansible/modules/storage/netapp/na_ontap_kerberos_realm.py
deleted file mode 100644
index 26bfc32005..0000000000
--- a/lib/ansible/modules/storage/netapp/na_ontap_kerberos_realm.py
+++ /dev/null
@@ -1,318 +0,0 @@
-#!/usr/bin/python
-'''
-(c) 2019, Red Hat, Inc
-GNU General Public License v3.0+
-(see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-'''
-
-from __future__ import absolute_import, division, print_function
-__metaclass__ = type
-
-ANSIBLE_METADATA = {'metadata_version': '1.1',
- 'status': ['preview'],
- 'supported_by': 'certified'}
-
-DOCUMENTATION = '''
-
-module: na_ontap_kerberos_realm
-
-short_description: NetApp ONTAP vserver nfs kerberos realm
-extends_documentation_fragment:
- - netapp.na_ontap
-version_added: '2.9'
-author: Milan Zink (@zeten30) <zeten30@gmail.com>,<mzink@redhat.com>
-
-description:
-- Create, modify or delete vserver kerberos realm configuration
-
-options:
-
- state:
- description:
- - Whether the Kerberos realm is present or absent.
- choices: ['present', 'absent']
- default: 'present'
- type: str
-
- vserver:
- description:
- - vserver/svm with kerberos realm configured
- required: true
- type: str
-
- realm:
- description:
- - Kerberos realm name
- required: true
- type: str
-
- kdc_vendor:
- description:
- - The vendor of the Key Distribution Centre (KDC) server
- - Required if I(state=present)
- choices: ['Other', 'Microsoft']
- type: str
-
- kdc_ip:
- description:
- - IP address of the Key Distribution Centre (KDC) server
- - Required if I(state=present)
- type: str
-
- kdc_port:
- description:
- - TCP port on the KDC to be used for Kerberos communication.
- - The default for this parameter is '88'.
- type: str
-
- clock_skew:
- description:
- - The clock skew in minutes is the tolerance for accepting tickets with time stamps that do not exactly match the host's system clock.
- - The default for this parameter is '5' minutes.
- type: str
-
- comment:
- description:
- - Optional comment
- type: str
-
- admin_server_ip:
- description:
- - IP address of the host where the Kerberos administration daemon is running. This is usually the master KDC.
- - If this parameter is omitted, the address specified in kdc_ip is used.
- type: str
-
- admin_server_port:
- description:
- - The TCP port on the Kerberos administration server where the Kerberos administration service is running.
- - The default for this parameter is '749'
- type: str
-
- pw_server_ip:
- description:
- - IP address of the host where the Kerberos password-changing server is running.
- - Typically, this is the same as the host indicated in the adminserver-ip.
- - If this parameter is omitted, the IP address in kdc-ip is used.
- type: str
-
- pw_server_port:
- description:
- - The TCP port on the Kerberos password-changing server where the Kerberos password-changing service is running.
- - The default for this parameter is '464'.
- type: str
-'''
-
-EXAMPLES = '''
-
- - name: Create kerberos realm
- na_ontap_kerberos_realm:
- state: present
- realm: 'EXAMPLE.COM'
- vserver: 'vserver1'
- kdc_ip: '1.2.3.4'
- kdc_vendor: 'Other'
- hostname: "{{ netapp_hostname }}"
- username: "{{ netapp_username }}"
- password: "{{ netapp_password }}"
-
-'''
-
-RETURN = '''
-'''
-
-import traceback
-import ansible.module_utils.netapp as netapp_utils
-from ansible.module_utils.netapp_module import NetAppModule
-from ansible.module_utils._text import to_native
-from ansible.module_utils.basic import AnsibleModule
-
-HAS_NETAPP_LIB = netapp_utils.has_netapp_lib()
-
-
-class NetAppOntapKerberosRealm(object):
- '''
- Kerberos Realm definition class
- '''
-
- def __init__(self):
- self.argument_spec = netapp_utils.na_ontap_host_argument_spec()
- self.argument_spec.update(dict(
- admin_server_ip=dict(required=False, default=None, type='str'),
- admin_server_port=dict(required=False, default=None, type='str'),
- clock_skew=dict(required=False, default=None, type='str'),
- comment=dict(required=False, default=None, type='str'),
- kdc_ip=dict(required_if=[["state", "present"]], default=None, type='str'),
- kdc_port=dict(required=False, default=None, type='str'),
- kdc_vendor=dict(required_if=[["state", "present"]], default=None, type='str', choices=['Microsoft', 'Other']),
- pw_server_ip=dict(required=False, default=None, type='str'),
- pw_server_port=dict(required=False, default=None, type='str'),
- realm=dict(required=True, type='str'),
- state=dict(required=False, choices=['present', 'absent'], default='present'),
- vserver=dict(required=True, type='str')
- ))
-
- self.module = AnsibleModule(
- argument_spec=self.argument_spec,
- supports_check_mode=True,
- required_if=[('state', 'present', ['kdc_vendor', 'kdc_ip'])],
- )
- self.na_helper = NetAppModule()
- self.parameters = self.na_helper.set_parameters(self.module.params)
-
- if HAS_NETAPP_LIB is False:
- self.module.fail_json(
- msg="the python NetApp-Lib module is required")
- else:
- self.server = netapp_utils.setup_na_ontap_zapi(module=self.module, vserver=self.parameters['vserver'])
-
- self.simple_attributes = [
- 'admin_server_ip',
- 'admin_server_port',
- 'clock_skew',
- 'kdc_ip',
- 'kdc_port',
- 'kdc_vendor',
- ]
-
- def get_krbrealm(self, realm_name=None, vserver_name=None):
- '''
- Checks if Kerberos Realm config exists.
-
- :return:
- kerberos realm object if found
- None if not found
- :rtype: object/None
- '''
- # Make query
- krbrealm_info = netapp_utils.zapi.NaElement('kerberos-realm-get-iter')
-
- if realm_name is None:
- realm_name = self.parameters['realm']
-
- if vserver_name is None:
- vserver_name = self.parameters['vserver']
-
- query_details = netapp_utils.zapi.NaElement.create_node_with_children('kerberos-realm', **{'realm': realm_name, 'vserver-name': vserver_name})
-
- query = netapp_utils.zapi.NaElement('query')
- query.add_child_elem(query_details)
- krbrealm_info.add_child_elem(query)
-
- result = self.server.invoke_successfully(krbrealm_info, enable_tunneling=True)
-
- # Get Kerberos Realm details
- krbrealm_details = None
- if (result.get_child_by_name('num-records') and int(result.get_child_content('num-records')) >= 1):
- attributes_list = result.get_child_by_name('attributes-list')
- config_info = attributes_list.get_child_by_name('kerberos-realm')
-
- krbrealm_details = {
- 'admin_server_ip': config_info.get_child_content('admin-server-ip'),
- 'admin_server_port': config_info.get_child_content('admin-server-port'),
- 'clock_skew': config_info.get_child_content('clock-skew'),
- 'kdc_ip': config_info.get_child_content('kdc-ip'),
- 'kdc_port': config_info.get_child_content('kdc-port'),
- 'kdc_vendor': config_info.get_child_content('kdc-vendor'),
- 'pw_server_ip': config_info.get_child_content('password-server-ip'),
- 'pw_server_port': config_info.get_child_content('password-server-port'),
- 'realm': config_info.get_child_content('realm'),
- 'vserver': config_info.get_child_content('vserver'),
- }
-
- return krbrealm_details
-
- def create_krbrealm(self):
- '''supported
- Create Kerberos Realm configuration
- '''
- options = {
- 'realm': self.parameters['realm']
- }
-
- # Other options/attributes
- for attribute in self.simple_attributes:
- if self.parameters.get(attribute) is not None:
- options[str(attribute).replace('_', '-')] = self.parameters[attribute]
-
- if self.parameters.get('pw_server_ip') is not None:
- options['password-server-ip'] = self.parameters['pw_server_ip']
- if self.parameters.get('pw_server_port') is not None:
- options['password-server-port'] = self.parameters['pw_server_port']
-
- # Initialize NaElement
- krbrealm_create = netapp_utils.zapi.NaElement.create_node_with_children('kerberos-realm-create', **options)
-
- # Try to create Kerberos Realm configuration
- try:
- self.server.invoke_successfully(krbrealm_create, enable_tunneling=True)
- except netapp_utils.zapi.NaApiError as errcatch:
- self.module.fail_json(msg='Error creating Kerberos Realm configuration %s: %s' % (self.parameters['realm'], to_native(errcatch)),
- exception=traceback.format_exc())
-
- def delete_krbrealm(self):
- '''
- Delete Kerberos Realm configuration
- '''
- krbrealm_delete = netapp_utils.zapi.NaElement.create_node_with_children('kerberos-realm-delete', **{'realm': self.parameters['realm']})
-
- try:
- self.server.invoke_successfully(krbrealm_delete, enable_tunneling=True)
- except netapp_utils.zapi.NaApiError as errcatch:
- self.module.fail_json(msg='Error deleting Kerberos Realm configuration %s: %s' % (
- self.parameters['realm'], to_native(errcatch)), exception=traceback.format_exc())
-
- def modify_krbrealm(self, modify):
- '''
- Modify Kerberos Realm
- :param modify: list of modify attributes
- '''
- krbrealm_modify = netapp_utils.zapi.NaElement('kerberos-realm-modify')
- krbrealm_modify.add_new_child('realm', self.parameters['realm'])
-
- for attribute in modify:
- if attribute in self.simple_attributes:
- krbrealm_modify.add_new_child(str(attribute).replace('_', '-'), self.parameters[attribute])
- if attribute == 'pw_server_ip':
- krbrealm_modify.add_new_child('password-server-ip', self.parameters['pw_server_ip'])
- if attribute == 'pw_server_port':
- krbrealm_modify.add_new_child('password-server-port', self.parameters['pw_server_port'])
-
- # Try to modify Kerberos Realm
- try:
- self.server.invoke_successfully(krbrealm_modify, enable_tunneling=True)
- except netapp_utils.zapi.NaApiError as errcatch:
- self.module.fail_json(msg='Error modifying Kerberos Realm %s: %s' % (self.parameters['realm'], to_native(errcatch)),
- exception=traceback.format_exc())
-
- def apply(self):
- '''Call create/modify/delete operations.'''
- current = self.get_krbrealm()
- cd_action = self.na_helper.get_cd_action(current, self.parameters)
- modify = self.na_helper.get_modified_attributes(current, self.parameters)
- # create an ems log event for users with auto support turned on
- netapp_utils.ems_log_event("na_ontap_kerberos_realm", self.server)
-
- if self.na_helper.changed:
- if self.module.check_mode:
- pass
- else:
- if cd_action == 'create':
- self.create_krbrealm()
- elif cd_action == 'delete':
- self.delete_krbrealm()
- elif modify:
- self.modify_krbrealm(modify)
- self.module.exit_json(changed=self.na_helper.changed)
-
-
-#
-# MAIN
-#
-def main():
- '''ONTAP Kerberos Realm'''
- krbrealm = NetAppOntapKerberosRealm()
- krbrealm.apply()
-
-
-if __name__ == '__main__':
- main()
diff --git a/lib/ansible/modules/storage/netapp/na_ontap_ldap.py b/lib/ansible/modules/storage/netapp/na_ontap_ldap.py
deleted file mode 100644
index 136bd1e974..0000000000
--- a/lib/ansible/modules/storage/netapp/na_ontap_ldap.py
+++ /dev/null
@@ -1,228 +0,0 @@
-#!/usr/bin/python
-'''
-(c) 2018-2019, NetApp, Inc
-GNU General Public License v3.0+
-(see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-'''
-
-from __future__ import absolute_import, division, print_function
-__metaclass__ = type
-
-ANSIBLE_METADATA = {'metadata_version': '1.1',
- 'status': ['preview'],
- 'supported_by': 'certified'}
-
-DOCUMENTATION = '''
-
-module: na_ontap_ldap
-
-short_description: NetApp ONTAP LDAP
-extends_documentation_fragment:
- - netapp.na_ontap
-version_added: '2.9'
-author: Milan Zink (@zeten30) <zeten30@gmail.com>/<mzink@redhat.com>
-
-description:
-- Create, modify or delete LDAP on NetApp ONTAP SVM/vserver
-
-options:
-
- state:
- description:
- - Whether the LDAP is present or not.
- choices: ['present', 'absent']
- default: 'present'
- type: str
-
- vserver:
- description:
- - vserver/svm configured to use LDAP
- required: true
- type: str
-
- name:
- description:
- - The name of LDAP client configuration
- required: true
- type: str
-
- skip_config_validation:
- description:
- - Skip LDAP validation
- choices: ['true', 'false']
- type: str
-'''
-
-EXAMPLES = '''
-
- - name: Enable LDAP on SVM
- na_ontap_ldap:
- state: present
- name: 'example_ldap'
- vserver: 'vserver1'
- hostname: "{{ netapp_hostname }}"
- username: "{{ netapp_username }}"
- password: "{{ netapp_password }}"
-
-'''
-
-RETURN = '''
-'''
-
-import traceback
-import ansible.module_utils.netapp as netapp_utils
-from ansible.module_utils.basic import AnsibleModule
-from ansible.module_utils._text import to_native
-from ansible.module_utils.netapp_module import NetAppModule
-
-HAS_NETAPP_LIB = netapp_utils.has_netapp_lib()
-
-
-class NetAppOntapLDAP(object):
- '''
- LDAP Client definition class
- '''
-
- def __init__(self):
- self.argument_spec = netapp_utils.na_ontap_host_argument_spec()
- self.argument_spec.update(dict(
- name=dict(required=True, type='str'),
- skip_config_validation=dict(required=False, default=None, choices=['true', 'false']),
- state=dict(required=False, choices=['present', 'absent'], default='present'),
- vserver=dict(required=True, type='str')
- ))
-
- self.module = AnsibleModule(
- argument_spec=self.argument_spec,
- supports_check_mode=True
- )
- self.na_helper = NetAppModule()
- self.parameters = self.na_helper.set_parameters(self.module.params)
-
- if HAS_NETAPP_LIB is False:
- self.module.fail_json(
- msg="the python NetApp-Lib module is required")
- else:
- self.server = netapp_utils.setup_na_ontap_zapi(module=self.module, vserver=self.parameters['vserver'])
-
- def get_ldap(self, client_config_name=None):
- '''
- Checks if LDAP config exists.
-
- :return:
- ldap config object if found
- None if not found
- :rtype: object/None
- '''
- # Make query
- config_info = netapp_utils.zapi.NaElement('ldap-config-get-iter')
-
- if client_config_name is None:
- client_config_name = self.parameters['name']
-
- query_details = netapp_utils.zapi.NaElement.create_node_with_children('ldap-config', **{'client-config': client_config_name})
-
- query = netapp_utils.zapi.NaElement('query')
- query.add_child_elem(query_details)
- config_info.add_child_elem(query)
-
- result = self.server.invoke_successfully(config_info, enable_tunneling=True)
-
- # Get LDAP configuration details
- config_details = None
- if (result.get_child_by_name('num-records') and int(result.get_child_content('num-records')) >= 1):
- attributes_list = result.get_child_by_name('attributes-list')
- config_info = attributes_list.get_child_by_name('ldap-config')
-
- # Define config details structure
- config_details = {'client_config': config_info.get_child_content('client-config'),
- 'skip_config_validation': config_info.get_child_content('skip-config-validation'),
- 'vserver': config_info.get_child_content('vserver')}
-
- return config_details
-
- def create_ldap(self):
- '''
- Create LDAP configuration
- '''
- options = {
- 'client-config': self.parameters['name'],
- 'client-enabled': 'true'
- }
-
- if self.parameters.get('skip_config_validation') is not None:
- options['skip-config-validation'] = self.parameters['skip_config_validation']
-
- # Initialize NaElement
- ldap_create = netapp_utils.zapi.NaElement.create_node_with_children('ldap-config-create', **options)
-
- # Try to create LDAP configuration
- try:
- self.server.invoke_successfully(ldap_create, enable_tunneling=True)
- except netapp_utils.zapi.NaApiError as errcatch:
- self.module.fail_json(msg='Error creating LDAP configuration %s: %s' % (self.parameters['name'], to_native(errcatch)),
- exception=traceback.format_exc())
-
- def delete_ldap(self):
- '''
- Delete LDAP configuration
- '''
- ldap_client_delete = netapp_utils.zapi.NaElement.create_node_with_children('ldap-config-delete', **{})
-
- try:
- self.server.invoke_successfully(ldap_client_delete, enable_tunneling=True)
- except netapp_utils.zapi.NaApiError as errcatch:
- self.module.fail_json(msg='Error deleting LDAP configuration %s: %s' % (
- self.parameters['name'], to_native(errcatch)), exception=traceback.format_exc())
-
- def modify_ldap(self, modify):
- '''
- Modify LDAP
- :param modify: list of modify attributes
- '''
- ldap_modify = netapp_utils.zapi.NaElement('ldap-config-modify')
- ldap_modify.add_new_child('client-config', self.parameters['name'])
-
- for attribute in modify:
- if attribute == 'skip_config_validation':
- ldap_modify.add_new_child('skip-config-validation', self.parameters[attribute])
-
- # Try to modify LDAP
- try:
- self.server.invoke_successfully(ldap_modify, enable_tunneling=True)
- except netapp_utils.zapi.NaApiError as errcatch:
- self.module.fail_json(msg='Error modifying LDAP %s: %s' % (self.parameters['name'], to_native(errcatch)),
- exception=traceback.format_exc())
-
- def apply(self):
- '''Call create/modify/delete operations.'''
- current = self.get_ldap()
- cd_action = self.na_helper.get_cd_action(current, self.parameters)
- modify = self.na_helper.get_modified_attributes(current, self.parameters)
- # create an ems log event for users with auto support turned on
- netapp_utils.ems_log_event("na_ontap_ldap", self.server)
-
- if self.na_helper.changed:
- if self.module.check_mode:
- pass
- else:
- if cd_action == 'create':
- self.create_ldap()
- elif cd_action == 'delete':
- self.delete_ldap()
- elif modify:
- self.modify_ldap(modify)
- self.module.exit_json(changed=self.na_helper.changed)
-
-
-#
-# MAIN
-#
-def main():
- '''ONTAP LDAP client configuration'''
- ldapclient = NetAppOntapLDAP()
- ldapclient.apply()
-
-
-if __name__ == '__main__':
- main()
diff --git a/lib/ansible/modules/storage/netapp/na_ontap_ldap_client.py b/lib/ansible/modules/storage/netapp/na_ontap_ldap_client.py
deleted file mode 100644
index 584274acbe..0000000000
--- a/lib/ansible/modules/storage/netapp/na_ontap_ldap_client.py
+++ /dev/null
@@ -1,359 +0,0 @@
-#!/usr/bin/python
-'''
-(c) 2018-2019, NetApp, Inc
-GNU General Public License v3.0+
-(see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-'''
-
-from __future__ import absolute_import, division, print_function
-__metaclass__ = type
-
-ANSIBLE_METADATA = {'metadata_version': '1.1',
- 'status': ['preview'],
- 'supported_by': 'certified'}
-
-DOCUMENTATION = '''
-
-module: na_ontap_ldap_client
-
-short_description: NetApp ONTAP LDAP client
-extends_documentation_fragment:
- - netapp.na_ontap
-version_added: '2.9'
-author: Milan Zink (@zeten30) <zeten30@gmail.com>/<mzink@redhat.com>
-
-description:
-- Create, modify or delete LDAP client on NetApp ONTAP
-
-options:
-
- state:
- description:
- - Whether the specified LDAP client configuration exist or not.
- choices: ['present', 'absent']
- default: 'present'
- type: str
-
- vserver:
- description:
- - vserver/svm that holds LDAP client configuration
- required: true
- type: str
-
- name:
- description:
- - The name of LDAP client configuration
- required: true
- type: str
-
- ldap_servers:
- description:
- - Comma separated list of LDAP servers. FQDN's or IP addresses
- - Required if I(state=present).
- type: list
-
- schema:
- description:
- - LDAP schema
- - Required if I(state=present).
- choices: ['AD-IDMU', 'AD-SFU', 'MS-AD-BIS', 'RFC-2307']
- type: str
-
- base_dn:
- description:
- - LDAP base DN
- type: str
-
- base_scope:
- description:
- - LDAP search scope
- choices: ['subtree', 'onelevel', 'base']
- type: str
-
- port:
- description:
- - LDAP server port
- type: int
-
- query_timeout:
- description:
- - LDAP server query timeout
- type: int
-
- min_bind_level:
- description:
- - Minimal LDAP server bind level.
- choices: ['anonymous', 'simple', 'sasl']
- type: str
-
- bind_dn:
- description:
- - LDAP bind user DN
- type: str
-
- bind_password:
- description:
- - LDAP bind user password
- type: str
-
- use_start_tls:
- description:
- - Start TLS on LDAP connection
- choices: ['true', 'false']
- type: str
-
- referral_enabled:
- description:
- - LDAP Referral Chasing
- choices: ['true', 'false']
- type: str
-
- session_security:
- description:
- - Client Session Security
- choices: ['true', 'false']
- type: str
-'''
-
-EXAMPLES = '''
-
- - name: Create LDAP client
- na_ontap_ldap_client:
- state: present
- name: 'example_ldap'
- vserver: 'vserver1'
- ldap_servers: 'ldap1.example.company.com,ldap2.example.company.com'
- base_dn: 'dc=example,dc=company,dc=com'
- hostname: "{{ netapp_hostname }}"
- username: "{{ netapp_username }}"
- password: "{{ netapp_password }}"
-
-'''
-
-RETURN = '''
-'''
-
-import traceback
-import ansible.module_utils.netapp as netapp_utils
-from ansible.module_utils.basic import AnsibleModule
-from ansible.module_utils._text import to_native
-from ansible.module_utils.netapp_module import NetAppModule
-
-HAS_NETAPP_LIB = netapp_utils.has_netapp_lib()
-
-
-class NetAppOntapLDAPClient(object):
- '''
- LDAP Client definition class
- '''
-
- def __init__(self):
- self.argument_spec = netapp_utils.na_ontap_host_argument_spec()
- self.argument_spec.update(dict(
- base_dn=dict(required=False, type='str'),
- base_scope=dict(required=False, default=None, choices=['subtree', 'onelevel', 'base']),
- bind_dn=dict(required=False, default=None, type='str'),
- bind_password=dict(type='str', required=False, default=None, no_log=True),
- name=dict(required=True, type='str'),
- ldap_servers=dict(required_if=[["state", "present"]], type='list'),
- min_bind_level=dict(required=False, default=None, choices=['anonymous', 'simple', 'sasl']),
- port=dict(required=False, default=None, type='int'),
- query_timeout=dict(required=False, default=None, type='int'),
- referral_enabled=dict(required=False, default=None, choices=['true', 'false']),
- schema=dict(required_if=[["state", "present"]], default=None, type='str', choices=['AD-IDMU', 'AD-SFU', 'MS-AD-BIS', 'RFC-2307']),
- session_security=dict(required=False, default=None, choices=['true', 'false']),
- state=dict(required=False, choices=['present', 'absent'], default='present'),
- use_start_tls=dict(required=False, default=None, choices=['true', 'false']),
- vserver=dict(required=True, type='str')
- ))
-
- self.module = AnsibleModule(
- argument_spec=self.argument_spec,
- supports_check_mode=True,
- required_if=[('state', 'present', ['ldap_servers', 'schema'])],
- )
- self.na_helper = NetAppModule()
- self.parameters = self.na_helper.set_parameters(self.module.params)
-
- if HAS_NETAPP_LIB is False:
- self.module.fail_json(
- msg="the python NetApp-Lib module is required")
- else:
- self.server = netapp_utils.setup_na_ontap_zapi(module=self.module, vserver=self.parameters['vserver'])
-
- self.simple_attributes = [
- 'base_dn',
- 'base_scope',
- 'bind_dn',
- 'bind_password',
- 'min_bind_level',
- 'port',
- 'query_timeout',
- 'referral_enabled',
- 'session_security',
- 'use_start_tls'
- ]
-
- def get_ldap_client(self, client_config_name=None, vserver_name=None):
- '''
- Checks if LDAP client config exists.
-
- :return:
- ldap client config object if found
- None if not found
- :rtype: object/None
- '''
- # Make query
- client_config_info = netapp_utils.zapi.NaElement('ldap-client-get-iter')
-
- if client_config_name is None:
- client_config_name = self.parameters['name']
-
- if vserver_name is None:
- vserver_name = '*'
-
- query_details = netapp_utils.zapi.NaElement.create_node_with_children('ldap-client',
- **{'ldap-client-config': client_config_name, 'vserver': vserver_name})
-
- query = netapp_utils.zapi.NaElement('query')
- query.add_child_elem(query_details)
- client_config_info.add_child_elem(query)
-
- result = self.server.invoke_successfully(client_config_info, enable_tunneling=False)
-
- # Get LDAP client configuration details
- client_config_details = None
- if (result.get_child_by_name('num-records') and int(result.get_child_content('num-records')) >= 1):
- attributes_list = result.get_child_by_name('attributes-list')
- client_config_info = attributes_list.get_child_by_name('ldap-client')
-
- # Get LDAP servers list
- ldap_server_list = list()
- get_list = client_config_info.get_child_by_name('ldap-servers')
- if get_list is not None:
- ldap_servers = get_list.get_children()
- for ldap_server in ldap_servers:
- ldap_server_list.append(ldap_server.get_content())
-
- # Define config details structure
- client_config_details = {'name': client_config_info.get_child_content('ldap-client-config'),
- 'ldap_servers': client_config_info.get_child_content('ldap-servers'),
- 'base_dn': client_config_info.get_child_content('base-dn'),
- 'base_scope': client_config_info.get_child_content('base-scope'),
- 'bind_dn': client_config_info.get_child_content('bind-dn'),
- 'bind_password': client_config_info.get_child_content('bind-password'),
- 'min_bind_level': client_config_info.get_child_content('min-bind-level'),
- 'port': client_config_info.get_child_content('port'),
- 'query_timeout': client_config_info.get_child_content('query-timeout'),
- 'referral_enabled': client_config_info.get_child_content('referral-enabled'),
- 'schema': client_config_info.get_child_content('schema'),
- 'session_security': client_config_info.get_child_content('session-security'),
- 'use_start_tls': client_config_info.get_child_content('use-start-tls'),
- 'vserver': client_config_info.get_child_content('vserver')}
-
- return client_config_details
-
- def create_ldap_client(self):
- '''
- Create LDAP client configuration
- '''
- # LDAP servers NaElement
- ldap_servers_element = netapp_utils.zapi.NaElement('ldap-servers')
-
- # Mandatory options
- for ldap_server_name in self.parameters['ldap_servers']:
- ldap_servers_element.add_new_child('string', ldap_server_name)
-
- options = {
- 'ldap-client-config': self.parameters['name'],
- 'schema': self.parameters['schema'],
- }
-
- # Other options/attributes
- for attribute in self.simple_attributes:
- if self.parameters.get(attribute) is not None:
- options[str(attribute).replace('_', '-')] = self.parameters[attribute]
-
- # Initialize NaElement
- ldap_client_create = netapp_utils.zapi.NaElement.create_node_with_children('ldap-client-create', **options)
- ldap_client_create.add_child_elem(ldap_servers_element)
-
- # Try to create LDAP configuration
- try:
- self.server.invoke_successfully(ldap_client_create, enable_tunneling=True)
- except netapp_utils.zapi.NaApiError as errcatch:
- self.module.fail_json(msg='Error creating LDAP client %s: %s' % (self.parameters['name'], to_native(errcatch)),
- exception=traceback.format_exc())
-
- def delete_ldap_client(self):
- '''
- Delete LDAP client configuration
- '''
- ldap_client_delete = netapp_utils.zapi.NaElement.create_node_with_children(
- 'ldap-client-delete', **{'ldap-client-config': self.parameters['name']})
-
- try:
- self.server.invoke_successfully(ldap_client_delete, enable_tunneling=True)
- except netapp_utils.zapi.NaApiError as errcatch:
- self.module.fail_json(msg='Error deleting LDAP client configuration %s: %s' % (
- self.parameters['name'], to_native(errcatch)), exception=traceback.format_exc())
-
- def modify_ldap_client(self, modify):
- '''
- Modify LDAP client
- :param modify: list of modify attributes
- '''
- ldap_client_modify = netapp_utils.zapi.NaElement('ldap-client-modify')
- ldap_client_modify.add_new_child('ldap-client-config', self.parameters['name'])
-
- for attribute in modify:
- # LDAP_servers
- if attribute == 'ldap_servers':
- ldap_servers_element = netapp_utils.zapi.NaElement('ldap-servers')
- for ldap_server_name in self.parameters['ldap_servers']:
- ldap_servers_element.add_new_child('string', ldap_server_name)
- ldap_client_modify.add_child_elem(ldap_servers_element)
-
- # Simple attributes
- if attribute in self.simple_attributes:
- ldap_client_modify.add_new_child(str(attribute).replace('_', '-'), self.parameters[attribute])
-
- # Try to modify LDAP client
- try:
- self.server.invoke_successfully(ldap_client_modify, enable_tunneling=True)
- except netapp_utils.zapi.NaApiError as errcatch:
- self.module.fail_json(msg='Error modifying LDAP client %s: %s' % (self.parameters['name'], to_native(errcatch)),
- exception=traceback.format_exc())
-
- def apply(self):
- '''Call create/modify/delete operations.'''
- current = self.get_ldap_client()
- cd_action = self.na_helper.get_cd_action(current, self.parameters)
- modify = self.na_helper.get_modified_attributes(current, self.parameters)
- # create an ems log event for users with auto support turned on
- netapp_utils.ems_log_event("na_ontap_ldap_client", self.server)
-
- if self.na_helper.changed:
- if self.module.check_mode:
- pass
- else:
- if cd_action == 'create':
- self.create_ldap_client()
- elif cd_action == 'delete':
- self.delete_ldap_client()
- elif modify:
- self.modify_ldap_client(modify)
- self.module.exit_json(changed=self.na_helper.changed)
-
-
-#
-# MAIN
-#
-def main():
- '''ONTAP LDAP client configuration'''
- ldapclient = NetAppOntapLDAPClient()
- ldapclient.apply()
-
-
-if __name__ == '__main__':
- main()
diff --git a/lib/ansible/modules/storage/netapp/na_ontap_license.py b/lib/ansible/modules/storage/netapp/na_ontap_license.py
deleted file mode 100644
index cddcc842e0..0000000000
--- a/lib/ansible/modules/storage/netapp/na_ontap_license.py
+++ /dev/null
@@ -1,326 +0,0 @@
-#!/usr/bin/python
-
-# (c) 2018-2019, NetApp, Inc
-# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-
-from __future__ import absolute_import, division, print_function
-__metaclass__ = type
-
-
-ANSIBLE_METADATA = {'metadata_version': '1.1',
- 'status': ['preview'],
- 'supported_by': 'certified'}
-
-
-DOCUMENTATION = '''
-
-module: na_ontap_license
-
-short_description: NetApp ONTAP protocol and feature licenses
-extends_documentation_fragment:
- - netapp.na_ontap
-version_added: '2.6'
-author: NetApp Ansible Team (@carchi8py) <ng-ansibleteam@netapp.com>
-
-description:
-- Add or remove licenses on NetApp ONTAP.
-
-options:
- state:
- description:
- - Whether the specified license should exist or not.
- choices: ['present', 'absent']
- default: present
-
- remove_unused:
- description:
- - Remove licenses that have no controller affiliation in the cluster.
- type: bool
-
- remove_expired:
- description:
- - Remove licenses that have expired in the cluster.
- type: bool
-
- serial_number:
- description:
- Serial number of the node associated with the license.
- This parameter is used primarily when removing license for a specific service.
-
- license_names:
- description:
- - List of license-names to delete.
- suboptions:
- base:
- description:
- - Cluster Base License
- nfs:
- description:
- - NFS License
- cifs:
- description:
- - CIFS License
- iscsi:
- description:
- - iSCSI License
- fcp:
- description:
- - FCP License
- cdmi:
- description:
- - CDMI License
- snaprestore:
- description:
- - SnapRestore License
- snapmirror:
- description:
- - SnapMirror License
- flexclone:
- description:
- - FlexClone License
- snapvault:
- description:
- - SnapVault License
- snaplock:
- description:
- - SnapLock License
- snapmanagersuite:
- description:
- - SnapManagerSuite License
- snapprotectapps:
- description:
- - SnapProtectApp License
- v_storageattach:
- description:
- - Virtual Attached Storage License
-
- license_codes:
- description:
- - List of license codes to be added.
-
-'''
-
-
-EXAMPLES = """
-- name: Add licenses
- na_ontap_license:
- state: present
- hostname: "{{ netapp_hostname }}"
- username: "{{ netapp_username }}"
- password: "{{ netapp_password }}"
- serial_number: #################
- license_codes: CODE1,CODE2
-
-- name: Remove licenses
- na_ontap_license:
- state: absent
- hostname: "{{ netapp_hostname }}"
- username: "{{ netapp_username }}"
- password: "{{ netapp_password }}"
- remove_unused: false
- remove_expired: true
- serial_number: #################
- license_names: nfs,cifs
-"""
-
-RETURN = """
-
-"""
-import traceback
-
-from ansible.module_utils.basic import AnsibleModule
-from ansible.module_utils._text import to_native
-import ansible.module_utils.netapp as netapp_utils
-
-
-HAS_NETAPP_LIB = netapp_utils.has_netapp_lib()
-
-
-def local_cmp(a, b):
- """
- compares with only values and not keys, keys should be the same for both dicts
- :param a: dict 1
- :param b: dict 2
- :return: difference of values in both dicts
- """
- diff = [key for key in a if a[key] != b[key]]
- return len(diff)
-
-
-class NetAppOntapLicense(object):
- '''ONTAP license class'''
-
- def __init__(self):
- self.argument_spec = netapp_utils.na_ontap_host_argument_spec()
- self.argument_spec.update(dict(
- state=dict(required=False, choices=[
- 'present', 'absent'], default='present'),
- serial_number=dict(required=False, type='str'),
- remove_unused=dict(default=None, type='bool'),
- remove_expired=dict(default=None, type='bool'),
- license_codes=dict(default=None, type='list'),
- license_names=dict(default=None, type='list'),
- ))
- self.module = AnsibleModule(
- argument_spec=self.argument_spec,
- supports_check_mode=False,
- required_if=[
- ('state', 'absent', ['serial_number', 'license_names'])]
- )
- parameters = self.module.params
- # set up state variables
- self.state = parameters['state']
- self.serial_number = parameters['serial_number']
- self.remove_unused = parameters['remove_unused']
- self.remove_expired = parameters['remove_expired']
- self.license_codes = parameters['license_codes']
- self.license_names = parameters['license_names']
-
- if HAS_NETAPP_LIB is False:
- self.module.fail_json(
- msg="the python NetApp-Lib module is required")
- else:
- self.server = netapp_utils.setup_na_ontap_zapi(module=self.module)
-
- def get_licensing_status(self):
- """
- Check licensing status
-
- :return: package (key) and licensing status (value)
- :rtype: dict
- """
- license_status = netapp_utils.zapi.NaElement(
- 'license-v2-status-list-info')
- result = None
- try:
- result = self.server.invoke_successfully(license_status,
- enable_tunneling=False)
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg="Error checking license status: %s" %
- to_native(error), exception=traceback.format_exc())
-
- return_dictionary = {}
- license_v2_status = result.get_child_by_name('license-v2-status')
- if license_v2_status:
- for license_v2_status_info in license_v2_status.get_children():
- package = license_v2_status_info.get_child_content('package')
- status = license_v2_status_info.get_child_content('method')
- return_dictionary[package] = status
-
- return return_dictionary
-
- def remove_licenses(self, package_name):
- """
- Remove requested licenses
- :param:
- package_name: Name of the license to be deleted
- """
- license_delete = netapp_utils.zapi.NaElement('license-v2-delete')
- license_delete.add_new_child('serial-number', self.serial_number)
- license_delete.add_new_child('package', package_name)
- try:
- self.server.invoke_successfully(license_delete,
- enable_tunneling=False)
- return True
- except netapp_utils.zapi.NaApiError as error:
- # Error 15661 - Object not found
- if to_native(error.code) == "15661":
- return False
- else:
- self.module.fail_json(msg="Error removing license %s" %
- to_native(error), exception=traceback.format_exc())
-
- def remove_unused_licenses(self):
- """
- Remove unused licenses
- """
- remove_unused = netapp_utils.zapi.NaElement('license-v2-delete-unused')
- try:
- self.server.invoke_successfully(remove_unused,
- enable_tunneling=False)
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg="Error removing unused licenses: %s" %
- to_native(error), exception=traceback.format_exc())
-
- def remove_expired_licenses(self):
- """
- Remove expired licenses
- """
- remove_expired = netapp_utils.zapi.NaElement(
- 'license-v2-delete-expired')
- try:
- self.server.invoke_successfully(remove_expired,
- enable_tunneling=False)
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg="Error removing expired licenses: %s" %
- to_native(error), exception=traceback.format_exc())
-
- def add_licenses(self):
- """
- Add licenses
- """
- license_add = netapp_utils.zapi.NaElement('license-v2-add')
- codes = netapp_utils.zapi.NaElement('codes')
- for code in self.license_codes:
- codes.add_new_child('license-code-v2', str(code.strip().lower()))
- license_add.add_child_elem(codes)
- try:
- self.server.invoke_successfully(license_add,
- enable_tunneling=False)
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg="Error adding licenses: %s" %
- to_native(error), exception=traceback.format_exc())
-
- def apply(self):
- '''Call add, delete or modify methods'''
- changed = False
- create_license = False
- remove_license = False
- results = netapp_utils.get_cserver(self.server)
- cserver = netapp_utils.setup_na_ontap_zapi(
- module=self.module, vserver=results)
- netapp_utils.ems_log_event("na_ontap_license", cserver)
- # Add / Update licenses.
- license_status = self.get_licensing_status()
-
- if self.state == 'absent': # delete
- changed = True
- else: # add or update
- if self.license_codes is not None:
- create_license = True
- changed = True
- if self.remove_unused is not None:
- remove_license = True
- changed = True
- if self.remove_expired is not None:
- remove_license = True
- changed = True
- if changed:
- if self.state == 'present': # execute create
- if create_license:
- self.add_licenses()
- if self.remove_unused is not None:
- self.remove_unused_licenses()
- if self.remove_expired is not None:
- self.remove_expired_licenses()
- if create_license or remove_license:
- new_license_status = self.get_licensing_status()
- if local_cmp(license_status, new_license_status) == 0:
- changed = False
- else: # execute delete
- license_deleted = False
- for package in self.license_names:
- license_deleted |= self.remove_licenses(package)
- changed = license_deleted
-
- self.module.exit_json(changed=changed)
-
-
-def main():
- '''Apply license operations'''
- obj = NetAppOntapLicense()
- obj.apply()
-
-
-if __name__ == '__main__':
- main()
diff --git a/lib/ansible/modules/storage/netapp/na_ontap_lun.py b/lib/ansible/modules/storage/netapp/na_ontap_lun.py
deleted file mode 100644
index 2ab43b1749..0000000000
--- a/lib/ansible/modules/storage/netapp/na_ontap_lun.py
+++ /dev/null
@@ -1,406 +0,0 @@
-#!/usr/bin/python
-
-# (c) 2017, NetApp, Inc
-# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-
-from __future__ import absolute_import, division, print_function
-__metaclass__ = type
-
-
-ANSIBLE_METADATA = {'metadata_version': '1.1',
- 'status': ['preview'],
- 'supported_by': 'certified'}
-
-
-DOCUMENTATION = '''
-
-module: na_ontap_lun
-
-short_description: NetApp ONTAP manage LUNs
-extends_documentation_fragment:
- - netapp.na_ontap
-version_added: '2.6'
-author: NetApp Ansible Team (@carchi8py) <ng-ansibleteam@netapp.com>
-
-description:
-- Create, destroy, resize LUNs on NetApp ONTAP.
-
-options:
-
- state:
- description:
- - Whether the specified LUN should exist or not.
- choices: ['present', 'absent']
- default: present
-
- name:
- description:
- - The name of the LUN to manage.
- required: true
-
- flexvol_name:
- description:
- - The name of the FlexVol the LUN should exist on.
- required: true
-
- size:
- description:
- - The size of the LUN in C(size_unit).
- - Required when C(state=present).
-
- size_unit:
- description:
- - The unit used to interpret the size parameter.
- choices: ['bytes', 'b', 'kb', 'mb', 'gb', 'tb', 'pb', 'eb', 'zb', 'yb']
- default: 'gb'
-
- force_resize:
- description:
- Forcibly reduce the size. This is required for reducing the size of the LUN to avoid accidentally
- reducing the LUN size.
- type: bool
- default: false
-
- force_remove:
- description:
- - If "true", override checks that prevent a LUN from being destroyed if it is online and mapped.
- - If "false", destroying an online and mapped LUN will fail.
- type: bool
- default: false
-
- force_remove_fenced:
- description:
- - If "true", override checks that prevent a LUN from being destroyed while it is fenced.
- - If "false", attempting to destroy a fenced LUN will fail.
- - The default if not specified is "false". This field is available in Data ONTAP 8.2 and later.
- type: bool
- default: false
-
- vserver:
- required: true
- description:
- - The name of the vserver to use.
-
- ostype:
- description:
- - The os type for the LUN.
- default: 'image'
-
- space_reserve:
- description:
- - This can be set to "false" which will create a LUN without any space being reserved.
- type: bool
- default: True
-
- space_allocation:
- description:
- - This enables support for the SCSI Thin Provisioning features. If the Host and file system do
- not support this do not enable it.
- type: bool
- default: False
- version_added: '2.7'
-
-'''
-
-EXAMPLES = """
-- name: Create LUN
- na_ontap_lun:
- state: present
- name: ansibleLUN
- flexvol_name: ansibleVolume
- vserver: ansibleVServer
- size: 5
- size_unit: mb
- ostype: linux
- space_reserve: True
- hostname: "{{ netapp_hostname }}"
- username: "{{ netapp_username }}"
- password: "{{ netapp_password }}"
-
-- name: Resize LUN
- na_ontap_lun:
- state: present
- name: ansibleLUN
- force_resize: True
- flexvol_name: ansibleVolume
- vserver: ansibleVServer
- size: 5
- size_unit: gb
- hostname: "{{ netapp_hostname }}"
- username: "{{ netapp_username }}"
- password: "{{ netapp_password }}"
-"""
-
-RETURN = """
-
-"""
-
-import traceback
-
-from ansible.module_utils.basic import AnsibleModule
-from ansible.module_utils._text import to_native
-import ansible.module_utils.netapp as netapp_utils
-
-HAS_NETAPP_LIB = netapp_utils.has_netapp_lib()
-
-
-class NetAppOntapLUN(object):
-
- def __init__(self):
-
- self._size_unit_map = dict(
- bytes=1,
- b=1,
- kb=1024,
- mb=1024 ** 2,
- gb=1024 ** 3,
- tb=1024 ** 4,
- pb=1024 ** 5,
- eb=1024 ** 6,
- zb=1024 ** 7,
- yb=1024 ** 8
- )
-
- self.argument_spec = netapp_utils.na_ontap_host_argument_spec()
- self.argument_spec.update(dict(
- state=dict(required=False, choices=['present', 'absent'], default='present'),
- name=dict(required=True, type='str'),
- size=dict(type='int'),
- size_unit=dict(default='gb',
- choices=['bytes', 'b', 'kb', 'mb', 'gb', 'tb',
- 'pb', 'eb', 'zb', 'yb'], type='str'),
- force_resize=dict(default=False, type='bool'),
- force_remove=dict(default=False, type='bool'),
- force_remove_fenced=dict(default=False, type='bool'),
- flexvol_name=dict(required=True, type='str'),
- vserver=dict(required=True, type='str'),
- ostype=dict(required=False, type='str', default='image'),
- space_reserve=dict(required=False, type='bool', default=True),
- space_allocation=dict(required=False, type='bool', default=False),
- ))
-
- self.module = AnsibleModule(
- argument_spec=self.argument_spec,
- required_if=[
- ('state', 'present', ['size'])
- ],
- supports_check_mode=True
- )
-
- parameters = self.module.params
-
- # set up state variables
- self.state = parameters['state']
- self.name = parameters['name']
- self.size_unit = parameters['size_unit']
- if parameters['size'] is not None:
- self.size = parameters['size'] * self._size_unit_map[self.size_unit]
- else:
- self.size = None
- self.force_resize = parameters['force_resize']
- self.force_remove = parameters['force_remove']
- self.force_remove_fenced = parameters['force_remove_fenced']
- self.flexvol_name = parameters['flexvol_name']
- self.vserver = parameters['vserver']
- self.ostype = parameters['ostype']
- self.space_reserve = parameters['space_reserve']
- self.space_allocation = parameters['space_allocation']
-
- if HAS_NETAPP_LIB is False:
- self.module.fail_json(msg="the python NetApp-Lib module is required")
- else:
- self.server = netapp_utils.setup_na_ontap_zapi(module=self.module, vserver=self.vserver)
-
- def get_lun(self):
- """
- Return details about the LUN
-
- :return: Details about the lun
- :rtype: dict
- """
-
- luns = []
- tag = None
- while True:
- lun_info = netapp_utils.zapi.NaElement('lun-get-iter')
- if tag:
- lun_info.add_new_child('tag', tag, True)
-
- query_details = netapp_utils.zapi.NaElement('lun-info')
- query_details.add_new_child('vserver', self.vserver)
- query_details.add_new_child('volume', self.flexvol_name)
-
- query = netapp_utils.zapi.NaElement('query')
- query.add_child_elem(query_details)
-
- lun_info.add_child_elem(query)
-
- result = self.server.invoke_successfully(lun_info, True)
- if result.get_child_by_name('num-records') and int(result.get_child_content('num-records')) >= 1:
- attr_list = result.get_child_by_name('attributes-list')
- luns.extend(attr_list.get_children())
-
- tag = result.get_child_content('next-tag')
-
- if tag is None:
- break
-
- # The LUNs have been extracted.
- # Find the specified lun and extract details.
- return_value = None
- for lun in luns:
- path = lun.get_child_content('path')
- _rest, _splitter, found_name = path.rpartition('/')
-
- if found_name == self.name:
- size = lun.get_child_content('size')
-
- # Find out if the lun is attached
- attached_to = None
- lun_id = None
- if lun.get_child_content('mapped') == 'true':
- lun_map_list = netapp_utils.zapi.NaElement.create_node_with_children(
- 'lun-map-list-info', **{'path': path})
-
- result = self.server.invoke_successfully(
- lun_map_list, enable_tunneling=True)
-
- igroups = result.get_child_by_name('initiator-groups')
- if igroups:
- for igroup_info in igroups.get_children():
- igroup = igroup_info.get_child_content(
- 'initiator-group-name')
- attached_to = igroup
- lun_id = igroup_info.get_child_content('lun-id')
-
- return_value = {
- 'name': found_name,
- 'size': size,
- 'attached_to': attached_to,
- 'lun_id': lun_id
- }
- else:
- continue
-
- return return_value
-
- def create_lun(self):
- """
- Create LUN with requested name and size
- """
- path = '/vol/%s/%s' % (self.flexvol_name, self.name)
- lun_create = netapp_utils.zapi.NaElement.create_node_with_children(
- 'lun-create-by-size', **{'path': path,
- 'size': str(self.size),
- 'ostype': self.ostype,
- 'space-reservation-enabled': str(self.space_reserve),
- 'space-allocation-enabled': str(self.space_allocation)})
-
- try:
- self.server.invoke_successfully(lun_create, enable_tunneling=True)
- except netapp_utils.zapi.NaApiError as e:
- self.module.fail_json(msg="Error provisioning lun %s of size %s: %s" % (self.name, self.size, to_native(e)),
- exception=traceback.format_exc())
-
- def delete_lun(self):
- """
- Delete requested LUN
- """
- path = '/vol/%s/%s' % (self.flexvol_name, self.name)
-
- lun_delete = netapp_utils.zapi.NaElement.create_node_with_children(
- 'lun-destroy', **{'path': path,
- 'force': str(self.force_remove),
- 'destroy-fenced-lun':
- str(self.force_remove_fenced)})
-
- try:
- self.server.invoke_successfully(lun_delete, enable_tunneling=True)
- except netapp_utils.zapi.NaApiError as e:
- self.module.fail_json(msg="Error deleting lun %s: %s" % (path, to_native(e)),
- exception=traceback.format_exc())
-
- def resize_lun(self):
- """
- Resize requested LUN.
-
- :return: True if LUN was actually re-sized, false otherwise.
- :rtype: bool
- """
- path = '/vol/%s/%s' % (self.flexvol_name, self.name)
-
- lun_resize = netapp_utils.zapi.NaElement.create_node_with_children(
- 'lun-resize', **{'path': path,
- 'size': str(self.size),
- 'force': str(self.force_resize)})
- try:
- self.server.invoke_successfully(lun_resize, enable_tunneling=True)
- except netapp_utils.zapi.NaApiError as e:
- if to_native(e.code) == "9042":
- # Error 9042 denotes the new LUN size being the same as the
- # old LUN size. This happens when there's barely any difference
- # in the two sizes. For example, from 8388608 bytes to
- # 8194304 bytes. This should go away if/when the default size
- # requested/reported to/from the controller is changed to a
- # larger unit (MB/GB/TB).
- return False
- else:
- self.module.fail_json(msg="Error resizing lun %s: %s" % (path, to_native(e)),
- exception=traceback.format_exc())
-
- return True
-
- def apply(self):
- property_changed = False
- size_changed = False
- lun_exists = False
- netapp_utils.ems_log_event("na_ontap_lun", self.server)
- lun_detail = self.get_lun()
-
- if lun_detail:
- lun_exists = True
- current_size = lun_detail['size']
-
- if self.state == 'absent':
- property_changed = True
-
- elif self.state == 'present':
- if not int(current_size) == self.size:
- size_changed = True
- property_changed = True
-
- else:
- if self.state == 'present':
- property_changed = True
-
- if property_changed:
- if self.module.check_mode:
- pass
- else:
- if self.state == 'present':
- if not lun_exists:
- self.create_lun()
-
- else:
- if size_changed:
- # Ensure that size was actually changed. Please
- # read notes in 'resize_lun' function for details.
- size_changed = self.resize_lun()
- if not size_changed:
- property_changed = False
-
- elif self.state == 'absent':
- self.delete_lun()
-
- changed = property_changed or size_changed
- # TODO: include other details about the lun (size, etc.)
- self.module.exit_json(changed=changed)
-
-
-def main():
- v = NetAppOntapLUN()
- v.apply()
-
-
-if __name__ == '__main__':
- main()
diff --git a/lib/ansible/modules/storage/netapp/na_ontap_lun_copy.py b/lib/ansible/modules/storage/netapp/na_ontap_lun_copy.py
deleted file mode 100644
index 625f3fc698..0000000000
--- a/lib/ansible/modules/storage/netapp/na_ontap_lun_copy.py
+++ /dev/null
@@ -1,182 +0,0 @@
-#!/usr/bin/python
-
-# (c) 2019, NetApp, Inc
-# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-
-from __future__ import absolute_import, division, print_function
-__metaclass__ = type
-
-
-ANSIBLE_METADATA = {'metadata_version': '1.1',
- 'status': ['preview'],
- 'supported_by': 'community'}
-
-
-DOCUMENTATION = '''
-module: na_ontap_lun_copy
-
-short_description: NetApp ONTAP copy LUNs
-extends_documentation_fragment:
- - netapp.na_ontap
-version_added: '2.8'
-author: NetApp Ansible Team (@carchi8py) <ng-ansibleteam@netapp.com>
-
-description:
- - Copy LUNs on NetApp ONTAP.
-
-options:
-
- state:
- description:
- - Whether the specified LUN should exist or not.
- choices: ['present']
- default: present
-
- destination_vserver:
- description:
- - Specifies the name of the Vserver that will host the new LUN.
- required: true
-
- destination_path:
- description:
- - Specifies the full path to the new LUN.
- required: true
-
- source_path:
- description:
- - Specifies the full path to the source LUN.
- required: true
-
- source_vserver:
- description:
- - Specifies the name of the vserver hosting the LUN to be copied.
-'''
-
-EXAMPLES = """
-- name: Copy LUN
- na_ontap_lun_copy:
- destination_vserver: ansible
- destination_path: /vol/test/test_copy_dest_dest_new
- source_path: /vol/test/test_copy_1
- source_vserver: ansible
- hostname: "{{ netapp_hostname }}"
- username: "{{ netapp_username }}"
- password: "{{ netapp_password }}"
-"""
-
-RETURN = """
-
-"""
-
-import traceback
-from ansible.module_utils.basic import AnsibleModule
-from ansible.module_utils._text import to_native
-from ansible.module_utils.netapp_module import NetAppModule
-import ansible.module_utils.netapp as netapp_utils
-
-
-HAS_NETAPP_LIB = netapp_utils.has_netapp_lib()
-
-
-class NetAppOntapLUNCopy(object):
-
- def __init__(self):
- self.argument_spec = netapp_utils.na_ontap_host_argument_spec()
- self.argument_spec.update(dict(
- state=dict(required=False, choices=['present'], default='present'),
- destination_vserver=dict(required=True, type='str'),
- destination_path=dict(required=True, type='str'),
- source_path=dict(required=True, type='str'),
- source_vserver=dict(required=False, type='str'),
-
- ))
-
- self.module = AnsibleModule(
- argument_spec=self.argument_spec,
- supports_check_mode=True
- )
-
- self.na_helper = NetAppModule()
- self.parameters = self.na_helper.set_parameters(self.module.params)
-
- if HAS_NETAPP_LIB is False:
- self.module.fail_json(msg="the python NetApp-Lib module is required")
- else:
- self.server = netapp_utils.setup_na_ontap_zapi(module=self.module, vserver=self.parameters['destination_vserver'])
-
- def get_lun(self):
- """
- Check if the LUN exists
-
- :return: true is it exists, false otherwise
- :rtype: bool
- """
-
- return_value = False
- lun_info = netapp_utils.zapi.NaElement('lun-get-iter')
- query_details = netapp_utils.zapi.NaElement('lun-info')
-
- query_details.add_new_child('path', self.parameters['destination_path'])
- query_details.add_new_child('vserver', self.parameters['destination_vserver'])
-
- query = netapp_utils.zapi.NaElement('query')
- query.add_child_elem(query_details)
-
- lun_info.add_child_elem(query)
- try:
- result = self.server.invoke_successfully(lun_info, True)
-
- except netapp_utils.zapi.NaApiError as e:
- self.module.fail_json(msg="Error getting lun info %s for verver %s: %s" %
- (self.parameters['destination_path'], self.parameters['destination_vserver'], to_native(e)),
- exception=traceback.format_exc())
-
- if result.get_child_by_name('num-records') and int(result.get_child_content('num-records')) >= 1:
- return_value = True
- return return_value
-
- def copy_lun(self):
- """
- Copy LUN with requested path and vserver
- """
- lun_copy = netapp_utils.zapi.NaElement.create_node_with_children(
- 'lun-copy-start', **{'source-vserver': self.parameters['source_vserver']})
-
- path_obj = netapp_utils.zapi.NaElement('paths')
- pair = netapp_utils.zapi.NaElement('lun-path-pair')
- pair.add_new_child('destination-path', self.parameters['destination_path'])
- pair.add_new_child('source-path', self.parameters['source_path'])
- path_obj.add_child_elem(pair)
- lun_copy.add_child_elem(path_obj)
-
- try:
- self.server.invoke_successfully(lun_copy, enable_tunneling=True)
- except netapp_utils.zapi.NaApiError as e:
- self.module.fail_json(msg="Error copying lun from %s to vserver %s: %s" %
- (self.parameters['source_vserver'], self.parameters['destination_vserver'], to_native(e)),
- exception=traceback.format_exc())
-
- def apply(self):
-
- netapp_utils.ems_log_event("na_ontap_lun_copy", self.server)
- if self.get_lun(): # lun already exists at destination
- changed = False
- else:
- changed = True
- if self.module.check_mode:
- pass
- else:
- # need to copy lun
- if self.parameters['state'] == 'present':
- self.copy_lun()
-
- self.module.exit_json(changed=changed)
-
-
-def main():
- v = NetAppOntapLUNCopy()
- v.apply()
-
-
-if __name__ == '__main__':
- main()
diff --git a/lib/ansible/modules/storage/netapp/na_ontap_lun_map.py b/lib/ansible/modules/storage/netapp/na_ontap_lun_map.py
deleted file mode 100644
index f46f7fea07..0000000000
--- a/lib/ansible/modules/storage/netapp/na_ontap_lun_map.py
+++ /dev/null
@@ -1,282 +0,0 @@
-#!/usr/bin/python
-
-""" this is lun mapping module
-
- (c) 2018-2019, NetApp, Inc
- # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-"""
-
-from __future__ import absolute_import, division, print_function
-__metaclass__ = type
-
-
-ANSIBLE_METADATA = {'metadata_version': '1.1',
- 'status': ['preview'],
- 'supported_by': 'certified'}
-
-
-DOCUMENTATION = """
-
-module: na_ontap_lun_map
-
-short_description: NetApp ONTAP LUN maps
-extends_documentation_fragment:
- - netapp.na_ontap
-version_added: '2.6'
-author: NetApp Ansible Team (@carchi8py) <ng-ansibleteam@netapp.com>
-
-description:
-- Map and unmap LUNs on NetApp ONTAP.
-
-options:
-
- state:
- description:
- - Whether the specified LUN should exist or not.
- choices: ['present', 'absent']
- default: present
-
- initiator_group_name:
- description:
- - Initiator group to map to the given LUN.
- required: true
-
- path:
- description:
- - Path of the LUN..
- required: true
-
- vserver:
- required: true
- description:
- - The name of the vserver to use.
-
- lun_id:
- description:
- - LUN ID assigned for the map.
-
-
-"""
-
-EXAMPLES = """
-- name: Create LUN mapping
- na_ontap_lun_map:
- state: present
- initiator_group_name: ansibleIgroup3234
- path: /vol/iscsi_path/iscsi_lun
- vserver: ci_dev
- hostname: "{{ netapp_hostname }}"
- username: "{{ netapp_username }}"
- password: "{{ netapp_password }}"
-
-- name: Unmap LUN
- na_ontap_lun_map:
- state: absent
- initiator_group_name: ansibleIgroup3234
- path: /vol/iscsi_path/iscsi_lun
- vserver: ci_dev
- hostname: "{{ netapp_hostname }}"
- username: "{{ netapp_username }}"
- password: "{{ netapp_password }}"
-"""
-
-RETURN = """
-lun_node:
- description: NetApp controller that is hosting the LUN.
- returned: success
- type: str
- sample: node01
-lun_ostype:
- description: Specifies the OS of the host accessing the LUN.
- returned: success
- type: str
- sample: vmware
-lun_serial:
- description: A unique, 12-byte, ASCII string used to identify the LUN.
- returned: success
- type: str
- sample: 80E7/]LZp1Tt
-lun_naa_id:
- description: The Network Address Authority (NAA) identifier for the LUN.
- returned: success
- type: str
- sample: 600a0980383045372f5d4c5a70315474
-lun_state:
- description: Online or offline status of the LUN.
- returned: success
- type: str
- sample: online
-lun_size:
- description: Size of the LUN in bytes.
- returned: success
- type: int
- sample: 2199023255552
-"""
-
-import traceback
-
-from ansible.module_utils.basic import AnsibleModule
-from ansible.module_utils._text import to_native
-import ansible.module_utils.netapp as netapp_utils
-import codecs
-from ansible.module_utils._text import to_text, to_bytes
-
-HAS_NETAPP_LIB = netapp_utils.has_netapp_lib()
-
-
-class NetAppOntapLUNMap(object):
-
- def __init__(self):
-
- self.argument_spec = netapp_utils.na_ontap_host_argument_spec()
- self.argument_spec.update(dict(
- state=dict(required=False, choices=['present', 'absent'], default='present'),
- initiator_group_name=dict(required=True, type='str'),
- path=dict(required=True, type='str'),
- vserver=dict(required=True, type='str'),
- lun_id=dict(required=False, type='str', default=None),
- ))
-
- self.module = AnsibleModule(
- argument_spec=self.argument_spec,
- required_if=[
- ('state', 'present', ['path'])
- ],
- supports_check_mode=True
- )
-
- self.result = dict(
- changed=False,
- )
-
- p = self.module.params
-
- # set up state variables
- self.state = p['state']
- self.initiator_group_name = p['initiator_group_name']
- self.path = p['path']
- self.vserver = p['vserver']
- self.lun_id = p['lun_id']
-
- if HAS_NETAPP_LIB is False:
- self.module.fail_json(msg="the python NetApp-Lib module is required")
- else:
- self.server = netapp_utils.setup_na_ontap_zapi(module=self.module, vserver=self.vserver)
-
- def get_lun_map(self):
- """
- Return details about the LUN map
-
- :return: Details about the lun map
- :rtype: dict
- """
- lun_info = netapp_utils.zapi.NaElement('lun-map-list-info')
- lun_info.add_new_child('path', self.path)
- result = self.server.invoke_successfully(lun_info, True)
- return_value = None
- igroups = result.get_child_by_name('initiator-groups')
- if igroups:
- for igroup_info in igroups.get_children():
- initiator_group_name = igroup_info.get_child_content('initiator-group-name')
- lun_id = igroup_info.get_child_content('lun-id')
- if initiator_group_name == self.initiator_group_name:
- return_value = {
- 'lun_id': lun_id
- }
- break
-
- return return_value
-
- def get_lun(self):
- """
- Return details about the LUN
-
- :return: Details about the lun
- :rtype: dict
- """
- # build the lun query
- query_details = netapp_utils.zapi.NaElement('lun-info')
- query_details.add_new_child('path', self.path)
-
- query = netapp_utils.zapi.NaElement('query')
- query.add_child_elem(query_details)
-
- lun_query = netapp_utils.zapi.NaElement('lun-get-iter')
- lun_query.add_child_elem(query)
-
- # find lun using query
- result = self.server.invoke_successfully(lun_query, True)
- return_value = None
- if result.get_child_by_name('num-records') and int(result.get_child_content('num-records')) >= 1:
- lun = result.get_child_by_name('attributes-list').get_child_by_name('lun-info')
-
- # extract and assign lun information to return value
- hexlify = codecs.getencoder('hex')
- naa_hex = to_text(hexlify(to_bytes(lun.get_child_content('serial-number')))[0])
- return_value = {
- 'lun_node': lun.get_child_content('node'),
- 'lun_ostype': lun.get_child_content('multiprotocol-type'),
- 'lun_serial': lun.get_child_content('serial-number'),
- 'lun_naa_id': '600a0980' + naa_hex,
- 'lun_state': lun.get_child_content('state'),
- 'lun_size': lun.get_child_content('size'),
- }
-
- return return_value
-
- def create_lun_map(self):
- """
- Create LUN map
- """
- options = {'path': self.path, 'initiator-group': self.initiator_group_name}
- if self.lun_id is not None:
- options['lun-id'] = self.lun_id
- lun_map_create = netapp_utils.zapi.NaElement.create_node_with_children('lun-map', **options)
-
- try:
- self.server.invoke_successfully(lun_map_create, enable_tunneling=True)
- except netapp_utils.zapi.NaApiError as e:
- self.module.fail_json(msg="Error mapping lun %s of initiator_group_name %s: %s" %
- (self.path, self.initiator_group_name, to_native(e)),
- exception=traceback.format_exc())
-
- def delete_lun_map(self):
- """
- Unmap LUN map
- """
- lun_map_delete = netapp_utils.zapi.NaElement.create_node_with_children('lun-unmap', **{'path': self.path, 'initiator-group': self.initiator_group_name})
-
- try:
- self.server.invoke_successfully(lun_map_delete, enable_tunneling=True)
- except netapp_utils.zapi.NaApiError as e:
- self.module.fail_json(msg="Error unmapping lun %s of initiator_group_name %s: %s" %
- (self.path, self.initiator_group_name, to_native(e)),
- exception=traceback.format_exc())
-
- def apply(self):
- netapp_utils.ems_log_event("na_ontap_lun_map", self.server)
- lun_details = self.get_lun()
- lun_map_details = self.get_lun_map()
-
- if self.state == 'present' and lun_details:
- self.result.update(lun_details)
-
- if self.state == 'present' and not lun_map_details:
- self.result['changed'] = True
- if not self.module.check_mode:
- self.create_lun_map()
- elif self.state == 'absent' and lun_map_details:
- self.result['changed'] = True
- if not self.module.check_mode:
- self.delete_lun_map()
-
- self.module.exit_json(**self.result)
-
-
-def main():
- v = NetAppOntapLUNMap()
- v.apply()
-
-
-if __name__ == '__main__':
- main()
diff --git a/lib/ansible/modules/storage/netapp/na_ontap_motd.py b/lib/ansible/modules/storage/netapp/na_ontap_motd.py
deleted file mode 100644
index 7f48ebcb8d..0000000000
--- a/lib/ansible/modules/storage/netapp/na_ontap_motd.py
+++ /dev/null
@@ -1,209 +0,0 @@
-#!/usr/bin/python
-
-# (c) 2018-2019, NetApp, Inc
-# (c) 2018 Piotr Olczak <piotr.olczak@redhat.com>
-# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-
-from __future__ import absolute_import, division, print_function
-__metaclass__ = type
-
-ANSIBLE_METADATA = {'metadata_version': '1.1',
- 'status': ['preview'],
- 'supported_by': 'certified'}
-
-
-DOCUMENTATION = '''
-module: na_ontap_motd
-author:
- - Piotr Olczak (@dprts) <polczak@redhat.com>
- - NetApp Ansible Team (@carchi8py) <ng-ansibleteam@netapp.com>
-extends_documentation_fragment:
- - netapp.na_ontap
-short_description: Setup motd
-description:
- - This module allows you to manipulate motd for a vserver
- - It also allows to manipulate motd at the cluster level by using the cluster vserver (cserver)
-version_added: "2.7"
-requirements:
- - netapp_lib
-options:
- state:
- description:
- - If C(state=present) sets MOTD given in I(message) C(state=absent) removes it.
- choices: ['present', 'absent']
- default: present
- message:
- description:
- - MOTD Text message, required when C(state=present).
- type: str
- vserver:
- description:
- - The name of the SVM motd should be set for.
- required: true
- type: str
- show_cluster_motd:
- description:
- - Set to I(false) if Cluster-level Message of the Day should not be shown
- type: bool
- default: True
-
-'''
-
-EXAMPLES = '''
-
-- name: Set Cluster-Level MOTD
- na_ontap_motd:
- vserver: my_ontap_cluster
- message: "Cluster wide MOTD"
- hostname: "{{ netapp_hostname }}"
- username: "{{ netapp_username }}"
- password: "{{ netapp_password }}"
- state: present
- https: true
-
-- name: Set MOTD for I(rhev_nfs_krb) SVM, do not show Cluster-Level MOTD
- na_ontap_motd:
- vserver: rhev_nfs_krb
- message: "Access to rhev_nfs_krb is also restricted"
- hostname: "{{ netapp_hostname }}"
- username: "{{ netapp_username }}"
- password: "{{ netapp_password }}"
- state: present
- show_cluster_motd: False
- https: true
-
-- name: Remove Cluster-Level MOTD
- na_ontap_motd:
- vserver: my_ontap_cluster
- hostname: "{{ netapp_hostname }}"
- username: "{{ netapp_username }}"
- password: "{{ netapp_password }}"
- state: absent
- https: true
-
-'''
-
-RETURN = '''
-
-'''
-
-import traceback
-from ansible.module_utils.basic import AnsibleModule
-from ansible.module_utils._text import to_native
-import ansible.module_utils.netapp as netapp_utils
-from ansible.module_utils.netapp_module import NetAppModule
-
-
-HAS_NETAPP_LIB = netapp_utils.has_netapp_lib()
-
-
-class NetAppONTAPMotd(object):
-
- def __init__(self):
- argument_spec = netapp_utils.na_ontap_host_argument_spec()
- argument_spec.update(dict(
- state=dict(required=False, default='present', choices=['present', 'absent']),
- vserver=dict(required=True, type='str'),
- message=dict(default='', type='str'),
- show_cluster_motd=dict(default=True, type='bool')
- ))
-
- self.module = AnsibleModule(
- argument_spec=argument_spec,
- supports_check_mode=True
- )
-
- self.na_helper = NetAppModule()
- self.parameters = self.na_helper.set_parameters(self.module.params)
-
- if HAS_NETAPP_LIB is False:
- self.module.fail_json(msg="the python NetApp-Lib module is required")
-
- self.server = netapp_utils.setup_na_ontap_zapi(module=self.module, vserver=self.parameters['vserver'])
-
- def motd_get_iter(self):
- """
- Compose NaElement object to query current motd
- :return: NaElement object for vserver-motd-get-iter
- """
- motd_get_iter = netapp_utils.zapi.NaElement('vserver-motd-get-iter')
- query = netapp_utils.zapi.NaElement('query')
- motd_info = netapp_utils.zapi.NaElement('vserver-motd-info')
- motd_info.add_new_child('is-cluster-message-enabled', str(self.parameters['show_cluster_motd']))
- motd_info.add_new_child('vserver', self.parameters['vserver'])
- query.add_child_elem(motd_info)
- motd_get_iter.add_child_elem(query)
- return motd_get_iter
-
- def motd_get(self):
- """
- Get current motd
- :return: Dictionary of current motd details if query successful, else None
- """
- motd_get_iter = self.motd_get_iter()
- motd_result = dict()
- try:
- result = self.server.invoke_successfully(motd_get_iter, enable_tunneling=True)
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg='Error fetching motd info: %s' % to_native(error),
- exception=traceback.format_exc())
- if result.get_child_by_name('num-records') and \
- int(result.get_child_content('num-records')) > 0:
- motd_info = result.get_child_by_name('attributes-list').get_child_by_name(
- 'vserver-motd-info')
- motd_result['message'] = motd_info.get_child_content('message')
- motd_result['message'] = str(motd_result['message']).rstrip()
- motd_result['show_cluster_motd'] = True if motd_info.get_child_content(
- 'is-cluster-message-enabled') == 'true' else False
- motd_result['vserver'] = motd_info.get_child_content('vserver')
- return motd_result
- return None
-
- def modify_motd(self):
- motd_create = netapp_utils.zapi.NaElement('vserver-motd-modify-iter')
- motd_create.add_new_child('message', self.parameters['message'])
- motd_create.add_new_child(
- 'is-cluster-message-enabled', 'true' if self.parameters['show_cluster_motd'] is True else 'false')
- query = netapp_utils.zapi.NaElement('query')
- motd_info = netapp_utils.zapi.NaElement('vserver-motd-info')
- motd_info.add_new_child('vserver', self.parameters['vserver'])
- query.add_child_elem(motd_info)
- motd_create.add_child_elem(query)
- try:
- self.server.invoke_successfully(motd_create, enable_tunneling=False)
- except netapp_utils.zapi.NaApiError as err:
- self.module.fail_json(msg="Error creating motd: %s" % (to_native(err)), exception=traceback.format_exc())
- return motd_create
-
- def apply(self):
- """
- Applies action from playbook
- """
- netapp_utils.ems_log_event("na_ontap_motd", self.server)
- current = self.motd_get()
- if self.parameters['state'] == 'present' and self.parameters['message'] == "":
- self.module.fail_json(msg="message parameter cannot be empty")
- if self.parameters['state'] == 'absent':
- # Just make sure it is empty
- self.parameters['message'] = ''
- if current['message'] == 'None':
- current = None
- cd_action = self.na_helper.get_cd_action(current, self.parameters)
- if cd_action is None and self.parameters['state'] == 'present':
- self.na_helper.get_modified_attributes(current, self.parameters)
-
- if self.na_helper.changed:
- if self.module.check_mode:
- pass
- else:
- self.modify_motd()
- self.module.exit_json(changed=self.na_helper.changed)
-
-
-def main():
- motd_obj = NetAppONTAPMotd()
- motd_obj.apply()
-
-
-if __name__ == '__main__':
- main()
diff --git a/lib/ansible/modules/storage/netapp/na_ontap_ndmp.py b/lib/ansible/modules/storage/netapp/na_ontap_ndmp.py
deleted file mode 100644
index 53e03ee861..0000000000
--- a/lib/ansible/modules/storage/netapp/na_ontap_ndmp.py
+++ /dev/null
@@ -1,342 +0,0 @@
-#!/usr/bin/python
-""" this is ndmp module
-
- (c) 2019, NetApp, Inc
- # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-"""
-
-from __future__ import absolute_import, division, print_function
-__metaclass__ = type
-
-ANSIBLE_METADATA = {
- 'metadata_version': '1.1',
- 'status': ['preview'],
- 'supported_by': 'community'
-}
-
-
-DOCUMENTATION = '''
----
-module: na_ontap_ndmp
-short_description: NetApp ONTAP NDMP services configuration
-extends_documentation_fragment:
- - netapp.na_ontap
-version_added: '2.9'
-author: NetApp Ansible Team (@carchi8py) <ng-ansibleteam@netapp.com>
-
-description:
- - Modify NDMP Services.
-
-options:
-
- vserver:
- description:
- - Name of the vserver.
- required: true
- type: str
-
- abort_on_disk_error:
- description:
- - Enable abort on disk error.
- type: bool
-
- authtype:
- description:
- - Authentication type.
- type: list
-
- backup_log_enable:
- description:
- - Enable backup log.
- type: bool
-
- data_port_range:
- description:
- - Data port range.
- type: str
-
- debug_enable:
- description:
- - Enable debug.
- type: bool
-
- debug_filter:
- description:
- - Debug filter.
- type: str
-
- dump_detailed_stats:
- description:
- - Enable logging of VM stats for dump.
- type: bool
-
- dump_logical_find:
- description:
- - Enable logical find for dump.
- type: str
-
- enable:
- description:
- - Enable NDMP on vserver.
- type: bool
-
- fh_dir_retry_interval:
- description:
- - FH throttle value for dir.
- type: int
-
- fh_node_retry_interval:
- description:
- - FH throttle value for node.
- type: int
-
- ignore_ctime_enabled:
- description:
- - Ignore ctime.
- type: bool
-
- is_secure_control_connection_enabled:
- description:
- - Is secure control connection enabled.
- type: bool
-
- offset_map_enable:
- description:
- - Enable offset map.
- type: bool
-
- per_qtree_exclude_enable:
- description:
- - Enable per qtree exclusion.
- type: bool
-
- preferred_interface_role:
- description:
- - Preferred interface role.
- type: list
-
- restore_vm_cache_size:
- description:
- - Restore VM file cache size.
- type: int
-
- secondary_debug_filter:
- description:
- - Secondary debug filter.
- type: str
-
- tcpnodelay:
- description:
- - Enable TCP nodelay.
- type: bool
-
- tcpwinsize:
- description:
- - TCP window size.
- type: int
-'''
-
-EXAMPLES = '''
- - name: modify ndmp
- na_ontap_ndmp:
- vserver: ansible
- hostname: "{{ hostname }}"
- abort_on_disk_error: true
- authtype: plaintext,challenge
- backup_log_enable: true
- data_port_range: 8000-9000
- debug_enable: true
- debug_filter: filter
- dump_detailed_stats: true
- dump_logical_find: default
- enable: true
- fh_dir_retry_interval: 100
- fh_node_retry_interval: 100
- ignore_ctime_enabled: true
- is_secure_control_connection_enabled: true
- offset_map_enable: true
- per_qtree_exclude_enable: true
- preferred_interface_role: node_mgmt,intercluster
- restore_vm_cache_size: 1000
- secondary_debug_filter: filter
- tcpnodelay: true
- tcpwinsize: 10000
- username: user
- password: pass
- https: False
-'''
-
-RETURN = '''
-'''
-
-import traceback
-from ansible.module_utils.basic import AnsibleModule
-from ansible.module_utils._text import to_native
-import ansible.module_utils.netapp as netapp_utils
-from ansible.module_utils.netapp_module import NetAppModule
-
-HAS_NETAPP_LIB = netapp_utils.has_netapp_lib()
-
-
-class NetAppONTAPNdmp(object):
- '''
- modify vserver cifs security
- '''
- def __init__(self):
-
- self.argument_spec = netapp_utils.na_ontap_host_argument_spec()
- self.modifiable_options = dict(
- abort_on_disk_error=dict(required=False, type='bool'),
- authtype=dict(required=False, type='list'),
- backup_log_enable=dict(required=False, type='bool'),
- data_port_range=dict(required=False, type='str'),
- debug_enable=dict(required=False, type='bool'),
- debug_filter=dict(required=False, type='str'),
- dump_detailed_stats=dict(required=False, type='bool'),
- dump_logical_find=dict(required=False, type='str'),
- enable=dict(required=False, type='bool'),
- fh_dir_retry_interval=dict(required=False, type='int'),
- fh_node_retry_interval=dict(required=False, type='int'),
- ignore_ctime_enabled=dict(required=False, type='bool'),
- is_secure_control_connection_enabled=dict(required=False, type='bool'),
- offset_map_enable=dict(required=False, type='bool'),
- per_qtree_exclude_enable=dict(required=False, type='bool'),
- preferred_interface_role=dict(required=False, type='list'),
- restore_vm_cache_size=dict(required=False, type='int'),
- secondary_debug_filter=dict(required=False, type='str'),
- tcpnodelay=dict(required=False, type='bool'),
- tcpwinsize=dict(required=False, type='int')
- )
- self.argument_spec.update(dict(
- vserver=dict(required=True, type='str')
- ))
-
- self.argument_spec.update(self.modifiable_options)
-
- self.module = AnsibleModule(
- argument_spec=self.argument_spec,
- supports_check_mode=True
- )
-
- self.na_helper = NetAppModule()
- self.parameters = self.na_helper.set_parameters(self.module.params)
-
- if HAS_NETAPP_LIB is False:
- self.module.fail_json(msg="the python NetApp-Lib module is required")
- else:
- self.server = netapp_utils.setup_na_ontap_zapi(module=self.module, vserver=self.parameters['vserver'])
-
- def ndmp_get_iter(self):
- """
- get current vserver ndmp attributes.
- :return: a dict of ndmp attributes.
- """
- ndmp_get = netapp_utils.zapi.NaElement('ndmp-vserver-attributes-get-iter')
- query = netapp_utils.zapi.NaElement('query')
- ndmp_info = netapp_utils.zapi.NaElement('ndmp-vserver-attributes-info')
- ndmp_info.add_new_child('vserver', self.parameters['vserver'])
- query.add_child_elem(ndmp_info)
- ndmp_get.add_child_elem(query)
- ndmp_details = dict()
- try:
- result = self.server.invoke_successfully(ndmp_get, enable_tunneling=True)
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg='Error fetching ndmp from %s: %s'
- % (self.parameters['vserver'], to_native(error)),
- exception=traceback.format_exc())
-
- if result.get_child_by_name('num-records') and int(result.get_child_content('num-records')) > 0:
- ndmp_attributes = result.get_child_by_name('attributes-list').get_child_by_name('ndmp-vserver-attributes-info')
- self.get_ndmp_details(ndmp_details, ndmp_attributes)
- return ndmp_details
-
- def get_ndmp_details(self, ndmp_details, ndmp_attributes):
- """
- :param ndmp_details: a dict of current ndmp.
- :param ndmp_attributes: ndmp returned from api call in xml format.
- :return: None
- """
- for option in self.modifiable_options.keys():
- option_type = self.modifiable_options[option]['type']
- if option_type == 'bool':
- ndmp_details[option] = self.str_to_bool(ndmp_attributes.get_child_content(self.attribute_to_name(option)))
- elif option_type == 'int':
- ndmp_details[option] = int(ndmp_attributes.get_child_content(self.attribute_to_name(option)))
- elif option_type == 'list':
- child_list = ndmp_attributes.get_child_by_name(self.attribute_to_name(option))
- values = [child.get_content() for child in child_list.get_children()]
- ndmp_details[option] = values
- else:
- ndmp_details[option] = ndmp_attributes.get_child_content(self.attribute_to_name(option))
-
- def modify_ndmp(self, modify):
- """
- :param modify: A list of attributes to modify
- :return: None
- """
- ndmp_modify = netapp_utils.zapi.NaElement('ndmp-vserver-attributes-modify')
- for attribute in modify:
- if attribute == 'authtype':
- authtypes = netapp_utils.zapi.NaElement('authtype')
- types = self.parameters['authtype']
- for authtype in types:
- authtypes.add_new_child('ndmpd-authtypes', authtype)
- ndmp_modify.add_child_elem(authtypes)
- elif attribute == 'preferred_interface_role':
- preferred_interface_roles = netapp_utils.zapi.NaElement('preferred-interface-role')
- roles = self.parameters['preferred_interface_role']
- for role in roles:
- preferred_interface_roles.add_new_child('netport-role', role)
- ndmp_modify.add_child_elem(preferred_interface_roles)
- else:
- ndmp_modify.add_new_child(self.attribute_to_name(attribute), str(self.parameters[attribute]))
- try:
- self.server.invoke_successfully(ndmp_modify, enable_tunneling=True)
- except netapp_utils.zapi.NaApiError as e:
- self.module.fail_json(msg='Error modifying ndmp on %s: %s'
- % (self.parameters['vserver'], to_native(e)),
- exception=traceback.format_exc())
-
- @staticmethod
- def attribute_to_name(attribute):
- return str.replace(attribute, '_', '-')
-
- @staticmethod
- def str_to_bool(s):
- if s == 'true':
- return True
- else:
- return False
-
- def apply(self):
- """Call modify operations."""
- self.asup_log_for_cserver("na_ontap_ndmp")
- current = self.ndmp_get_iter()
- modify = self.na_helper.get_modified_attributes(current, self.parameters)
- if self.na_helper.changed:
- if self.module.check_mode:
- pass
- else:
- if modify:
- self.modify_ndmp(modify)
- self.module.exit_json(changed=self.na_helper.changed)
-
- def asup_log_for_cserver(self, event_name):
- """
- Fetch admin vserver for the given cluster
- Create and Autosupport log event with the given module name
- :param event_name: Name of the event log
- :return: None
- """
- results = netapp_utils.get_cserver(self.server)
- cserver = netapp_utils.setup_na_ontap_zapi(module=self.module, vserver=results)
- netapp_utils.ems_log_event(event_name, cserver)
-
-
-def main():
- obj = NetAppONTAPNdmp()
- obj.apply()
-
-
-if __name__ == '__main__':
- main()
diff --git a/lib/ansible/modules/storage/netapp/na_ontap_net_ifgrp.py b/lib/ansible/modules/storage/netapp/na_ontap_net_ifgrp.py
deleted file mode 100644
index afc22151a3..0000000000
--- a/lib/ansible/modules/storage/netapp/na_ontap_net_ifgrp.py
+++ /dev/null
@@ -1,307 +0,0 @@
-#!/usr/bin/python
-
-# (c) 2018-2019, NetApp, Inc
-# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-
-from __future__ import absolute_import, division, print_function
-__metaclass__ = type
-
-
-ANSIBLE_METADATA = {'metadata_version': '1.1',
- 'status': ['preview'],
- 'supported_by': 'certified'}
-
-DOCUMENTATION = """
-module: na_ontap_net_ifgrp
-short_description: NetApp Ontap modify network interface group
-extends_documentation_fragment:
- - netapp.na_ontap
-version_added: '2.6'
-author: NetApp Ansible Team (@carchi8py) <ng-ansibleteam@netapp.com>
-description:
-- Create, modify ports, destroy the network interface group
-options:
- state:
- description:
- - Whether the specified network interface group should exist or not.
- choices: ['present', 'absent']
- default: present
-
- distribution_function:
- description:
- - Specifies the traffic distribution function for the ifgrp.
- choices: ['mac', 'ip', 'sequential', 'port']
-
- name:
- description:
- - Specifies the interface group name.
- required: true
-
- mode:
- description:
- - Specifies the link policy for the ifgrp.
-
- node:
- description:
- - Specifies the name of node.
- required: true
-
- ports:
- aliases:
- - port
- description:
- - List of expected ports to be present in the interface group.
- - If a port is present in this list, but not on the target, it will be added.
- - If a port is not in the list, but present on the target, it will be removed.
- - Make sure the list contains all ports you want to see on the target.
- version_added: '2.8'
-"""
-
-EXAMPLES = """
- - name: create ifgrp
- na_ontap_net_ifgrp:
- state: present
- username: "{{ netapp_username }}"
- password: "{{ netapp_password }}"
- hostname: "{{ netapp_hostname }}"
- distribution_function: ip
- name: a0c
- ports: [e0a]
- mode: multimode
- node: "{{ Vsim node name }}"
- - name: modify ports in an ifgrp
- na_ontap_net_ifgrp:
- state: present
- username: "{{ netapp_username }}"
- password: "{{ netapp_password }}"
- hostname: "{{ netapp_hostname }}"
- distribution_function: ip
- name: a0c
- port: [e0a, e0c]
- mode: multimode
- node: "{{ Vsim node name }}"
- - name: delete ifgrp
- na_ontap_net_ifgrp:
- state: absent
- username: "{{ netapp_username }}"
- password: "{{ netapp_password }}"
- hostname: "{{ netapp_hostname }}"
- name: a0c
- node: "{{ Vsim node name }}"
-"""
-
-RETURN = """
-
-"""
-
-import traceback
-
-from ansible.module_utils.basic import AnsibleModule
-from ansible.module_utils.netapp_module import NetAppModule
-from ansible.module_utils._text import to_native
-import ansible.module_utils.netapp as netapp_utils
-
-HAS_NETAPP_LIB = netapp_utils.has_netapp_lib()
-
-
-class NetAppOntapIfGrp(object):
- """
- Create, Modifies and Destroys a IfGrp
- """
- def __init__(self):
- """
- Initialize the Ontap IfGrp class
- """
- self.argument_spec = netapp_utils.na_ontap_host_argument_spec()
- self.argument_spec.update(dict(
- state=dict(required=False, choices=['present', 'absent'], default='present'),
- distribution_function=dict(required=False, type='str', choices=['mac', 'ip', 'sequential', 'port']),
- name=dict(required=True, type='str'),
- mode=dict(required=False, type='str'),
- node=dict(required=True, type='str'),
- ports=dict(required=False, type='list', aliases=["port"]),
- ))
-
- self.module = AnsibleModule(
- argument_spec=self.argument_spec,
- required_if=[
- ('state', 'present', ['distribution_function', 'mode'])
- ],
- supports_check_mode=True
- )
-
- # set up variables
- self.na_helper = NetAppModule()
- self.parameters = self.na_helper.set_parameters(self.module.params)
-
- if HAS_NETAPP_LIB is False:
- self.module.fail_json(msg="the python NetApp-Lib module is required")
- else:
- self.server = netapp_utils.setup_na_ontap_zapi(module=self.module)
- return
-
- def get_if_grp(self):
- """
- Return details about the if_group
- :param:
- name : Name of the if_group
-
- :return: Details about the if_group. None if not found.
- :rtype: dict
- """
- if_group_iter = netapp_utils.zapi.NaElement('net-port-get-iter')
- if_group_info = netapp_utils.zapi.NaElement('net-port-info')
- if_group_info.add_new_child('port', self.parameters['name'])
- if_group_info.add_new_child('port-type', 'if_group')
- if_group_info.add_new_child('node', self.parameters['node'])
- query = netapp_utils.zapi.NaElement('query')
- query.add_child_elem(if_group_info)
- if_group_iter.add_child_elem(query)
- try:
- result = self.server.invoke_successfully(if_group_iter, True)
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg='Error getting if_group %s: %s' % (self.parameters['name'], to_native(error)),
- exception=traceback.format_exc())
-
- return_value = None
-
- if result.get_child_by_name('num-records') and int(result['num-records']) >= 1:
- if_group_attributes = result['attributes-list']['net-port-info']
- return_value = {
- 'name': if_group_attributes['port'],
- 'distribution_function': if_group_attributes['ifgrp-distribution-function'],
- 'mode': if_group_attributes['ifgrp-mode'],
- 'node': if_group_attributes['node'],
- }
-
- return return_value
-
- def get_if_grp_ports(self):
- """
- Return ports of the if_group
- :param:
- name : Name of the if_group
- :return: Ports of the if_group. None if not found.
- :rtype: dict
- """
- if_group_iter = netapp_utils.zapi.NaElement('net-port-ifgrp-get')
- if_group_iter.add_new_child('ifgrp-name', self.parameters['name'])
- if_group_iter.add_new_child('node', self.parameters['node'])
- try:
- result = self.server.invoke_successfully(if_group_iter, True)
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg='Error getting if_group ports %s: %s' % (self.parameters['name'], to_native(error)),
- exception=traceback.format_exc())
-
- port_list = []
- if result.get_child_by_name('attributes'):
- if_group_attributes = result['attributes']['net-ifgrp-info']
- if if_group_attributes.get_child_by_name('ports'):
- ports = if_group_attributes.get_child_by_name('ports').get_children()
- for each in ports:
- port_list.append(each.get_content())
- return {'ports': port_list}
-
- def create_if_grp(self):
- """
- Creates a new ifgrp
- """
- route_obj = netapp_utils.zapi.NaElement("net-port-ifgrp-create")
- route_obj.add_new_child("distribution-function", self.parameters['distribution_function'])
- route_obj.add_new_child("ifgrp-name", self.parameters['name'])
- route_obj.add_new_child("mode", self.parameters['mode'])
- route_obj.add_new_child("node", self.parameters['node'])
- try:
- self.server.invoke_successfully(route_obj, True)
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg='Error creating if_group %s: %s' % (self.parameters['name'], to_native(error)),
- exception=traceback.format_exc())
- for port in self.parameters.get('ports'):
- self.add_port_to_if_grp(port)
-
- def delete_if_grp(self):
- """
- Deletes a ifgrp
- """
- route_obj = netapp_utils.zapi.NaElement("net-port-ifgrp-destroy")
- route_obj.add_new_child("ifgrp-name", self.parameters['name'])
- route_obj.add_new_child("node", self.parameters['node'])
- try:
- self.server.invoke_successfully(route_obj, True)
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg='Error deleting if_group %s: %s' % (self.parameters['name'], to_native(error)),
- exception=traceback.format_exc())
-
- def add_port_to_if_grp(self, port):
- """
- adds port to a ifgrp
- """
- route_obj = netapp_utils.zapi.NaElement("net-port-ifgrp-add-port")
- route_obj.add_new_child("ifgrp-name", self.parameters['name'])
- route_obj.add_new_child("port", port)
- route_obj.add_new_child("node", self.parameters['node'])
- try:
- self.server.invoke_successfully(route_obj, True)
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg='Error adding port %s to if_group %s: %s' %
- (port, self.parameters['name'], to_native(error)),
- exception=traceback.format_exc())
-
- def modify_ports(self, current_ports):
- add_ports = set(self.parameters['ports']) - set(current_ports)
- remove_ports = set(current_ports) - set(self.parameters['ports'])
- for port in add_ports:
- self.add_port_to_if_grp(port)
- for port in remove_ports:
- self.remove_port_to_if_grp(port)
-
- def remove_port_to_if_grp(self, port):
- """
- removes port from a ifgrp
- """
- route_obj = netapp_utils.zapi.NaElement("net-port-ifgrp-remove-port")
- route_obj.add_new_child("ifgrp-name", self.parameters['name'])
- route_obj.add_new_child("port", port)
- route_obj.add_new_child("node", self.parameters['node'])
- try:
- self.server.invoke_successfully(route_obj, True)
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg='Error removing port %s to if_group %s: %s' %
- (port, self.parameters['name'], to_native(error)),
- exception=traceback.format_exc())
-
- def autosupport_log(self):
- results = netapp_utils.get_cserver(self.server)
- cserver = netapp_utils.setup_na_ontap_zapi(module=self.module, vserver=results)
- netapp_utils.ems_log_event("na_ontap_net_ifgrp", cserver)
-
- def apply(self):
- self.autosupport_log()
- current, modify = self.get_if_grp(), None
- cd_action = self.na_helper.get_cd_action(current, self.parameters)
- if cd_action is None and self.parameters['state'] == 'present':
- current_ports = self.get_if_grp_ports()
- modify = self.na_helper.get_modified_attributes(current_ports, self.parameters)
- if self.na_helper.changed:
- if self.module.check_mode:
- pass
- else:
- if cd_action == 'create':
- self.create_if_grp()
- elif cd_action == 'delete':
- self.delete_if_grp()
- elif modify:
- self.modify_ports(current_ports['ports'])
- self.module.exit_json(changed=self.na_helper.changed)
-
-
-def main():
- """
- Creates the NetApp Ontap Net Route object and runs the correct play task
- """
- obj = NetAppOntapIfGrp()
- obj.apply()
-
-
-if __name__ == '__main__':
- main()
diff --git a/lib/ansible/modules/storage/netapp/na_ontap_net_port.py b/lib/ansible/modules/storage/netapp/na_ontap_net_port.py
deleted file mode 100644
index a1631d4bdb..0000000000
--- a/lib/ansible/modules/storage/netapp/na_ontap_net_port.py
+++ /dev/null
@@ -1,226 +0,0 @@
-#!/usr/bin/python
-
-# (c) 2018-2019, NetApp, Inc
-# GNU General Public License v3.0+
-# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-from __future__ import absolute_import, division, print_function
-__metaclass__ = type
-
-
-ANSIBLE_METADATA = {'metadata_version': '1.1',
- 'status': ['preview'],
- 'supported_by': 'certified'}
-
-DOCUMENTATION = """
-module: na_ontap_net_port
-short_description: NetApp ONTAP network ports.
-extends_documentation_fragment:
- - netapp.na_ontap
-version_added: '2.6'
-author: NetApp Ansible Team (@carchi8py) <ng-ansibleteam@netapp.com>
-description:
-- Modify a ONTAP network port.
-options:
- state:
- description:
- - Whether the specified net port should exist or not.
- choices: ['present']
- default: present
- node:
- description:
- - Specifies the name of node.
- required: true
- ports:
- aliases:
- - port
- description:
- - Specifies the name of port(s).
- required: true
- mtu:
- description:
- - Specifies the maximum transmission unit (MTU) reported by the port.
- autonegotiate_admin:
- description:
- - Enables or disables Ethernet auto-negotiation of speed,
- duplex and flow control.
- duplex_admin:
- description:
- - Specifies the user preferred duplex setting of the port.
- - Valid values auto, half, full
- speed_admin:
- description:
- - Specifies the user preferred speed setting of the port.
- flowcontrol_admin:
- description:
- - Specifies the user preferred flow control setting of the port.
- ipspace:
- description:
- - Specifies the port's associated IPspace name.
- - The 'Cluster' ipspace is reserved for cluster ports.
-"""
-
-EXAMPLES = """
- - name: Modify Net Port
- na_ontap_net_port:
- state: present
- username: "{{ netapp_username }}"
- password: "{{ netapp_password }}"
- hostname: "{{ netapp_hostname }}"
- node: "{{ node_name }}"
- ports: e0d,e0c
- autonegotiate_admin: true
-"""
-
-RETURN = """
-
-"""
-import traceback
-
-from ansible.module_utils.basic import AnsibleModule
-from ansible.module_utils._text import to_native
-import ansible.module_utils.netapp as netapp_utils
-from ansible.module_utils.netapp_module import NetAppModule
-
-HAS_NETAPP_LIB = netapp_utils.has_netapp_lib()
-
-
-class NetAppOntapNetPort(object):
- """
- Modify a Net port
- """
-
- def __init__(self):
- """
- Initialize the Ontap Net Port Class
- """
- self.argument_spec = netapp_utils.na_ontap_host_argument_spec()
- self.argument_spec.update(dict(
- state=dict(required=False, choices=['present'], default='present'),
- node=dict(required=True, type="str"),
- ports=dict(required=True, type="list", aliases=['port']),
- mtu=dict(required=False, type="str", default=None),
- autonegotiate_admin=dict(required=False, type="str", default=None),
- duplex_admin=dict(required=False, type="str", default=None),
- speed_admin=dict(required=False, type="str", default=None),
- flowcontrol_admin=dict(required=False, type="str", default=None),
- ipspace=dict(required=False, type="str", default=None),
- ))
-
- self.module = AnsibleModule(
- argument_spec=self.argument_spec,
- supports_check_mode=True
- )
-
- self.na_helper = NetAppModule()
- self.parameters = self.na_helper.set_parameters(self.module.params)
- self.set_playbook_zapi_key_map()
-
- if HAS_NETAPP_LIB is False:
- self.module.fail_json(msg="the python NetApp-Lib module is required")
- else:
- self.server = netapp_utils.setup_na_ontap_zapi(module=self.module)
- return
-
- def set_playbook_zapi_key_map(self):
- self.na_helper.zapi_string_keys = {
- 'mtu': 'mtu',
- 'autonegotiate_admin': 'is-administrative-auto-negotiate',
- 'duplex_admin': 'administrative-duplex',
- 'speed_admin': 'administrative-speed',
- 'flowcontrol_admin': 'administrative-flowcontrol',
- 'ipspace': 'ipspace'
- }
-
- def get_net_port(self, port):
- """
- Return details about the net port
- :param: port: Name of the port
- :return: Dictionary with current state of the port. None if not found.
- :rtype: dict
- """
- net_port_get = netapp_utils.zapi.NaElement('net-port-get-iter')
- attributes = {
- 'query': {
- 'net-port-info': {
- 'node': self.parameters['node'],
- 'port': port
- }
- }
- }
- net_port_get.translate_struct(attributes)
-
- try:
- result = self.server.invoke_successfully(net_port_get, True)
- if result.get_child_by_name('num-records') and int(result.get_child_content('num-records')) >= 1:
- port_info = result['attributes-list']['net-port-info']
- port_details = dict()
- else:
- return None
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg='Error getting net ports for %s: %s' % (self.parameters['node'], to_native(error)),
- exception=traceback.format_exc())
-
- for item_key, zapi_key in self.na_helper.zapi_string_keys.items():
- port_details[item_key] = port_info.get_child_content(zapi_key)
- return port_details
-
- def modify_net_port(self, port, modify):
- """
- Modify a port
-
- :param port: Name of the port
- :param modify: dict with attributes to be modified
- :return: None
- """
- port_modify = netapp_utils.zapi.NaElement('net-port-modify')
- port_attributes = {'node': self.parameters['node'],
- 'port': port}
- for key in modify:
- if key in self.na_helper.zapi_string_keys:
- zapi_key = self.na_helper.zapi_string_keys.get(key)
- port_attributes[zapi_key] = modify[key]
- port_modify.translate_struct(port_attributes)
- try:
- self.server.invoke_successfully(port_modify, enable_tunneling=True)
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg='Error modifying net ports for %s: %s' % (self.parameters['node'], to_native(error)),
- exception=traceback.format_exc())
-
- def autosupport_log(self):
- """
- AutoSupport log for na_ontap_net_port
- :return: None
- """
- results = netapp_utils.get_cserver(self.server)
- cserver = netapp_utils.setup_na_ontap_zapi(module=self.module, vserver=results)
- netapp_utils.ems_log_event("na_ontap_net_port", cserver)
-
- def apply(self):
- """
- Run Module based on play book
- """
-
- self.autosupport_log()
- # Run the task for all ports in the list of 'ports'
- for port in self.parameters['ports']:
- current = self.get_net_port(port)
- modify = self.na_helper.get_modified_attributes(current, self.parameters)
- if self.na_helper.changed:
- if self.module.check_mode:
- pass
- else:
- if modify:
- self.modify_net_port(port, modify)
- self.module.exit_json(changed=self.na_helper.changed)
-
-
-def main():
- """
- Create the NetApp Ontap Net Port Object and modify it
- """
- obj = NetAppOntapNetPort()
- obj.apply()
-
-
-if __name__ == '__main__':
- main()
diff --git a/lib/ansible/modules/storage/netapp/na_ontap_net_routes.py b/lib/ansible/modules/storage/netapp/na_ontap_net_routes.py
deleted file mode 100644
index 76ccb62e49..0000000000
--- a/lib/ansible/modules/storage/netapp/na_ontap_net_routes.py
+++ /dev/null
@@ -1,324 +0,0 @@
-#!/usr/bin/python
-
-# (c) 2018-2019, NetApp, Inc
-# GNU General Public License v3.0+
-# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-
-from __future__ import absolute_import, division, print_function
-__metaclass__ = type
-
-ANSIBLE_METADATA = {'metadata_version': '1.1',
- 'status': ['preview'],
- 'supported_by': 'certified'}
-
-DOCUMENTATION = '''
-module: na_ontap_net_routes
-short_description: NetApp ONTAP network routes
-extends_documentation_fragment:
- - netapp.na_ontap
-version_added: '2.6'
-author: NetApp Ansible Team (@carchi8py) <ng-ansibleteam@netapp.com>
-description:
-- Modify ONTAP network routes.
-options:
- state:
- description:
- - Whether you want to create or delete a network route.
- choices: ['present', 'absent']
- default: present
- vserver:
- description:
- - The name of the vserver.
- required: true
- destination:
- description:
- - Specify the route destination.
- - Example 10.7.125.5/20, fd20:13::/64.
- required: true
- gateway:
- description:
- - Specify the route gateway.
- - Example 10.7.125.1, fd20:13::1.
- required: true
- metric:
- description:
- - Specify the route metric.
- - If this field is not provided the default will be set to 20.
- from_destination:
- description:
- - Specify the route destination that should be changed.
- - new_destination was removed to fix idempotency issues. To rename destination the original goes to from_destination and the new goes to destination.
- version_added: '2.8'
- from_gateway:
- description:
- - Specify the route gateway that should be changed.
- version_added: '2.8'
- from_metric:
- description:
- - Specify the route metric that should be changed.
- version_added: '2.8'
-'''
-
-EXAMPLES = """
- - name: create route
- na_ontap_net_routes:
- state: present
- vserver: "{{ Vserver name }}"
- username: "{{ netapp_username }}"
- password: "{{ netapp_password }}"
- hostname: "{{ netapp_hostname }}"
- destination: 10.7.125.5/20
- gateway: 10.7.125.1
- metric: 30
-"""
-
-RETURN = """
-
-"""
-
-import traceback
-
-from ansible.module_utils.basic import AnsibleModule
-from ansible.module_utils._text import to_native
-import ansible.module_utils.netapp as netapp_utils
-from ansible.module_utils.netapp_module import NetAppModule
-
-HAS_NETAPP_LIB = netapp_utils.has_netapp_lib()
-
-
-class NetAppOntapNetRoutes(object):
- """
- Create, Modifies and Destroys a Net Route
- """
-
- def __init__(self):
- """
- Initialize the Ontap Net Route class
- """
- self.argument_spec = netapp_utils.na_ontap_host_argument_spec()
- self.argument_spec.update(dict(
- state=dict(required=False, choices=[
- 'present', 'absent'], default='present'),
- vserver=dict(required=True, type='str'),
- destination=dict(required=True, type='str'),
- gateway=dict(required=True, type='str'),
- metric=dict(required=False, type='str'),
- from_destination=dict(required=False, type='str', default=None),
- from_gateway=dict(required=False, type='str', default=None),
- from_metric=dict(required=False, type='str', default=None),
- ))
-
- self.module = AnsibleModule(
- argument_spec=self.argument_spec,
- supports_check_mode=True
- )
-
- self.na_helper = NetAppModule()
- self.parameters = self.na_helper.set_parameters(self.module.params)
-
- if HAS_NETAPP_LIB is False:
- self.module.fail_json(msg="the python NetApp-Lib module is required")
- else:
- self.server = netapp_utils.setup_na_ontap_zapi(module=self.module, vserver=self.parameters['vserver'])
- return
-
- def create_net_route(self, current_metric=None):
- """
- Creates a new Route
- """
- route_obj = netapp_utils.zapi.NaElement('net-routes-create')
- route_obj.add_new_child("destination", self.parameters['destination'])
- route_obj.add_new_child("gateway", self.parameters['gateway'])
- if current_metric is None and self.parameters.get('metric') is not None:
- metric = self.parameters['metric']
- else:
- metric = current_metric
- # Metric can be None, Can't set metric to none
- if metric is not None:
- route_obj.add_new_child("metric", metric)
- try:
- self.server.invoke_successfully(route_obj, True)
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg='Error creating net route: %s'
- % (to_native(error)),
- exception=traceback.format_exc())
-
- def delete_net_route(self, params=None):
- """
- Deletes a given Route
- """
- route_obj = netapp_utils.zapi.NaElement('net-routes-destroy')
- if params is None:
- params = self.parameters
- route_obj.add_new_child("destination", params['destination'])
- route_obj.add_new_child("gateway", params['gateway'])
- try:
- self.server.invoke_successfully(route_obj, True)
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg='Error deleting net route: %s'
- % (to_native(error)),
- exception=traceback.format_exc())
-
- def modify_net_route(self, current, desired):
- """
- Modify a net route
- """
- # return if there is nothing to change
- for key, val in desired.items():
- if val != current[key]:
- self.na_helper.changed = True
- break
- if not self.na_helper.changed:
- return
- # delete and re-create with new params
- self.delete_net_route(current)
- route_obj = netapp_utils.zapi.NaElement('net-routes-create')
- for attribute in ['metric', 'destination', 'gateway']:
- if desired.get(attribute) is not None:
- value = desired[attribute]
- else:
- value = current[attribute]
- route_obj.add_new_child(attribute, value)
- try:
- result = self.server.invoke_successfully(route_obj, True)
- except netapp_utils.zapi.NaApiError as error:
- # restore the old route, create the route with the existing metric
- self.create_net_route(current['metric'])
- # return if desired route already exists
- if to_native(error.code) == '13001':
- return
- # Invalid value specified for any of the attributes
- self.module.fail_json(msg='Error modifying net route: %s'
- % (to_native(error)),
- exception=traceback.format_exc())
-
- def get_net_route(self, params=None):
- """
- Checks to see if a route exist or not
- :return: NaElement object if a route exists, None otherwise
- """
- if params is not None:
- # we need at least on of the new_destination or new_gateway to fetch desired route
- if params.get('destination') is None and params.get('gateway') is None:
- return None
- current = None
- route_obj = netapp_utils.zapi.NaElement('net-routes-get')
- for attr in ['destination', 'gateway']:
- if params and params.get(attr) is not None:
- value = params[attr]
- else:
- value = self.parameters[attr]
- route_obj.add_new_child(attr, value)
- try:
- result = self.server.invoke_successfully(route_obj, True)
- if result.get_child_by_name('attributes') is not None:
- route_info = result.get_child_by_name('attributes').get_child_by_name('net-vs-routes-info')
- current = {
- 'destination': route_info.get_child_content('destination'),
- 'gateway': route_info.get_child_content('gateway'),
- 'metric': route_info.get_child_content('metric')
- }
-
- except netapp_utils.zapi.NaApiError as error:
- # Error 13040 denotes a route doesn't exist.
- if to_native(error.code) == "15661":
- return None
- self.module.fail_json(msg='Error fetching net route: %s'
- % (to_native(error)),
- exception=traceback.format_exc())
- return current
-
- def is_modify_action(self, current, desired):
- """
- Get desired action to be applied for net routes
- Destination and gateway are unique params for a route and cannot be duplicated
- So if a route with desired destination or gateway exists already, we don't try to modify
- :param current: current details
- :param desired: desired details
- :return: create / delete / modify / None
- """
- if current is None and desired is None:
- # this is invalid
- # cannot modify a non existent resource
- return None
- if current is None and desired is not None:
- # idempotency or duplication
- # we need not create
- return False
- if current is not None and desired is not None:
- # we can't modify an ambiguous route (idempotency/duplication)
- return False
- return True
-
- def get_params_to_be_modified(self, current):
- """
- Get parameters and values that need to be modified
- :param current: current details
- :return: dict(), None
- """
- if current is None:
- return None
- desired = dict()
- if self.parameters.get('new_destination') is not None and \
- self.parameters['new_destination'] != current['destination']:
- desired['destination'] = self.parameters['new_destination']
- if self.parameters.get('new_gateway') is not None and \
- self.parameters['new_gateway'] != current['gateway']:
- desired['gateway'] = self.parameters['new_gateway']
- if self.parameters.get('new_metric') is not None and \
- self.parameters['new_metric'] != current['metric']:
- desired['metric'] = self.parameters['new_metric']
- return desired
-
- def apply(self):
- """
- Run Module based on play book
- """
- netapp_utils.ems_log_event("na_ontap_net_routes", self.server)
- current = self.get_net_route()
- modify, cd_action = None, None
- modify_params = {'destination': self.parameters.get('from_destination'),
- 'gateway': self.parameters.get('from_gateway'),
- 'metric': self.parameters.get('from_metric')}
- # if any from_* param is present in playbook, check for modify action
- if any(modify_params.values()):
- # destination and gateway combination is unique, and is considered like a id. so modify destination
- # or gateway is considered a rename action. metric is considered an attribute of the route so it is
- # considered as modify.
- if modify_params.get('metric') is not None:
- modify = True
- old_params = current
- else:
- # get parameters that are eligible for modify
- old_params = self.get_net_route(modify_params)
- modify = self.na_helper.is_rename_action(old_params, current)
- if modify is None:
- self.module.fail_json(msg="Error modifying: route %s does not exist" % self.parameters['from_destination'])
- else:
- cd_action = self.na_helper.get_cd_action(current, self.parameters)
-
- if cd_action == 'create':
- self.create_net_route()
- elif cd_action == 'delete':
- self.delete_net_route()
- elif modify:
- desired = {}
- for key, value in old_params.items():
- desired[key] = value
- for key, value in modify_params.items():
- if value is not None:
- desired[key] = self.parameters.get(key)
- self.modify_net_route(old_params, desired)
- self.module.exit_json(changed=self.na_helper.changed)
-
-
-def main():
- """
- Creates the NetApp Ontap Net Route object and runs the correct play task
- """
- obj = NetAppOntapNetRoutes()
- obj.apply()
-
-
-if __name__ == '__main__':
- main()
diff --git a/lib/ansible/modules/storage/netapp/na_ontap_net_subnet.py b/lib/ansible/modules/storage/netapp/na_ontap_net_subnet.py
deleted file mode 100644
index 96b50f7905..0000000000
--- a/lib/ansible/modules/storage/netapp/na_ontap_net_subnet.py
+++ /dev/null
@@ -1,326 +0,0 @@
-#!/usr/bin/python
-
-# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-
-from __future__ import absolute_import, division, print_function
-__metaclass__ = type
-
-
-ANSIBLE_METADATA = {'metadata_version': '1.1',
- 'status': ['preview'],
- 'supported_by': 'certified'}
-
-DOCUMENTATION = """
-module: na_ontap_net_subnet
-short_description: NetApp ONTAP Create, delete, modify network subnets.
-extends_documentation_fragment:
- - netapp.na_ontap
-version_added: '2.8'
-author: Storage Engineering (@Albinpopote) <ansible@black-perl.fr>
-description:
-- Create, modify, destroy the network subnet
-options:
- state:
- description:
- - Whether the specified network interface group should exist or not.
- choices: ['present', 'absent']
- default: present
-
- broadcast_domain:
- description:
- - Specify the required broadcast_domain name for the subnet.
- - A broadcast domain can not be modified after the subnet has been created
- required: true
-
- name:
- description:
- - Specify the subnet name.
- required: true
-
- from_name:
- description:
- - Name of the subnet to be renamed
-
- gateway:
- description:
- - Specify the gateway for the default route of the subnet.
-
- ipspace:
- description:
- - Specify the ipspace for the subnet.
- - The default value for this parameter is the default IPspace, named 'Default'.
-
- ip_ranges:
- description:
- - Specify the list of IP address ranges associated with the subnet.
-
- subnet:
- description:
- - Specify the subnet (ip and mask).
- required: true
-"""
-
-EXAMPLES = """
- - name: create subnet
- na_ontap_net_subnet:
- state: present
- username: "{{ netapp_username }}"
- password: "{{ netapp_password }}"
- hostname: "{{ netapp_hostname }}"
- subnet: 10.10.10.0/24
- name: subnet-adm
- ip_ranges: [ '10.10.10.30-10.10.10.40', '10.10.10.51' ]
- gateway: 10.10.10.254
- ipspace: Default
- broadcast_domain: Default
- - name: delete subnet
- na_ontap_net_subnet:
- state: absent
- username: "{{ netapp_username }}"
- password: "{{ netapp_password }}"
- hostname: "{{ netapp_hostname }}"
- name: subnet-adm
- ipspace: Default
- - name: rename subnet
- na_ontap_net_subnet:
- state: present
- username: "{{ netapp_username }}"
- password: "{{ netapp_password }}"
- hostname: "{{ netapp_hostname }}"
- name: subnet-adm-new
- from_name: subnet-adm
- ipspace: Default
-"""
-
-RETURN = """
-
-"""
-
-import traceback
-
-from ansible.module_utils.basic import AnsibleModule
-from ansible.module_utils._text import to_native
-import ansible.module_utils.netapp as netapp_utils
-from ansible.module_utils.netapp_module import NetAppModule
-
-HAS_NETAPP_LIB = netapp_utils.has_netapp_lib()
-
-
-class NetAppOntapSubnet(object):
- """
- Create, Modifies and Destroys a subnet
- """
- def __init__(self):
- """
- Initialize the ONTAP Subnet class
- """
- self.argument_spec = netapp_utils.na_ontap_host_argument_spec()
- self.argument_spec.update(dict(
- state=dict(required=False, choices=['present', 'absent'], default='present'),
- name=dict(required=True, type='str'),
- from_name=dict(required=False, type='str'),
- broadcast_domain=dict(required=False, type='str'),
- gateway=dict(required=False, type='str'),
- ip_ranges=dict(required=False, type=list),
- ipspace=dict(required=False, type='str'),
- subnet=dict(required=False, type='str')
- ))
-
- self.module = AnsibleModule(
- argument_spec=self.argument_spec,
- supports_check_mode=True
- )
- self.na_helper = NetAppModule()
- self.parameters = self.na_helper.set_parameters(self.module.params)
-
- if HAS_NETAPP_LIB is False:
- self.module.fail_json(msg="the python NetApp-Lib module is required")
- else:
- self.server = netapp_utils.setup_na_ontap_zapi(module=self.module)
- return
-
- def get_subnet(self, name=None):
- """
- Return details about the subnet
- :param:
- name : Name of the subnet
- :return: Details about the subnet. None if not found.
- :rtype: dict
- """
- if name is None:
- name = self.parameters.get('name')
-
- subnet_iter = netapp_utils.zapi.NaElement('net-subnet-get-iter')
- subnet_info = netapp_utils.zapi.NaElement('net-subnet-info')
- subnet_info.add_new_child('subnet-name', name)
-
- query = netapp_utils.zapi.NaElement('query')
- query.add_child_elem(subnet_info)
-
- subnet_iter.add_child_elem(query)
-
- result = self.server.invoke_successfully(subnet_iter, True)
-
- return_value = None
- # check if query returns the expected subnet
- if result.get_child_by_name('num-records') and \
- int(result.get_child_content('num-records')) == 1:
-
- subnet_attributes = result.get_child_by_name('attributes-list').get_child_by_name('net-subnet-info')
- broadcast_domain = subnet_attributes.get_child_content('broadcast-domain')
- gateway = subnet_attributes.get_child_content('gateway')
- ipspace = subnet_attributes.get_child_content('ipspace')
- subnet = subnet_attributes.get_child_content('subnet')
- name = subnet_attributes.get_child_content('subnet-name')
-
- ip_ranges = []
- range_obj = subnet_attributes.get_child_by_name('ip-ranges').get_children()
- for elem in range_obj:
- ip_ranges.append(elem.get_content())
-
- return_value = {
- 'name': name,
- 'broadcast_domain': broadcast_domain,
- 'gateway': gateway,
- 'ip_ranges': ip_ranges,
- 'ipspace': ipspace,
- 'subnet': subnet
- }
-
- return return_value
-
- def create_subnet(self):
- """
- Creates a new subnet
- """
- options = {'subnet-name': self.parameters.get('name'),
- 'broadcast-domain': self.parameters.get('broadcast_domain'),
- 'subnet': self.parameters.get('subnet')}
- subnet_create = netapp_utils.zapi.NaElement.create_node_with_children(
- 'net-subnet-create', **options)
-
- if self.parameters.get('gateway'):
- subnet_create.add_new_child('gateway', self.parameters.get('gateway'))
- if self.parameters.get('ip_ranges'):
- subnet_ips = netapp_utils.zapi.NaElement('ip-ranges')
- subnet_create.add_child_elem(subnet_ips)
- for ip_range in self.parameters.get('ip_ranges'):
- subnet_ips.add_new_child('ip-range', ip_range)
- if self.parameters.get('ipspace'):
- subnet_create.add_new_child('ipspace', self.parameters.get('ipspace'))
-
- try:
- self.server.invoke_successfully(subnet_create, True)
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg='Error creating subnet %s: %s' % (self.parameters.get('name'), to_native(error)),
- exception=traceback.format_exc())
-
- def delete_subnet(self):
- """
- Deletes a subnet
- """
- subnet_delete = netapp_utils.zapi.NaElement.create_node_with_children(
- 'net-subnet-destroy', **{'subnet-name': self.parameters.get('name')})
-
- try:
- self.server.invoke_successfully(subnet_delete, True)
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg='Error deleting subnet %s: %s' % (self.parameters.get('name'), to_native(error)),
- exception=traceback.format_exc())
-
- def modify_subnet(self):
- """
- Modifies a subnet
- """
- options = {'subnet-name': self.parameters.get('name')}
-
- subnet_modify = netapp_utils.zapi.NaElement.create_node_with_children(
- 'net-subnet-modify', **options)
-
- if self.parameters.get('gateway'):
- subnet_modify.add_new_child('gateway', self.parameters.get('gateway'))
- if self.parameters.get('ip_ranges'):
- subnet_ips = netapp_utils.zapi.NaElement('ip-ranges')
- subnet_modify.add_child_elem(subnet_ips)
- for ip_range in self.parameters.get('ip_ranges'):
- subnet_ips.add_new_child('ip-range', ip_range)
- if self.parameters.get('ipspace'):
- subnet_modify.add_new_child('ipspace', self.parameters.get('ipspace'))
- if self.parameters.get('subnet'):
- subnet_modify.add_new_child('subnet', self.parameters.get('subnet'))
-
- try:
- self.server.invoke_successfully(subnet_modify, True)
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg='Error modifying subnet %s: %s' % (self.parameters.get('name'), to_native(error)),
- exception=traceback.format_exc())
-
- def rename_subnet(self):
- """
- TODO
- """
- options = {'subnet-name': self.parameters.get('from_name'),
- 'new-name': self.parameters.get('name')}
-
- subnet_rename = netapp_utils.zapi.NaElement.create_node_with_children(
- 'net-subnet-rename', **options)
-
- if self.parameters.get('ipspace'):
- subnet_rename.add_new_child('ipspace', self.parameters.get('ipspace'))
-
- try:
- self.server.invoke_successfully(subnet_rename, True)
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg='Error renaming subnet %s: %s' % (self.parameters.get('name'), to_native(error)),
- exception=traceback.format_exc())
-
- def apply(self):
- '''Apply action to subnet'''
- results = netapp_utils.get_cserver(self.server)
- cserver = netapp_utils.setup_na_ontap_zapi(module=self.module, vserver=results)
- netapp_utils.ems_log_event("na_ontap_net_subnet", cserver)
- current = self.get_subnet()
- cd_action, rename = None, None
-
- if self.parameters.get('from_name'):
- rename = self.na_helper.is_rename_action(self.get_subnet(self.parameters.get('from_name')), current)
- if rename is False:
- self.module.fail_json(msg="Error renaming: subnet %s does not exist" %
- self.parameters.get('from_name'))
- else:
- cd_action = self.na_helper.get_cd_action(current, self.parameters)
-
- modify = self.na_helper.get_modified_attributes(current, self.parameters)
- for attribute in modify:
- if attribute in ['broadcast_domain']:
- self.module.fail_json(msg='Error modifying subnet %s: cannot modify broadcast_domain parameter.' % self.parameters.get('name'))
-
- if self.na_helper.changed:
- if self.module.check_mode:
- pass
- else:
- if rename:
- self.rename_subnet()
- # If rename is True, cd_action is NOne but modify could be true
- if cd_action == 'create':
- for attribute in ['subnet', 'broadcast_domain']:
- if not self.parameters.get(attribute):
- self.module.fail_json(msg='Error - missing required arguments: %s.' % attribute)
- self.create_subnet()
- elif cd_action == 'delete':
- self.delete_subnet()
- elif modify:
- self.modify_subnet()
- self.module.exit_json(changed=self.na_helper.changed)
-
-
-def main():
- """
- Creates the NetApp ONTAP Net Route object and runs the correct play task
- """
- subnet_obj = NetAppOntapSubnet()
- subnet_obj.apply()
-
-
-if __name__ == '__main__':
- main()
diff --git a/lib/ansible/modules/storage/netapp/na_ontap_net_vlan.py b/lib/ansible/modules/storage/netapp/na_ontap_net_vlan.py
deleted file mode 100644
index 432b608ad9..0000000000
--- a/lib/ansible/modules/storage/netapp/na_ontap_net_vlan.py
+++ /dev/null
@@ -1,186 +0,0 @@
-#!/usr/bin/python
-
-# (c) 2018-2019, NetApp, Inc
-# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-
-from __future__ import absolute_import, division, print_function
-__metaclass__ = type
-
-ANSIBLE_METADATA = {'metadata_version': '1.1',
- 'status': ['preview'],
- 'supported_by': 'certified'}
-
-DOCUMENTATION = '''
-module: na_ontap_net_vlan
-short_description: NetApp ONTAP network VLAN
-extends_documentation_fragment:
- - netapp.na_ontap
-version_added: '2.6'
-author: NetApp Ansible Team (@carchi8py) <ng-ansibleteam@netapp.com>
-description:
-- Create or Delete a network VLAN
-options:
- state:
- description:
- - Whether the specified network VLAN should exist or not
- choices: ['present', 'absent']
- default: present
- parent_interface:
- description:
- - The interface that hosts the VLAN interface.
- required: true
- vlanid:
- description:
- - The VLAN id. Ranges from 1 to 4094.
- required: true
- node:
- description:
- - Node name of VLAN interface.
- required: true
-notes:
- - The C(interface_name) option has been removed and should be deleted from playbooks
-'''
-
-EXAMPLES = """
- - name: create VLAN
- na_ontap_net_vlan:
- state: present
- vlanid: 13
- node: "{{ vlan node }}"
- parent_interface: "{{ vlan parent interface name }}"
- username: "{{ netapp_username }}"
- password: "{{ netapp_password }}"
- hostname: "{{ netapp_hostname }}"
-"""
-
-RETURN = """
-
-"""
-
-from ansible.module_utils.basic import AnsibleModule
-import ansible.module_utils.netapp as netapp_utils
-
-HAS_NETAPP_LIB = netapp_utils.has_netapp_lib()
-
-
-class NetAppOntapVlan(object):
- """
- Created, and destorys Net Vlans's
- """
- def __init__(self):
- """
- Initializes the NetAppOntapVlan function
- """
- self.argument_spec = netapp_utils.na_ontap_host_argument_spec()
- self.argument_spec.update(dict(
- state=dict(required=False, choices=['present', 'absent'], default='present'),
- parent_interface=dict(required=True, type='str'),
- vlanid=dict(required=True, type='str'),
- node=dict(required=True, type='str'),
- ))
-
- self.module = AnsibleModule(
- argument_spec=self.argument_spec,
- supports_check_mode=True
- )
- p = self.module.params
-
- # set up state variables
- self.state = p['state']
- self.parent_interface = p['parent_interface']
- self.vlanid = p['vlanid']
- self.node = p['node']
- self.interface_name = str(p['parent_interface']) + '-' + str(self.vlanid)
-
- if HAS_NETAPP_LIB is False:
- self.module.fail_json(msg="the python NetApp-Lib module is required")
- else:
- self.server = netapp_utils.setup_na_ontap_zapi(module=self.module)
- return
-
- def create_vlan(self):
- """
- Creates a new vlan
- """
- vlan_obj = netapp_utils.zapi.NaElement("net-vlan-create")
- vlan_info = self.create_vlan_info()
-
- vlan_obj.add_child_elem(vlan_info)
- self.server.invoke_successfully(vlan_obj, True)
-
- def delete_vlan(self):
- """
- Deletes a vland
- """
- vlan_obj = netapp_utils.zapi.NaElement("net-vlan-delete")
- vlan_info = self.create_vlan_info()
-
- vlan_obj.add_child_elem(vlan_info)
- self.server.invoke_successfully(vlan_obj, True)
-
- def does_vlan_exist(self):
- """
- Checks to see if a vlan already exists or not
- :return: Returns True if the vlan exists, false if it doesn't
- """
- vlan_obj = netapp_utils.zapi.NaElement("net-vlan-get")
- vlan_obj.add_new_child("interface-name", self.interface_name)
- vlan_obj.add_new_child("node", self.node)
- try:
- result = self.server.invoke_successfully(vlan_obj, True)
- result.get_child_by_name("attributes").get_child_by_name("vlan-info").get_child_by_name("interface-name")
- except netapp_utils.zapi.NaApiError:
- return False
- return True
-
- def create_vlan_info(self):
- """
- Create a vlan_info object to be used in a create/delete
- :return:
- """
- vlan_info = netapp_utils.zapi.NaElement("vlan-info")
-
- # set up the vlan_info object:
- vlan_info.add_new_child("parent-interface", self.parent_interface)
- vlan_info.add_new_child("vlanid", self.vlanid)
- vlan_info.add_new_child("node", self.node)
- return vlan_info
-
- def apply(self):
- """
- check the option in the playbook to see what needs to be done
- :return:
- """
- changed = False
- result = None
- results = netapp_utils.get_cserver(self.server)
- cserver = netapp_utils.setup_na_ontap_zapi(module=self.module, vserver=results)
- netapp_utils.ems_log_event("na_ontap_net_vlan", cserver)
- existing_vlan = self.does_vlan_exist()
- if existing_vlan:
- if self.state == 'absent': # delete
- changed = True
- else:
- if self.state == 'present': # create
- changed = True
- if changed:
- if self.module.check_mode:
- pass
- else:
- if self.state == 'present':
- self.create_vlan()
- elif self.state == 'absent':
- self.delete_vlan()
- self.module.exit_json(changed=changed, meta=result)
-
-
-def main():
- """
- Creates the NetApp Ontap vlan object, and runs the correct play task.
- """
- v = NetAppOntapVlan()
- v.apply()
-
-
-if __name__ == '__main__':
- main()
diff --git a/lib/ansible/modules/storage/netapp/na_ontap_nfs.py b/lib/ansible/modules/storage/netapp/na_ontap_nfs.py
deleted file mode 100644
index e691ff6e95..0000000000
--- a/lib/ansible/modules/storage/netapp/na_ontap_nfs.py
+++ /dev/null
@@ -1,576 +0,0 @@
-#!/usr/bin/python
-
-# (c) 2018-2019, NetApp, Inc
-# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-
-from __future__ import absolute_import, division, print_function
-__metaclass__ = type
-
-
-ANSIBLE_METADATA = {'metadata_version': '1.1',
- 'status': ['preview'],
- 'supported_by': 'certified'}
-
-
-DOCUMENTATION = """
-module: na_ontap_nfs
-short_description: NetApp ONTAP NFS status
-extends_documentation_fragment:
- - netapp.na_ontap
-version_added: '2.6'
-author: NetApp Ansible Team (@carchi8py) <ng-ansibleteam@netapp.com>
-description:
-- Enable or disable NFS on ONTAP
-options:
- state:
- description:
- - Whether NFS should exist or not.
- choices: ['present', 'absent']
- default: present
- service_state:
- description:
- - Whether the specified NFS should be enabled or disabled. Creates NFS service if does not exist.
- choices: ['started', 'stopped']
- vserver:
- description:
- - Name of the vserver to use.
- required: true
- nfsv3:
- description:
- - status of NFSv3.
- choices: ['enabled', 'disabled']
- nfsv3_fsid_change:
- description:
- - status of if NFSv3 clients see change in FSID as they traverse filesystems.
- choices: ['enabled', 'disabled']
- version_added: '2.7'
- nfsv4_fsid_change:
- description:
- - status of if NFSv4 clients see change in FSID as they traverse filesystems.
- choices: ['enabled', 'disabled']
- version_added: '2.9'
- nfsv4:
- description:
- - status of NFSv4.
- choices: ['enabled', 'disabled']
- nfsv41:
- description:
- - status of NFSv41.
- aliases: ['nfsv4.1']
- choices: ['enabled', 'disabled']
- nfsv41_pnfs:
- description:
- - status of NFSv41 pNFS.
- choices: ['enabled', 'disabled']
- version_added: '2.9'
- nfsv4_numeric_ids:
- description:
- - status of NFSv4 numeric ID's.
- choices: ['enabled', 'disabled']
- version_added: '2.9'
- vstorage_state:
- description:
- - status of vstorage_state.
- choices: ['enabled', 'disabled']
- nfsv4_id_domain:
- description:
- - Name of the nfsv4_id_domain to use.
- nfsv40_acl:
- description:
- - status of NFS v4.0 ACL feature
- choices: ['enabled', 'disabled']
- version_added: '2.7'
- nfsv40_read_delegation:
- description:
- - status for NFS v4.0 read delegation feature.
- choices: ['enabled', 'disabled']
- version_added: '2.7'
- nfsv40_write_delegation:
- description:
- - status for NFS v4.0 write delegation feature.
- choices: ['enabled', 'disabled']
- version_added: '2.7'
- nfsv41_acl:
- description:
- - status of NFS v4.1 ACL feature
- choices: ['enabled', 'disabled']
- version_added: '2.7'
- nfsv41_read_delegation:
- description:
- - status for NFS v4.1 read delegation feature.
- choices: ['enabled', 'disabled']
- version_added: '2.7'
- nfsv41_write_delegation:
- description:
- - status for NFS v4.1 write delegation feature.
- choices: ['enabled', 'disabled']
- version_added: '2.7'
- nfsv40_referrals:
- description:
- - status for NFS v4.0 referrals.
- choices: ['enabled', 'disabled']
- version_added: '2.9'
- nfsv41_referrals:
- description:
- - status for NFS v4.1 referrals.
- choices: ['enabled', 'disabled']
- version_added: '2.9'
- tcp:
- description:
- - Enable TCP (support from ONTAP 9.3 onward).
- choices: ['enabled', 'disabled']
- udp:
- description:
- - Enable UDP (support from ONTAP 9.3 onward).
- choices: ['enabled', 'disabled']
- showmount:
- description:
- - Whether SVM allows showmount
- choices: ['enabled', 'disabled']
- version_added: '2.7'
- tcp_max_xfer_size:
- description:
- - TCP Maximum Transfer Size (bytes). The default value is 65536.
- version_added: '2.8'
- type: int
-
-"""
-
-EXAMPLES = """
- - name: change nfs status
- na_ontap_nfs:
- state: present
- service_state: stopped
- vserver: vs_hack
- nfsv3: disabled
- nfsv4: disabled
- nfsv41: enabled
- tcp: disabled
- udp: disabled
- vstorage_state: disabled
- nfsv4_id_domain: example.com
- hostname: "{{ netapp_hostname }}"
- username: "{{ netapp_username }}"
- password: "{{ netapp_password }}"
-"""
-
-RETURN = """
-"""
-import traceback
-
-from ansible.module_utils.basic import AnsibleModule
-from ansible.module_utils._text import to_native
-import ansible.module_utils.netapp as netapp_utils
-
-
-HAS_NETAPP_LIB = netapp_utils.has_netapp_lib()
-
-
-class NetAppONTAPNFS(object):
- """ object initialize and class methods """
-
- def __init__(self):
-
- self.argument_spec = netapp_utils.na_ontap_host_argument_spec()
- self.argument_spec.update(dict(
- state=dict(required=False, type='str', choices=['present', 'absent'], default='present'),
- service_state=dict(required=False, choices=['started', 'stopped']),
- vserver=dict(required=True, type='str'),
- nfsv3=dict(required=False, default=None, choices=['enabled', 'disabled']),
- nfsv3_fsid_change=dict(required=False, default=None, choices=['enabled', 'disabled']),
- nfsv4_fsid_change=dict(required=False, default=None, choices=['enabled', 'disabled']),
- nfsv4=dict(required=False, default=None, choices=['enabled', 'disabled']),
- nfsv41=dict(required=False, default=None, choices=['enabled', 'disabled'], aliases=['nfsv4.1']),
- nfsv41_pnfs=dict(required=False, default=None, choices=['enabled', 'disabled']),
- nfsv4_numeric_ids=dict(required=False, default=None, choices=['enabled', 'disabled']),
- vstorage_state=dict(required=False, default=None, choices=['enabled', 'disabled']),
- tcp=dict(required=False, default=None, choices=['enabled', 'disabled']),
- udp=dict(required=False, default=None, choices=['enabled', 'disabled']),
- nfsv4_id_domain=dict(required=False, type='str', default=None),
- nfsv40_acl=dict(required=False, default=None, choices=['enabled', 'disabled']),
- nfsv40_read_delegation=dict(required=False, default=None, choices=['enabled', 'disabled']),
- nfsv40_referrals=dict(required=False, default=None, choices=['enabled', 'disabled']),
- nfsv40_write_delegation=dict(required=False, default=None, choices=['enabled', 'disabled']),
- nfsv41_acl=dict(required=False, default=None, choices=['enabled', 'disabled']),
- nfsv41_read_delegation=dict(required=False, default=None, choices=['enabled', 'disabled']),
- nfsv41_referrals=dict(required=False, default=None, choices=['enabled', 'disabled']),
- nfsv41_write_delegation=dict(required=False, default=None, choices=['enabled', 'disabled']),
- showmount=dict(required=False, default=None, choices=['enabled', 'disabled']),
- tcp_max_xfer_size=dict(required=False, default=None, type='int')
- ))
-
- self.module = AnsibleModule(
- argument_spec=self.argument_spec,
- supports_check_mode=True
- )
-
- parameters = self.module.params
-
- # set up service_state variables
- self.state = parameters['state']
- self.service_state = parameters['service_state']
- self.vserver = parameters['vserver']
- self.nfsv3 = parameters['nfsv3']
- self.nfsv3_fsid_change = parameters['nfsv3_fsid_change']
- self.nfsv4_fsid_change = parameters['nfsv4_fsid_change']
- self.nfsv4 = parameters['nfsv4']
- self.nfsv41 = parameters['nfsv41']
- self.vstorage_state = parameters['vstorage_state']
- self.nfsv4_id_domain = parameters['nfsv4_id_domain']
- self.udp = parameters['udp']
- self.tcp = parameters['tcp']
- self.nfsv40_acl = parameters['nfsv40_acl']
- self.nfsv40_read_delegation = parameters['nfsv40_read_delegation']
- self.nfsv40_referrals = parameters['nfsv40_referrals']
- self.nfsv40_write_delegation = parameters['nfsv40_write_delegation']
- self.nfsv41_acl = parameters['nfsv41_acl']
- self.nfsv41_read_delegation = parameters['nfsv41_read_delegation']
- self.nfsv41_referrals = parameters['nfsv41_referrals']
- self.nfsv41_write_delegation = parameters['nfsv41_write_delegation']
- self.nfsv41_pnfs = parameters['nfsv41_pnfs']
- self.nfsv4_numeric_ids = parameters['nfsv4_numeric_ids']
- self.showmount = parameters['showmount']
- self.tcp_max_xfer_size = parameters['tcp_max_xfer_size']
-
- if HAS_NETAPP_LIB is False:
- self.module.fail_json(msg="the python NetApp-Lib module is required")
- else:
- self.server = netapp_utils.setup_na_ontap_zapi(module=self.module, vserver=self.vserver)
-
- def get_nfs_service(self):
- """
- Return details about nfs
- :param:
- name : name of the vserver
- :return: Details about nfs. None if not found.
- :rtype: dict
- """
- nfs_get_iter = netapp_utils.zapi.NaElement('nfs-service-get-iter')
- nfs_info = netapp_utils.zapi.NaElement('nfs-info')
- nfs_info.add_new_child('vserver', self.vserver)
- query = netapp_utils.zapi.NaElement('query')
- query.add_child_elem(nfs_info)
- nfs_get_iter.add_child_elem(query)
- result = self.server.invoke_successfully(nfs_get_iter, True)
- nfs_details = None
- # check if job exists
- if result.get_child_by_name('num-records') and \
- int(result.get_child_content('num-records')) >= 1:
- attributes_list = result.get_child_by_name('attributes-list').get_child_by_name('nfs-info')
- is_nfsv3_enabled = attributes_list.get_child_content('is-nfsv3-enabled')
- is_nfsv3_fsid_change_enabled = attributes_list.get_child_content('is-nfsv3-fsid-change-enabled')
- is_nfsv4_fsid_change_enabled = attributes_list.get_child_content('is-nfsv4-fsid-change-enabled')
- is_nfsv40_enabled = attributes_list.get_child_content('is-nfsv40-enabled')
- is_nfsv41_enabled = attributes_list.get_child_content('is-nfsv41-enabled')
- is_vstorage_enabled = attributes_list.get_child_content('is-vstorage-enabled')
- nfsv4_id_domain_value = attributes_list.get_child_content('nfsv4-id-domain')
- is_tcp_enabled = attributes_list.get_child_content('is-tcp-enabled')
- is_udp_enabled = attributes_list.get_child_content('is-udp-enabled')
- is_nfsv40_acl_enabled = attributes_list.get_child_content('is-nfsv40-acl-enabled')
- is_nfsv40_write_delegation_enabled = attributes_list.get_child_content('is-nfsv40-write-delegation-enabled')
- is_nfsv40_read_delegation_enabled = attributes_list.get_child_content('is-nfsv40-read-delegation-enabled')
- is_nfsv40_referrals_enabled = attributes_list.get_child_content('is-nfsv40-referrals-enabled')
- is_nfsv41_acl_enabled = attributes_list.get_child_content('is-nfsv41-acl-enabled')
- is_nfsv41_write_delegation_enabled = attributes_list.get_child_content('is-nfsv41-write-delegation-enabled')
- is_nfsv41_read_delegation_enabled = attributes_list.get_child_content('is-nfsv41-read-delegation-enabled')
- is_nfsv41_referrals_enabled = attributes_list.get_child_content('is-nfsv41-referrals-enabled')
- is_nfsv41_pnfs_enabled = attributes_list.get_child_content('is-nfsv41-pnfs-enabled')
- is_nfsv4_numeric_ids_enabled = attributes_list.get_child_content('is-nfsv4-numeric-ids-enabled')
- is_showmount_enabled = attributes_list.get_child_content('showmount')
- tcp_max_xfer_size = attributes_list.get_child_content('tcp-max-xfer-size')
- nfs_details = {
- 'is_nfsv3_enabled': is_nfsv3_enabled,
- 'is_nfsv3_fsid_change_enabled': is_nfsv3_fsid_change_enabled,
- 'is_nfsv4_fsid_change_enabled': is_nfsv4_fsid_change_enabled,
- 'is_nfsv40_enabled': is_nfsv40_enabled,
- 'is_nfsv41_enabled': is_nfsv41_enabled,
- 'is_nfsv41_pnfs_enabled': is_nfsv41_pnfs_enabled,
- 'is_nfsv4_numeric_ids_enabled': is_nfsv4_numeric_ids_enabled,
- 'is_vstorage_enabled': is_vstorage_enabled,
- 'nfsv4_id_domain': nfsv4_id_domain_value,
- 'is_tcp_enabled': is_tcp_enabled,
- 'is_udp_enabled': is_udp_enabled,
- 'is_nfsv40_acl_enabled': is_nfsv40_acl_enabled,
- 'is_nfsv40_read_delegation_enabled': is_nfsv40_read_delegation_enabled,
- 'is_nfsv40_referrals_enabled': is_nfsv40_referrals_enabled,
- 'is_nfsv40_write_delegation_enabled': is_nfsv40_write_delegation_enabled,
- 'is_nfsv41_acl_enabled': is_nfsv41_acl_enabled,
- 'is_nfsv41_read_delegation_enabled': is_nfsv41_read_delegation_enabled,
- 'is_nfsv41_referrals_enabled': is_nfsv41_referrals_enabled,
- 'is_nfsv41_write_delegation_enabled': is_nfsv41_write_delegation_enabled,
- 'is_showmount_enabled': is_showmount_enabled,
- 'tcp_max_xfer_size': tcp_max_xfer_size
- }
- return nfs_details
-
- def get_nfs_status(self):
- """
- Return status of nfs
- :param:
- name : Name of the vserver
- :return: status of nfs. None if not found.
- :rtype: bool
- """
- nfs_status = netapp_utils.zapi.NaElement('nfs-status')
- result = self.server.invoke_successfully(nfs_status, True)
- return_value = result.get_child_content('is-enabled')
-
- return return_value
-
- def enable_nfs(self):
- """
- enable nfs (online). If the NFS service was not explicitly created,
- this API will create one with default options.
- """
- nfs_enable = netapp_utils.zapi.NaElement.create_node_with_children('nfs-enable')
- try:
- self.server.invoke_successfully(nfs_enable,
- enable_tunneling=True)
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg='Error changing the service_state of nfs %s to %s: %s' %
- (self.vserver, self.service_state, to_native(error)),
- exception=traceback.format_exc())
-
- def disable_nfs(self):
- """
- disable nfs (offline).
- """
- nfs_disable = netapp_utils.zapi.NaElement.create_node_with_children('nfs-disable')
- try:
- self.server.invoke_successfully(nfs_disable,
- enable_tunneling=True)
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg='Error changing the service_state of nfs %s to %s: %s' %
- (self.vserver, self.service_state, to_native(error)),
- exception=traceback.format_exc())
-
- def modify_nfs(self):
- """
- modify nfs service
- """
- nfs_modify = netapp_utils.zapi.NaElement('nfs-service-modify')
- if self.nfsv3 == 'enabled':
- nfs_modify.add_new_child('is-nfsv3-enabled', 'true')
- elif self.nfsv3 == 'disabled':
- nfs_modify.add_new_child('is-nfsv3-enabled', 'false')
- if self.nfsv3_fsid_change == 'enabled':
- nfs_modify.add_new_child('is-nfsv3-fsid-change-enabled', 'true')
- elif self.nfsv3_fsid_change == 'disabled':
- nfs_modify.add_new_child('is-nfsv3-fsid-change-enabled', 'false')
- if self.nfsv4_fsid_change == 'enabled':
- nfs_modify.add_new_child('is-nfsv4-fsid-change-enabled', 'true')
- elif self.nfsv4_fsid_change == 'disabled':
- nfs_modify.add_new_child('is-nfsv4-fsid-change-enabled', 'false')
- if self.nfsv4 == 'enabled':
- nfs_modify.add_new_child('is-nfsv40-enabled', 'true')
- elif self.nfsv4 == 'disabled':
- nfs_modify.add_new_child('is-nfsv40-enabled', 'false')
- if self.nfsv41 == 'enabled':
- nfs_modify.add_new_child('is-nfsv41-enabled', 'true')
- elif self.nfsv41 == 'disabled':
- nfs_modify.add_new_child('is-nfsv41-enabled', 'false')
- if self.vstorage_state == 'enabled':
- nfs_modify.add_new_child('is-vstorage-enabled', 'true')
- elif self.vstorage_state == 'disabled':
- nfs_modify.add_new_child('is-vstorage-enabled', 'false')
- if self.tcp == 'enabled':
- nfs_modify.add_new_child('is-tcp-enabled', 'true')
- elif self.tcp == 'disabled':
- nfs_modify.add_new_child('is-tcp-enabled', 'false')
- if self.udp == 'enabled':
- nfs_modify.add_new_child('is-udp-enabled', 'true')
- elif self.udp == 'disabled':
- nfs_modify.add_new_child('is-udp-enabled', 'false')
- if self.nfsv40_acl == 'enabled':
- nfs_modify.add_new_child('is-nfsv40-acl-enabled', 'true')
- elif self.nfsv40_acl == 'disabled':
- nfs_modify.add_new_child('is-nfsv40-acl-enabled', 'false')
- if self.nfsv40_read_delegation == 'enabled':
- nfs_modify.add_new_child('is-nfsv40-read-delegation-enabled', 'true')
- elif self.nfsv40_read_delegation == 'disabled':
- nfs_modify.add_new_child('is-nfsv40-read-delegation-enabled', 'false')
- if self.nfsv40_referrals == 'enabled':
- nfs_modify.add_new_child('is-nfsv40-referrals-enabled', 'true')
- elif self.nfsv40_referrals == 'disabled':
- nfs_modify.add_new_child('is-nfsv40-referrals-enabled', 'false')
- if self.nfsv40_write_delegation == 'enabled':
- nfs_modify.add_new_child('is-nfsv40-write-delegation-enabled', 'true')
- elif self.nfsv40_write_delegation == 'disabled':
- nfs_modify.add_new_child('is-nfsv40-write-delegation-enabled', 'false')
- if self.nfsv41_acl == 'enabled':
- nfs_modify.add_new_child('is-nfsv41-acl-enabled', 'true')
- elif self.nfsv41_acl == 'disabled':
- nfs_modify.add_new_child('is-nfsv41-acl-enabled', 'false')
- if self.nfsv41_read_delegation == 'enabled':
- nfs_modify.add_new_child('is-nfsv41-read-delegation-enabled', 'true')
- elif self.nfsv41_read_delegation == 'disabled':
- nfs_modify.add_new_child('is-nfsv41-read-delegation-enabled', 'false')
- if self.nfsv41_referrals == 'enabled':
- nfs_modify.add_new_child('is-nfsv41-referrals-enabled', 'true')
- elif self.nfsv41_referrals == 'disabled':
- nfs_modify.add_new_child('is-nfsv41-referrals-enabled', 'false')
- if self.nfsv41_write_delegation == 'enabled':
- nfs_modify.add_new_child('is-nfsv41-write-delegation-enabled', 'true')
- elif self.nfsv41_write_delegation == 'disabled':
- nfs_modify.add_new_child('is-nfsv41-write-delegation-enabled', 'false')
- if self.nfsv41_pnfs == 'enabled':
- nfs_modify.add_new_child('is-nfsv41-pnfs-enabled', 'true')
- elif self.nfsv41_pnfs == 'disabled':
- nfs_modify.add_new_child('is-nfsv41-pnfs-enabled', 'false')
- if self.nfsv4_numeric_ids == 'enabled':
- nfs_modify.add_new_child('is-nfsv4-numeric-ids-enabled', 'true')
- elif self.nfsv4_numeric_ids == 'disabled':
- nfs_modify.add_new_child('is-nfsv4-numeric-ids-enabled', 'false')
- if self.showmount == 'enabled':
- nfs_modify.add_new_child('showmount', 'true')
- elif self.showmount == 'disabled':
- nfs_modify.add_new_child('showmount', 'false')
- if self.tcp_max_xfer_size is not None:
- nfs_modify.add_new_child('tcp-max-xfer-size', str(self.tcp_max_xfer_size))
- try:
- self.server.invoke_successfully(nfs_modify,
- enable_tunneling=True)
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg='Error modifying nfs: %s'
- % (to_native(error)),
- exception=traceback.format_exc())
-
- def modify_nfsv4_id_domain(self):
- """
- modify nfs service
- """
- nfsv4_id_domain_modify = netapp_utils.zapi.NaElement.create_node_with_children(
- 'nfs-service-modify', **{'nfsv4-id-domain': self.nfsv4_id_domain})
- if nfsv4_id_domain_modify is not None:
- try:
- self.server.invoke_successfully(nfsv4_id_domain_modify,
- enable_tunneling=True)
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg='Error modifying nfs: %s'
- % (to_native(error)),
- exception=traceback.format_exc())
-
- def delete_nfs(self):
- """
- delete nfs service.
- """
- nfs_delete = netapp_utils.zapi.NaElement.create_node_with_children('nfs-service-destroy')
- try:
- self.server.invoke_successfully(nfs_delete,
- enable_tunneling=True)
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg='Error deleting nfs: %s' %
- (to_native(error)),
- exception=traceback.format_exc())
-
- def apply(self):
- """Apply action to nfs"""
- changed = False
- nfs_exists = False
- modify_nfs = False
- enable_nfs = False
- disable_nfs = False
- netapp_utils.ems_log_event("na_ontap_nfs", self.server)
- nfs_enabled = self.get_nfs_status()
- nfs_service_details = self.get_nfs_service()
- is_nfsv4_id_domain_changed = False
-
- def state_changed(expected, current):
- if expected == "enabled" and current == "true":
- return False
- if expected == "disabled" and current == "false":
- return False
- return True
-
- def is_modify_needed():
- if (((self.nfsv3 is not None) and state_changed(self.nfsv3, nfs_service_details['is_nfsv3_enabled'])) or
- ((self.nfsv3_fsid_change is not None) and state_changed(self.nfsv3_fsid_change, nfs_service_details['is_nfsv3_fsid_change_enabled'])) or
- ((self.nfsv4_fsid_change is not None) and state_changed(self.nfsv4_fsid_change, nfs_service_details['is_nfsv4_fsid_change_enabled'])) or
- ((self.nfsv4 is not None) and state_changed(self.nfsv4, nfs_service_details['is_nfsv40_enabled'])) or
- ((self.nfsv41 is not None) and state_changed(self.nfsv41, nfs_service_details['is_nfsv41_enabled'])) or
- ((self.nfsv41_pnfs is not None) and state_changed(self.nfsv41_pnfs, nfs_service_details['is_nfsv41_pnfs_enabled'])) or
- ((self.nfsv4_numeric_ids is not None) and state_changed(self.nfsv4_numeric_ids, nfs_service_details['is_nfsv4_numeric_ids_enabled'])) or
- ((self.tcp is not None) and state_changed(self.tcp, nfs_service_details['is_tcp_enabled'])) or
- ((self.udp is not None) and state_changed(self.udp, nfs_service_details['is_udp_enabled'])) or
- ((self.nfsv40_acl is not None) and state_changed(self.nfsv40_acl, nfs_service_details['is_nfsv40_acl_enabled'])) or
- ((self.nfsv40_read_delegation is not None) and state_changed(self.nfsv40_read_delegation,
- nfs_service_details['is_nfsv40_read_delegation_enabled'])) or
- ((self.nfsv40_write_delegation is not None) and state_changed(self.nfsv40_write_delegation,
- nfs_service_details['is_nfsv40_write_delegation_enabled'])) or
- ((self.nfsv41_acl is not None) and state_changed(self.nfsv41_acl, nfs_service_details['is_nfsv41_acl_enabled'])) or
- ((self.nfsv41_read_delegation is not None) and state_changed(self.nfsv41_read_delegation,
- nfs_service_details['is_nfsv41_read_delegation_enabled'])) or
- ((self.nfsv41_write_delegation is not None) and state_changed(self.nfsv41_write_delegation,
- nfs_service_details['is_nfsv41_write_delegation_enabled'])) or
- ((self.nfsv40_referrals is not None) and state_changed(self.nfsv40_referrals,
- nfs_service_details['is_nfsv40_referrals_enabled'])) or
- ((self.nfsv41_referrals is not None) and state_changed(self.nfsv41_referrals,
- nfs_service_details['is_nfsv41_referrals_enabled'])) or
- ((self.showmount is not None) and state_changed(self.showmount, nfs_service_details['is_showmount_enabled'])) or
- ((self.vstorage_state is not None) and state_changed(self.vstorage_state, nfs_service_details['is_vstorage_enabled'])) or
- ((self.tcp_max_xfer_size is not None) and int(self.tcp_max_xfer_size) != int(nfs_service_details['tcp_max_xfer_size']))):
- return True
- return False
-
- def is_domain_changed():
- if (self.nfsv4_id_domain is not None) and (self.nfsv4_id_domain != nfs_service_details['nfsv4_id_domain']):
- return True
- return False
-
- if nfs_service_details:
- nfs_exists = True
- if self.state == 'absent': # delete
- changed = True
- elif self.state == 'present': # modify
- if self.service_state == 'started' and nfs_enabled == 'false':
- enable_nfs = True
- changed = True
- elif self.service_state == 'stopped' and nfs_enabled == 'true':
- disable_nfs = True
- changed = True
- if is_modify_needed():
- modify_nfs = True
- changed = True
- if is_domain_changed():
- is_nfsv4_id_domain_changed = True
- changed = True
- else:
- if self.state == 'present': # create
- changed = True
- if changed:
- if self.module.check_mode:
- pass
- else:
- if self.state == 'present': # execute create
- if not nfs_exists:
- self.enable_nfs()
- nfs_service_details = self.get_nfs_service()
- if self.service_state == 'stopped':
- self.disable_nfs()
- if is_modify_needed():
- self.modify_nfs()
- if is_domain_changed():
- self.modify_nfsv4_id_domain()
- else:
- if enable_nfs:
- self.enable_nfs()
- elif disable_nfs:
- self.disable_nfs()
- if modify_nfs:
- self.modify_nfs()
- if is_nfsv4_id_domain_changed:
- self.modify_nfsv4_id_domain()
- elif self.state == 'absent': # execute delete
- self.delete_nfs()
-
- self.module.exit_json(changed=changed)
-
-
-def main():
- """ Create object and call apply """
- obj = NetAppONTAPNFS()
- obj.apply()
-
-
-if __name__ == '__main__':
- main()
diff --git a/lib/ansible/modules/storage/netapp/na_ontap_node.py b/lib/ansible/modules/storage/netapp/na_ontap_node.py
deleted file mode 100644
index 1e68f92750..0000000000
--- a/lib/ansible/modules/storage/netapp/na_ontap_node.py
+++ /dev/null
@@ -1,144 +0,0 @@
-#!/usr/bin/python
-
-# (c) 2018-2019, NetApp, Inc
-# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-
-from __future__ import absolute_import, division, print_function
-__metaclass__ = type
-
-
-ANSIBLE_METADATA = {'metadata_version': '1.1',
- 'status': ['preview'],
- 'supported_by': 'certified'}
-
-DOCUMENTATION = '''
-module: na_ontap_node
-short_description: NetApp ONTAP Rename a node.
-extends_documentation_fragment:
- - netapp.na_ontap
-version_added: '2.7'
-author: NetApp Ansible Team (@carchi8py) <ng-ansibleteam@netapp.com>
-description:
-- Rename an ONTAP node.
-options:
- name:
- description:
- - The new name for the node
- required: true
-
- from_name:
- description:
- - The name of the node to be renamed. If I(name) already exists, no action will be performed.
- required: true
-
-'''
-
-EXAMPLES = """
-- name: rename node
- na_ontap_node:
- hostname: "{{ netapp_hostname }}"
- username: "{{ netapp_username }}"
- password: "{{ netapp_password }}"
- from_name: laurentn-vsim1
- name: laurentncluster-2
-"""
-
-RETURN = """
-
-"""
-import traceback
-
-from ansible.module_utils.basic import AnsibleModule
-from ansible.module_utils._text import to_native
-import ansible.module_utils.netapp as netapp_utils
-from ansible.module_utils.netapp_module import NetAppModule
-
-HAS_NETAPP_LIB = netapp_utils.has_netapp_lib()
-
-
-class NetAppOntapNode(object):
- """
- Rename node
- """
-
- def __init__(self):
- self.argument_spec = netapp_utils.na_ontap_host_argument_spec()
- self.argument_spec.update(dict(
- name=dict(required=True, type='str'),
- from_name=dict(required=True, type='str'),
- ))
-
- self.module = AnsibleModule(
- argument_spec=self.argument_spec,
- supports_check_mode=True
- )
-
- self.na_helper = NetAppModule()
- self.parameters = self.na_helper.set_parameters(self.module.params)
-
- if HAS_NETAPP_LIB is False:
- self.module.fail_json(msg="the python NetApp-Lib module is required")
- else:
- self.cluster = netapp_utils.setup_na_ontap_zapi(module=self.module)
- return
-
- def rename_node(self):
- """
- Rename an existing node
- :return: none
- """
- node_obj = netapp_utils.zapi.NaElement('system-node-rename')
- node_obj.add_new_child('node', self.parameters['from_name'])
- node_obj.add_new_child('new-name', self.parameters['name'])
- try:
- self.cluster.invoke_successfully(node_obj, True)
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg='Error creating node: %s' %
- (to_native(error)),
- exception=traceback.format_exc())
-
- def get_node(self, name):
- node_obj = netapp_utils.zapi.NaElement('system-node-get')
- node_obj.add_new_child('node', name)
- try:
- self.cluster.invoke_successfully(node_obj, True)
- except netapp_utils.zapi.NaApiError as error:
- if to_native(error.code) == "13115":
- # 13115 (EINVALIDINPUTERROR) if the node does not exist
- return None
- else:
- self.module.fail_json(msg=to_native(
- error), exception=traceback.format_exc())
- return True
-
- def apply(self):
- # logging ems event
- results = netapp_utils.get_cserver(self.cluster)
- cserver = netapp_utils.setup_na_ontap_zapi(module=self.module, vserver=results)
- netapp_utils.ems_log_event("na_ontap_node", cserver)
-
- exists = self.get_node(self.parameters['name'])
- from_exists = self.get_node(self.parameters['from_name'])
- changed = False
- if exists:
- pass
- else:
- if from_exists:
- self.rename_node()
- changed = True
- else:
- self.module.fail_json(msg='Error renaming node, from_name %s does not exist' % self.parameters['from_name'])
-
- self.module.exit_json(changed=changed)
-
-
-def main():
- """
- Start, Stop and Enable node services.
- """
- obj = NetAppOntapNode()
- obj.apply()
-
-
-if __name__ == '__main__':
- main()
diff --git a/lib/ansible/modules/storage/netapp/na_ontap_ntp.py b/lib/ansible/modules/storage/netapp/na_ontap_ntp.py
deleted file mode 100644
index 6c57880587..0000000000
--- a/lib/ansible/modules/storage/netapp/na_ontap_ntp.py
+++ /dev/null
@@ -1,226 +0,0 @@
-#!/usr/bin/python
-
-# (c) 2018-2019, NetApp, Inc
-# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-
-from __future__ import absolute_import, division, print_function
-__metaclass__ = type
-
-
-ANSIBLE_METADATA = {'metadata_version': '1.1',
- 'status': ['preview'],
- 'supported_by': 'certified'}
-
-
-DOCUMENTATION = """
-module: na_ontap_ntp
-short_description: NetApp ONTAP NTP server
-extends_documentation_fragment:
- - netapp.na_ontap
-version_added: '2.6'
-author: NetApp Ansible Team (@carchi8py) <ng-ansibleteam@netapp.com>
-description:
-- Create or delete or modify NTP server in ONTAP
-options:
- state:
- description:
- - Whether the specified NTP server should exist or not.
- choices: ['present', 'absent']
- default: 'present'
- server_name:
- description:
- - The name of the NTP server to manage.
- required: True
- version:
- description:
- - give version for NTP server
- choices: ['auto', '3', '4']
- default: 'auto'
-"""
-
-EXAMPLES = """
- - name: Create NTP server
- na_ontap_ntp:
- state: present
- version: auto
- server_name: "{{ server_name }}"
- hostname: "{{ netapp_hostname }}"
- username: "{{ netapp_username }}"
- password: "{{ netapp_password }}"
- - name: Delete NTP server
- na_ontap_ntp:
- state: absent
- server_name: "{{ server_name }}"
- hostname: "{{ netapp_hostname }}"
- username: "{{ netapp_username }}"
- password: "{{ netapp_password }}"
-"""
-
-RETURN = """
-"""
-import traceback
-
-from ansible.module_utils.basic import AnsibleModule
-from ansible.module_utils._text import to_native
-import ansible.module_utils.netapp as netapp_utils
-
-HAS_NETAPP_LIB = netapp_utils.has_netapp_lib()
-
-
-class NetAppOntapNTPServer(object):
- """ object initialize and class methods """
- def __init__(self):
- self.argument_spec = netapp_utils.na_ontap_host_argument_spec()
- self.argument_spec.update(dict(
- state=dict(required=False, choices=[
- 'present', 'absent'], default='present'),
- server_name=dict(required=True, type='str'),
- version=dict(required=False, type='str', default='auto',
- choices=['auto', '3', '4']),
- ))
-
- self.module = AnsibleModule(
- argument_spec=self.argument_spec,
- supports_check_mode=True
- )
-
- parameters = self.module.params
-
- # set up state variables
- self.state = parameters['state']
- self.server_name = parameters['server_name']
- self.version = parameters['version']
-
- if HAS_NETAPP_LIB is False:
- self.module.fail_json(
- msg="the python NetApp-Lib module is required")
- else:
- self.server = netapp_utils.setup_na_ontap_zapi(module=self.module)
-
- def get_ntp_server(self):
- """
- Return details about the ntp server
- :param:
- name : Name of the server_name
- :return: Details about the ntp server. None if not found.
- :rtype: dict
- """
- ntp_iter = netapp_utils.zapi.NaElement('ntp-server-get-iter')
- ntp_info = netapp_utils.zapi.NaElement('ntp-server-info')
- ntp_info.add_new_child('server-name', self.server_name)
-
- query = netapp_utils.zapi.NaElement('query')
- query.add_child_elem(ntp_info)
-
- ntp_iter.add_child_elem(query)
- result = self.server.invoke_successfully(ntp_iter, True)
- return_value = None
-
- if result.get_child_by_name('num-records') and \
- int(result.get_child_content('num-records')) == 1:
-
- ntp_server_name = result.get_child_by_name('attributes-list').\
- get_child_by_name('ntp-server-info').\
- get_child_content('server-name')
- server_version = result.get_child_by_name('attributes-list').\
- get_child_by_name('ntp-server-info').\
- get_child_content('version')
- return_value = {
- 'server-name': ntp_server_name,
- 'version': server_version
- }
-
- return return_value
-
- def create_ntp_server(self):
- """
- create ntp server.
- """
- ntp_server_create = netapp_utils.zapi.NaElement.create_node_with_children(
- 'ntp-server-create', **{'server-name': self.server_name,
- 'version': self.version
- })
-
- try:
- self.server.invoke_successfully(ntp_server_create,
- enable_tunneling=True)
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg='Error creating ntp server %s: %s'
- % (self.server_name, to_native(error)),
- exception=traceback.format_exc())
-
- def delete_ntp_server(self):
- """
- delete ntp server.
- """
- ntp_server_delete = netapp_utils.zapi.NaElement.create_node_with_children(
- 'ntp-server-delete', **{'server-name': self.server_name})
-
- try:
- self.server.invoke_successfully(ntp_server_delete,
- enable_tunneling=True)
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg='Error deleting ntp server %s: %s'
- % (self.server_name, to_native(error)),
- exception=traceback.format_exc())
-
- def modify_version(self):
- """
- modify the version.
- """
- ntp_modify_versoin = netapp_utils.zapi.NaElement.create_node_with_children(
- 'ntp-server-modify',
- **{'server-name': self.server_name, 'version': self.version})
- try:
- self.server.invoke_successfully(ntp_modify_versoin,
- enable_tunneling=True)
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg='Error modifying version for ntp server %s: %s'
- % (self.server_name, to_native(error)),
- exception=traceback.format_exc())
-
- def apply(self):
- """Apply action to ntp-server"""
-
- changed = False
- ntp_modify = False
- results = netapp_utils.get_cserver(self.server)
- cserver = netapp_utils.setup_na_ontap_zapi(
- module=self.module, vserver=results)
- netapp_utils.ems_log_event("na_ontap_ntp", cserver)
- ntp_server_details = self.get_ntp_server()
- if ntp_server_details is not None:
- if self.state == 'absent': # delete
- changed = True
- elif self.state == 'present' and self.version:
- # modify version
- if self.version != ntp_server_details['version']:
- ntp_modify = True
- changed = True
- else:
- if self.state == 'present': # create
- changed = True
-
- if changed:
- if self.module.check_mode:
- pass
- else:
- if self.state == 'present':
- if ntp_server_details is None:
- self.create_ntp_server()
- elif ntp_modify:
- self.modify_version()
- elif self.state == 'absent':
- self.delete_ntp_server()
-
- self.module.exit_json(changed=changed)
-
-
-def main():
- """ Create object and call apply """
- ntp_obj = NetAppOntapNTPServer()
- ntp_obj.apply()
-
-
-if __name__ == '__main__':
- main()
diff --git a/lib/ansible/modules/storage/netapp/na_ontap_nvme.py b/lib/ansible/modules/storage/netapp/na_ontap_nvme.py
deleted file mode 100644
index 61e32c64fc..0000000000
--- a/lib/ansible/modules/storage/netapp/na_ontap_nvme.py
+++ /dev/null
@@ -1,209 +0,0 @@
-#!/usr/bin/python
-
-# (c) 2019, NetApp, Inc
-# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-from __future__ import absolute_import, division, print_function
-__metaclass__ = type
-
-ANSIBLE_METADATA = {'metadata_version': '1.1',
- 'status': ['preview'],
- 'supported_by': 'certified'}
-
-
-DOCUMENTATION = '''
-author: NetApp Ansible Team (@carchi8py) <ng-ansibleteam@netapp.com>
-description:
- - Create/Delete NVMe Service
-extends_documentation_fragment:
- - netapp.na_ontap
-module: na_ontap_nvme
-options:
- state:
- choices: ['present', 'absent']
- description:
- - Whether the specified NVMe should exist or not.
- default: present
- vserver:
- description:
- - Name of the vserver to use.
- required: true
- status_admin:
- description:
- - Whether the status of NVMe should be up or down
- type: bool
-short_description: "NetApp ONTAP Manage NVMe Service"
-version_added: "2.8"
-'''
-
-EXAMPLES = """
-
- - name: Create NVMe
- na_ontap_nvme:
- state: present
- status_admin: False
- vserver: "{{ vserver }}"
- hostname: "{{ hostname }}"
- username: "{{ username }}"
- password: "{{ password }}"
-
- - name: Modify NVMe
- na_ontap_nvme:
- state: present
- status_admin: True
- vserver: "{{ vserver }}"
- hostname: "{{ hostname }}"
- username: "{{ username }}"
- password: "{{ password }}"
-
- - name: Delete NVMe
- na_ontap_nvme:
- state: absent
- vserver: "{{ vserver }}"
- hostname: "{{ hostname }}"
- username: "{{ username }}"
- password: "{{ password }}"
-"""
-
-RETURN = """
-"""
-
-import traceback
-from ansible.module_utils.basic import AnsibleModule
-from ansible.module_utils._text import to_native
-import ansible.module_utils.netapp as netapp_utils
-from ansible.module_utils.netapp_module import NetAppModule
-
-HAS_NETAPP_LIB = netapp_utils.has_netapp_lib()
-
-
-class NetAppONTAPNVMe(object):
- """
- Class with NVMe service methods
- """
-
- def __init__(self):
-
- self.argument_spec = netapp_utils.na_ontap_host_argument_spec()
- self.argument_spec.update(dict(
- state=dict(required=False, type='str', choices=['present', 'absent'], default='present'),
- vserver=dict(required=True, type='str'),
- status_admin=dict(required=False, type='bool')
- ))
-
- self.module = AnsibleModule(
- argument_spec=self.argument_spec,
- supports_check_mode=True
- )
-
- self.na_helper = NetAppModule()
- self.parameters = self.na_helper.set_parameters(self.module.params)
-
- if HAS_NETAPP_LIB is False:
- self.module.fail_json(msg="the python NetApp-Lib module is required")
- else:
- self.server = netapp_utils.setup_na_ontap_zapi(module=self.module, vserver=self.parameters['vserver'])
-
- def get_nvme(self):
- """
- Get current nvme details
- :return: dict if nvme exists, None otherwise
- """
- nvme_get = netapp_utils.zapi.NaElement('nvme-get-iter')
- query = {
- 'query': {
- 'nvme-target-service-info': {
- 'vserver': self.parameters['vserver']
- }
- }
- }
- nvme_get.translate_struct(query)
- try:
- result = self.server.invoke_successfully(nvme_get, enable_tunneling=False)
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg='Error fetching nvme info: %s' % to_native(error),
- exception=traceback.format_exc())
- if result.get_child_by_name('num-records') and int(result.get_child_content('num-records')) >= 1:
- attributes_list = result.get_child_by_name('attributes-list')
- nvme_info = attributes_list.get_child_by_name('nvme-target-service-info')
- return_value = {'status_admin': nvme_info.get_child_content('is-available')}
- return return_value
- return None
-
- def create_nvme(self):
- """
- Create NVMe service
- """
- nvme_create = netapp_utils.zapi.NaElement('nvme-create')
- if self.parameters.get('status_admin') is not None:
- options = {'is-available': self.parameters['status_admin']}
- nvme_create.translate_struct(options)
- try:
- self.server.invoke_successfully(nvme_create, enable_tunneling=True)
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg='Error creating nvme for vserver %s: %s'
- % (self.parameters['vserver'], to_native(error)),
- exception=traceback.format_exc())
-
- def delete_nvme(self):
- """
- Delete NVMe service
- """
- nvme_delete = netapp_utils.zapi.NaElement('nvme-delete')
- try:
- self.server.invoke_successfully(nvme_delete, enable_tunneling=True)
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg='Error deleting nvme for vserver %s: %s'
- % (self.parameters['vserver'], to_native(error)),
- exception=traceback.format_exc())
-
- def modify_nvme(self, status=None):
- """
- Modify NVMe service
- """
- if status is None:
- status = self.parameters['status_admin']
- options = {'is-available': status}
- nvme_modify = netapp_utils.zapi.NaElement('nvme-modify')
- nvme_modify.translate_struct(options)
- try:
- self.server.invoke_successfully(nvme_modify, enable_tunneling=True)
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg='Error modifying nvme for vserver %s: %s'
- % (self.parameters['vserver'], to_native(error)),
- exception=traceback.format_exc())
-
- def apply(self):
- """
- Apply action to NVMe service
- """
- netapp_utils.ems_log_event("na_ontap_nvme", self.server)
- current = self.get_nvme()
- cd_action = self.na_helper.get_cd_action(current, self.parameters)
- if self.parameters.get('status_admin') is not None:
- self.parameters['status_admin'] = self.na_helper.get_value_for_bool(False, self.parameters['status_admin'])
- if cd_action is None and self.parameters['state'] == 'present':
- modify = self.na_helper.get_modified_attributes(current, self.parameters)
- if self.na_helper.changed:
- if self.module.check_mode:
- pass
- else:
- if cd_action == 'create':
- self.create_nvme()
- elif cd_action == 'delete':
- # NVMe status_admin needs to be down before deleting it
- self.modify_nvme('false')
- self.delete_nvme()
- elif modify:
- self.modify_nvme()
-
- self.module.exit_json(changed=self.na_helper.changed)
-
-
-def main():
- """Execute action"""
- community_obj = NetAppONTAPNVMe()
- community_obj.apply()
-
-
-if __name__ == '__main__':
- main()
diff --git a/lib/ansible/modules/storage/netapp/na_ontap_nvme_namespace.py b/lib/ansible/modules/storage/netapp/na_ontap_nvme_namespace.py
deleted file mode 100644
index aa0a4a34e8..0000000000
--- a/lib/ansible/modules/storage/netapp/na_ontap_nvme_namespace.py
+++ /dev/null
@@ -1,195 +0,0 @@
-#!/usr/bin/python
-
-# (c) 2019, NetApp, Inc
-# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-from __future__ import absolute_import, division, print_function
-__metaclass__ = type
-
-ANSIBLE_METADATA = {'metadata_version': '1.1',
- 'status': ['preview'],
- 'supported_by': 'certified'}
-
-
-DOCUMENTATION = '''
-author: NetApp Ansible Team (@carchi8py) <ng-ansibleteam@netapp.com>
-description:
- - Create/Delete NVME namespace
-extends_documentation_fragment:
- - netapp.na_ontap
-module: na_ontap_nvme_namespace
-options:
- state:
- choices: ['present', 'absent']
- description:
- - Whether the specified namespace should exist or not.
- default: present
- vserver:
- description:
- - Name of the vserver to use.
- required: true
- ostype:
- description:
- - Specifies the ostype for initiators
- choices: ['windows', 'linux', 'vmware', 'xen', 'hyper_v']
- size:
- description:
- - Size in bytes.
- Range is [0..2^63-1].
- type: int
- path:
- description:
- - Namespace path.
- type: str
-short_description: "NetApp ONTAP Manage NVME Namespace"
-version_added: "2.8"
-'''
-
-EXAMPLES = """
-
- - name: Create NVME Namespace
- na_ontap_nvme_namespace:
- state: present
- ostype: linux
- path: /vol/ansible/test
- size: 20
- vserver: "{{ vserver }}"
- hostname: "{{ hostname }}"
- username: "{{ username }}"
- password: "{{ password }}"
-
- - name: Create NVME Namespace (Idempotency)
- na_ontap_nvme_namespace:
- state: present
- ostype: linux
- path: /vol/ansible/test
- size: 20
- vserver: "{{ vserver }}"
- hostname: "{{ hostname }}"
- username: "{{ username }}"
- password: "{{ password }}"
-"""
-
-RETURN = """
-"""
-
-import traceback
-from ansible.module_utils.basic import AnsibleModule
-from ansible.module_utils._text import to_native
-import ansible.module_utils.netapp as netapp_utils
-from ansible.module_utils.netapp_module import NetAppModule
-
-HAS_NETAPP_LIB = netapp_utils.has_netapp_lib()
-
-
-class NetAppONTAPNVMENamespace(object):
- """
- Class with NVME namespace methods
- """
-
- def __init__(self):
-
- self.argument_spec = netapp_utils.na_ontap_host_argument_spec()
- self.argument_spec.update(dict(
- state=dict(required=False, type='str', choices=['present', 'absent'], default='present'),
- vserver=dict(required=True, type='str'),
- ostype=dict(required=False, type='str', choices=['windows', 'linux', 'vmware', 'xen', 'hyper_v']),
- path=dict(required=True, type='str'),
- size=dict(required=False, type='int')
- ))
-
- self.module = AnsibleModule(
- argument_spec=self.argument_spec,
- required_if=[('state', 'present', ['ostype', 'size'])],
- supports_check_mode=True
- )
-
- self.na_helper = NetAppModule()
- self.parameters = self.na_helper.set_parameters(self.module.params)
-
- if HAS_NETAPP_LIB is False:
- self.module.fail_json(msg="the python NetApp-Lib module is required")
- else:
- self.server = netapp_utils.setup_na_ontap_zapi(module=self.module, vserver=self.parameters['vserver'])
-
- def get_namespace(self):
- """
- Get current namespace details
- :return: dict if namespace exists, None otherwise
- """
- namespace_get = netapp_utils.zapi.NaElement('nvme-namespace-get-iter')
- query = {
- 'query': {
- 'nvme-namespace-info': {
- 'path': self.parameters['path'],
- 'vserver': self.parameters['vserver']
- }
- }
- }
- namespace_get.translate_struct(query)
- try:
- result = self.server.invoke_successfully(namespace_get, enable_tunneling=False)
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg='Error fetching namespace info: %s' % to_native(error),
- exception=traceback.format_exc())
- if result.get_child_by_name('num-records') and int(result.get_child_content('num-records')) >= 1:
- return result
- return None
-
- def create_namespace(self):
- """
- Create a NVME Namespace
- """
- options = {'path': self.parameters['path'],
- 'ostype': self.parameters['ostype'],
- 'size': self.parameters['size']
- }
- namespace_create = netapp_utils.zapi.NaElement('nvme-namespace-create')
- namespace_create.translate_struct(options)
- try:
- self.server.invoke_successfully(namespace_create, enable_tunneling=True)
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg='Error creating namespace for path %s: %s'
- % (self.parameters.get('path'), to_native(error)),
- exception=traceback.format_exc())
-
- def delete_namespace(self):
- """
- Delete a NVME Namespace
- """
- options = {'path': self.parameters['path']
- }
- namespace_delete = netapp_utils.zapi.NaElement.create_node_with_children('nvme-namespace-delete', **options)
- try:
- self.server.invoke_successfully(namespace_delete, enable_tunneling=True)
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg='Error deleting namespace for path %s: %s'
- % (self.parameters.get('path'), to_native(error)),
- exception=traceback.format_exc())
-
- def apply(self):
- """
- Apply action to NVME Namespace
- """
- netapp_utils.ems_log_event("na_ontap_nvme_namespace", self.server)
- current = self.get_namespace()
- cd_action = self.na_helper.get_cd_action(current, self.parameters)
- if self.na_helper.changed:
- if self.module.check_mode:
- pass
- else:
- if cd_action == 'create':
- self.create_namespace()
- elif cd_action == 'delete':
- self.delete_namespace()
-
- self.module.exit_json(changed=self.na_helper.changed)
-
-
-def main():
- """Execute action"""
- community_obj = NetAppONTAPNVMENamespace()
- community_obj.apply()
-
-
-if __name__ == '__main__':
- main()
diff --git a/lib/ansible/modules/storage/netapp/na_ontap_nvme_subsystem.py b/lib/ansible/modules/storage/netapp/na_ontap_nvme_subsystem.py
deleted file mode 100644
index 6de354dc09..0000000000
--- a/lib/ansible/modules/storage/netapp/na_ontap_nvme_subsystem.py
+++ /dev/null
@@ -1,355 +0,0 @@
-#!/usr/bin/python
-
-# (c) 2019, NetApp, Inc
-# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-from __future__ import absolute_import, division, print_function
-__metaclass__ = type
-
-ANSIBLE_METADATA = {'metadata_version': '1.1',
- 'status': ['preview'],
- 'supported_by': 'certified'}
-
-
-DOCUMENTATION = '''
-author: NetApp Ansible Team (@carchi8py) <ng-ansibleteam@netapp.com>
-description:
- - Create/Delete NVME subsystem
- - Associate(modify) host/map to NVME subsystem
- - NVMe service should be existing in the data vserver with NVMe protocol as a pre-requisite
-extends_documentation_fragment:
- - netapp.na_ontap
-module: na_ontap_nvme_subsystem
-options:
- state:
- choices: ['present', 'absent']
- description:
- - Whether the specified subsystem should exist or not.
- default: present
- vserver:
- description:
- - Name of the vserver to use.
- required: true
- subsystem:
- description:
- - Specifies the subsystem
- required: true
- ostype:
- description:
- - Specifies the ostype for initiators
- choices: ['windows', 'linux', 'vmware', 'xen', 'hyper_v']
- skip_host_check:
- description:
- - Skip host check
- - Required to delete an NVMe Subsystem with attached NVMe namespaces
- default: false
- type: bool
- skip_mapped_check:
- description:
- - Skip mapped namespace check
- - Required to delete an NVMe Subsystem with attached NVMe namespaces
- default: false
- type: bool
- hosts:
- description:
- - List of host NQNs (NVMe Qualification Name) associated to the controller.
- type: list
- paths:
- description:
- - List of Namespace paths to be associated with the subsystem.
- type: list
-short_description: "NetApp ONTAP Manage NVME Subsystem"
-version_added: "2.8"
-'''
-
-EXAMPLES = """
-
- - name: Create NVME Subsystem
- na_ontap_nvme_subsystem:
- state: present
- subsystem: test_sub
- vserver: test_dest
- ostype: linux
- hostname: "{{ netapp_hostname }}"
- username: "{{ netapp_username }}"
- password: "{{ netapp_password }}"
-
- - name: Delete NVME Subsystem
- na_ontap_nvme_subsystem:
- state: absent
- subsystem: test_sub
- vserver: test_dest
- skip_host_check: True
- skip_mapped_check: True
- hostname: "{{ netapp_hostname }}"
- username: "{{ netapp_username }}"
- password: "{{ netapp_password }}"
-
- - name: Associate NVME Subsystem host/map
- na_ontap_nvme_subsystem:
- state: present
- subsystem: "{{ subsystem }}"
- ostype: linux
- hosts: nqn.1992-08.com.netapp:sn.3017cfc1e2ba11e89c55005056b36338:subsystem.ansible
- paths: /vol/ansible/test,/vol/ansible/test1
- vserver: "{{ vserver }}"
- hostname: "{{ hostname }}"
- username: "{{ username }}"
- password: "{{ password }}"
-
- - name: Modify NVME subsystem map
- na_ontap_nvme_subsystem:
- state: present
- subsystem: test_sub
- vserver: test_dest
- skip_host_check: True
- skip_mapped_check: True
- paths: /vol/ansible/test
- hostname: "{{ netapp_hostname }}"
- username: "{{ netapp_username }}"
- password: "{{ netapp_password }}"
-
-"""
-
-RETURN = """
-"""
-
-import traceback
-from ansible.module_utils.basic import AnsibleModule
-from ansible.module_utils._text import to_native
-import ansible.module_utils.netapp as netapp_utils
-from ansible.module_utils.netapp_module import NetAppModule
-
-HAS_NETAPP_LIB = netapp_utils.has_netapp_lib()
-
-
-class NetAppONTAPNVMESubsystem(object):
- """
- Class with NVME subsytem methods
- """
-
- def __init__(self):
-
- self.argument_spec = netapp_utils.na_ontap_host_argument_spec()
- self.argument_spec.update(dict(
- state=dict(required=False, type='str', choices=['present', 'absent'], default='present'),
- vserver=dict(required=True, type='str'),
- subsystem=dict(required=True, type='str'),
- ostype=dict(required=False, type='str', choices=['windows', 'linux', 'vmware', 'xen', 'hyper_v']),
- skip_host_check=dict(required=False, type='bool', default=False),
- skip_mapped_check=dict(required=False, type='bool', default=False),
- hosts=dict(required=False, type='list'),
- paths=dict(required=False, type='list')
- ))
-
- self.module = AnsibleModule(
- argument_spec=self.argument_spec,
- supports_check_mode=True
- )
-
- self.na_helper = NetAppModule()
- self.parameters = self.na_helper.set_parameters(self.module.params)
-
- if HAS_NETAPP_LIB is False:
- self.module.fail_json(msg="the python NetApp-Lib module is required")
- else:
- self.server = netapp_utils.setup_na_ontap_zapi(module=self.module, vserver=self.parameters['vserver'])
-
- def get_subsystem(self):
- """
- Get current subsystem details
- :return: dict if subsystem exists, None otherwise
- """
- subsystem_get = netapp_utils.zapi.NaElement('nvme-subsystem-get-iter')
- query = {
- 'query': {
- 'nvme-subsytem-info': {
- 'subsystem': self.parameters.get('subsystem')
- }
- }
- }
- subsystem_get.translate_struct(query)
- try:
- result = self.server.invoke_successfully(subsystem_get, enable_tunneling=False)
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg='Error fetching subsystem info: %s' % to_native(error),
- exception=traceback.format_exc())
- if result.get_child_by_name('num-records') and int(result.get_child_content('num-records')) >= 1:
- return True
- return None
-
- def create_subsystem(self):
- """
- Create a NVME Subsystem
- """
- if self.parameters.get('ostype') is None:
- self.module.fail_json(msg="Error: Missing required parameter 'os_type' for creating subsystem")
- options = {'subsystem': self.parameters['subsystem'],
- 'ostype': self.parameters['ostype']
- }
- subsystem_create = netapp_utils.zapi.NaElement('nvme-subsystem-create')
- subsystem_create.translate_struct(options)
- try:
- self.server.invoke_successfully(subsystem_create, enable_tunneling=True)
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg='Error creating subsystem for %s: %s'
- % (self.parameters.get('subsystem'), to_native(error)),
- exception=traceback.format_exc())
-
- def delete_subsystem(self):
- """
- Delete a NVME subsystem
- """
- options = {'subsystem': self.parameters['subsystem'],
- 'skip-host-check': 'true' if self.parameters.get('skip_host_check') else 'false',
- 'skip-mapped-check': 'true' if self.parameters.get('skip_mapped_check') else 'false',
- }
- subsystem_delete = netapp_utils.zapi.NaElement.create_node_with_children('nvme-subsystem-delete', **options)
- try:
- self.server.invoke_successfully(subsystem_delete, enable_tunneling=True)
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg='Error deleting subsystem for %s: %s'
- % (self.parameters.get('subsystem'), to_native(error)),
- exception=traceback.format_exc())
-
- def get_subsystem_host_map(self, type):
- """
- Get current subsystem host details
- :return: list if host exists, None otherwise
- """
- if type == 'hosts':
- zapi_get, zapi_info, zapi_type = 'nvme-subsystem-host-get-iter', 'nvme-target-subsystem-host-info',\
- 'host-nqn'
- elif type == 'paths':
- zapi_get, zapi_info, zapi_type = 'nvme-subsystem-map-get-iter', 'nvme-target-subsystem-map-info', 'path'
- subsystem_get = netapp_utils.zapi.NaElement(zapi_get)
- query = {
- 'query': {
- zapi_info: {
- 'subsystem': self.parameters.get('subsystem')
- }
- }
- }
- subsystem_get.translate_struct(query)
- try:
- result = self.server.invoke_successfully(subsystem_get, enable_tunneling=False)
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg='Error fetching subsystem info: %s' % to_native(error),
- exception=traceback.format_exc())
- if result.get_child_by_name('num-records') and int(result.get_child_content('num-records')) >= 1:
- attrs_list = result.get_child_by_name('attributes-list')
- return_list = []
- for item in attrs_list.get_children():
- return_list.append(item[zapi_type])
- return {type: return_list}
- return None
-
- def add_subsystem_host_map(self, data, type):
- """
- Add a NVME Subsystem host/map
- :param: data: list of hosts/paths to be added
- :param: type: hosts/paths
- """
- if type == 'hosts':
- zapi_add, zapi_type = 'nvme-subsystem-host-add', 'host-nqn'
- elif type == 'paths':
- zapi_add, zapi_type = 'nvme-subsystem-map-add', 'path'
-
- for item in data:
- options = {'subsystem': self.parameters['subsystem'],
- zapi_type: item
- }
- subsystem_add = netapp_utils.zapi.NaElement.create_node_with_children(zapi_add, **options)
- try:
- self.server.invoke_successfully(subsystem_add, enable_tunneling=True)
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg='Error adding %s for subsystem %s: %s'
- % (item, self.parameters.get('subsystem'), to_native(error)),
- exception=traceback.format_exc())
-
- def remove_subsystem_host_map(self, data, type):
- """
- Remove a NVME Subsystem host/map
- :param: data: list of hosts/paths to be added
- :param: type: hosts/paths
- """
- if type == 'hosts':
- zapi_remove, zapi_type = 'nvme-subsystem-host-remove', 'host-nqn'
- elif type == 'paths':
- zapi_remove, zapi_type = 'nvme-subsystem-map-remove', 'path'
-
- for item in data:
- options = {'subsystem': self.parameters['subsystem'],
- zapi_type: item
- }
- subsystem_remove = netapp_utils.zapi.NaElement.create_node_with_children(zapi_remove, **options)
- try:
- self.server.invoke_successfully(subsystem_remove, enable_tunneling=True)
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg='Error removing %s for subsystem %s: %s'
- % (item, self.parameters.get('subsystem'), to_native(error)),
- exception=traceback.format_exc())
-
- def associate_host_map(self, types):
- """
- Check if there are hosts or paths to be associated with the subsystem
- """
- action_add_dict = {}
- action_remove_dict = {}
- for type in types:
- if self.parameters.get(type):
- current = self.get_subsystem_host_map(type)
- if current:
- add_items = self.na_helper.\
- get_modified_attributes(current, self.parameters, get_list_diff=True).get(type)
- remove_items = [item for item in current[type] if item not in self.parameters.get(type)]
- else:
- add_items = self.parameters[type]
- remove_items = {}
- if add_items:
- action_add_dict[type] = add_items
- self.na_helper.changed = True
- if remove_items:
- action_remove_dict[type] = remove_items
- self.na_helper.changed = True
- return action_add_dict, action_remove_dict
-
- def modify_host_map(self, add_host_map, remove_host_map):
- for type, data in add_host_map.items():
- self.add_subsystem_host_map(data, type)
- for type, data in remove_host_map.items():
- self.remove_subsystem_host_map(data, type)
-
- def apply(self):
- """
- Apply action to NVME subsystem
- """
- netapp_utils.ems_log_event("na_ontap_nvme_subsystem", self.server)
- types = ['hosts', 'paths']
- current = self.get_subsystem()
- add_host_map, remove_host_map = dict(), dict()
- cd_action = self.na_helper.get_cd_action(current, self.parameters)
- if cd_action != 'delete' and self.parameters['state'] == 'present':
- add_host_map, remove_host_map = self.associate_host_map(types)
- if self.na_helper.changed:
- if self.module.check_mode:
- pass
- else:
- if cd_action == 'create':
- self.create_subsystem()
- self.modify_host_map(add_host_map, remove_host_map)
- elif cd_action == 'delete':
- self.delete_subsystem()
- elif cd_action is None:
- self.modify_host_map(add_host_map, remove_host_map)
-
- self.module.exit_json(changed=self.na_helper.changed)
-
-
-def main():
- """Execute action"""
- community_obj = NetAppONTAPNVMESubsystem()
- community_obj.apply()
-
-
-if __name__ == '__main__':
- main()
diff --git a/lib/ansible/modules/storage/netapp/na_ontap_object_store.py b/lib/ansible/modules/storage/netapp/na_ontap_object_store.py
deleted file mode 100644
index bb5bb1ee3a..0000000000
--- a/lib/ansible/modules/storage/netapp/na_ontap_object_store.py
+++ /dev/null
@@ -1,237 +0,0 @@
-#!/usr/bin/python
-
-# (c) 2019, NetApp, Inc
-# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-
-from __future__ import absolute_import, division, print_function
-__metaclass__ = type
-
-
-ANSIBLE_METADATA = {'metadata_version': '1.1',
- 'status': ['preview'],
- 'supported_by': 'certified'}
-
-
-DOCUMENTATION = '''
-
-module: na_ontap_object_store
-short_description: NetApp ONTAP manage object store config.
-extends_documentation_fragment:
- - netapp.na_ontap
-version_added: '2.9'
-author: NetApp Ansible Team (@carchi8py) <ng-ansibleteam@netapp.com>
-
-description:
-- Create or delete object store config on ONTAP.
-
-options:
-
- state:
- description:
- - Whether the specified object store config should exist or not.
- choices: ['present', 'absent']
- default: 'present'
- type: str
-
- name:
- required: true
- description:
- - The name of the object store config to manage.
- type: str
-
- provider_type:
- required: false
- description:
- - The name of the object store config provider.
- type: str
-
- server:
- required: false
- description:
- - Fully qualified domain name of the object store config.
- type: str
-
- container:
- required: false
- description:
- - Data bucket/container name used in S3 requests.
- type: str
-
- access_key:
- required: false
- description:
- - Access key ID for AWS_S3 and SGWS provider types.
- type: str
-
- secret_password:
- required: false
- description:
- - Secret access key for AWS_S3 and SGWS provider types.
- type: str
-'''
-
-EXAMPLES = """
-- name: object store Create
- na_ontap_object_store:
- state: present
- name: ansible
- provider_type: SGWS
- server: abc
- container: abc
- access_key: s3.amazonaws.com
- secret_password: abc
- hostname: "{{ hostname }}"
- username: "{{ username }}"
- password: "{{ password }}"
-
-- name: object store Create
- na_ontap_object_store:
- state: absent
- name: ansible
- hostname: "{{ hostname }}"
- username: "{{ username }}"
- password: "{{ password }}"
-"""
-
-RETURN = """
-
-"""
-import traceback
-
-from ansible.module_utils.basic import AnsibleModule
-from ansible.module_utils._text import to_native
-import ansible.module_utils.netapp as netapp_utils
-from ansible.module_utils.netapp_module import NetAppModule
-
-HAS_NETAPP_LIB = netapp_utils.has_netapp_lib()
-
-
-class NetAppOntapObjectStoreConfig(object):
- ''' object initialize and class methods '''
-
- def __init__(self):
- self.argument_spec = netapp_utils.na_ontap_host_argument_spec()
- self.argument_spec.update(dict(
- name=dict(required=True, type='str'),
- state=dict(required=False, choices=['present', 'absent'], default='present'),
- provider_type=dict(required=False, type='str'),
- server=dict(required=False, type='str'),
- container=dict(required=False, type='str'),
- access_key=dict(required=False, type='str'),
- secret_password=dict(required=False, type='str', no_log=True)
- ))
-
- self.module = AnsibleModule(
- argument_spec=self.argument_spec,
- supports_check_mode=True
- )
-
- self.na_helper = NetAppModule()
- self.parameters = self.na_helper.set_parameters(self.module.params)
- if HAS_NETAPP_LIB is False:
- self.module.fail_json(msg="the python NetApp-Lib module is required")
- else:
- self.server = netapp_utils.setup_na_ontap_zapi(module=self.module)
-
- def get_aggr_object_store(self):
- """
- Fetch details if object store config exists.
- :return:
- Dictionary of current details if object store config found
- None if object store config is not found
- """
- aggr_object_store_get_iter = netapp_utils.zapi.NaElement.create_node_with_children(
- 'aggr-object-store-config-get', **{'object-store-name': self.parameters['name']})
- result = None
- try:
- result = self.server.invoke_successfully(aggr_object_store_get_iter, enable_tunneling=False)
- except netapp_utils.zapi.NaApiError as error:
- # Error 15661 denotes an object store not being found.
- if to_native(error.code) == "15661":
- pass
- else:
- self.module.fail_json(msg=to_native(error), exception=traceback.format_exc())
- return result
-
- def create_aggr_object_store(self):
- """
- Create aggregate object store config
- :return: None
- """
- required_keys = set(['provider_type', 'server', 'container', 'access_key'])
- if not required_keys.issubset(set(self.parameters.keys())):
- self.module.fail_json(msg='Error provisioning object store %s: one of the following parameters are missing '
- '%s' % (self.parameters['name'], ', '.join(required_keys)))
- options = {'object-store-name': self.parameters['name'],
- 'provider-type': self.parameters['provider_type'],
- 'server': self.parameters['server'],
- 's3-name': self.parameters['container'],
- 'access-key': self.parameters['access_key']}
- if self.parameters.get('secret_password'):
- options['secret-password'] = self.parameters['secret_password']
- object_store_create = netapp_utils.zapi.NaElement.create_node_with_children('aggr-object-store-config-create', **options)
-
- try:
- self.server.invoke_successfully(object_store_create, enable_tunneling=False)
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg="Error provisioning object store config %s: %s"
- % (self.parameters['name'], to_native(error)),
- exception=traceback.format_exc())
-
- def delete_aggr_object_store(self):
- """
- Delete aggregate object store config
- :return: None
- """
- object_store_destroy = netapp_utils.zapi.NaElement.create_node_with_children(
- 'aggr-object-store-config-delete', **{'object-store-name': self.parameters['name']})
-
- try:
- self.server.invoke_successfully(object_store_destroy,
- enable_tunneling=False)
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg="Error removing object store config %s: %s" %
- (self.parameters['name'], to_native(error)), exception=traceback.format_exc())
-
- def asup_log_for_cserver(self, event_name):
- """
- Fetch admin vserver for the given cluster
- Create and Autosupport log event with the given module name
- :param event_name: Name of the event log
- :return: None
- """
- results = netapp_utils.get_cserver(self.server)
- cserver = netapp_utils.setup_na_ontap_zapi(module=self.module, vserver=results)
- netapp_utils.ems_log_event(event_name, cserver)
-
- def apply(self):
- """
- Apply action to the object store config
- :return: None
- """
- self.asup_log_for_cserver("na_ontap_object_store_config")
- current = self.get_aggr_object_store()
- cd_action = self.na_helper.get_cd_action(current, self.parameters)
-
- if self.na_helper.changed:
- if self.module.check_mode:
- pass
- else:
- if cd_action == 'create':
- self.create_aggr_object_store()
- elif cd_action == 'delete':
- self.delete_aggr_object_store()
- self.module.exit_json(changed=self.na_helper.changed)
-
-
-def main():
- """
- Create Object Store Config class instance and invoke apply
- :return: None
- """
- obj_store = NetAppOntapObjectStoreConfig()
- obj_store.apply()
-
-
-if __name__ == '__main__':
- main()
diff --git a/lib/ansible/modules/storage/netapp/na_ontap_ports.py b/lib/ansible/modules/storage/netapp/na_ontap_ports.py
deleted file mode 100644
index 4ec11f5260..0000000000
--- a/lib/ansible/modules/storage/netapp/na_ontap_ports.py
+++ /dev/null
@@ -1,380 +0,0 @@
-#!/usr/bin/python
-''' This is an Ansible module for ONTAP to manage ports for various resources.
-
- (c) 2019, NetApp, Inc
- # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-'''
-
-from __future__ import absolute_import, division, print_function
-
-__metaclass__ = type
-
-ANSIBLE_METADATA = {
- 'metadata_version': '1.1',
- 'status': ['preview'],
- 'supported_by': 'community'
-}
-
-DOCUMENTATION = '''
-
-module: na_ontap_ports
-short_description: NetApp ONTAP add/remove ports
-extends_documentation_fragment:
- - netapp.na_ontap
-version_added: '2.9'
-author: NetApp Ansible Team (@carchi8py) <ng-ansibleteam@netapp.com>
-
-description:
- - Add or remove ports for broadcast domain and portset.
-
-options:
- state:
- description:
- - Whether the specified port should be added or removed.
- choices: ['present', 'absent']
- default: present
- type: str
-
- vserver:
- description:
- - Name of the SVM.
- - Specify this option when operating on portset.
- type: str
-
- names:
- description:
- - List of ports.
- type: list
- required: true
-
- resource_name:
- description:
- - name of the portset or broadcast domain.
- type: str
- required: true
-
- resource_type:
- description:
- - type of the resource to add a port to or remove a port from.
- choices: ['broadcast_domain', 'portset']
- required: true
- type: str
-
- ipspace:
- description:
- - Specify the required ipspace for the broadcast domain.
- - A domain ipspace can not be modified after the domain has been created.
- type: str
-
- portset_type:
- description:
- - Protocols accepted for portset.
- choices: ['fcp', 'iscsi', 'mixed']
- type: str
-
-'''
-
-EXAMPLES = '''
-
- - name: broadcast domain remove port
- tags:
- - remove
- na_ontap_ports:
- state: absent
- names: test-vsim1:e0d-1,test-vsim1:e0d-2
- resource_type: broadcast_domain
- resource_name: ansible_domain
- hostname: "{{ hostname }}"
- username: user
- password: password
- https: False
-
- - name: broadcast domain add port
- tags:
- - add
- na_ontap_ports:
- state: present
- names: test-vsim1:e0d-1,test-vsim1:e0d-2
- resource_type: broadcast_domain
- resource_name: ansible_domain
- ipspace: Default
- hostname: "{{ hostname }}"
- username: user
- password: password
- https: False
-
- - name: portset remove port
- tags:
- - remove
- na_ontap_ports:
- state: absent
- names: lif_2
- resource_type: portset
- resource_name: portset_1
- vserver: "{{ vserver }}"
- hostname: "{{ hostname }}"
- username: user
- password: password
- https: False
-
- - name: portset add port
- tags:
- - add
- na_ontap_ports:
- state: present
- names: lif_2
- resource_type: portset
- resource_name: portset_1
- portset_type: iscsi
- vserver: "{{ vserver }}"
- hostname: "{{ hostname }}"
- username: user
- password: password
- https: False
-
-'''
-
-RETURN = '''
-'''
-
-import traceback
-
-from ansible.module_utils.basic import AnsibleModule
-from ansible.module_utils._text import to_native
-import ansible.module_utils.netapp as netapp_utils
-from ansible.module_utils.netapp_module import NetAppModule
-
-HAS_NETAPP_LIB = netapp_utils.has_netapp_lib()
-
-
-class NetAppOntapPorts(object):
-
- def __init__(self):
- self.argument_spec = netapp_utils.na_ontap_host_argument_spec()
- self.argument_spec.update(dict(
- state=dict(required=False, choices=['present', 'absent'], default='present'),
- vserver=dict(required=False, type='str'),
- names=dict(required=True, type='list'),
- resource_name=dict(required=True, type='str'),
- resource_type=dict(required=True, type='str', choices=['broadcast_domain', 'portset']),
- ipspace=dict(required=False, type='str'),
- portset_type=dict(required=False, type='str', choices=['fcp', 'iscsi', 'mixed']),
- ))
-
- self.module = AnsibleModule(
- argument_spec=self.argument_spec,
- required_if=[
- ('resource_type', 'portset', ['vserver']),
- ],
- supports_check_mode=True
- )
-
- self.na_helper = NetAppModule()
- self.parameters = self.na_helper.set_parameters(self.module.params)
-
- if HAS_NETAPP_LIB is False:
- self.module.fail_json(
- msg="the python NetApp-Lib module is required")
- else:
- if self.parameters['resource_type'] == 'broadcast_domain':
- self.server = netapp_utils.setup_na_ontap_zapi(module=self.module)
- elif self.parameters['resource_type'] == 'portset':
- self.server = netapp_utils.setup_na_ontap_zapi(
- module=self.module, vserver=self.parameters['vserver'])
-
- def add_broadcast_domain_ports(self, ports):
- """
- Add broadcast domain ports
- :param: ports to be added.
- """
- domain_obj = netapp_utils.zapi.NaElement('net-port-broadcast-domain-add-ports')
- domain_obj.add_new_child("broadcast-domain", self.parameters['resource_name'])
- if self.parameters.get('ipspace'):
- domain_obj.add_new_child("ipspace", self.parameters['ipspace'])
- ports_obj = netapp_utils.zapi.NaElement('ports')
- domain_obj.add_child_elem(ports_obj)
- for port in ports:
- ports_obj.add_new_child('net-qualified-port-name', port)
- try:
- self.server.invoke_successfully(domain_obj, True)
- return True
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg='Error adding port for broadcast domain %s: %s' %
- (self.parameters['resource_name'], to_native(error)),
- exception=traceback.format_exc())
-
- def remove_broadcast_domain_ports(self, ports):
- """
- Deletes broadcast domain ports
- :param: ports to be removed.
- """
- domain_obj = netapp_utils.zapi.NaElement('net-port-broadcast-domain-remove-ports')
- domain_obj.add_new_child("broadcast-domain", self.parameters['resource_name'])
- if self.parameters.get('ipspace'):
- domain_obj.add_new_child("ipspace", self.parameters['ipspace'])
- ports_obj = netapp_utils.zapi.NaElement('ports')
- domain_obj.add_child_elem(ports_obj)
- for port in ports:
- ports_obj.add_new_child('net-qualified-port-name', port)
- try:
- self.server.invoke_successfully(domain_obj, True)
- return True
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg='Error removing port for broadcast domain %s: %s' %
- (self.parameters['resource_name'], to_native(error)),
- exception=traceback.format_exc())
-
- def get_broadcast_domain_ports(self):
- """
- Return details about the broadcast domain ports.
- :return: Details about the broadcast domain ports. [] if not found.
- :rtype: list
- """
- domain_get_iter = netapp_utils.zapi.NaElement('net-port-broadcast-domain-get-iter')
- broadcast_domain_info = netapp_utils.zapi.NaElement('net-port-broadcast-domain-info')
- broadcast_domain_info.add_new_child('broadcast-domain', self.parameters['resource_name'])
- query = netapp_utils.zapi.NaElement('query')
- query.add_child_elem(broadcast_domain_info)
- domain_get_iter.add_child_elem(query)
- result = self.server.invoke_successfully(domain_get_iter, True)
- ports = []
- if result.get_child_by_name('num-records') and \
- int(result.get_child_content('num-records')) == 1:
- domain_info = result.get_child_by_name('attributes-list').get_child_by_name('net-port-broadcast-domain-info')
- domain_ports = domain_info.get_child_by_name('ports')
- if domain_ports is not None:
- ports = [port.get_child_content('port') for port in domain_ports.get_children()]
- return ports
-
- def remove_portset_ports(self, port):
- """
- Removes all existing ports from portset
- :return: None
- """
- options = {'portset-name': self.parameters['resource_name'],
- 'portset-port-name': port.strip()}
-
- portset_modify = netapp_utils.zapi.NaElement.create_node_with_children('portset-remove', **options)
-
- try:
- self.server.invoke_successfully(portset_modify, enable_tunneling=True)
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg='Error removing port in portset %s: %s' %
- (self.parameters['resource_name'], to_native(error)), exception=traceback.format_exc())
-
- def add_portset_ports(self, port):
- """
- Add the list of ports to portset
- :return: None
- """
- options = {'portset-name': self.parameters['resource_name'],
- 'portset-port-name': port.strip()}
-
- portset_modify = netapp_utils.zapi.NaElement.create_node_with_children('portset-add', **options)
-
- try:
- self.server.invoke_successfully(portset_modify, enable_tunneling=True)
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg='Error adding port in portset %s: %s' %
- (self.parameters['resource_name'], to_native(error)), exception=traceback.format_exc())
-
- def portset_get_iter(self):
- """
- Compose NaElement object to query current portset using vserver, portset-name and portset-type parameters
- :return: NaElement object for portset-get-iter with query
- """
- portset_get = netapp_utils.zapi.NaElement('portset-get-iter')
- query = netapp_utils.zapi.NaElement('query')
- portset_info = netapp_utils.zapi.NaElement('portset-info')
- portset_info.add_new_child('vserver', self.parameters['vserver'])
- portset_info.add_new_child('portset-name', self.parameters['resource_name'])
- if self.parameters.get('portset_type'):
- portset_info.add_new_child('portset-type', self.parameters['portset_type'])
- query.add_child_elem(portset_info)
- portset_get.add_child_elem(query)
- return portset_get
-
- def portset_get(self):
- """
- Get current portset info
- :return: List of current ports if query successful, else return []
- """
- portset_get_iter = self.portset_get_iter()
- result, ports = None, []
- try:
- result = self.server.invoke_successfully(portset_get_iter, enable_tunneling=True)
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg='Error fetching portset %s: %s'
- % (self.parameters['resource_name'], to_native(error)),
- exception=traceback.format_exc())
- # return portset details
- if result.get_child_by_name('num-records') and int(result.get_child_content('num-records')) > 0:
- portset_get_info = result.get_child_by_name('attributes-list').get_child_by_name('portset-info')
- if int(portset_get_info.get_child_content('portset-port-total')) > 0:
- port_info = portset_get_info.get_child_by_name('portset-port-info')
- ports = [port.get_content() for port in port_info.get_children()]
- return ports
-
- def modify_broadcast_domain_ports(self):
- """
- compare current and desire ports. Call add or remove ports methods if needed.
- :return: None.
- """
- current_ports = self.get_broadcast_domain_ports()
- cd_ports = self.parameters['names']
- if self.parameters['state'] == 'present':
- ports_to_add = [port for port in cd_ports if port not in current_ports]
- if len(ports_to_add) > 0:
- self.add_broadcast_domain_ports(ports_to_add)
- self.na_helper.changed = True
-
- if self.parameters['state'] == 'absent':
- ports_to_remove = [port for port in cd_ports if port in current_ports]
- if len(ports_to_remove) > 0:
- self.remove_broadcast_domain_ports(ports_to_remove)
- self.na_helper.changed = True
-
- def modify_portset_ports(self):
- current_ports = self.portset_get()
- cd_ports = self.parameters['names']
- if self.parameters['state'] == 'present':
- ports_to_add = [port for port in cd_ports if port not in current_ports]
- if len(ports_to_add) > 0:
- for port in ports_to_add:
- self.add_portset_ports(port)
- self.na_helper.changed = True
-
- if self.parameters['state'] == 'absent':
- ports_to_remove = [port for port in cd_ports if port in current_ports]
- if len(ports_to_remove) > 0:
- for port in ports_to_remove:
- self.remove_portset_ports(port)
- self.na_helper.changed = True
-
- def apply(self):
- self.asup_log_for_cserver("na_ontap_ports")
- if self.parameters['resource_type'] == 'broadcast_domain':
- self.modify_broadcast_domain_ports()
- elif self.parameters['resource_type'] == 'portset':
- self.modify_portset_ports()
- self.module.exit_json(changed=self.na_helper.changed)
-
- def asup_log_for_cserver(self, event_name):
- """
- Fetch admin vserver for the given cluster
- Create and Autosupport log event with the given module name
- :param event_name: Name of the event log
- :return: None
- """
- results = netapp_utils.get_cserver(self.server)
- cserver = netapp_utils.setup_na_ontap_zapi(module=self.module, vserver=results)
- netapp_utils.ems_log_event(event_name, cserver)
-
-
-def main():
- portset_obj = NetAppOntapPorts()
- portset_obj.apply()
-
-
-if __name__ == '__main__':
- main()
diff --git a/lib/ansible/modules/storage/netapp/na_ontap_portset.py b/lib/ansible/modules/storage/netapp/na_ontap_portset.py
deleted file mode 100644
index 2cb2d7b289..0000000000
--- a/lib/ansible/modules/storage/netapp/na_ontap_portset.py
+++ /dev/null
@@ -1,278 +0,0 @@
-#!/usr/bin/python
-
-# (c) 2018-2019, NetApp, Inc
-# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-
-from __future__ import absolute_import, division, print_function
-
-__metaclass__ = type
-
-ANSIBLE_METADATA = {'metadata_version': '1.1',
- 'status': ['preview'],
- 'supported_by': 'community'}
-
-DOCUMENTATION = '''
-short_description: NetApp ONTAP Create/Delete portset
-author: NetApp Ansible Team (@carchi8py) <ng-ansibleteam@netapp.com>
-description:
- - Create/Delete ONTAP portset, modify ports in a portset.
-extends_documentation_fragment:
- - netapp.na_ontap
-module: na_ontap_portset
-options:
- state:
- description:
- - If you want to create a portset.
- default: present
- vserver:
- required: true
- description:
- - Name of the SVM.
- name:
- required: true
- description:
- - Name of the port set to create.
- type:
- description:
- - Required for create.
- - Protocols accepted for this portset.
- choices: ['fcp', 'iscsi', 'mixed']
- force:
- description:
- - If 'false' or not specified, the request will fail if there are any igroups bound to this portset.
- - If 'true', forcibly destroy the portset, even if there are existing igroup bindings.
- type: bool
- default: False
- ports:
- description:
- - Specify the ports associated with this portset. Should be comma separated.
- - It represents the expected state of a list of ports at any time, and replaces the current value of ports.
- - Adds a port if it is specified in expected state but not in current state.
- - Deletes a port if it is in current state but not in expected state.
-version_added: "2.8"
-
-'''
-
-EXAMPLES = """
- - name: Create Portset
- na_ontap_portset:
- state: present
- vserver: vserver_name
- name: portset_name
- ports: a1
- type: "{{ protocol type }}"
- username: "{{ netapp username }}"
- password: "{{ netapp password }}"
- hostname: "{{ netapp hostname }}"
-
- - name: Modify ports in portset
- na_ontap_portset:
- state: present
- vserver: vserver_name
- name: portset_name
- ports: a1,a2
- username: "{{ netapp username }}"
- password: "{{ netapp password }}"
- hostname: "{{ netapp hostname }}"
-
- - name: Delete Portset
- na_ontap_portset:
- state: absent
- vserver: vserver_name
- name: portset_name
- force: True
- type: "{{ protocol type }}"
- username: "{{ netapp username }}"
- password: "{{ netapp password }}"
- hostname: "{{ netapp hostname }}"
-"""
-
-RETURN = """
-"""
-
-import traceback
-
-from ansible.module_utils.basic import AnsibleModule
-from ansible.module_utils._text import to_native
-import ansible.module_utils.netapp as netapp_utils
-from ansible.module_utils.netapp_module import NetAppModule
-
-HAS_NETAPP_LIB = netapp_utils.has_netapp_lib()
-
-
-class NetAppONTAPPortset(object):
- """
- Methods to create or delete portset
- """
-
- def __init__(self):
- self.argument_spec = netapp_utils.na_ontap_host_argument_spec()
- self.argument_spec.update(dict(
- state=dict(required=False, default='present'),
- vserver=dict(required=True, type='str'),
- name=dict(required=True, type='str'),
- type=dict(required=False, type='str', choices=[
- 'fcp', 'iscsi', 'mixed']),
- force=dict(required=False, type='bool', default=False),
- ports=dict(required=False, type='list')
- ))
-
- self.module = AnsibleModule(
- argument_spec=self.argument_spec,
- supports_check_mode=True
- )
-
- self.na_helper = NetAppModule()
- self.parameters = self.na_helper.set_parameters(self.module.params)
-
- if HAS_NETAPP_LIB is False:
- self.module.fail_json(
- msg="the python NetApp-Lib module is required")
- else:
- self.server = netapp_utils.setup_na_ontap_zapi(
- module=self.module, vserver=self.parameters['vserver'])
-
- def portset_get_iter(self):
- """
- Compose NaElement object to query current portset using vserver, portset-name and portset-type parameters
- :return: NaElement object for portset-get-iter with query
- """
- portset_get = netapp_utils.zapi.NaElement('portset-get-iter')
- query = netapp_utils.zapi.NaElement('query')
- portset_info = netapp_utils.zapi.NaElement('portset-info')
- portset_info.add_new_child('vserver', self.parameters['vserver'])
- portset_info.add_new_child('portset-name', self.parameters['name'])
- if self.parameters.get('type'):
- portset_info.add_new_child('portset-type', self.parameters['type'])
- query.add_child_elem(portset_info)
- portset_get.add_child_elem(query)
- return portset_get
-
- def portset_get(self):
- """
- Get current portset info
- :return: Dictionary of current portset details if query successful, else return None
- """
- portset_get_iter = self.portset_get_iter()
- result, portset_info = None, dict()
- try:
- result = self.server.invoke_successfully(portset_get_iter, enable_tunneling=True)
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg='Error fetching portset %s: %s'
- % (self.parameters['name'], to_native(error)),
- exception=traceback.format_exc())
- # return portset details
- if result.get_child_by_name('num-records') and int(result.get_child_content('num-records')) > 0:
- portset_get_info = result.get_child_by_name('attributes-list').get_child_by_name('portset-info')
- if int(portset_get_info.get_child_content('portset-port-total')) > 0:
- ports = portset_get_info.get_child_by_name('portset-port-info')
- portset_info['ports'] = [port.get_content() for port in ports.get_children()]
- else:
- portset_info['ports'] = []
- return portset_info
- return None
-
- def create_portset(self):
- """
- Create a portset
- """
- if self.parameters.get('type') is None:
- self.module.fail_json(msg='Error: Missing required parameter for create (type)')
- portset_info = netapp_utils.zapi.NaElement("portset-create")
- portset_info.add_new_child("portset-name", self.parameters['name'])
- portset_info.add_new_child("portset-type", self.parameters['type'])
- try:
- self.server.invoke_successfully(
- portset_info, enable_tunneling=True)
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg="Error creating portset %s: %s" %
- (self.parameters['name'], to_native(error)),
- exception=traceback.format_exc())
-
- def delete_portset(self):
- """
- Delete a portset
- """
- portset_info = netapp_utils.zapi.NaElement("portset-destroy")
- portset_info.add_new_child("portset-name", self.parameters['name'])
- if self.parameters.get('force'):
- portset_info.add_new_child("force", str(self.parameters['force']))
- try:
- self.server.invoke_successfully(
- portset_info, enable_tunneling=True)
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg="Error deleting portset %s: %s" %
- (self.parameters['name'], to_native(error)),
- exception=traceback.format_exc())
-
- def remove_ports(self, ports):
- """
- Removes all existing ports from portset
- :return: None
- """
- for port in ports:
- self.modify_port(port, 'portset-remove', 'removing')
-
- def add_ports(self):
- """
- Add the list of ports to portset
- :return: None
- """
- # don't add if ports is empty string
- if self.parameters.get('ports') == [''] or self.parameters.get('ports') is None:
- return
- for port in self.parameters['ports']:
- self.modify_port(port, 'portset-add', 'adding')
-
- def modify_port(self, port, zapi, action):
- """
- Add or remove an port to/from a portset
- """
- port.strip() # remove leading spaces if any (eg: if user types a space after comma in initiators list)
- options = {'portset-name': self.parameters['name'],
- 'portset-port-name': port}
-
- portset_modify = netapp_utils.zapi.NaElement.create_node_with_children(zapi, **options)
-
- try:
- self.server.invoke_successfully(portset_modify, enable_tunneling=True)
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg='Error %s port in portset %s: %s' % (action, self.parameters['name'],
- to_native(error)),
- exception=traceback.format_exc())
-
- def apply(self):
- """
- Applies action from playbook
- """
- netapp_utils.ems_log_event("na_ontap_autosupport", self.server)
- current, modify = self.portset_get(), None
- cd_action = self.na_helper.get_cd_action(current, self.parameters)
- if cd_action is None and self.parameters['state'] == 'present':
- modify = self.na_helper.get_modified_attributes(current, self.parameters)
-
- if self.na_helper.changed:
- if self.module.check_mode:
- pass
- else:
- if cd_action == 'create':
- self.create_portset()
- self.add_ports()
- elif cd_action == 'delete':
- self.delete_portset()
- elif modify:
- self.remove_ports(current['ports'])
- self.add_ports()
- self.module.exit_json(changed=self.na_helper.changed)
-
-
-def main():
- """
- Execute action from playbook
- """
- portset_obj = NetAppONTAPPortset()
- portset_obj.apply()
-
-
-if __name__ == '__main__':
- main()
diff --git a/lib/ansible/modules/storage/netapp/na_ontap_qos_adaptive_policy_group.py b/lib/ansible/modules/storage/netapp/na_ontap_qos_adaptive_policy_group.py
deleted file mode 100644
index 1a5b611c1c..0000000000
--- a/lib/ansible/modules/storage/netapp/na_ontap_qos_adaptive_policy_group.py
+++ /dev/null
@@ -1,335 +0,0 @@
-#!/usr/bin/python
-
-# (c) 2018-2019, NetApp, Inc
-# GNU General Public License v3.0+
-# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-
-from __future__ import absolute_import, division, print_function
-__metaclass__ = type
-
-ANSIBLE_METADATA = {'metadata_version': '1.1',
- 'status': ['preview'],
- 'supported_by': 'certified'}
-
-DOCUMENTATION = '''
-module: na_ontap_qos_adaptive_policy_group
-short_description: NetApp ONTAP Adaptive Quality of Service policy group.
-extends_documentation_fragment:
- - netapp.na_ontap
-version_added: '2.9'
-author: NetApp Ansible Team (@joshedmonds) <ng-ansibleteam@netapp.com>
-
-description:
- - Create, destroy, modify, or rename an Adaptive QoS policy group on NetApp ONTAP. Module is based on the standard QoS policy group module.
-
-options:
- state:
- choices: ['present', 'absent']
- description:
- - Whether the specified policy group should exist or not.
- default: 'present'
- type: str
-
- name:
- description:
- - The name of the policy group to manage.
- type: str
- required: true
-
- vserver:
- description:
- - Name of the vserver to use.
- type: str
- required: true
-
- from_name:
- description:
- - Name of the existing policy group to be renamed to name.
- type: str
-
- absolute_min_iops:
- description:
- - Absolute minimum IOPS defined by this policy.
- type: str
-
- expected_iops:
- description:
- - Minimum expected IOPS defined by this policy.
- type: str
-
- peak_iops:
- description:
- - Maximum possible IOPS per allocated or used TB|GB.
- type: str
-
- peak_iops_allocation:
- choices: ['allocated_space', 'used_space']
- description:
- - Whether peak_iops is specified by allocated or used space.
- default: 'used_space'
- type: str
-
- force:
- type: bool
- default: False
- description:
- - Setting to 'true' forces the deletion of the workloads associated with the policy group along with the policy group.
-'''
-
-EXAMPLES = """
- - name: create adaptive qos policy group
- na_ontap_qos_adaptive_policy_group:
- state: present
- name: aq_policy_1
- vserver: policy_vserver
- absolute_min_iops: 70IOPS
- expected_iops: 100IOPS/TB
- peak_iops: 250IOPS/TB
- peak_iops_allocation: allocated_space
- hostname: 10.193.78.30
- username: admin
- password: netapp1!
-
- - name: modify adaptive qos policy group expected iops
- na_ontap_qos_adaptive_policy_group:
- state: present
- name: aq_policy_1
- vserver: policy_vserver
- absolute_min_iops: 70IOPS
- expected_iops: 125IOPS/TB
- peak_iops: 250IOPS/TB
- peak_iops_allocation: allocated_space
- hostname: 10.193.78.30
- username: admin
- password: netapp1!
-
- - name: modify adaptive qos policy group peak iops allocation
- na_ontap_qos_adaptive_policy_group:
- state: present
- name: aq_policy_1
- vserver: policy_vserver
- absolute_min_iops: 70IOPS
- expected_iops: 125IOPS/TB
- peak_iops: 250IOPS/TB
- peak_iops_allocation: used_space
- hostname: 10.193.78.30
- username: admin
- password: netapp1!
-
- - name: delete qos policy group
- na_ontap_qos_adaptive_policy_group:
- state: absent
- name: aq_policy_1
- vserver: policy_vserver
- hostname: 10.193.78.30
- username: admin
- password: netapp1!
-
-"""
-
-RETURN = """
-"""
-
-import traceback
-
-import ansible.module_utils.netapp as netapp_utils
-from ansible.module_utils.netapp_module import NetAppModule
-from ansible.module_utils.basic import AnsibleModule
-from ansible.module_utils._text import to_native
-
-HAS_NETAPP_LIB = netapp_utils.has_netapp_lib()
-
-
-class NetAppOntapAdaptiveQosPolicyGroup(object):
- """
- Create, delete, modify and rename a policy group.
- """
- def __init__(self):
- """
- Initialize the Ontap qos policy group class.
- """
- self.argument_spec = netapp_utils.na_ontap_host_argument_spec()
- self.argument_spec.update(dict(
- state=dict(required=False, type='str', choices=['present', 'absent'], default='present'),
- name=dict(required=True, type='str'),
- from_name=dict(required=False, type='str'),
- vserver=dict(required=True, type='str'),
- absolute_min_iops=dict(required=False, type='str'),
- expected_iops=dict(required=False, type='str'),
- peak_iops=dict(required=False, type='str'),
- peak_iops_allocation=dict(choices=['allocated_space', 'used_space'], default='used_space'),
- force=dict(required=False, type='bool', default=False)
- ))
-
- self.module = AnsibleModule(
- argument_spec=self.argument_spec,
- supports_check_mode=True
- )
- self.na_helper = NetAppModule()
- self.parameters = self.na_helper.set_parameters(self.module.params)
-
- if HAS_NETAPP_LIB is False:
- self.module.fail_json(
- msg="the python NetApp-Lib module is required")
- else:
- self.server = netapp_utils.setup_na_ontap_zapi(
- module=self.module)
-
- def get_policy_group(self, policy_group_name=None):
- """
- Return details of a policy group.
- :param policy_group_name: policy group name
- :return: policy group details.
- :rtype: dict.
- """
- if policy_group_name is None:
- policy_group_name = self.parameters['name']
- policy_group_get_iter = netapp_utils.zapi.NaElement('qos-adaptive-policy-group-get-iter')
- policy_group_info = netapp_utils.zapi.NaElement('qos-adaptive-policy-group-info')
- policy_group_info.add_new_child('policy-group', policy_group_name)
- policy_group_info.add_new_child('vserver', self.parameters['vserver'])
- query = netapp_utils.zapi.NaElement('query')
- query.add_child_elem(policy_group_info)
- policy_group_get_iter.add_child_elem(query)
- result = self.server.invoke_successfully(policy_group_get_iter, True)
- policy_group_detail = None
-
- if result.get_child_by_name('num-records') and int(result.get_child_content('num-records')) == 1:
- policy_info = result.get_child_by_name('attributes-list').get_child_by_name('qos-adaptive-policy-group-info')
-
- policy_group_detail = {
- 'name': policy_info.get_child_content('policy-group'),
- 'vserver': policy_info.get_child_content('vserver'),
- 'absolute_min_iops': policy_info.get_child_content('absolute-min-iops'),
- 'expected_iops': policy_info.get_child_content('expected-iops'),
- 'peak_iops': policy_info.get_child_content('peak-iops'),
- 'peak_iops_allocation': policy_info.get_child_content('peak-iops-allocation')
- }
- return policy_group_detail
-
- def create_policy_group(self):
- """
- create a policy group name.
- """
- policy_group = netapp_utils.zapi.NaElement('qos-adaptive-policy-group-create')
- policy_group.add_new_child('policy-group', self.parameters['name'])
- policy_group.add_new_child('vserver', self.parameters['vserver'])
- if self.parameters.get('absolute_min_iops'):
- policy_group.add_new_child('absolute-min-iops', self.parameters['absolute_min_iops'])
- if self.parameters.get('expected_iops'):
- policy_group.add_new_child('expected-iops', self.parameters['expected_iops'])
- if self.parameters.get('peak_iops'):
- policy_group.add_new_child('peak-iops', self.parameters['peak_iops'])
- if self.parameters.get('peak_iops_allocation'):
- policy_group.add_new_child('peak-iops-allocation', self.parameters['peak_iops_allocation'])
- try:
- self.server.invoke_successfully(policy_group, True)
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg='Error creating adaptive qos policy group %s: %s' %
- (self.parameters['name'], to_native(error)),
- exception=traceback.format_exc())
-
- def delete_policy_group(self, policy_group=None):
- """
- delete an existing policy group.
- :param policy_group: policy group name.
- """
- if policy_group is None:
- policy_group = self.parameters['name']
- policy_group_obj = netapp_utils.zapi.NaElement('qos-adaptive-policy-group-delete')
- policy_group_obj.add_new_child('policy-group', policy_group)
- if self.parameters.get('force'):
- policy_group_obj.add_new_child('force', str(self.parameters['force']))
- try:
- self.server.invoke_successfully(policy_group_obj, True)
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg='Error deleting adaptive qos policy group %s: %s' %
- (policy_group, to_native(error)),
- exception=traceback.format_exc())
-
- def modify_policy_group(self):
- """
- Modify policy group.
- """
- policy_group_obj = netapp_utils.zapi.NaElement('qos-adaptive-policy-group-modify')
- policy_group_obj.add_new_child('policy-group', self.parameters['name'])
- if self.parameters.get('absolute_min_iops'):
- policy_group_obj.add_new_child('absolute-min-iops', self.parameters['absolute_min_iops'])
- if self.parameters.get('expected_iops'):
- policy_group_obj.add_new_child('expected-iops', self.parameters['expected_iops'])
- if self.parameters.get('peak_iops'):
- policy_group_obj.add_new_child('peak-iops', self.parameters['peak_iops'])
- if self.parameters.get('peak_iops_allocation'):
- policy_group_obj.add_new_child('peak-iops-allocation', self.parameters['peak_iops_allocation'])
- try:
- self.server.invoke_successfully(policy_group_obj, True)
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg='Error modifying adaptive qos policy group %s: %s' %
- (self.parameters['name'], to_native(error)),
- exception=traceback.format_exc())
-
- def rename_policy_group(self):
- """
- Rename policy group name.
- """
- rename_obj = netapp_utils.zapi.NaElement('qos-adaptive-policy-group-rename')
- rename_obj.add_new_child('new-name', self.parameters['name'])
- rename_obj.add_new_child('policy-group-name', self.parameters['from_name'])
- try:
- self.server.invoke_successfully(rename_obj, True)
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg='Error renaming adaptive qos policy group %s: %s' %
- (self.parameters['from_name'], to_native(error)),
- exception=traceback.format_exc())
-
- def modify_helper(self, modify):
- """
- helper method to modify policy group.
- :param modify: modified attributes.
- """
- for attribute in modify.keys():
- if attribute in ['absolute_min_iops', 'expected_iops', 'peak_iops', 'peak_iops_allocation']:
- self.modify_policy_group()
-
- def apply(self):
- """
- Run module based on playbook
- """
- self.autosupport_log("na_ontap_qos_policy_group")
- current = self.get_policy_group()
- rename, cd_action = None, None
- if self.parameters.get('from_name'):
- rename = self.na_helper.is_rename_action(self.get_policy_group(self.parameters['from_name']), current)
- else:
- cd_action = self.na_helper.get_cd_action(current, self.parameters)
- modify = self.na_helper.get_modified_attributes(current, self.parameters)
- if self.na_helper.changed:
- if self.module.check_mode:
- pass
- else:
- if rename:
- self.rename_policy_group()
- if cd_action == 'create':
- self.create_policy_group()
- elif cd_action == 'delete':
- self.delete_policy_group()
- elif modify:
- self.modify_helper(modify)
- self.module.exit_json(changed=self.na_helper.changed)
-
- def autosupport_log(self, event_name):
- """
- Create a log event against the provided vserver
- """
- server = netapp_utils.setup_na_ontap_zapi(module=self.module, vserver=self.parameters['vserver'])
- netapp_utils.ems_log_event(event_name, server)
-
-
-def main():
- '''Apply vserver operations from playbook'''
- qos_policy_group = NetAppOntapAdaptiveQosPolicyGroup()
- qos_policy_group.apply()
-
-
-if __name__ == '__main__':
- main()
diff --git a/lib/ansible/modules/storage/netapp/na_ontap_qos_policy_group.py b/lib/ansible/modules/storage/netapp/na_ontap_qos_policy_group.py
deleted file mode 100644
index ed925fb30c..0000000000
--- a/lib/ansible/modules/storage/netapp/na_ontap_qos_policy_group.py
+++ /dev/null
@@ -1,290 +0,0 @@
-#!/usr/bin/python
-
-# (c) 2018-2019, NetApp, Inc
-# GNU General Public License v3.0+
-# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-
-from __future__ import absolute_import, division, print_function
-__metaclass__ = type
-
-ANSIBLE_METADATA = {'metadata_version': '1.1',
- 'status': ['preview'],
- 'supported_by': 'certified'}
-
-DOCUMENTATION = '''
-module: na_ontap_qos_policy_group
-short_description: NetApp ONTAP manage policy group in Quality of Service.
-extends_documentation_fragment:
- - netapp.na_ontap
-version_added: '2.8'
-author: NetApp Ansible Team (@carchi8py) <ng-ansibleteam@netapp.com>
-
-description:
- - Create, destroy, modify, or rename QoS policy group on NetApp ONTAP.
-
-options:
- state:
- choices: ['present', 'absent']
- description:
- - Whether the specified policy group should exist or not.
- default: 'present'
-
- name:
- description:
- - The name of the policy group to manage.
-
- vserver:
- description:
- - Name of the vserver to use.
-
- from_name:
- description:
- - Name of the existing policy group to be renamed to name.
-
- max_throughput:
- description:
- - Maximum throughput defined by this policy.
-
- min_throughput:
- description:
- - Minimum throughput defined by this policy.
-
- force:
- type: bool
- default: False
- description:
- - Setting to 'true' forces the deletion of the workloads associated with the policy group along with the policy group.
-'''
-
-EXAMPLES = """
- - name: create qos policy group
- na_ontap_qos_policy_group:
- state: present
- name: policy_1
- vserver: policy_vserver
- max_throughput: 800KB/s,800iops
- min_throughput: 100iops
- hostname: 10.193.78.30
- username: admin
- password: netapp1!
-
- - name: modify qos policy group max throughput
- na_ontap_qos_policy_group:
- state: present
- name: policy_1
- vserver: policy_vserver
- max_throughput: 900KB/s,800iops
- min_throughput: 100iops
- hostname: 10.193.78.30
- username: admin
- password: netapp1!
-
- - name: delete qos policy group
- na_ontap_qos_policy_group:
- state: absent
- name: policy_1
- vserver: policy_vserver
- hostname: 10.193.78.30
- username: admin
- password: netapp1!
-
-"""
-
-RETURN = """
-"""
-
-import traceback
-
-import ansible.module_utils.netapp as netapp_utils
-from ansible.module_utils.netapp_module import NetAppModule
-from ansible.module_utils.basic import AnsibleModule
-from ansible.module_utils._text import to_native
-
-HAS_NETAPP_LIB = netapp_utils.has_netapp_lib()
-
-
-class NetAppOntapQosPolicyGroup(object):
- """
- Create, delete, modify and rename a policy group.
- """
- def __init__(self):
- """
- Initialize the Ontap qos policy group class.
- """
- self.argument_spec = netapp_utils.na_ontap_host_argument_spec()
- self.argument_spec.update(dict(
- state=dict(required=False, type='str', choices=['present', 'absent'], default='present'),
- name=dict(required=True, type='str'),
- from_name=dict(required=False, type='str'),
- vserver=dict(required=True, type='str'),
- max_throughput=dict(required=False, type='str'),
- min_throughput=dict(required=False, type='str'),
- force=dict(required=False, type='bool', default=False)
- ))
-
- self.module = AnsibleModule(
- argument_spec=self.argument_spec,
- supports_check_mode=True
- )
- self.na_helper = NetAppModule()
- self.parameters = self.na_helper.set_parameters(self.module.params)
-
- if HAS_NETAPP_LIB is False:
- self.module.fail_json(
- msg="the python NetApp-Lib module is required")
- else:
- self.server = netapp_utils.setup_na_ontap_zapi(
- module=self.module)
-
- def get_policy_group(self, policy_group_name=None):
- """
- Return details of a policy group.
- :param policy_group_name: policy group name
- :return: policy group details.
- :rtype: dict.
- """
- if policy_group_name is None:
- policy_group_name = self.parameters['name']
- policy_group_get_iter = netapp_utils.zapi.NaElement('qos-policy-group-get-iter')
- policy_group_info = netapp_utils.zapi.NaElement('qos-policy-group-info')
- policy_group_info.add_new_child('policy-group', policy_group_name)
- policy_group_info.add_new_child('vserver', self.parameters['vserver'])
- query = netapp_utils.zapi.NaElement('query')
- query.add_child_elem(policy_group_info)
- policy_group_get_iter.add_child_elem(query)
- result = self.server.invoke_successfully(policy_group_get_iter, True)
- policy_group_detail = None
-
- if result.get_child_by_name('num-records') and int(result.get_child_content('num-records')) == 1:
- policy_info = result.get_child_by_name('attributes-list').get_child_by_name('qos-policy-group-info')
-
- policy_group_detail = {
- 'name': policy_info.get_child_content('policy-group'),
- 'vserver': policy_info.get_child_content('vserver'),
- 'max_throughput': policy_info.get_child_content('max-throughput'),
- 'min_throughput': policy_info.get_child_content('min-throughput')
- }
- return policy_group_detail
-
- def create_policy_group(self):
- """
- create a policy group name.
- """
- policy_group = netapp_utils.zapi.NaElement('qos-policy-group-create')
- policy_group.add_new_child('policy-group', self.parameters['name'])
- policy_group.add_new_child('vserver', self.parameters['vserver'])
- if self.parameters.get('max_throughput'):
- policy_group.add_new_child('max-throughput', self.parameters['max_throughput'])
- if self.parameters.get('min_throughput'):
- policy_group.add_new_child('min-throughput', self.parameters['min_throughput'])
- try:
- self.server.invoke_successfully(policy_group, True)
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg='Error creating qos policy group %s: %s' %
- (self.parameters['name'], to_native(error)),
- exception=traceback.format_exc())
-
- def delete_policy_group(self, policy_group=None):
- """
- delete an existing policy group.
- :param policy_group: policy group name.
- """
- if policy_group is None:
- policy_group = self.parameters['name']
- policy_group_obj = netapp_utils.zapi.NaElement('qos-policy-group-delete')
- policy_group_obj.add_new_child('policy-group', policy_group)
- if self.parameters.get('force'):
- policy_group_obj.add_new_child('force', str(self.parameters['force']))
- try:
- self.server.invoke_successfully(policy_group_obj, True)
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg='Error deleting qos policy group %s: %s' %
- (policy_group, to_native(error)),
- exception=traceback.format_exc())
-
- def modify_policy_group(self):
- """
- Modify policy group.
- """
- policy_group_obj = netapp_utils.zapi.NaElement('qos-policy-group-modify')
- policy_group_obj.add_new_child('policy-group', self.parameters['name'])
- if self.parameters.get('max_throughput'):
- policy_group_obj.add_new_child('max-throughput', self.parameters['max_throughput'])
- if self.parameters.get('min_throughput'):
- policy_group_obj.add_new_child('min-throughput', self.parameters['min_throughput'])
- try:
- self.server.invoke_successfully(policy_group_obj, True)
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg='Error modifying qos policy group %s: %s' %
- (self.parameters['name'], to_native(error)),
- exception=traceback.format_exc())
-
- def rename_policy_group(self):
- """
- Rename policy group name.
- """
- rename_obj = netapp_utils.zapi.NaElement('qos-policy-group-rename')
- rename_obj.add_new_child('new-name', self.parameters['name'])
- rename_obj.add_new_child('policy-group-name', self.parameters['from_name'])
- try:
- self.server.invoke_successfully(rename_obj, True)
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg='Error renaming qos policy group %s: %s' %
- (self.parameters['from_name'], to_native(error)),
- exception=traceback.format_exc())
-
- def modify_helper(self, modify):
- """
- helper method to modify policy group.
- :param modify: modified attributes.
- """
- for attribute in modify.keys():
- if attribute in ['max_throughput', 'min_throughput']:
- self.modify_policy_group()
-
- def apply(self):
- """
- Run module based on playbook
- """
- self.asup_log_for_cserver("na_ontap_qos_policy_group")
- current = self.get_policy_group()
- rename, cd_action = None, None
- if self.parameters.get('from_name'):
- rename = self.na_helper.is_rename_action(self.get_policy_group(self.parameters['from_name']), current)
- else:
- cd_action = self.na_helper.get_cd_action(current, self.parameters)
- modify = self.na_helper.get_modified_attributes(current, self.parameters)
- if self.na_helper.changed:
- if self.module.check_mode:
- pass
- else:
- if rename:
- self.rename_policy_group()
- if cd_action == 'create':
- self.create_policy_group()
- elif cd_action == 'delete':
- self.delete_policy_group()
- elif modify:
- self.modify_helper(modify)
- self.module.exit_json(changed=self.na_helper.changed)
-
- def asup_log_for_cserver(self, event_name):
- """
- Fetch admin vserver for the given cluster
- Create and Autosupport log event with the given module name
- :param event_name: Name of the event log
- :return: None
- """
- results = netapp_utils.get_cserver(self.server)
- cserver = netapp_utils.setup_na_ontap_zapi(module=self.module, vserver=results)
- netapp_utils.ems_log_event(event_name, cserver)
-
-
-def main():
- '''Apply vserver operations from playbook'''
- qos_policy_group = NetAppOntapQosPolicyGroup()
- qos_policy_group.apply()
-
-
-if __name__ == '__main__':
- main()
diff --git a/lib/ansible/modules/storage/netapp/na_ontap_qtree.py b/lib/ansible/modules/storage/netapp/na_ontap_qtree.py
deleted file mode 100644
index 9dc1336b02..0000000000
--- a/lib/ansible/modules/storage/netapp/na_ontap_qtree.py
+++ /dev/null
@@ -1,303 +0,0 @@
-#!/usr/bin/python
-
-# (c) 2018-2019, NetApp, Inc
-# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-from __future__ import absolute_import, division, print_function
-__metaclass__ = type
-
-ANSIBLE_METADATA = {'metadata_version': '1.1',
- 'status': ['preview'],
- 'supported_by': 'certified'}
-
-
-DOCUMENTATION = '''
-
-module: na_ontap_qtree
-
-short_description: NetApp ONTAP manage qtrees
-extends_documentation_fragment:
- - netapp.na_ontap
-version_added: '2.6'
-author: NetApp Ansible Team (@carchi8py) <ng-ansibleteam@netapp.com>
-
-description:
-- Create or destroy Qtrees.
-
-options:
-
- state:
- description:
- - Whether the specified qtree should exist or not.
- choices: ['present', 'absent']
- default: 'present'
-
- name:
- description:
- - The name of the qtree to manage.
- required: true
- type: str
-
- from_name:
- description:
- - Name of the qtree to be renamed.
- version_added: '2.7'
- type: str
-
- flexvol_name:
- description:
- - The name of the FlexVol the qtree should exist on. Required when C(state=present).
- required: true
- type: str
-
- vserver:
- description:
- - The name of the vserver to use.
- required: true
- type: str
-
- export_policy:
- description:
- - The name of the export policy to apply.
- version_added: '2.9'
- type: str
-
- security_style:
- description:
- - The security style for the qtree.
- choices: ['unix', 'ntfs', 'mixed']
- version_added: '2.9'
-
- oplocks:
- description:
- - Whether the oplocks should be enabled or not for the qtree.
- choices: ['enabled', 'disabled']
- version_added: '2.9'
-
- unix_permissions:
- description:
- - File permissions bits of the qtree.
- version_added: '2.9'
- type: str
-
-'''
-
-EXAMPLES = """
-- name: Create Qtrees
- na_ontap_qtree:
- state: present
- name: ansibleQTree
- flexvol_name: ansibleVolume
- export_policy: policyName
- security_style: mixed
- oplocks: disabled
- unix_permissions:
- vserver: ansibleVServer
- hostname: "{{ netapp_hostname }}"
- username: "{{ netapp_username }}"
- password: "{{ netapp_password }}"
-
-- name: Rename Qtrees
- na_ontap_qtree:
- state: present
- from_name: ansibleQTree_rename
- name: ansibleQTree
- flexvol_name: ansibleVolume
- vserver: ansibleVServer
- hostname: "{{ netapp_hostname }}"
- username: "{{ netapp_username }}"
- password: "{{ netapp_password }}"
-"""
-
-RETURN = """
-
-"""
-import traceback
-from ansible.module_utils.basic import AnsibleModule
-from ansible.module_utils._text import to_native
-import ansible.module_utils.netapp as netapp_utils
-from ansible.module_utils.netapp_module import NetAppModule
-
-HAS_NETAPP_LIB = netapp_utils.has_netapp_lib()
-
-
-class NetAppOntapQTree(object):
- '''Class with qtree operations'''
-
- def __init__(self):
- self.argument_spec = netapp_utils.na_ontap_host_argument_spec()
- self.argument_spec.update(dict(
- state=dict(required=False,
- choices=['present', 'absent'],
- default='present'),
- name=dict(required=True, type='str'),
- from_name=dict(required=False, type='str'),
- flexvol_name=dict(type='str'),
- vserver=dict(required=True, type='str'),
- export_policy=dict(required=False, type='str'),
- security_style=dict(required=False, choices=['unix', 'ntfs', 'mixed']),
- oplocks=dict(required=False, choices=['enabled', 'disabled']),
- unix_permissions=dict(required=False, type='str'),
- ))
-
- self.module = AnsibleModule(
- argument_spec=self.argument_spec,
- required_if=[
- ('state', 'present', ['flexvol_name'])
- ],
- supports_check_mode=True
- )
- self.na_helper = NetAppModule()
- self.parameters = self.na_helper.set_parameters(self.module.params)
-
- if HAS_NETAPP_LIB is False:
- self.module.fail_json(
- msg="the python NetApp-Lib module is required")
- else:
- self.server = netapp_utils.setup_na_ontap_zapi(
- module=self.module, vserver=self.parameters['vserver'])
-
- def get_qtree(self, name=None):
- """
- Checks if the qtree exists.
- :param:
- name : qtree name
- :return:
- Details about the qtree
- False if qtree is not found
- :rtype: bool
- """
- if name is None:
- name = self.parameters['name']
-
- qtree_list_iter = netapp_utils.zapi.NaElement('qtree-list-iter')
- query_details = netapp_utils.zapi.NaElement.create_node_with_children(
- 'qtree-info', **{'vserver': self.parameters['vserver'],
- 'volume': self.parameters['flexvol_name'],
- 'qtree': name})
- query = netapp_utils.zapi.NaElement('query')
- query.add_child_elem(query_details)
- qtree_list_iter.add_child_elem(query)
- result = self.server.invoke_successfully(qtree_list_iter,
- enable_tunneling=True)
- return_q = None
- if (result.get_child_by_name('num-records') and
- int(result.get_child_content('num-records')) >= 1):
- return_q = {'export_policy': result['attributes-list']['qtree-info']['export-policy'],
- 'unix_permissions': result['attributes-list']['qtree-info']['mode'],
- 'oplocks': result['attributes-list']['qtree-info']['oplocks'],
- 'security_style': result['attributes-list']['qtree-info']['security-style']}
-
- return return_q
-
- def create_qtree(self):
- """
- Create a qtree
- """
- options = {'qtree': self.parameters['name'], 'volume': self.parameters['flexvol_name']}
- if self.parameters.get('export_policy'):
- options['export-policy'] = self.parameters['export_policy']
- if self.parameters.get('security_style'):
- options['security-style'] = self.parameters['security_style']
- if self.parameters.get('oplocks'):
- options['oplocks'] = self.parameters['oplocks']
- if self.parameters.get('unix_permissions'):
- options['mode'] = self.parameters['unix_permissions']
- qtree_create = netapp_utils.zapi.NaElement.create_node_with_children(
- 'qtree-create', **options)
- try:
- self.server.invoke_successfully(qtree_create,
- enable_tunneling=True)
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg="Error provisioning qtree %s: %s"
- % (self.parameters['name'], to_native(error)),
- exception=traceback.format_exc())
-
- def delete_qtree(self):
- """
- Delete a qtree
- """
- path = '/vol/%s/%s' % (self.parameters['flexvol_name'], self.parameters['name'])
- qtree_delete = netapp_utils.zapi.NaElement.create_node_with_children(
- 'qtree-delete', **{'qtree': path})
-
- try:
- self.server.invoke_successfully(qtree_delete,
- enable_tunneling=True)
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg="Error deleting qtree %s: %s" % (path, to_native(error)),
- exception=traceback.format_exc())
-
- def rename_qtree(self):
- """
- Rename a qtree
- """
- path = '/vol/%s/%s' % (self.parameters['flexvol_name'], self.parameters['from_name'])
- new_path = '/vol/%s/%s' % (self.parameters['flexvol_name'], self.parameters['name'])
- qtree_rename = netapp_utils.zapi.NaElement.create_node_with_children(
- 'qtree-rename', **{'qtree': path,
- 'new-qtree-name': new_path})
-
- try:
- self.server.invoke_successfully(qtree_rename,
- enable_tunneling=True)
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg="Error renaming qtree %s: %s"
- % (self.parameters['from_name'], to_native(error)),
- exception=traceback.format_exc())
-
- def modify_qtree(self):
- """
- Modify a qtree
- """
- options = {'qtree': self.parameters['name'], 'volume': self.parameters['flexvol_name']}
- if self.parameters.get('export_policy'):
- options['export-policy'] = self.parameters['export_policy']
- if self.parameters.get('security_style'):
- options['security-style'] = self.parameters['security_style']
- if self.parameters.get('oplocks'):
- options['oplocks'] = self.parameters['oplocks']
- if self.parameters.get('unix_permissions'):
- options['mode'] = self.parameters['unix_permissions']
- qtree_modify = netapp_utils.zapi.NaElement.create_node_with_children(
- 'qtree-modify', **options)
- try:
- self.server.invoke_successfully(qtree_modify, enable_tunneling=True)
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg='Error modifying qtree %s: %s'
- % (self.parameters['name'], to_native(error)),
- exception=traceback.format_exc())
-
- def apply(self):
- '''Call create/delete/modify/rename operations'''
- netapp_utils.ems_log_event("na_ontap_qtree", self.server)
- current = self.get_qtree()
- rename, cd_action, modify = None, None, None
- if self.parameters.get('from_name'):
- rename = self.na_helper.is_rename_action(self.get_qtree(self.parameters['from_name']), current)
- else:
- cd_action = self.na_helper.get_cd_action(current, self.parameters)
- if cd_action is None and self.parameters['state'] == 'present':
- modify = self.na_helper.get_modified_attributes(current, self.parameters)
- if self.na_helper.changed:
- if self.module.check_mode:
- pass
- else:
- if rename:
- self.rename_qtree()
- if cd_action == 'create':
- self.create_qtree()
- elif cd_action == 'delete':
- self.delete_qtree()
- elif modify:
- self.modify_qtree()
- self.module.exit_json(changed=self.na_helper.changed)
-
-
-def main():
- '''Apply qtree operations from playbook'''
- qtree_obj = NetAppOntapQTree()
- qtree_obj.apply()
-
-
-if __name__ == '__main__':
- main()
diff --git a/lib/ansible/modules/storage/netapp/na_ontap_quotas.py b/lib/ansible/modules/storage/netapp/na_ontap_quotas.py
deleted file mode 100644
index 9a210b7246..0000000000
--- a/lib/ansible/modules/storage/netapp/na_ontap_quotas.py
+++ /dev/null
@@ -1,345 +0,0 @@
-#!/usr/bin/python
-
-# (c) 2018-2019, NetApp, Inc
-# GNU General Public License v3.0+
-# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-from __future__ import absolute_import, division, print_function
-__metaclass__ = type
-
-
-ANSIBLE_METADATA = {'metadata_version': '1.1',
- 'status': ['preview'],
- 'supported_by': 'certified'}
-
-
-DOCUMENTATION = '''
-module: na_ontap_quotas
-short_description: NetApp ONTAP Quotas
-extends_documentation_fragment:
- - netapp.na_ontap
-version_added: '2.8'
-author: NetApp Ansible Team (@carchi8py) <ng-ansibleteam@netapp.com>
-description:
-- Set/Modify/Delete quota on ONTAP
-options:
- state:
- description:
- - Whether the specified quota should exist or not.
- choices: ['present', 'absent']
- default: present
- type: str
- vserver:
- required: true
- description:
- - Name of the vserver to use.
- type: str
- volume:
- description:
- - The name of the volume that the quota resides on.
- required: true
- type: str
- quota_target:
- description:
- - The quota target of the type specified.
- required: true
- type: str
- qtree:
- description:
- - Name of the qtree for the quota.
- - For user or group rules, it can be the qtree name or "" if no qtree.
- - For tree type rules, this field must be "".
- default: ""
- type: str
- type:
- description:
- - The type of quota rule
- choices: ['user', 'group', 'tree']
- required: true
- type: str
- policy:
- description:
- - Name of the quota policy from which the quota rule should be obtained.
- type: str
- set_quota_status:
- description:
- - Whether the specified volume should have quota status on or off.
- type: bool
- file_limit:
- description:
- - The number of files that the target can have.
- default: '-'
- type: str
- disk_limit:
- description:
- - The amount of disk space that is reserved for the target.
- default: '-'
- type: str
- threshold:
- description:
- - The amount of disk space the target would have to exceed before a message is logged.
- default: '-'
- type: str
-'''
-
-EXAMPLES = """
- - name: Add/Set quota
- na_ontap_quotas:
- state: present
- vserver: ansible
- volume: ansible
- quota_target: /vol/ansible
- type: user
- policy: ansible
- file_limit: 2
- disk_limit: 3
- set_quota_status: True
- hostname: "{{ netapp_hostname }}"
- username: "{{ netapp_username }}"
- password: "{{ netapp_password }}"
- - name: modify quota
- na_ontap_quotas:
- state: present
- vserver: ansible
- volume: ansible
- quota_target: /vol/ansible
- type: user
- policy: ansible
- file_limit: 2
- disk_limit: 3
- threshold: 3
- set_quota_status: False
- hostname: "{{ netapp_hostname }}"
- username: "{{ netapp_username }}"
- password: "{{ netapp_password }}"
- - name: Delete quota
- na_ontap_quotas:
- state: absent
- vserver: ansible
- volume: ansible
- quota_target: /vol/ansible
- type: user
- policy: ansible
- hostname: "{{ netapp_hostname }}"
- username: "{{ netapp_username }}"
- password: "{{ netapp_password }}"
-"""
-
-RETURN = """
-
-"""
-
-
-import traceback
-from ansible.module_utils.basic import AnsibleModule
-from ansible.module_utils._text import to_native
-import ansible.module_utils.netapp as netapp_utils
-from ansible.module_utils.netapp_module import NetAppModule
-
-HAS_NETAPP_LIB = netapp_utils.has_netapp_lib()
-
-
-class NetAppONTAPQuotas(object):
- '''Class with quotas methods'''
-
- def __init__(self):
-
- self.argument_spec = netapp_utils.na_ontap_host_argument_spec()
- self.argument_spec.update(dict(
- state=dict(required=False, choices=['present', 'absent'], default='present'),
- vserver=dict(required=True, type='str'),
- volume=dict(required=True, type='str'),
- quota_target=dict(required=True, type='str'),
- qtree=dict(required=False, type='str', default=""),
- type=dict(required=True, type='str', choices=['user', 'group', 'tree']),
- policy=dict(required=False, type='str'),
- set_quota_status=dict(required=False, type='bool'),
- file_limit=dict(required=False, type='str', default='-'),
- disk_limit=dict(required=False, type='str', default='-'),
- threshold=dict(required=False, type='str', default='-')
- ))
-
- self.module = AnsibleModule(
- argument_spec=self.argument_spec,
- supports_check_mode=True
- )
-
- self.na_helper = NetAppModule()
- self.parameters = self.na_helper.set_parameters(self.module.params)
-
- if HAS_NETAPP_LIB is False:
- self.module.fail_json(
- msg="the python NetApp-Lib module is required")
- else:
- self.server = netapp_utils.setup_na_ontap_zapi(module=self.module, vserver=self.parameters['vserver'])
-
- def get_quota_status(self):
- """
- Return details about the quota status
- :param:
- name : volume name
- :return: status of the quota. None if not found.
- :rtype: dict
- """
- quota_status_get = netapp_utils.zapi.NaElement('quota-status')
- quota_status_get.translate_struct({
- 'volume': self.parameters['volume']
- })
- try:
- result = self.server.invoke_successfully(quota_status_get, enable_tunneling=True)
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg='Error fetching quotas status info: %s' % to_native(error),
- exception=traceback.format_exc())
- if result:
- return result['status']
- return None
-
- def get_quotas(self):
- """
- Get quota details
- :return: name of volume if quota exists, None otherwise
- """
- quota_get = netapp_utils.zapi.NaElement('quota-list-entries-iter')
- query = {
- 'query': {
- 'quota-entry': {
- 'volume': self.parameters['volume'],
- 'quota-target': self.parameters['quota_target'],
- 'quota-type': self.parameters['type'],
- 'vserver': self.parameters['vserver']
- }
- }
- }
- quota_get.translate_struct(query)
- if self.parameters.get('policy'):
- quota_get['query']['quota-entry'].add_new_child('policy', self.parameters['policy'])
- try:
- result = self.server.invoke_successfully(quota_get, enable_tunneling=True)
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg='Error fetching quotas info: %s' % to_native(error),
- exception=traceback.format_exc())
- if result.get_child_by_name('num-records') and int(result.get_child_content('num-records')) >= 1:
- return_values = {'volume': result['attributes-list']['quota-entry']['volume'],
- 'file_limit': result['attributes-list']['quota-entry']['file-limit'],
- 'disk_limit': result['attributes-list']['quota-entry']['disk-limit'],
- 'threshold': result['attributes-list']['quota-entry']['threshold']}
- return return_values
- return None
-
- def quota_entry_set(self):
- """
- Adds a quota entry
- """
- options = {'volume': self.parameters['volume'],
- 'quota-target': self.parameters['quota_target'],
- 'quota-type': self.parameters['type'],
- 'qtree': self.parameters['qtree'],
- 'file-limit': self.parameters['file_limit'],
- 'disk-limit': self.parameters['disk_limit'],
- 'threshold': self.parameters['threshold']}
- if self.parameters.get('policy'):
- options['policy'] = self.parameters['policy']
- set_entry = netapp_utils.zapi.NaElement.create_node_with_children(
- 'quota-set-entry', **options)
- try:
- self.server.invoke_successfully(set_entry, enable_tunneling=True)
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg='Error adding/modifying quota entry %s: %s'
- % (self.parameters['volume'], to_native(error)),
- exception=traceback.format_exc())
-
- def quota_entry_delete(self):
- """
- Deletes a quota entry
- """
- options = {'volume': self.parameters['volume'],
- 'quota-target': self.parameters['quota_target'],
- 'quota-type': self.parameters['type'],
- 'qtree': self.parameters['qtree']}
- set_entry = netapp_utils.zapi.NaElement.create_node_with_children(
- 'quota-delete-entry', **options)
- if self.parameters.get('policy'):
- set_entry.add_new_child('policy', self.parameters['policy'])
- try:
- self.server.invoke_successfully(set_entry, enable_tunneling=True)
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg='Error deleting quota entry %s: %s'
- % (self.parameters['volume'], to_native(error)),
- exception=traceback.format_exc())
-
- def quota_entry_modify(self, modify_attrs):
- """
- Modifies a quota entry
- """
- options = {'volume': self.parameters['volume'],
- 'quota-target': self.parameters['quota_target'],
- 'quota-type': self.parameters['type'],
- 'qtree': self.parameters['qtree']}
- options.update(modify_attrs)
- if self.parameters.get('policy'):
- options['policy'] = str(self.parameters['policy'])
- modify_entry = netapp_utils.zapi.NaElement.create_node_with_children(
- 'quota-modify-entry', **options)
- try:
- self.server.invoke_successfully(modify_entry, enable_tunneling=True)
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg='Error modifying quota entry %s: %s'
- % (self.parameters['volume'], to_native(error)),
- exception=traceback.format_exc())
-
- def on_or_off_quota(self, status):
- """
- on or off quota
- """
- quota = netapp_utils.zapi.NaElement.create_node_with_children(
- status, **{'volume': self.parameters['volume']})
- try:
- self.server.invoke_successfully(quota,
- enable_tunneling=True)
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg='Error setting %s for %s: %s'
- % (status, self.parameters['volume'], to_native(error)),
- exception=traceback.format_exc())
-
- def apply(self):
- """
- Apply action to quotas
- """
- netapp_utils.ems_log_event("na_ontap_quotas", self.server)
- modify_quota_status = None
- modify_quota = None
- current = self.get_quotas()
- if 'set_quota_status' in self.parameters:
- quota_status = self.get_quota_status()
- if quota_status is not None:
- quota_status_action = self.na_helper.get_modified_attributes(
- {'set_quota_status': True if quota_status == 'on' else False}, self.parameters)
- if quota_status_action:
- modify_quota_status = 'quota-on' if quota_status_action['set_quota_status'] else 'quota-off'
- cd_action = self.na_helper.get_cd_action(current, self.parameters)
- if cd_action is None:
- modify_quota = self.na_helper.get_modified_attributes(current, self.parameters)
- if self.na_helper.changed:
- if self.module.check_mode:
- pass
- else:
- if cd_action == 'create':
- self.quota_entry_set()
- elif cd_action == 'delete':
- self.quota_entry_delete()
- elif modify_quota is not None:
- for key in list(modify_quota):
- modify_quota[key.replace("_", "-")] = modify_quota.pop(key)
- self.quota_entry_modify(modify_quota)
- if modify_quota_status is not None:
- self.on_or_off_quota(modify_quota_status)
- self.module.exit_json(changed=self.na_helper.changed)
-
-
-def main():
- '''Execute action'''
- quota_obj = NetAppONTAPQuotas()
- quota_obj.apply()
-
-
-if __name__ == '__main__':
- main()
diff --git a/lib/ansible/modules/storage/netapp/na_ontap_security_key_manager.py b/lib/ansible/modules/storage/netapp/na_ontap_security_key_manager.py
deleted file mode 100644
index 598bacbd1c..0000000000
--- a/lib/ansible/modules/storage/netapp/na_ontap_security_key_manager.py
+++ /dev/null
@@ -1,229 +0,0 @@
-#!/usr/bin/python
-
-# (c) 2019, NetApp, Inc
-# GNU General Public License v3.0+
-# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-
-from __future__ import absolute_import, division, print_function
-__metaclass__ = type
-
-ANSIBLE_METADATA = {'metadata_version': '1.1',
- 'status': ['preview'],
- 'supported_by': 'community'}
-
-
-DOCUMENTATION = '''
-
-module: na_ontap_security_key_manager
-
-short_description: NetApp ONTAP security key manager.
-extends_documentation_fragment:
- - netapp.na_ontap
-version_added: '2.8'
-author: NetApp Ansible Team (@carchi8py) <ng-ansibleteam@netapp.com>
-
-description:
-- Add or delete or setup key management on NetApp ONTAP.
-
-options:
-
- state:
- description:
- - Whether the specified key manager should exist or not.
- choices: ['present', 'absent']
- default: 'present'
-
- ip_address:
- description:
- - The IP address of the key management server.
- required: true
-
- tcp_port:
- description:
- - The TCP port on which the key management server listens for incoming connections.
- default: 5696
-
- node:
- description:
- - The node which key management server runs on.
-
-'''
-
-EXAMPLES = """
-
- - name: Delete Key Manager
- tags:
- - delete
- na_ontap_security_key_manager:
- state: absent
- node: swenjun-vsim1
- hostname: "{{ hostname }}"
- username: "{{ netapp_username }}"
- password: "{{ netapp_password }}"
- https: False
- ip_address: 0.0.0.0
-
- - name: Add Key Manager
- tags:
- - add
- na_ontap_security_key_manager:
- state: present
- node: swenjun-vsim1
- hostname: "{{ hostname }}"
- username: "{{ netapp_username }}"
- password: "{{ netapp_password }}"
- https: False
- ip_address: 0.0.0.0
-
-"""
-
-RETURN = """
-"""
-
-import traceback
-import ansible.module_utils.netapp as netapp_utils
-from ansible.module_utils.netapp_module import NetAppModule
-from ansible.module_utils.basic import AnsibleModule
-from ansible.module_utils._text import to_native
-
-HAS_NETAPP_LIB = netapp_utils.has_netapp_lib()
-
-
-class NetAppOntapSecurityKeyManager(object):
- '''class with key manager operations'''
-
- def __init__(self):
- '''Initialize module parameters'''
- self.argument_spec = netapp_utils.na_ontap_host_argument_spec()
- self.argument_spec.update(
- state=dict(required=False, choices=['present', 'absent'], default='present'),
- ip_address=dict(required=True, type='str'),
- node=dict(required=False, type='str'),
- tcp_port=dict(required=False, type='int', default=5696)
- )
- self.module = AnsibleModule(
- argument_spec=self.argument_spec,
- supports_check_mode=True
- )
- self.na_helper = NetAppModule()
- self.parameters = self.na_helper.set_parameters(self.module.params)
-
- if HAS_NETAPP_LIB is False:
- self.module.fail_json(
- msg="the python NetApp-Lib module is required"
- )
- else:
- self.cluster = netapp_utils.setup_na_ontap_zapi(module=self.module)
-
- def get_key_manager(self):
- """
- get key manager by ip address.
- :return: a dict of key manager
- """
- key_manager_info = netapp_utils.zapi.NaElement('security-key-manager-get-iter')
- query_details = netapp_utils.zapi.NaElement.create_node_with_children(
- 'key-manager-info', **{'key-manager-ip-address': self.parameters['ip_address']})
- query = netapp_utils.zapi.NaElement('query')
- query.add_child_elem(query_details)
- key_manager_info.add_child_elem(query)
-
- try:
- result = self.cluster.invoke_successfully(key_manager_info, enable_tunneling=False)
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg='Error fetching key manager %s : %s'
- % (self.parameters['node'], to_native(error)),
- exception=traceback.format_exc())
-
- return_value = None
- if result.get_child_by_name('num-records') and int(result.get_child_content('num-records')) > 0:
- key_manager = result.get_child_by_name('attributes-list').get_child_by_name('key-manager-info')
- return_value = {}
- if key_manager.get_child_by_name('key-manager-ip-address'):
- return_value['ip_address'] = key_manager.get_child_content('key-manager-ip-address')
- if key_manager.get_child_by_name('key-manager-server-status'):
- return_value['server_status'] = key_manager.get_child_content('key-manager-server-status')
- if key_manager.get_child_by_name('key-manager-tcp-port'):
- return_value['tcp_port'] = key_manager.get_child_content('key-manager-tcp-port')
- if key_manager.get_child_by_name('node-name'):
- return_value['node'] = key_manager.get_child_content('node-name')
-
- return return_value
-
- def key_manager_setup(self):
- """
- set up external key manager.
- """
- key_manager_setup = netapp_utils.zapi.NaElement('security-key-manager-setup')
- # if specify on-boarding passphrase, it is on-boarding key management.
- # it not, then it's external key management.
- try:
- self.cluster.invoke_successfully(key_manager_setup, True)
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg='Error setting up key manager %s : %s'
- % (self.parameters['node'], to_native(error)),
- exception=traceback.format_exc())
-
- def create_key_manager(self):
- """
- add key manager.
- """
- key_manager_create = netapp_utils.zapi.NaElement('security-key-manager-add')
- key_manager_create.add_new_child('key-manager-ip-address', self.parameters['ip_address'])
- if self.parameters.get('tcp_port'):
- key_manager_create.add_new_child('key-manager-tcp-port', str(self.parameters['tcp_port']))
- try:
- self.cluster.invoke_successfully(key_manager_create, True)
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg='Error creating key manager %s : %s'
- % (self.parameters['node'], to_native(error)),
- exception=traceback.format_exc())
-
- def delete_key_manager(self):
- """
- delete key manager.
- """
- key_manager_delete = netapp_utils.zapi.NaElement('security-key-manager-delete')
- key_manager_delete.add_new_child('key-manager-ip-address', self.parameters['ip_address'])
- try:
- self.cluster.invoke_successfully(key_manager_delete, True)
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg='Error deleting key manager %s : %s'
- % (self.parameters['node'], to_native(error)),
- exception=traceback.format_exc())
-
- def apply(self):
- self.asup_log_for_cserver("na_ontap_security_key_manager")
- self.key_manager_setup()
- current = self.get_key_manager()
- cd_action = None
- cd_action = self.na_helper.get_cd_action(current, self.parameters)
- if self.na_helper.changed:
- if self.module.check_mode:
- pass
- else:
- if cd_action == 'create':
- self.create_key_manager()
- elif cd_action == 'delete':
- self.delete_key_manager()
- self.module.exit_json(changed=self.na_helper.changed)
-
- def asup_log_for_cserver(self, event_name):
- """
- Fetch admin vserver for the given cluster
- Create and Autosupport log event with the given module name
- :param event_name: Name of the event log
- :return: None
- """
- results = netapp_utils.get_cserver(self.cluster)
- cserver = netapp_utils.setup_na_ontap_zapi(module=self.module, vserver=results)
- netapp_utils.ems_log_event(event_name, cserver)
-
-
-def main():
- '''Apply volume operations from playbook'''
- obj = NetAppOntapSecurityKeyManager()
- obj.apply()
-
-
-if __name__ == '__main__':
- main()
diff --git a/lib/ansible/modules/storage/netapp/na_ontap_service_processor_network.py b/lib/ansible/modules/storage/netapp/na_ontap_service_processor_network.py
deleted file mode 100644
index 14c974804b..0000000000
--- a/lib/ansible/modules/storage/netapp/na_ontap_service_processor_network.py
+++ /dev/null
@@ -1,284 +0,0 @@
-#!/usr/bin/python
-
-# (c) 2018-2019, NetApp, Inc
-# GNU General Public License v3.0+
-# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-
-from __future__ import absolute_import, division, print_function
-__metaclass__ = type
-
-
-ANSIBLE_METADATA = {'metadata_version': '1.1',
- 'status': ['preview'],
- 'supported_by': 'certified'}
-
-DOCUMENTATION = '''
-module: na_ontap_service_processor_network
-short_description: NetApp ONTAP service processor network
-extends_documentation_fragment:
- - netapp.na_ontap
-version_added: '2.6'
-author: NetApp Ansible Team (@carchi8py) <ng-ansibleteam@netapp.com>
-description:
-- Modify a ONTAP service processor network
-options:
- state:
- description:
- - Whether the specified service processor network should exist or not.
- choices: ['present']
- default: present
- address_type:
- description:
- - Specify address class.
- required: true
- choices: ['ipv4', 'ipv6']
- is_enabled:
- description:
- - Specify whether to enable or disable the service processor network.
- required: true
- type: bool
- node:
- description:
- - The node where the service processor network should be enabled
- required: true
- dhcp:
- description:
- - Specify dhcp type.
- choices: ['v4', 'none']
- gateway_ip_address:
- description:
- - Specify the gateway ip.
- ip_address:
- description:
- - Specify the service processor ip address.
- netmask:
- description:
- - Specify the service processor netmask.
- prefix_length:
- description:
- - Specify the service processor prefix_length.
- wait_for_completion:
- description:
- - Set this parameter to 'true' for synchronous execution (wait until SP status is successfully updated)
- - Set this parameter to 'false' for asynchronous execution
- - For asynchronous, execution exits as soon as the request is sent, without checking SP status
- type: bool
- default: false
- version_added: '2.8'
-'''
-
-EXAMPLES = """
- - name: Modify Service Processor Network
- na_ontap_service_processor_network:
- state: present
- address_type: ipv4
- is_enabled: true
- dhcp: v4
- node: "{{ netapp_node }}"
- username: "{{ netapp_username }}"
- password: "{{ netapp_password }}"
- hostname: "{{ netapp_hostname }}"
-"""
-
-RETURN = """
-"""
-import traceback
-
-from ansible.module_utils.basic import AnsibleModule
-from ansible.module_utils._text import to_native
-import ansible.module_utils.netapp as netapp_utils
-from ansible.module_utils.netapp_module import NetAppModule
-import time
-
-HAS_NETAPP_LIB = netapp_utils.has_netapp_lib()
-
-
-class NetAppOntapServiceProcessorNetwork(object):
- """
- Modify a Service Processor Network
- """
-
- def __init__(self):
- """
- Initialize the NetAppOntapServiceProcessorNetwork class
- """
- self.argument_spec = netapp_utils.na_ontap_host_argument_spec()
- self.argument_spec.update(dict(
- state=dict(required=False, choices=['present'], default='present'),
- address_type=dict(required=True, choices=['ipv4', 'ipv6']),
- is_enabled=dict(required=True, type='bool'),
- node=dict(required=True, type='str'),
- dhcp=dict(required=False, choices=['v4', 'none']),
- gateway_ip_address=dict(required=False, type='str'),
- ip_address=dict(required=False, type='str'),
- netmask=dict(required=False, type='str'),
- prefix_length=dict(required=False, type='int'),
- wait_for_completion=dict(required=False, type='bool', default=False)
- ))
-
- self.module = AnsibleModule(
- argument_spec=self.argument_spec,
- supports_check_mode=True
- )
-
- self.na_helper = NetAppModule()
- self.parameters = self.na_helper.set_parameters(self.module.params)
- self.set_playbook_zapi_key_map()
-
- if HAS_NETAPP_LIB is False:
- self.module.fail_json(
- msg="the python NetApp-Lib module is required")
- else:
- self.server = netapp_utils.setup_na_ontap_zapi(
- module=self.module, vserver=None)
- return
-
- def set_playbook_zapi_key_map(self):
- self.na_helper.zapi_string_keys = {
- 'address_type': 'address-type',
- 'node': 'node',
- 'dhcp': 'dhcp',
- 'gateway_ip_address': 'gateway-ip-address',
- 'ip_address': 'ip-address',
- 'netmask': 'netmask'
- }
- self.na_helper.zapi_int_keys = {
- 'prefix_length': 'prefix-length'
- }
- self.na_helper.zapi_bool_keys = {
- 'is_enabled': 'is-enabled',
- }
- self.na_helper.zapi_required = {
- 'address_type': 'address-type',
- 'node': 'node',
- 'is_enabled': 'is-enabled'
- }
-
- def get_sp_network_status(self):
- """
- Return status of service processor network
- :param:
- name : name of the node
- :return: Status of the service processor network
- :rtype: dict
- """
- spn_get_iter = netapp_utils.zapi.NaElement('service-processor-network-get-iter')
- query_info = {
- 'query': {
- 'service-processor-network-info': {
- 'node': self.parameters['node'],
- 'address-type': self.parameters['address_type']
- }
- }
- }
- spn_get_iter.translate_struct(query_info)
- result = self.server.invoke_successfully(spn_get_iter, True)
- if int(result['num-records']) >= 1:
- sp_attr_info = result['attributes-list']['service-processor-network-info']
- return sp_attr_info.get_child_content('setup-status')
- return None
-
- def get_service_processor_network(self):
- """
- Return details about service processor network
- :param:
- name : name of the node
- :return: Details about service processor network. None if not found.
- :rtype: dict
- """
- spn_get_iter = netapp_utils.zapi.NaElement('service-processor-network-get-iter')
- query_info = {
- 'query': {
- 'service-processor-network-info': {
- 'node': self.parameters['node']
- }
- }
- }
- spn_get_iter.translate_struct(query_info)
- result = self.server.invoke_successfully(spn_get_iter, True)
- sp_details = None
- # check if job exists
- if int(result['num-records']) >= 1:
- sp_details = dict()
- sp_attr_info = result['attributes-list']['service-processor-network-info']
- for item_key, zapi_key in self.na_helper.zapi_string_keys.items():
- sp_details[item_key] = sp_attr_info.get_child_content(zapi_key)
- for item_key, zapi_key in self.na_helper.zapi_bool_keys.items():
- sp_details[item_key] = self.na_helper.get_value_for_bool(from_zapi=True,
- value=sp_attr_info.get_child_content(zapi_key))
- for item_key, zapi_key in self.na_helper.zapi_int_keys.items():
- sp_details[item_key] = self.na_helper.get_value_for_int(from_zapi=True,
- value=sp_attr_info.get_child_content(zapi_key))
- return sp_details
-
- def modify_service_processor_network(self, params=None):
- """
- Modify a service processor network.
- :param params: A dict of modified options.
- When dhcp is not set to v4, ip_address, netmask, and gateway_ip_address must be specified even if remains the same.
- """
- if self.parameters['is_enabled'] is False:
- if params.get('is_enabled') and len(params) > 1:
- self.module.fail_json(msg='Error: Cannot modify any other parameter for a service processor network if option "is_enabled" is set to false.')
- elif params.get('is_enabled') is None and len(params) > 0:
- self.module.fail_json(msg='Error: Cannot modify a service processor network if it is disabled.')
-
- sp_modify = netapp_utils.zapi.NaElement('service-processor-network-modify')
- sp_modify.add_new_child("node", self.parameters['node'])
- sp_modify.add_new_child("address-type", self.parameters['address_type'])
- sp_attributes = dict()
- for item_key in self.parameters:
- if item_key in self.na_helper.zapi_string_keys:
- zapi_key = self.na_helper.zapi_string_keys.get(item_key)
- sp_attributes[zapi_key] = self.parameters[item_key]
- elif item_key in self.na_helper.zapi_bool_keys:
- zapi_key = self.na_helper.zapi_bool_keys.get(item_key)
- sp_attributes[zapi_key] = self.na_helper.get_value_for_bool(from_zapi=False, value=self.parameters[item_key])
- elif item_key in self.na_helper.zapi_int_keys:
- zapi_key = self.na_helper.zapi_int_keys.get(item_key)
- sp_attributes[zapi_key] = self.na_helper.get_value_for_int(from_zapi=False, value=self.parameters[item_key])
- sp_modify.translate_struct(sp_attributes)
- try:
- self.server.invoke_successfully(sp_modify, enable_tunneling=True)
- if self.parameters.get('wait_for_completion'):
- retries = 10
- while self.get_sp_network_status() == 'in_progress' and retries > 0:
- time.sleep(10)
- retries = retries - 1
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg='Error modifying service processor network: %s' % (to_native(error)),
- exception=traceback.format_exc())
-
- def autosupport_log(self):
- results = netapp_utils.get_cserver(self.server)
- cserver = netapp_utils.setup_na_ontap_zapi(module=self.module, vserver=results)
- netapp_utils.ems_log_event("na_ontap_service_processor_network", cserver)
-
- def apply(self):
- """
- Run Module based on play book
- """
- self.autosupport_log()
- current = self.get_service_processor_network()
- modify = self.na_helper.get_modified_attributes(current, self.parameters)
- if not current:
- self.module.fail_json(msg='Error No Service Processor for node: %s' % self.parameters['node'])
- if self.na_helper.changed:
- if self.module.check_mode:
- pass
- else:
- self.modify_service_processor_network(modify)
- self.module.exit_json(changed=self.na_helper.changed)
-
-
-def main():
- """
- Create the NetApp Ontap Service Processor Network Object and modify it
- """
-
- obj = NetAppOntapServiceProcessorNetwork()
- obj.apply()
-
-
-if __name__ == '__main__':
- main()
diff --git a/lib/ansible/modules/storage/netapp/na_ontap_snapmirror.py b/lib/ansible/modules/storage/netapp/na_ontap_snapmirror.py
deleted file mode 100644
index aacfb32e81..0000000000
--- a/lib/ansible/modules/storage/netapp/na_ontap_snapmirror.py
+++ /dev/null
@@ -1,716 +0,0 @@
-#!/usr/bin/python
-
-# (c) 2018-2019, NetApp, Inc
-# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-from __future__ import absolute_import, division, print_function
-__metaclass__ = type
-
-ANSIBLE_METADATA = {'metadata_version': '1.1',
- 'status': ['preview'],
- 'supported_by': 'certified'}
-
-
-DOCUMENTATION = '''
-author: NetApp Ansible Team (@carchi8py) <ng-ansibleteam@netapp.com>
-description:
- - Create/Delete/Initialize SnapMirror volume/vserver relationships for ONTAP/ONTAP
- - Create/Delete/Initialize SnapMirror volume relationship between ElementSW and ONTAP
- - Modify schedule for a SnapMirror relationship for ONTAP/ONTAP and ElementSW/ONTAP
- - Pre-requisite for ElementSW to ONTAP relationship or vice-versa is an established SnapMirror endpoint for ONTAP cluster with ElementSW UI
- - Pre-requisite for ElementSW to ONTAP relationship or vice-versa is to have SnapMirror enabled in the ElementSW volume
- - For creating a SnapMirror ElementSW/ONTAP relationship, an existing ONTAP/ElementSW relationship should be present
-extends_documentation_fragment:
- - netapp.na_ontap
-module: na_ontap_snapmirror
-options:
- state:
- choices: ['present', 'absent']
- description:
- - Whether the specified relationship should exist or not.
- default: present
- source_volume:
- description:
- - Specifies the name of the source volume for the SnapMirror.
- destination_volume:
- description:
- - Specifies the name of the destination volume for the SnapMirror.
- source_vserver:
- description:
- - Name of the source vserver for the SnapMirror.
- destination_vserver:
- description:
- - Name of the destination vserver for the SnapMirror.
- source_path:
- description:
- - Specifies the source endpoint of the SnapMirror relationship.
- - If the source is an ONTAP volume, format should be <[vserver:][volume]> or <[[cluster:]//vserver/]volume>
- - If the source is an ElementSW volume, format should be <[Element_SVIP]:/lun/[Element_VOLUME_ID]>
- - If the source is an ElementSW volume, the volume should have SnapMirror enabled.
- destination_path:
- description:
- - Specifies the destination endpoint of the SnapMirror relationship.
- relationship_type:
- choices: ['data_protection', 'load_sharing', 'vault', 'restore', 'transition_data_protection',
- 'extended_data_protection']
- description:
- - Specify the type of SnapMirror relationship.
- schedule:
- description:
- - Specify the name of the current schedule, which is used to update the SnapMirror relationship.
- - Optional for create, modifiable.
- policy:
- description:
- - Specify the name of the SnapMirror policy that applies to this relationship.
- version_added: "2.8"
- source_hostname:
- description:
- - Source hostname or management IP address for ONTAP or ElementSW cluster.
- - Required for SnapMirror delete
- source_username:
- description:
- - Source username for ONTAP or ElementSW cluster.
- - Optional if this is same as destination username.
- source_password:
- description:
- - Source password for ONTAP or ElementSW cluster.
- - Optional if this is same as destination password.
- connection_type:
- description:
- - Type of SnapMirror relationship.
- - Pre-requisite for either elementsw_ontap or ontap_elementsw the ElementSW volume should have enableSnapmirror option set to true.
- - For using ontap_elementsw, elementsw_ontap snapmirror relationship should exist.
- choices: ['ontap_ontap', 'elementsw_ontap', 'ontap_elementsw']
- default: ontap_ontap
- version_added: '2.9'
- max_transfer_rate:
- description:
- - Specifies the upper bound, in kilobytes per second, at which data is transferred.
- - Default is unlimited, it can be explicitly set to 0 as unlimited.
- type: int
- version_added: '2.9'
- identity_preserve:
- description:
- - Specifies whether or not the identity of the source Vserver is replicated to the destination Vserver.
- - If this parameter is set to true, the source Vserver's configuration will additionally be replicated to the destination.
- - If the parameter is set to false, then only the source Vserver's volumes and RBAC configuration are replicated to the destination.
- type: bool
- version_added: '2.9'
-short_description: "NetApp ONTAP or ElementSW Manage SnapMirror"
-version_added: "2.7"
-'''
-
-EXAMPLES = """
-
- # creates and initializes the snapmirror
- - name: Create ONTAP/ONTAP SnapMirror
- na_ontap_snapmirror:
- state: present
- source_volume: test_src
- destination_volume: test_dest
- source_vserver: ansible_src
- destination_vserver: ansible_dest
- schedule: hourly
- policy: MirrorAllSnapshots
- max_transfer_rate: 1000
- hostname: "{{ destination_cluster_hostname }}"
- username: "{{ destination_cluster_username }}"
- password: "{{ destination_cluster_password }}"
-
- # creates and initializes the snapmirror between vservers
- - name: Create ONTAP/ONTAP vserver SnapMirror
- na_ontap_snapmirror:
- state: present
- source_vserver: ansible_src
- destination_vserver: ansible_dest
- identity_preserve: true
- hostname: "{{ destination_cluster_hostname }}"
- username: "{{ destination_cluster_username }}"
- password: "{{ destination_cluster_password }}"
-
- # existing snapmirror relation with status 'snapmirrored' will be initialized
- - name: Initialize ONTAP/ONTAP SnapMirror
- na_ontap_snapmirror:
- state: present
- source_path: 'ansible:test'
- destination_path: 'ansible:dest'
- hostname: "{{ destination_cluster_hostname }}"
- username: "{{ destination_cluster_username }}"
- password: "{{ destination_cluster_password }}"
-
- - name: Delete SnapMirror
- na_ontap_snapmirror:
- state: absent
- destination_path: <path>
- source_hostname: "{{ source_hostname }}"
- hostname: "{{ destination_cluster_hostname }}"
- username: "{{ destination_cluster_username }}"
- password: "{{ destination_cluster_password }}"
-
- - name: Set schedule to NULL
- na_ontap_snapmirror:
- state: present
- destination_path: <path>
- schedule: ""
- hostname: "{{ destination_cluster_hostname }}"
- username: "{{ destination_cluster_username }}"
- password: "{{ destination_cluster_password }}"
-
- - name: Create SnapMirror from ElementSW to ONTAP
- na_ontap_snapmirror:
- state: present
- connection_type: elementsw_ontap
- source_path: '10.10.10.10:/lun/300'
- destination_path: 'ansible_test:ansible_dest_vol'
- schedule: hourly
- policy: MirrorLatest
- hostname: "{{ netapp_hostname }}"
- username: "{{ netapp_username }}"
- password: "{{ netapp_password }}"
- source_hostname: " {{ Element_cluster_mvip }}"
- source_username: "{{ Element_cluster_username }}"
- source_password: "{{ Element_cluster_password }}"
-
- - name: Create SnapMirror from ONTAP to ElementSW
- na_ontap_snapmirror:
- state: present
- connection_type: ontap_elementsw
- destination_path: '10.10.10.10:/lun/300'
- source_path: 'ansible_test:ansible_dest_vol'
- policy: MirrorLatest
- hostname: "{{ Element_cluster_mvip }}"
- username: "{{ Element_cluster_username }}"
- password: "{{ Element_cluster_password }}"
- source_hostname: " {{ netapp_hostname }}"
- source_username: "{{ netapp_username }}"
- source_password: "{{ netapp_password }}"
-"""
-
-RETURN = """
-"""
-
-import re
-import traceback
-from ansible.module_utils.basic import AnsibleModule
-from ansible.module_utils._text import to_native
-import ansible.module_utils.netapp as netapp_utils
-from ansible.module_utils.netapp_elementsw_module import NaElementSWModule
-from ansible.module_utils.netapp_module import NetAppModule
-
-HAS_NETAPP_LIB = netapp_utils.has_netapp_lib()
-
-HAS_SF_SDK = netapp_utils.has_sf_sdk()
-try:
- import solidfire.common
-except ImportError:
- HAS_SF_SDK = False
-
-
-class NetAppONTAPSnapmirror(object):
- """
- Class with Snapmirror methods
- """
-
- def __init__(self):
-
- self.argument_spec = netapp_utils.na_ontap_host_argument_spec()
- self.argument_spec.update(dict(
- state=dict(required=False, type='str', choices=['present', 'absent'], default='present'),
- source_vserver=dict(required=False, type='str'),
- destination_vserver=dict(required=False, type='str'),
- source_volume=dict(required=False, type='str'),
- destination_volume=dict(required=False, type='str'),
- source_path=dict(required=False, type='str'),
- destination_path=dict(required=False, type='str'),
- schedule=dict(required=False, type='str'),
- policy=dict(required=False, type='str'),
- relationship_type=dict(required=False, type='str',
- choices=['data_protection', 'load_sharing',
- 'vault', 'restore',
- 'transition_data_protection',
- 'extended_data_protection']
- ),
- source_hostname=dict(required=False, type='str'),
- connection_type=dict(required=False, type='str',
- choices=['ontap_ontap', 'elementsw_ontap', 'ontap_elementsw'],
- default='ontap_ontap'),
- source_username=dict(required=False, type='str'),
- source_password=dict(required=False, type='str', no_log=True),
- max_transfer_rate=dict(required=False, type='int'),
- identity_preserve=dict(required=False, type='bool')
- ))
-
- self.module = AnsibleModule(
- argument_spec=self.argument_spec,
- required_together=(['source_volume', 'destination_volume'],
- ['source_vserver', 'destination_vserver']),
- supports_check_mode=True
- )
-
- self.na_helper = NetAppModule()
- self.parameters = self.na_helper.set_parameters(self.module.params)
- # setup later if required
- self.source_server = None
- # only for ElementSW -> ONTAP snapmirroring, validate if ElementSW SDK is available
- if self.parameters.get('connection_type') in ['elementsw_ontap', 'ontap_elementsw']:
- if HAS_SF_SDK is False:
- self.module.fail_json(msg="Unable to import the SolidFire Python SDK")
- if HAS_NETAPP_LIB is False:
- self.module.fail_json(msg="the python NetApp-Lib module is required")
- if self.parameters.get('connection_type') != 'ontap_elementsw':
- self.server = netapp_utils.setup_na_ontap_zapi(module=self.module)
- else:
- if self.parameters.get('source_username'):
- self.module.params['username'] = self.parameters['source_username']
- if self.parameters.get('source_password'):
- self.module.params['password'] = self.parameters['source_password']
- self.module.params['hostname'] = self.parameters['source_hostname']
- self.server = netapp_utils.setup_na_ontap_zapi(module=self.module)
-
- def set_element_connection(self, kind):
- if kind == 'source':
- self.module.params['hostname'] = self.parameters['source_hostname']
- self.module.params['username'] = self.parameters['source_username']
- self.module.params['password'] = self.parameters['source_password']
- elif kind == 'destination':
- self.module.params['hostname'] = self.parameters['hostname']
- self.module.params['username'] = self.parameters['username']
- self.module.params['password'] = self.parameters['password']
- elem = netapp_utils.create_sf_connection(module=self.module)
- elementsw_helper = NaElementSWModule(elem)
- return elementsw_helper, elem
-
- def snapmirror_get_iter(self, destination=None):
- """
- Compose NaElement object to query current SnapMirror relations using destination-path
- SnapMirror relation for a destination path is unique
- :return: NaElement object for SnapMirror-get-iter
- """
- snapmirror_get_iter = netapp_utils.zapi.NaElement('snapmirror-get-iter')
- query = netapp_utils.zapi.NaElement('query')
- snapmirror_info = netapp_utils.zapi.NaElement('snapmirror-info')
- if destination is None:
- destination = self.parameters['destination_path']
- snapmirror_info.add_new_child('destination-location', destination)
- query.add_child_elem(snapmirror_info)
- snapmirror_get_iter.add_child_elem(query)
- return snapmirror_get_iter
-
- def snapmirror_get(self, destination=None):
- """
- Get current SnapMirror relations
- :return: Dictionary of current SnapMirror details if query successful, else None
- """
- snapmirror_get_iter = self.snapmirror_get_iter(destination)
- snap_info = dict()
- try:
- result = self.server.invoke_successfully(snapmirror_get_iter, enable_tunneling=True)
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg='Error fetching snapmirror info: %s' % to_native(error),
- exception=traceback.format_exc())
- if result.get_child_by_name('num-records') and \
- int(result.get_child_content('num-records')) > 0:
- snapmirror_info = result.get_child_by_name('attributes-list').get_child_by_name(
- 'snapmirror-info')
- snap_info['mirror_state'] = snapmirror_info.get_child_content('mirror-state')
- snap_info['status'] = snapmirror_info.get_child_content('relationship-status')
- snap_info['schedule'] = snapmirror_info.get_child_content('schedule')
- snap_info['policy'] = snapmirror_info.get_child_content('policy')
- snap_info['relationship'] = snapmirror_info.get_child_content('relationship-type')
- if snapmirror_info.get_child_by_name('max-transfer-rate'):
- snap_info['max_transfer_rate'] = int(snapmirror_info.get_child_content('max-transfer-rate'))
- if snap_info['schedule'] is None:
- snap_info['schedule'] = ""
- return snap_info
- return None
-
- def check_if_remote_volume_exists(self):
- """
- Validate existence of source volume
- :return: True if volume exists, False otherwise
- """
- self.set_source_cluster_connection()
- # do a get volume to check if volume exists or not
- volume_info = netapp_utils.zapi.NaElement('volume-get-iter')
- volume_attributes = netapp_utils.zapi.NaElement('volume-attributes')
- volume_id_attributes = netapp_utils.zapi.NaElement('volume-id-attributes')
- volume_id_attributes.add_new_child('name', self.parameters['source_volume'])
- # if source_volume is present, then source_vserver is also guaranteed to be present
- volume_id_attributes.add_new_child('vserver-name', self.parameters['source_vserver'])
- volume_attributes.add_child_elem(volume_id_attributes)
- query = netapp_utils.zapi.NaElement('query')
- query.add_child_elem(volume_attributes)
- volume_info.add_child_elem(query)
- try:
- result = self.source_server.invoke_successfully(volume_info, True)
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg='Error fetching source volume details %s : %s'
- % (self.parameters['source_volume'], to_native(error)),
- exception=traceback.format_exc())
- if result.get_child_by_name('num-records') and int(result.get_child_content('num-records')) > 0:
- return True
- return False
-
- def snapmirror_create(self):
- """
- Create a SnapMirror relationship
- """
- if self.parameters.get('source_hostname') and self.parameters.get('source_volume'):
- if not self.check_if_remote_volume_exists():
- self.module.fail_json(msg='Source volume does not exist. Please specify a volume that exists')
- options = {'source-location': self.parameters['source_path'],
- 'destination-location': self.parameters['destination_path']}
- snapmirror_create = netapp_utils.zapi.NaElement.create_node_with_children('snapmirror-create', **options)
- if self.parameters.get('relationship_type'):
- snapmirror_create.add_new_child('relationship-type', self.parameters['relationship_type'])
- if self.parameters.get('schedule'):
- snapmirror_create.add_new_child('schedule', self.parameters['schedule'])
- if self.parameters.get('policy'):
- snapmirror_create.add_new_child('policy', self.parameters['policy'])
- if self.parameters.get('max_transfer_rate'):
- snapmirror_create.add_new_child('max-transfer-rate', str(self.parameters['max_transfer_rate']))
- if self.parameters.get('identity_preserve'):
- snapmirror_create.add_new_child('identity-preserve', str(self.parameters['identity_preserve']))
- try:
- self.server.invoke_successfully(snapmirror_create, enable_tunneling=True)
- self.snapmirror_initialize()
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg='Error creating SnapMirror %s' % to_native(error),
- exception=traceback.format_exc())
-
- def set_source_cluster_connection(self):
- """
- Setup ontap ZAPI server connection for source hostname
- :return: None
- """
- if self.parameters.get('source_username'):
- self.module.params['username'] = self.parameters['source_username']
- if self.parameters.get('source_password'):
- self.module.params['password'] = self.parameters['source_password']
- self.module.params['hostname'] = self.parameters['source_hostname']
- self.source_server = netapp_utils.setup_na_ontap_zapi(module=self.module)
-
- def delete_snapmirror(self, is_hci, relationship_type):
- """
- Delete a SnapMirror relationship
- #1. Quiesce the SnapMirror relationship at destination
- #2. Break the SnapMirror relationship at the destination
- #3. Release the SnapMirror at source
- #4. Delete SnapMirror at destination
- """
- if not is_hci:
- if not self.parameters.get('source_hostname'):
- self.module.fail_json(msg='Missing parameters for delete: Please specify the '
- 'source cluster hostname to release the SnapMirror relation')
- # Quiesce at destination
- self.snapmirror_quiesce()
- # Break at destination
- if relationship_type not in ['load_sharing', 'vault']:
- self.snapmirror_break()
- # if source is ONTAP, release the destination at source cluster
- if not is_hci:
- self.set_source_cluster_connection()
- if self.get_destination():
- # Release at source
- self.snapmirror_release()
- # Delete at destination
- self.snapmirror_delete()
-
- def snapmirror_quiesce(self):
- """
- Quiesce SnapMirror relationship - disable all future transfers to this destination
- """
- options = {'destination-location': self.parameters['destination_path']}
-
- snapmirror_quiesce = netapp_utils.zapi.NaElement.create_node_with_children(
- 'snapmirror-quiesce', **options)
- try:
- self.server.invoke_successfully(snapmirror_quiesce,
- enable_tunneling=True)
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg='Error Quiescing SnapMirror : %s'
- % (to_native(error)),
- exception=traceback.format_exc())
-
- def snapmirror_delete(self):
- """
- Delete SnapMirror relationship at destination cluster
- """
- options = {'destination-location': self.parameters['destination_path']}
-
- snapmirror_delete = netapp_utils.zapi.NaElement.create_node_with_children(
- 'snapmirror-destroy', **options)
- try:
- self.server.invoke_successfully(snapmirror_delete,
- enable_tunneling=True)
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg='Error deleting SnapMirror : %s'
- % (to_native(error)),
- exception=traceback.format_exc())
-
- def snapmirror_break(self, destination=None):
- """
- Break SnapMirror relationship at destination cluster
- """
- if destination is None:
- destination = self.parameters['destination_path']
- options = {'destination-location': destination}
- snapmirror_break = netapp_utils.zapi.NaElement.create_node_with_children(
- 'snapmirror-break', **options)
- try:
- self.server.invoke_successfully(snapmirror_break,
- enable_tunneling=True)
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg='Error breaking SnapMirror relationship : %s'
- % (to_native(error)),
- exception=traceback.format_exc())
-
- def snapmirror_release(self):
- """
- Release SnapMirror relationship from source cluster
- """
- options = {'destination-location': self.parameters['destination_path']}
- snapmirror_release = netapp_utils.zapi.NaElement.create_node_with_children(
- 'snapmirror-release', **options)
- try:
- self.source_server.invoke_successfully(snapmirror_release,
- enable_tunneling=True)
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg='Error releasing SnapMirror relationship : %s'
- % (to_native(error)),
- exception=traceback.format_exc())
-
- def snapmirror_abort(self):
- """
- Abort a SnapMirror relationship in progress
- """
- options = {'destination-location': self.parameters['destination_path']}
- snapmirror_abort = netapp_utils.zapi.NaElement.create_node_with_children(
- 'snapmirror-abort', **options)
- try:
- self.server.invoke_successfully(snapmirror_abort,
- enable_tunneling=True)
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg='Error aborting SnapMirror relationship : %s'
- % (to_native(error)),
- exception=traceback.format_exc())
-
- def snapmirror_initialize(self):
- """
- Initialize SnapMirror based on relationship type
- """
- current = self.snapmirror_get()
- if current['mirror_state'] != 'snapmirrored':
- initialize_zapi = 'snapmirror-initialize'
- if self.parameters.get('relationship_type') and self.parameters['relationship_type'] == 'load_sharing':
- initialize_zapi = 'snapmirror-initialize-ls-set'
- options = {'source-location': self.parameters['source_path']}
- else:
- options = {'destination-location': self.parameters['destination_path']}
- snapmirror_init = netapp_utils.zapi.NaElement.create_node_with_children(
- initialize_zapi, **options)
- try:
- self.server.invoke_successfully(snapmirror_init,
- enable_tunneling=True)
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg='Error initializing SnapMirror : %s'
- % (to_native(error)),
- exception=traceback.format_exc())
-
- def snapmirror_modify(self, modify):
- """
- Modify SnapMirror schedule or policy
- """
- options = {'destination-location': self.parameters['destination_path']}
- snapmirror_modify = netapp_utils.zapi.NaElement.create_node_with_children(
- 'snapmirror-modify', **options)
- if modify.get('schedule') is not None:
- snapmirror_modify.add_new_child('schedule', modify.get('schedule'))
- if modify.get('policy'):
- snapmirror_modify.add_new_child('policy', modify.get('policy'))
- if modify.get('max_transfer_rate'):
- snapmirror_modify.add_new_child('max-transfer-rate', str(modify.get('max_transfer_rate')))
- try:
- self.server.invoke_successfully(snapmirror_modify,
- enable_tunneling=True)
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg='Error modifying SnapMirror schedule or policy : %s'
- % (to_native(error)),
- exception=traceback.format_exc())
-
- def snapmirror_update(self):
- """
- Update data in destination endpoint
- """
- options = {'destination-location': self.parameters['destination_path']}
- snapmirror_update = netapp_utils.zapi.NaElement.create_node_with_children(
- 'snapmirror-update', **options)
- try:
- result = self.server.invoke_successfully(snapmirror_update,
- enable_tunneling=True)
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg='Error updating SnapMirror : %s'
- % (to_native(error)),
- exception=traceback.format_exc())
-
- def check_parameters(self):
- """
- Validate parameters and fail if one or more required params are missing
- Update source and destination path from vserver and volume parameters
- """
- if self.parameters['state'] == 'present'\
- and (self.parameters.get('source_path') or self.parameters.get('destination_path')):
- if not self.parameters.get('destination_path') or not self.parameters.get('source_path'):
- self.module.fail_json(msg='Missing parameters: Source path or Destination path')
- elif self.parameters.get('source_volume'):
- if not self.parameters.get('source_vserver') or not self.parameters.get('destination_vserver'):
- self.module.fail_json(msg='Missing parameters: source vserver or destination vserver or both')
- self.parameters['source_path'] = self.parameters['source_vserver'] + ":" + self.parameters['source_volume']
- self.parameters['destination_path'] = self.parameters['destination_vserver'] + ":" +\
- self.parameters['destination_volume']
- elif self.parameters.get('source_vserver'):
- self.parameters['source_path'] = self.parameters['source_vserver'] + ":"
- self.parameters['destination_path'] = self.parameters['destination_vserver'] + ":"
-
- def get_destination(self):
- result = None
- release_get = netapp_utils.zapi.NaElement('snapmirror-get-destination-iter')
- query = netapp_utils.zapi.NaElement('query')
- snapmirror_dest_info = netapp_utils.zapi.NaElement('snapmirror-destination-info')
- snapmirror_dest_info.add_new_child('destination-location', self.parameters['destination_path'])
- query.add_child_elem(snapmirror_dest_info)
- release_get.add_child_elem(query)
- try:
- result = self.source_server.invoke_successfully(release_get, enable_tunneling=True)
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg='Error fetching snapmirror destinations info: %s' % to_native(error),
- exception=traceback.format_exc())
- if result.get_child_by_name('num-records') and \
- int(result.get_child_content('num-records')) > 0:
- return True
- return None
-
- @staticmethod
- def element_source_path_format_matches(value):
- return re.match(pattern=r"\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}:\/lun\/[0-9]+",
- string=value)
-
- def check_elementsw_parameters(self, kind='source'):
- """
- Validate all ElementSW cluster parameters required for managing the SnapMirror relationship
- Validate if both source and destination paths are present
- Validate if source_path follows the required format
- Validate SVIP
- Validate if ElementSW volume exists
- :return: None
- """
- path = None
- if kind == 'destination':
- path = self.parameters.get('destination_path')
- elif kind == 'source':
- path = self.parameters.get('source_path')
- if path is None:
- self.module.fail_json(msg="Error: Missing required parameter %s_path for "
- "connection_type %s" % (kind, self.parameters['connection_type']))
- else:
- if NetAppONTAPSnapmirror.element_source_path_format_matches(path) is None:
- self.module.fail_json(msg="Error: invalid %s_path %s. "
- "If the path is a ElementSW cluster, the value should be of the format"
- " <Element_SVIP>:/lun/<Element_VOLUME_ID>" % (kind, path))
- # validate source_path
- elementsw_helper, elem = self.set_element_connection(kind)
- self.validate_elementsw_svip(path, elem)
- self.check_if_elementsw_volume_exists(path, elementsw_helper)
-
- def validate_elementsw_svip(self, path, elem):
- """
- Validate ElementSW cluster SVIP
- :return: None
- """
- result = None
- try:
- result = elem.get_cluster_info()
- except solidfire.common.ApiServerError as err:
- self.module.fail_json(msg="Error fetching SVIP", exception=to_native(err))
- if result and result.cluster_info.svip:
- cluster_svip = result.cluster_info.svip
- svip = path.split(':')[0] # split IP address from source_path
- if svip != cluster_svip:
- self.module.fail_json(msg="Error: Invalid SVIP")
-
- def check_if_elementsw_volume_exists(self, path, elementsw_helper):
- """
- Check if remote ElementSW volume exists
- :return: None
- """
- volume_id, vol_id = None, path.split('/')[-1]
- try:
- volume_id = elementsw_helper.volume_id_exists(int(vol_id))
- except solidfire.common.ApiServerError as err:
- self.module.fail_json(msg="Error fetching Volume details", exception=to_native(err))
-
- if volume_id is None:
- self.module.fail_json(msg="Error: Source volume does not exist in the ElementSW cluster")
-
- def asup_log_for_cserver(self, event_name):
- """
- Fetch admin vserver for the given cluster
- Create and Autosupport log event with the given module name
- :param event_name: Name of the event log
- :return: None
- """
- results = netapp_utils.get_cserver(self.server)
- cserver = netapp_utils.setup_na_ontap_zapi(module=self.module, vserver=results)
- netapp_utils.ems_log_event(event_name, cserver)
-
- def apply(self):
- """
- Apply action to SnapMirror
- """
- self.asup_log_for_cserver("na_ontap_snapmirror")
- # source is ElementSW
- if self.parameters['state'] == 'present' and self.parameters.get('connection_type') == 'elementsw_ontap':
- self.check_elementsw_parameters()
- elif self.parameters.get('connection_type') == 'ontap_elementsw':
- self.check_elementsw_parameters('destination')
- else:
- self.check_parameters()
- if self.parameters['state'] == 'present' and self.parameters.get('connection_type') == 'ontap_elementsw':
- current_elementsw_ontap = self.snapmirror_get(self.parameters['source_path'])
- if current_elementsw_ontap is None:
- self.module.fail_json(msg='Error: creating an ONTAP to ElementSW snapmirror relationship requires an '
- 'established SnapMirror relation from ElementSW to ONTAP cluster')
- current = self.snapmirror_get()
- cd_action = self.na_helper.get_cd_action(current, self.parameters)
- modify = self.na_helper.get_modified_attributes(current, self.parameters)
- element_snapmirror = False
- if cd_action == 'create':
- self.snapmirror_create()
- elif cd_action == 'delete':
- if current['status'] == 'transferring':
- self.snapmirror_abort()
- else:
- if self.parameters.get('connection_type') == 'elementsw_ontap':
- element_snapmirror = True
- self.delete_snapmirror(element_snapmirror, current['relationship'])
- else:
- if modify:
- self.snapmirror_modify(modify)
- # check for initialize
- if current and current['mirror_state'] != 'snapmirrored':
- self.snapmirror_initialize()
- # set changed explicitly for initialize
- self.na_helper.changed = True
- # Update when create is called again, or modify is being called
- if self.parameters['state'] == 'present':
- self.snapmirror_update()
- self.module.exit_json(changed=self.na_helper.changed)
-
-
-def main():
- """Execute action"""
- community_obj = NetAppONTAPSnapmirror()
- community_obj.apply()
-
-
-if __name__ == '__main__':
- main()
diff --git a/lib/ansible/modules/storage/netapp/na_ontap_snapshot.py b/lib/ansible/modules/storage/netapp/na_ontap_snapshot.py
deleted file mode 100644
index ba4b360074..0000000000
--- a/lib/ansible/modules/storage/netapp/na_ontap_snapshot.py
+++ /dev/null
@@ -1,326 +0,0 @@
-#!/usr/bin/python
-
-# (c) 2018-2019, NetApp, Inc
-# GNU General Public License v3.0+
-# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-
-from __future__ import absolute_import, division, print_function
-__metaclass__ = type
-
-
-ANSIBLE_METADATA = {'metadata_version': '1.1',
- 'status': ['preview'],
- 'supported_by': 'community'}
-
-DOCUMENTATION = '''
-module: na_ontap_snapshot
-short_description: NetApp ONTAP manage Snapshots
-extends_documentation_fragment:
- - netapp.na_ontap
-version_added: '2.6'
-author: NetApp Ansible Team (@carchi8py) <ng-ansibleteam@netapp.com>
-description:
-- Create/Modify/Delete ONTAP snapshots
-options:
- state:
- description:
- - If you want to create/modify a snapshot, or delete it.
- choices: ['present', 'absent']
- default: present
- snapshot:
- description:
- Name of the snapshot to be managed.
- The maximum string length is 256 characters.
- required: true
- from_name:
- description:
- - Name of the existing snapshot to be renamed to.
- version_added: '2.8'
- volume:
- description:
- - Name of the volume on which the snapshot is to be created.
- required: true
- async_bool:
- description:
- - If true, the snapshot is to be created asynchronously.
- type: bool
- comment:
- description:
- A human readable comment attached with the snapshot.
- The size of the comment can be at most 255 characters.
- snapmirror_label:
- description:
- A human readable SnapMirror Label attached with the snapshot.
- Size of the label can be at most 31 characters.
- ignore_owners:
- description:
- - if this field is true, snapshot will be deleted
- even if some other processes are accessing it.
- type: bool
- snapshot_instance_uuid:
- description:
- - The 128 bit unique snapshot identifier expressed in the form of UUID.
- vserver:
- description:
- - The Vserver name
- required: true
-'''
-EXAMPLES = """
- - name: create SnapShot
- tags:
- - create
- na_ontap_snapshot:
- state: present
- snapshot: "{{ snapshot name }}"
- volume: "{{ vol name }}"
- comment: "i am a comment"
- vserver: "{{ vserver name }}"
- username: "{{ netapp username }}"
- password: "{{ netapp password }}"
- hostname: "{{ netapp hostname }}"
- - name: delete SnapShot
- tags:
- - delete
- na_ontap_snapshot:
- state: absent
- snapshot: "{{ snapshot name }}"
- volume: "{{ vol name }}"
- vserver: "{{ vserver name }}"
- username: "{{ netapp username }}"
- password: "{{ netapp password }}"
- hostname: "{{ netapp hostname }}"
- - name: modify SnapShot
- tags:
- - modify
- na_ontap_snapshot:
- state: present
- snapshot: "{{ snapshot name }}"
- comment: "New comments are great"
- volume: "{{ vol name }}"
- vserver: "{{ vserver name }}"
- username: "{{ netapp username }}"
- password: "{{ netapp password }}"
- hostname: "{{ netapp hostname }}"
-"""
-
-RETURN = """
-"""
-import traceback
-
-from ansible.module_utils.basic import AnsibleModule
-from ansible.module_utils.netapp_module import NetAppModule
-from ansible.module_utils._text import to_native
-import ansible.module_utils.netapp as netapp_utils
-
-HAS_NETAPP_LIB = netapp_utils.has_netapp_lib()
-
-
-class NetAppOntapSnapshot(object):
- """
- Creates, modifies, and deletes a Snapshot
- """
-
- def __init__(self):
- self.argument_spec = netapp_utils.na_ontap_host_argument_spec()
- self.argument_spec.update(dict(
- state=dict(required=False, choices=[
- 'present', 'absent'], default='present'),
- from_name=dict(required=False, type='str'),
- snapshot=dict(required=True, type="str"),
- volume=dict(required=True, type="str"),
- async_bool=dict(required=False, type="bool", default=False),
- comment=dict(required=False, type="str"),
- snapmirror_label=dict(required=False, type="str"),
- ignore_owners=dict(required=False, type="bool", default=False),
- snapshot_instance_uuid=dict(required=False, type="str"),
- vserver=dict(required=True, type="str"),
-
- ))
- self.module = AnsibleModule(
- argument_spec=self.argument_spec,
- supports_check_mode=True
- )
-
- self.na_helper = NetAppModule()
- self.parameters = self.na_helper.set_parameters(self.module.params)
-
- if HAS_NETAPP_LIB is False:
- self.module.fail_json(
- msg="the python NetApp-Lib module is required")
- else:
- self.server = netapp_utils.setup_na_ontap_zapi(
- module=self.module, vserver=self.parameters['vserver'])
- return
-
- def get_snapshot(self, snapshot_name=None):
- """
- Checks to see if a snapshot exists or not
- :return: Return True if a snapshot exists, False if it doesn't
- """
- if snapshot_name is None:
- snapshot_name = self.parameters['snapshot']
- snapshot_obj = netapp_utils.zapi.NaElement("snapshot-get-iter")
- desired_attr = netapp_utils.zapi.NaElement("desired-attributes")
- snapshot_info = netapp_utils.zapi.NaElement('snapshot-info')
- comment = netapp_utils.zapi.NaElement('comment')
- snapmirror_label = netapp_utils.zapi.NaElement('snapmirror-label')
- # add more desired attributes that are allowed to be modified
- snapshot_info.add_child_elem(comment)
- snapshot_info.add_child_elem(snapmirror_label)
- desired_attr.add_child_elem(snapshot_info)
- snapshot_obj.add_child_elem(desired_attr)
- # compose query
- query = netapp_utils.zapi.NaElement("query")
- snapshot_info_obj = netapp_utils.zapi.NaElement("snapshot-info")
- snapshot_info_obj.add_new_child("name", snapshot_name)
- snapshot_info_obj.add_new_child("volume", self.parameters['volume'])
- snapshot_info_obj.add_new_child("vserver", self.parameters['vserver'])
- query.add_child_elem(snapshot_info_obj)
- snapshot_obj.add_child_elem(query)
- result = self.server.invoke_successfully(snapshot_obj, True)
- return_value = None
- if result.get_child_by_name('num-records') and \
- int(result.get_child_content('num-records')) == 1:
- attributes_list = result.get_child_by_name('attributes-list')
- snap_info = attributes_list.get_child_by_name('snapshot-info')
- return_value = {'comment': snap_info.get_child_content('comment')}
- if snap_info.get_child_by_name('snapmirror-label'):
- return_value['snapmirror_label'] = snap_info.get_child_content('snapmirror-label')
- else:
- return_value['snapmirror_label'] = None
- return return_value
-
- def create_snapshot(self):
- """
- Creates a new snapshot
- """
- snapshot_obj = netapp_utils.zapi.NaElement("snapshot-create")
-
- # set up required variables to create a snapshot
- snapshot_obj.add_new_child("snapshot", self.parameters['snapshot'])
- snapshot_obj.add_new_child("volume", self.parameters['volume'])
- # Set up optional variables to create a snapshot
- if self.parameters.get('async_bool'):
- snapshot_obj.add_new_child("async", str(self.parameters['async_bool']))
- if self.parameters.get('comment'):
- snapshot_obj.add_new_child("comment", self.parameters['comment'])
- if self.parameters.get('snapmirror_label'):
- snapshot_obj.add_new_child(
- "snapmirror-label", self.parameters['snapmirror_label'])
- try:
- self.server.invoke_successfully(snapshot_obj, True)
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg='Error creating snapshot %s: %s' %
- (self.parameters['snapshot'], to_native(error)),
- exception=traceback.format_exc())
-
- def delete_snapshot(self):
- """
- Deletes an existing snapshot
- """
- snapshot_obj = netapp_utils.zapi.NaElement("snapshot-delete")
-
- # Set up required variables to delete a snapshot
- snapshot_obj.add_new_child("snapshot", self.parameters['snapshot'])
- snapshot_obj.add_new_child("volume", self.parameters['volume'])
- # set up optional variables to delete a snapshot
- if self.parameters.get('ignore_owners'):
- snapshot_obj.add_new_child("ignore-owners", str(self.parameters['ignore_owners']))
- if self.parameters.get('snapshot_instance_uuid'):
- snapshot_obj.add_new_child("snapshot-instance-uuid", self.parameters['snapshot_instance_uuid'])
- try:
- self.server.invoke_successfully(snapshot_obj, True)
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg='Error deleting snapshot %s: %s' %
- (self.parameters['snapshot'], to_native(error)),
- exception=traceback.format_exc())
-
- def modify_snapshot(self):
- """
- Modify an existing snapshot
- :return:
- """
- snapshot_obj = netapp_utils.zapi.NaElement("snapshot-modify-iter")
- # Create query object, this is the existing object
- query = netapp_utils.zapi.NaElement("query")
- snapshot_info_obj = netapp_utils.zapi.NaElement("snapshot-info")
- snapshot_info_obj.add_new_child("name", self.parameters['snapshot'])
- snapshot_info_obj.add_new_child("vserver", self.parameters['vserver'])
- query.add_child_elem(snapshot_info_obj)
- snapshot_obj.add_child_elem(query)
-
- # this is what we want to modify in the snapshot object
- attributes = netapp_utils.zapi.NaElement("attributes")
- snapshot_info_obj = netapp_utils.zapi.NaElement("snapshot-info")
- snapshot_info_obj.add_new_child("name", self.parameters['snapshot'])
- if self.parameters.get('comment'):
- snapshot_info_obj.add_new_child("comment", self.parameters['comment'])
- if self.parameters.get('snapmirror_label'):
- snapshot_info_obj.add_new_child("snapmirror-label", self.parameters['snapmirror_label'])
- attributes.add_child_elem(snapshot_info_obj)
- snapshot_obj.add_child_elem(attributes)
- try:
- self.server.invoke_successfully(snapshot_obj, True)
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg='Error modifying snapshot %s: %s' %
- (self.parameters['snapshot'], to_native(error)),
- exception=traceback.format_exc())
-
- def rename_snapshot(self):
- """
- Rename the snapshot
- """
- snapshot_obj = netapp_utils.zapi.NaElement("snapshot-rename")
-
- # set up required variables to rename a snapshot
- snapshot_obj.add_new_child("current-name", self.parameters['from_name'])
- snapshot_obj.add_new_child("new-name", self.parameters['snapshot'])
- snapshot_obj.add_new_child("volume", self.parameters['volume'])
- try:
- self.server.invoke_successfully(snapshot_obj, True)
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg='Error renaming snapshot %s to %s: %s' %
- (self.parameters['from_name'], self.parameters['snapshot'], to_native(error)),
- exception=traceback.format_exc())
-
- def apply(self):
- """
- Check to see which play we should run
- """
- current = self.get_snapshot()
- netapp_utils.ems_log_event("na_ontap_snapshot", self.server)
- rename, cd_action = None, None
- modify = {}
- if self.parameters.get('from_name'):
- current_old_name = self.get_snapshot(self.parameters['from_name'])
- rename = self.na_helper.is_rename_action(current_old_name, current)
- modify = self.na_helper.get_modified_attributes(current_old_name, self.parameters)
- else:
- cd_action = self.na_helper.get_cd_action(current, self.parameters)
- if cd_action is None:
- modify = self.na_helper.get_modified_attributes(current, self.parameters)
- if self.na_helper.changed:
- if self.module.check_mode:
- pass
- else:
- if rename:
- self.rename_snapshot()
- if cd_action == 'create':
- self.create_snapshot()
- elif cd_action == 'delete':
- self.delete_snapshot()
- elif modify:
- self.modify_snapshot()
- self.module.exit_json(changed=self.na_helper.changed)
-
-
-def main():
- """
- Creates, modifies, and deletes a Snapshot
- """
- obj = NetAppOntapSnapshot()
- obj.apply()
-
-
-if __name__ == '__main__':
- main()
diff --git a/lib/ansible/modules/storage/netapp/na_ontap_snapshot_policy.py b/lib/ansible/modules/storage/netapp/na_ontap_snapshot_policy.py
deleted file mode 100644
index 2e714f9f3d..0000000000
--- a/lib/ansible/modules/storage/netapp/na_ontap_snapshot_policy.py
+++ /dev/null
@@ -1,453 +0,0 @@
-#!/usr/bin/python
-
-# (c) 2018-2019, NetApp, Inc
-# GNU General Public License v3.0+
-# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-
-from __future__ import absolute_import, division, print_function
-__metaclass__ = type
-
-
-ANSIBLE_METADATA = {'metadata_version': '1.1',
- 'status': ['preview'],
- 'supported_by': 'community'}
-
-DOCUMENTATION = '''
-module: na_ontap_snapshot_policy
-short_description: NetApp ONTAP manage Snapshot Policy
-extends_documentation_fragment:
- - netapp.na_ontap
-version_added: '2.8'
-author: NetApp Ansible Team (@carchi8py) <ng-ansibleteam@netapp.com>
-description:
-- Create/Modify/Delete ONTAP snapshot policies
-options:
- state:
- description:
- - If you want to create, modify or delete a snapshot policy.
- choices: ['present', 'absent']
- default: present
- name:
- description:
- Name of the snapshot policy to be managed.
- The maximum string length is 256 characters.
- required: true
- enabled:
- description:
- - Status of the snapshot policy indicating whether the policy will be enabled or disabled.
- type: bool
- comment:
- description:
- A human readable comment attached with the snapshot.
- The size of the comment can be at most 255 characters.
- count:
- description:
- Retention count for the snapshots created by the schedule.
- type: list
- schedule:
- description:
- - Schedule to be added inside the policy.
- type: list
- snapmirror_label:
- description:
- - SnapMirror label assigned to each schedule inside the policy. Use an empty
- string ('') for no label.
- type: list
- required: false
- version_added: '2.9'
- vserver:
- description:
- - The name of the vserver to use. In a multi-tenanted environment, assigning a
- Snapshot Policy to a vserver will restrict its use to that vserver.
- required: false
- version_added: '2.9'
-'''
-EXAMPLES = """
- - name: Create Snapshot policy
- na_ontap_snapshot_policy:
- state: present
- name: ansible2
- schedule: hourly
- count: 150
- enabled: True
- username: "{{ netapp_username }}"
- password: "{{ netapp_password }}"
- hostname: "{{ netapp_hostname }}"
- https: False
-
- - name: Create Snapshot policy with multiple schedules
- na_ontap_snapshot_policy:
- state: present
- name: ansible2
- schedule: ['hourly', 'daily', 'weekly', 'monthly', '5min']
- count: [1, 2, 3, 4, 5]
- enabled: True
- username: "{{ netapp_username }}"
- password: "{{ netapp_password }}"
- hostname: "{{ netapp_hostname }}"
- https: False
-
- - name: Create Snapshot policy owned by a vserver
- na_ontap_snapshot_policy:
- state: present
- name: ansible3
- vserver: ansible
- schedule: ['hourly', 'daily', 'weekly', 'monthly', '5min']
- count: [1, 2, 3, 4, 5]
- snapmirror_label: ['hourly', 'daily', 'weekly', 'monthly', '']
- enabled: True
- username: "{{ netapp_username }}"
- password: "{{ netapp_password }}"
- hostname: "{{ netapp_hostname }}"
- https: False
-
- - name: Modify Snapshot policy with multiple schedules
- na_ontap_snapshot_policy:
- state: present
- name: ansible2
- schedule: ['daily', 'weekly']
- count: [20, 30]
- snapmirror_label: ['daily', 'weekly']
- enabled: True
- username: "{{ netapp_username }}"
- password: "{{ netapp_password }}"
- hostname: "{{ netapp_hostname }}"
- https: False
-
- - name: Delete Snapshot policy
- na_ontap_snapshot_policy:
- state: absent
- name: ansible2
- username: "{{ netapp_username }}"
- password: "{{ netapp_password }}"
- hostname: "{{ netapp_hostname }}"
- https: False
-"""
-
-RETURN = """
-"""
-import traceback
-
-from ansible.module_utils.basic import AnsibleModule
-from ansible.module_utils.netapp_module import NetAppModule
-from ansible.module_utils._text import to_native
-import ansible.module_utils.netapp as netapp_utils
-
-HAS_NETAPP_LIB = netapp_utils.has_netapp_lib()
-
-
-class NetAppOntapSnapshotPolicy(object):
- """
- Creates and deletes a Snapshot Policy
- """
-
- def __init__(self):
- self.argument_spec = netapp_utils.na_ontap_host_argument_spec()
- self.argument_spec.update(dict(
- state=dict(required=False, choices=[
- 'present', 'absent'], default='present'),
- name=dict(required=True, type="str"),
- enabled=dict(required=False, type="bool"),
- # count is a list of integers
- count=dict(required=False, type="list", elements="int"),
- comment=dict(required=False, type="str"),
- schedule=dict(required=False, type="list", elements="str"),
- snapmirror_label=dict(required=False, type="list", elements="str"),
- vserver=dict(required=False, type="str")
- ))
- self.module = AnsibleModule(
- argument_spec=self.argument_spec,
- required_if=[
- ('state', 'present', ['enabled', 'count', 'schedule']),
- ],
- supports_check_mode=True
- )
-
- self.na_helper = NetAppModule()
- self.parameters = self.na_helper.set_parameters(self.module.params)
-
- if HAS_NETAPP_LIB is False:
- self.module.fail_json(
- msg="the python NetApp-Lib module is required")
- else:
- if 'vserver' in self.parameters:
- self.server = netapp_utils.setup_na_ontap_zapi(module=self.module, vserver=self.parameters['vserver'])
- else:
- self.server = netapp_utils.setup_na_ontap_zapi(module=self.module)
- return
-
- def get_snapshot_policy(self):
- """
- Checks to see if a snapshot policy exists or not
- :return: Return policy details if a snapshot policy exists, None if it doesn't
- """
- snapshot_obj = netapp_utils.zapi.NaElement("snapshot-policy-get-iter")
- # compose query
- query = netapp_utils.zapi.NaElement("query")
- snapshot_info_obj = netapp_utils.zapi.NaElement("snapshot-policy-info")
- snapshot_info_obj.add_new_child("policy", self.parameters['name'])
- if 'vserver' in self.parameters:
- snapshot_info_obj.add_new_child("vserver-name", self.parameters['vserver'])
- query.add_child_elem(snapshot_info_obj)
- snapshot_obj.add_child_elem(query)
- try:
- result = self.server.invoke_successfully(snapshot_obj, True)
- if result.get_child_by_name('num-records') and \
- int(result.get_child_content('num-records')) == 1:
- snapshot_policy = result.get_child_by_name('attributes-list').get_child_by_name('snapshot-policy-info')
- current = {}
- current['name'] = snapshot_policy.get_child_content('policy')
- current['vserver'] = snapshot_policy.get_child_content('vserver-name')
- current['enabled'] = False if snapshot_policy.get_child_content('enabled').lower() == 'false' else True
- current['comment'] = snapshot_policy.get_child_content('comment') or ''
- current['schedule'], current['count'], current['snapmirror_label'] = [], [], []
- if snapshot_policy.get_child_by_name('snapshot-policy-schedules'):
- for schedule in snapshot_policy['snapshot-policy-schedules'].get_children():
- current['schedule'].append(schedule.get_child_content('schedule'))
- current['count'].append(int(schedule.get_child_content('count')))
- snapmirror_label = schedule.get_child_content('snapmirror-label')
- if snapmirror_label is None or snapmirror_label == '-':
- snapmirror_label = ''
- current['snapmirror_label'].append(snapmirror_label)
- return current
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg=to_native(error), exception=traceback.format_exc())
- return None
-
- def validate_parameters(self):
- """
- Validate if each schedule has a count associated
- :return: None
- """
- if 'count' not in self.parameters or 'schedule' not in self.parameters or \
- len(self.parameters['count']) > 5 or len(self.parameters['schedule']) > 5 or \
- len(self.parameters['count']) < 1 or len(self.parameters['schedule']) < 1 or \
- len(self.parameters['count']) != len(self.parameters['schedule']):
- self.module.fail_json(msg="Error: A Snapshot policy must have at least 1 "
- "schedule and can have up to a maximum of 5 schedules, with a count "
- "representing the maximum number of Snapshot copies for each schedule")
-
- if 'snapmirror_label' in self.parameters:
- if len(self.parameters['snapmirror_label']) != len(self.parameters['schedule']):
- self.module.fail_json(msg="Error: Each Snapshot Policy schedule must have an "
- "accompanying SnapMirror Label")
-
- def modify_snapshot_policy(self, current):
- """
- Modifies an existing snapshot policy
- """
- # Set up required variables to modify snapshot policy
- options = {'policy': self.parameters['name']}
- modify = False
-
- # Set up optional variables to modify snapshot policy
- if 'enabled' in self.parameters and self.parameters['enabled'] != current['enabled']:
- options['enabled'] = str(self.parameters['enabled'])
- modify = True
- if 'comment' in self.parameters and self.parameters['comment'] != current['comment']:
- options['comment'] = self.parameters['comment']
- modify = True
-
- if modify:
- snapshot_obj = netapp_utils.zapi.NaElement.create_node_with_children('snapshot-policy-modify', **options)
- try:
- self.server.invoke_successfully(snapshot_obj, True)
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg='Error modifying snapshot policy %s: %s' %
- (self.parameters['name'], to_native(error)),
- exception=traceback.format_exc())
-
- def modify_snapshot_policy_schedules(self, current):
- """
- Modify existing schedules in snapshot policy
- :return: None
- """
- self.validate_parameters()
-
- delete_schedules, modify_schedules, add_schedules = [], [], []
-
- if 'snapmirror_label' in self.parameters:
- snapmirror_labels = self.parameters['snapmirror_label']
- else:
- # User hasn't supplied any snapmirror labels.
- snapmirror_labels = [None] * len(self.parameters['schedule'])
-
- # Identify schedules for deletion
- for schedule in current['schedule']:
- schedule = schedule.strip()
- if schedule not in [item.strip() for item in self.parameters['schedule']]:
- options = {'policy': current['name'],
- 'schedule': schedule}
- delete_schedules.append(options)
-
- # Identify schedules to be modified or added
- for schedule, count, snapmirror_label in zip(self.parameters['schedule'], self.parameters['count'], snapmirror_labels):
- schedule = schedule.strip()
- if snapmirror_label is not None:
- snapmirror_label = snapmirror_label.strip()
-
- options = {'policy': current['name'],
- 'schedule': schedule}
-
- if schedule in current['schedule']:
- # Schedule exists. Only modify if it has changed.
- modify = False
- schedule_index = current['schedule'].index(schedule)
-
- if count != current['count'][schedule_index]:
- options['new-count'] = str(count)
- modify = True
-
- if snapmirror_label is not None:
- if snapmirror_label != current['snapmirror_label'][schedule_index]:
- options['new-snapmirror-label'] = snapmirror_label
- modify = True
-
- if modify:
- modify_schedules.append(options)
- else:
- # New schedule
- options['count'] = str(count)
- if snapmirror_label is not None and snapmirror_label != '':
- options['snapmirror-label'] = snapmirror_label
- add_schedules.append(options)
-
- # Delete N-1 schedules no longer required. Must leave 1 schedule in policy
- # at any one time. Delete last one afterwards.
- while len(delete_schedules) > 1:
- options = delete_schedules.pop()
- self.modify_snapshot_policy_schedule(options, 'snapshot-policy-remove-schedule')
-
- # Modify schedules.
- while len(modify_schedules) > 0:
- options = modify_schedules.pop()
- self.modify_snapshot_policy_schedule(options, 'snapshot-policy-modify-schedule')
-
- # Add N-1 new schedules. Add last one after last schedule has been deleted.
- while len(add_schedules) > 1:
- options = add_schedules.pop()
- self.modify_snapshot_policy_schedule(options, 'snapshot-policy-add-schedule')
-
- # Delete last schedule no longer required.
- while len(delete_schedules) > 0:
- options = delete_schedules.pop()
- self.modify_snapshot_policy_schedule(options, 'snapshot-policy-remove-schedule')
-
- # Add last new schedule.
- while len(add_schedules) > 0:
- options = add_schedules.pop()
- self.modify_snapshot_policy_schedule(options, 'snapshot-policy-add-schedule')
-
- def modify_snapshot_policy_schedule(self, options, zapi):
- """
- Add, modify or remove a schedule to/from a snapshot policy
- """
- snapshot_obj = netapp_utils.zapi.NaElement.create_node_with_children(zapi, **options)
- try:
- self.server.invoke_successfully(snapshot_obj, enable_tunneling=True)
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg='Error modifying snapshot policy schedule %s: %s' %
- (self.parameters['name'], to_native(error)),
- exception=traceback.format_exc())
-
- def create_snapshot_policy(self):
- """
- Creates a new snapshot policy
- """
- # set up required variables to create a snapshot policy
- self.validate_parameters()
- options = {'policy': self.parameters['name'],
- 'enabled': str(self.parameters['enabled']),
- }
-
- if 'snapmirror_label' in self.parameters:
- snapmirror_labels = self.parameters['snapmirror_label']
- else:
- # User hasn't supplied any snapmirror labels.
- snapmirror_labels = [None] * len(self.parameters['schedule'])
-
- # zapi attribute for first schedule is schedule1, second is schedule2 and so on
- positions = [str(i) for i in range(1, len(self.parameters['schedule']) + 1)]
- for schedule, count, snapmirror_label, position in zip(self.parameters['schedule'], self.parameters['count'], snapmirror_labels, positions):
- schedule = schedule.strip()
- options['count' + position] = str(count)
- options['schedule' + position] = schedule
- if snapmirror_label is not None:
- snapmirror_label = snapmirror_label.strip()
- if snapmirror_label != '':
- options['snapmirror-label' + position] = snapmirror_label
- snapshot_obj = netapp_utils.zapi.NaElement.create_node_with_children('snapshot-policy-create', **options)
-
- # Set up optional variables to create a snapshot policy
- if self.parameters.get('comment'):
- snapshot_obj.add_new_child("comment", self.parameters['comment'])
- try:
- self.server.invoke_successfully(snapshot_obj, True)
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg='Error creating snapshot policy %s: %s' %
- (self.parameters['name'], to_native(error)),
- exception=traceback.format_exc())
-
- def delete_snapshot_policy(self):
- """
- Deletes an existing snapshot policy
- """
- snapshot_obj = netapp_utils.zapi.NaElement("snapshot-policy-delete")
-
- # Set up required variables to delete a snapshot policy
- snapshot_obj.add_new_child("policy", self.parameters['name'])
- try:
- self.server.invoke_successfully(snapshot_obj, True)
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg='Error deleting snapshot policy %s: %s' %
- (self.parameters['name'], to_native(error)),
- exception=traceback.format_exc())
-
- def asup_log_for_cserver(self, event_name):
- """
- Fetch admin vserver for the given cluster
- Create and Autosupport log event with the given module name
- :param event_name: Name of the event log
- :return: None
- """
- results = netapp_utils.get_cserver(self.server)
- cserver = netapp_utils.setup_na_ontap_zapi(module=self.module, vserver=results)
- netapp_utils.ems_log_event(event_name, cserver)
-
- def apply(self):
- """
- Check to see which play we should run
- """
- self.asup_log_for_cserver("na_ontap_snapshot_policy")
- current = self.get_snapshot_policy()
- modify = None
- cd_action = self.na_helper.get_cd_action(current, self.parameters)
- if cd_action is None and self.parameters['state'] == 'present':
- # Don't sort schedule/count/snapmirror_label lists as it can
- # mess up the intended parameter order.
- modify = self.na_helper.get_modified_attributes(current, self.parameters)
-
- if self.na_helper.changed:
- if self.module.check_mode:
- pass
- else:
- if cd_action == 'create':
- self.create_snapshot_policy()
- elif cd_action == 'delete':
- self.delete_snapshot_policy()
- if modify:
- self.modify_snapshot_policy(current)
- self.modify_snapshot_policy_schedules(current)
- self.module.exit_json(changed=self.na_helper.changed)
-
-
-def main():
- """
- Creates and deletes a Snapshot Policy
- """
- obj = NetAppOntapSnapshotPolicy()
- obj.apply()
-
-
-if __name__ == '__main__':
- main()
diff --git a/lib/ansible/modules/storage/netapp/na_ontap_snmp.py b/lib/ansible/modules/storage/netapp/na_ontap_snmp.py
deleted file mode 100644
index caeadb9da3..0000000000
--- a/lib/ansible/modules/storage/netapp/na_ontap_snmp.py
+++ /dev/null
@@ -1,152 +0,0 @@
-#!/usr/bin/python
-"""
-create SNMP module to add/delete/modify SNMP user
-"""
-
-# (c) 2018-2019, NetApp, Inc
-# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-from __future__ import absolute_import, division, print_function
-__metaclass__ = type
-
-
-ANSIBLE_METADATA = {'metadata_version': '1.1',
- 'status': ['preview'],
- 'supported_by': 'certified'}
-
-
-DOCUMENTATION = '''
-author: NetApp Ansible Team (@carchi8py) <ng-ansibleteam@netapp.com>
-description:
- - "Create/Delete SNMP community"
-extends_documentation_fragment:
- - netapp.na_ontap
-module: na_ontap_snmp
-options:
- access_control:
- description:
- - "Access control for the community. The only supported value is 'ro' (read-only)"
- required: true
- community_name:
- description:
- - "The name of the SNMP community to manage."
- required: true
- state:
- choices: ['present', 'absent']
- description:
- - "Whether the specified SNMP community should exist or not."
- default: 'present'
-short_description: NetApp ONTAP SNMP community
-version_added: "2.6"
-'''
-
-EXAMPLES = """
- - name: Create SNMP community
- na_ontap_snmp:
- state: present
- community_name: communityName
- access_control: 'ro'
- hostname: "{{ netapp_hostname }}"
- username: "{{ netapp_username }}"
- password: "{{ netapp_password }}"
- - name: Delete SNMP community
- na_ontap_snmp:
- state: absent
- community_name: communityName
- access_control: 'ro'
- hostname: "{{ netapp_hostname }}"
- username: "{{ netapp_username }}"
- password: "{{ netapp_password }}"
-"""
-
-RETURN = """
-"""
-from ansible.module_utils.basic import AnsibleModule
-from ansible.module_utils._text import to_native
-import ansible.module_utils.netapp as netapp_utils
-
-HAS_NETAPP_LIB = netapp_utils.has_netapp_lib()
-
-
-class NetAppONTAPSnmp(object):
- '''Class with SNMP methods, doesn't support check mode'''
-
- def __init__(self):
-
- self.argument_spec = netapp_utils.na_ontap_host_argument_spec()
- self.argument_spec.update(dict(
- state=dict(required=False, type='str', choices=['present', 'absent'], default='present'),
- community_name=dict(required=True, type='str'),
- access_control=dict(required=True, type='str'),
- ))
-
- self.module = AnsibleModule(
- argument_spec=self.argument_spec,
- supports_check_mode=False
- )
-
- parameters = self.module.params
- # set up state variables
- self.state = parameters['state']
- self.community_name = parameters['community_name']
- self.access_control = parameters['access_control']
-
- if HAS_NETAPP_LIB is False:
- self.module.fail_json(msg="the python NetApp-Lib module is required")
- else:
- self.server = netapp_utils.setup_na_ontap_zapi(module=self.module)
-
- def invoke_snmp_community(self, zapi):
- """
- Invoke zapi - add/delete take the same NaElement structure
- @return: SUCCESS / FAILURE with an error_message
- """
- snmp_community = netapp_utils.zapi.NaElement.create_node_with_children(
- zapi, **{'community': self.community_name,
- 'access-control': self.access_control})
- try:
- self.server.invoke_successfully(snmp_community, enable_tunneling=True)
- except netapp_utils.zapi.NaApiError: # return False for duplicate entry
- return False
- return True
-
- def add_snmp_community(self):
- """
- Adds a SNMP community
- """
- return self.invoke_snmp_community('snmp-community-add')
-
- def delete_snmp_community(self):
- """
- Delete a SNMP community
- """
- return self.invoke_snmp_community('snmp-community-delete')
-
- def apply(self):
- """
- Apply action to SNMP community
- This module is not idempotent:
- Add doesn't fail the playbook if user is trying
- to add an already existing snmp community
- """
- changed = False
- results = netapp_utils.get_cserver(self.server)
- cserver = netapp_utils.setup_na_ontap_zapi(module=self.module, vserver=results)
- netapp_utils.ems_log_event("na_ontap_snmp", cserver)
- if self.state == 'present': # add
- if self.add_snmp_community():
- changed = True
- elif self.state == 'absent': # delete
- if self.delete_snmp_community():
- changed = True
-
- self.module.exit_json(changed=changed)
-
-
-def main():
- '''Execute action'''
- community_obj = NetAppONTAPSnmp()
- community_obj.apply()
-
-
-if __name__ == '__main__':
- main()
diff --git a/lib/ansible/modules/storage/netapp/na_ontap_software_update.py b/lib/ansible/modules/storage/netapp/na_ontap_software_update.py
deleted file mode 100644
index 7143827905..0000000000
--- a/lib/ansible/modules/storage/netapp/na_ontap_software_update.py
+++ /dev/null
@@ -1,301 +0,0 @@
-#!/usr/bin/python
-
-# (c) 2018-2019, NetApp, Inc
-# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-from __future__ import absolute_import, division, print_function
-__metaclass__ = type
-
-ANSIBLE_METADATA = {'metadata_version': '1.1',
- 'status': ['preview'],
- 'supported_by': 'community'}
-
-
-DOCUMENTATION = '''
-author: NetApp Ansible Team (@carchi8py) <ng-ansibleteam@netapp.com>
-description:
- - Update ONTAP software
- - Requires an https connection and is not supported over http
-extends_documentation_fragment:
- - netapp.na_ontap
-module: na_ontap_software_update
-options:
- state:
- choices: ['present', 'absent']
- description:
- - Whether the specified ONTAP package should update or not.
- default: present
- nodes:
- description:
- - List of nodes to be updated, the nodes have to be a part of a HA Pair.
- aliases:
- - node
- package_version:
- required: true
- description:
- - Specifies the package version to update software.
- package_url:
- required: true
- description:
- - Specifies the package URL to download the package.
- ignore_validation_warning:
- description:
- - Allows the update to continue if warnings are encountered during the validation phase.
- default: False
- type: bool
-short_description: NetApp ONTAP Update Software
-version_added: "2.7"
-'''
-
-EXAMPLES = """
-
- - name: ONTAP software update
- na_ontap_software_update:
- state: present
- nodes: vsim1
- package_url: "{{ url }}"
- package_version: "{{ version_name }}"
- ignore_validation_warning: True
- hostname: "{{ netapp_hostname }}"
- username: "{{ netapp_username }}"
- password: "{{ netapp_password }}"
-"""
-
-RETURN = """
-"""
-
-import traceback
-from ansible.module_utils.basic import AnsibleModule
-from ansible.module_utils._text import to_native
-import ansible.module_utils.netapp as netapp_utils
-from ansible.module_utils.netapp_module import NetAppModule
-import time
-
-HAS_NETAPP_LIB = netapp_utils.has_netapp_lib()
-
-
-class NetAppONTAPSoftwareUpdate(object):
- """
- Class with ONTAP software update methods
- """
-
- def __init__(self):
- self.argument_spec = netapp_utils.na_ontap_host_argument_spec()
- self.argument_spec.update(dict(
- state=dict(required=False, type='str', choices=['present', 'absent'], default='present'),
- nodes=dict(required=False, type='list', aliases=["node"]),
- package_version=dict(required=True, type='str'),
- package_url=dict(required=True, type='str'),
- ignore_validation_warning=dict(required=False, type='bool', default=False)
- ))
-
- self.module = AnsibleModule(
- argument_spec=self.argument_spec,
- supports_check_mode=True
- )
-
- self.na_helper = NetAppModule()
- self.parameters = self.na_helper.set_parameters(self.module.params)
-
- if HAS_NETAPP_LIB is False:
- self.module.fail_json(msg="the python NetApp-Lib module is required")
- else:
- self.server = netapp_utils.setup_na_ontap_zapi(module=self.module)
-
- def cluster_image_get_iter(self):
- """
- Compose NaElement object to query current version
- :return: NaElement object for cluster-image-get-iter with query
- """
- cluster_image_get = netapp_utils.zapi.NaElement('cluster-image-get-iter')
- query = netapp_utils.zapi.NaElement('query')
- cluster_image_info = netapp_utils.zapi.NaElement('cluster-image-info')
- query.add_child_elem(cluster_image_info)
- cluster_image_get.add_child_elem(query)
- return cluster_image_get
-
- def cluster_image_get(self):
- """
- Get current cluster image info
- :return: True if query successful, else return None
- """
- cluster_image_get_iter = self.cluster_image_get_iter()
- try:
- result = self.server.invoke_successfully(cluster_image_get_iter, enable_tunneling=True)
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg='Error fetching cluster image details: %s: %s'
- % (self.parameters['package_version'], to_native(error)),
- exception=traceback.format_exc())
- # return cluster image details
- if result.get_child_by_name('num-records') and \
- int(result.get_child_content('num-records')) > 0:
- return True
- return None
-
- def cluster_image_get_for_node(self, node_name):
- """
- Get current cluster image info for given node
- """
- cluster_image_get = netapp_utils.zapi.NaElement('cluster-image-get')
- cluster_image_get.add_new_child('node-id', node_name)
- try:
- self.server.invoke_successfully(cluster_image_get, enable_tunneling=True)
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg='Error fetching cluster image details for %s: %s'
- % (node_name, to_native(error)),
- exception=traceback.format_exc())
-
- def cluster_image_update_progress_get(self):
- """
- Get current cluster image update progress info
- :return: Dictionary of cluster image update progress if query successful, else return None
- """
- cluster_update_progress_get = netapp_utils.zapi.NaElement('cluster-image-update-progress-info')
- cluster_update_progress_info = dict()
- try:
- result = self.server.invoke_successfully(cluster_update_progress_get, enable_tunneling=True)
- except netapp_utils.zapi.NaApiError as error:
- # return empty dict on error to satisfy package delete upon image update
- if to_native(error.code) == 'Unexpected error' and self.parameters.get('https') is True:
- return cluster_update_progress_info
- else:
- self.module.fail_json(msg='Error fetching cluster image update progress details: %s'
- % (to_native(error)),
- exception=traceback.format_exc())
- # return cluster image update progress details
- if result.get_child_by_name('attributes').get_child_by_name('ndu-progress-info'):
- update_progress_info = result.get_child_by_name('attributes').get_child_by_name('ndu-progress-info')
- cluster_update_progress_info['overall_status'] = update_progress_info.get_child_content('overall-status')
- cluster_update_progress_info['completed_node_count'] = update_progress_info.\
- get_child_content('completed-node-count')
- return cluster_update_progress_info
-
- def cluster_image_update(self):
- """
- Update current cluster image
- """
- cluster_update_info = netapp_utils.zapi.NaElement('cluster-image-update')
- cluster_update_info.add_new_child('package-version', self.parameters['package_version'])
- cluster_update_info.add_new_child('ignore-validation-warning',
- str(self.parameters['ignore_validation_warning']))
- if self.parameters.get('nodes'):
- cluster_nodes = netapp_utils.zapi.NaElement('nodes')
- for node in self.parameters['nodes']:
- cluster_nodes.add_new_child('node-name', node)
- cluster_update_info.add_child_elem(cluster_nodes)
- try:
- self.server.invoke_successfully(cluster_update_info, enable_tunneling=True)
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg='Error updating cluster image for %s: %s'
- % (self.parameters['package_version'], to_native(error)),
- exception=traceback.format_exc())
-
- def cluster_image_package_download(self):
- """
- Get current cluster image package download
- :return: True if package already exists, else return False
- """
- cluster_image_package_download_info = netapp_utils.zapi.NaElement('cluster-image-package-download')
- cluster_image_package_download_info.add_new_child('package-url', self.parameters['package_url'])
- try:
- self.server.invoke_successfully(cluster_image_package_download_info, enable_tunneling=True)
- except netapp_utils.zapi.NaApiError as error:
- # Error 18408 denotes Package image with the same name already exists
- if to_native(error.code) == "18408":
- return True
- else:
- self.module.fail_json(msg='Error downloading cluster image package for %s: %s'
- % (self.parameters['package_url'], to_native(error)),
- exception=traceback.format_exc())
- return False
-
- def cluster_image_package_delete(self):
- """
- Delete current cluster image package
- """
- cluster_image_package_delete_info = netapp_utils.zapi.NaElement('cluster-image-package-delete')
- cluster_image_package_delete_info.add_new_child('package-version', self.parameters['package_version'])
- try:
- self.server.invoke_successfully(cluster_image_package_delete_info, enable_tunneling=True)
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg='Error deleting cluster image package for %s: %s'
- % (self.parameters['package_version'], to_native(error)),
- exception=traceback.format_exc())
-
- def cluster_image_package_download_progress(self):
- """
- Get current cluster image package download progress
- :return: Dictionary of cluster image download progress if query successful, else return None
- """
- cluster_image_package_download_progress_info = netapp_utils.zapi.\
- NaElement('cluster-image-get-download-progress')
- try:
- result = self.server.invoke_successfully(
- cluster_image_package_download_progress_info, enable_tunneling=True)
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg='Error fetching cluster image package download progress for %s: %s'
- % (self.parameters['package_url'], to_native(error)),
- exception=traceback.format_exc())
- # return cluster image download progress details
- cluster_download_progress_info = dict()
- if result.get_child_by_name('progress-status'):
- cluster_download_progress_info['progress_status'] = result.get_child_content('progress-status')
- cluster_download_progress_info['progress_details'] = result.get_child_content('progress-details')
- cluster_download_progress_info['failure_reason'] = result.get_child_content('failure-reason')
- return cluster_download_progress_info
- return None
-
- def autosupport_log(self):
- """
- Autosupport log for software_update
- :return:
- """
- results = netapp_utils.get_cserver(self.server)
- cserver = netapp_utils.setup_na_ontap_zapi(module=self.module, vserver=results)
- netapp_utils.ems_log_event("na_ontap_software_update", cserver)
-
- def apply(self):
- """
- Apply action to update ONTAP software
- """
- if self.parameters.get('https') is not True:
- self.module.fail_json(msg='https parameter must be True')
- changed = False
- self.autosupport_log()
- current = self.cluster_image_get()
- if self.parameters.get('nodes'):
- for node in self.parameters['nodes']:
- self.cluster_image_get_for_node(node)
- if self.parameters.get('state') == 'present' and current:
- package_exists = self.cluster_image_package_download()
- if package_exists is False:
- cluster_download_progress = self.cluster_image_package_download_progress()
- while cluster_download_progress.get('progress_status') == 'async_pkg_get_phase_running':
- time.sleep(5)
- cluster_download_progress = self.cluster_image_package_download_progress()
- if cluster_download_progress.get('progress_status') == 'async_pkg_get_phase_complete':
- self.cluster_image_update()
- changed = True
- else:
- self.module.fail_json(msg='Error downloading package: %s'
- % (cluster_download_progress['failure_reason']))
- else:
- self.cluster_image_update()
- changed = True
- # delete package once update is completed
- cluster_update_progress = self.cluster_image_update_progress_get()
- while not cluster_update_progress or cluster_update_progress.get('overall_status') == 'in_progress':
- time.sleep(25)
- cluster_update_progress = self.cluster_image_update_progress_get()
- if cluster_update_progress.get('overall_status') == 'completed':
- self.cluster_image_package_delete()
- self.module.exit_json(changed=changed)
-
-
-def main():
- """Execute action"""
- community_obj = NetAppONTAPSoftwareUpdate()
- community_obj.apply()
-
-
-if __name__ == '__main__':
- main()
diff --git a/lib/ansible/modules/storage/netapp/na_ontap_svm.py b/lib/ansible/modules/storage/netapp/na_ontap_svm.py
deleted file mode 100644
index 75418c709e..0000000000
--- a/lib/ansible/modules/storage/netapp/na_ontap_svm.py
+++ /dev/null
@@ -1,444 +0,0 @@
-#!/usr/bin/python
-
-# (c) 2018-2019, NetApp, Inc
-# GNU General Public License v3.0+
-# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-
-from __future__ import absolute_import, division, print_function
-__metaclass__ = type
-
-
-ANSIBLE_METADATA = {'metadata_version': '1.1',
- 'status': ['preview'],
- 'supported_by': 'certified'}
-
-
-DOCUMENTATION = '''
-
-module: na_ontap_svm
-
-short_description: NetApp ONTAP SVM
-extends_documentation_fragment:
- - netapp.na_ontap
-version_added: '2.6'
-author: NetApp Ansible Team (@carchi8py) <ng-ansibleteam@netapp.com>
-
-description:
-- Create, modify or delete SVM on NetApp ONTAP
-
-options:
-
- state:
- description:
- - Whether the specified SVM should exist or not.
- choices: ['present', 'absent']
- default: 'present'
-
- name:
- description:
- - The name of the SVM to manage.
- required: true
-
- from_name:
- description:
- - Name of the SVM to be renamed
- version_added: '2.7'
-
- root_volume:
- description:
- - Root volume of the SVM.
- - Cannot be modified after creation.
-
- root_volume_aggregate:
- description:
- - The aggregate on which the root volume will be created.
- - Cannot be modified after creation.
-
- root_volume_security_style:
- description:
- - Security Style of the root volume.
- - When specified as part of the vserver-create,
- this field represents the security style for the Vserver root volume.
- - When specified as part of vserver-get-iter call,
- this will return the list of matching Vservers.
- - The 'unified' security style, which applies only to Infinite Volumes,
- cannot be applied to a Vserver's root volume.
- - Cannot be modified after creation.
- choices: ['unix', 'ntfs', 'mixed', 'unified']
-
- allowed_protocols:
- description:
- - Allowed Protocols.
- - When specified as part of a vserver-create,
- this field represent the list of protocols allowed on the Vserver.
- - When part of vserver-get-iter call,
- this will return the list of Vservers
- which have any of the protocols specified
- as part of the allowed-protocols.
- - When part of vserver-modify,
- this field should include the existing list
- along with new protocol list to be added to prevent data disruptions.
- - Possible values
- - nfs NFS protocol,
- - cifs CIFS protocol,
- - fcp FCP protocol,
- - iscsi iSCSI protocol,
- - ndmp NDMP protocol,
- - http HTTP protocol,
- - nvme NVMe protocol
-
- aggr_list:
- description:
- - List of aggregates assigned for volume operations.
- - These aggregates could be shared for use with other Vservers.
- - When specified as part of a vserver-create,
- this field represents the list of aggregates
- that are assigned to the Vserver for volume operations.
- - When part of vserver-get-iter call,
- this will return the list of Vservers
- which have any of the aggregates specified as part of the aggr list.
-
- ipspace:
- description:
- - IPSpace name
- - Cannot be modified after creation.
- version_added: '2.7'
-
-
- snapshot_policy:
- description:
- - Default snapshot policy setting for all volumes of the Vserver.
- This policy will be assigned to all volumes created in this
- Vserver unless the volume create request explicitly provides a
- snapshot policy or volume is modified later with a specific
- snapshot policy. A volume-level snapshot policy always overrides
- the default Vserver-wide snapshot policy.
- version_added: '2.7'
-
- language:
- description:
- - Language to use for the SVM
- - Default to C.UTF-8
- - Possible values Language
- - c POSIX
- - ar Arabic
- - cs Czech
- - da Danish
- - de German
- - en English
- - en_us English (US)
- - es Spanish
- - fi Finnish
- - fr French
- - he Hebrew
- - hr Croatian
- - hu Hungarian
- - it Italian
- - ja Japanese euc-j
- - ja_v1 Japanese euc-j
- - ja_jp.pck Japanese PCK (sjis)
- - ja_jp.932 Japanese cp932
- - ja_jp.pck_v2 Japanese PCK (sjis)
- - ko Korean
- - no Norwegian
- - nl Dutch
- - pl Polish
- - pt Portuguese
- - ro Romanian
- - ru Russian
- - sk Slovak
- - sl Slovenian
- - sv Swedish
- - tr Turkish
- - zh Simplified Chinese
- - zh.gbk Simplified Chinese (GBK)
- - zh_tw Traditional Chinese euc-tw
- - zh_tw.big5 Traditional Chinese Big 5
- version_added: '2.7'
-
- subtype:
- description:
- - The subtype for vserver to be created.
- - Cannot be modified after creation.
- choices: ['default', 'dp_destination', 'sync_source', 'sync_destination']
- version_added: '2.7'
-
- comment:
- description:
- - When specified as part of a vserver-create, this field represents the comment associated with the Vserver.
- - When part of vserver-get-iter call, this will return the list of matching Vservers.
- version_added: '2.8'
-'''
-
-EXAMPLES = """
-
- - name: Create SVM
- na_ontap_svm:
- state: present
- name: ansibleVServer
- root_volume: vol1
- root_volume_aggregate: aggr1
- root_volume_security_style: mixed
- hostname: "{{ netapp_hostname }}"
- username: "{{ netapp_username }}"
- password: "{{ netapp_password }}"
-
-"""
-
-RETURN = """
-"""
-import traceback
-
-import ansible.module_utils.netapp as netapp_utils
-from ansible.module_utils.basic import AnsibleModule
-from ansible.module_utils._text import to_native
-from ansible.module_utils.netapp_module import NetAppModule
-
-HAS_NETAPP_LIB = netapp_utils.has_netapp_lib()
-
-
-class NetAppOntapSVM(object):
-
- def __init__(self):
- self.argument_spec = netapp_utils.na_ontap_host_argument_spec()
- self.argument_spec.update(dict(
- state=dict(required=False, choices=[
- 'present', 'absent'], default='present'),
- name=dict(required=True, type='str'),
- from_name=dict(required=False, type='str'),
- root_volume=dict(type='str'),
- root_volume_aggregate=dict(type='str'),
- root_volume_security_style=dict(type='str', choices=['unix',
- 'ntfs',
- 'mixed',
- 'unified'
- ]),
- allowed_protocols=dict(type='list'),
- aggr_list=dict(type='list'),
- ipspace=dict(type='str', required=False),
- snapshot_policy=dict(type='str', required=False),
- language=dict(type='str', required=False),
- subtype=dict(choices=['default', 'dp_destination', 'sync_source', 'sync_destination']),
- comment=dict(type="str", required=False)
- ))
-
- self.module = AnsibleModule(
- argument_spec=self.argument_spec,
- supports_check_mode=True
- )
- self.na_helper = NetAppModule()
- self.parameters = self.na_helper.set_parameters(self.module.params)
-
- if HAS_NETAPP_LIB is False:
- self.module.fail_json(
- msg="the python NetApp-Lib module is required")
- else:
- self.server = netapp_utils.setup_na_ontap_zapi(module=self.module)
-
- def get_vserver(self, vserver_name=None):
- """
- Checks if vserver exists.
-
- :return:
- vserver object if vserver found
- None if vserver is not found
- :rtype: object/None
- """
- if vserver_name is None:
- vserver_name = self.parameters['name']
-
- vserver_info = netapp_utils.zapi.NaElement('vserver-get-iter')
- query_details = netapp_utils.zapi.NaElement.create_node_with_children(
- 'vserver-info', **{'vserver-name': vserver_name})
-
- query = netapp_utils.zapi.NaElement('query')
- query.add_child_elem(query_details)
- vserver_info.add_child_elem(query)
-
- result = self.server.invoke_successfully(vserver_info,
- enable_tunneling=False)
- vserver_details = None
- if (result.get_child_by_name('num-records') and
- int(result.get_child_content('num-records')) >= 1):
- attributes_list = result.get_child_by_name('attributes-list')
- vserver_info = attributes_list.get_child_by_name('vserver-info')
- aggr_list = list()
- ''' vserver aggr-list can be empty by default'''
- get_list = vserver_info.get_child_by_name('aggr-list')
- if get_list is not None:
- aggregates = get_list.get_children()
- for aggr in aggregates:
- aggr_list.append(aggr.get_content())
-
- protocols = list()
- '''allowed-protocols is not empty for data SVM, but is for node SVM'''
- allowed_protocols = vserver_info.get_child_by_name('allowed-protocols')
- if allowed_protocols is not None:
- get_protocols = allowed_protocols.get_children()
- for protocol in get_protocols:
- protocols.append(protocol.get_content())
- vserver_details = {'name': vserver_info.get_child_content('vserver-name'),
- 'root_volume': vserver_info.get_child_content('root-volume'),
- 'root_volume_aggregate': vserver_info.get_child_content('root-volume-aggregate'),
- 'root_volume_security_style': vserver_info.get_child_content('root-volume-security-style'),
- 'subtype': vserver_info.get_child_content('vserver-subtype'),
- 'aggr_list': aggr_list,
- 'language': vserver_info.get_child_content('language'),
- 'snapshot_policy': vserver_info.get_child_content('snapshot-policy'),
- 'allowed_protocols': protocols,
- 'ipspace': vserver_info.get_child_content('ipspace'),
- 'comment': vserver_info.get_child_content('comment')}
- return vserver_details
-
- def create_vserver(self):
- options = {'vserver-name': self.parameters['name']}
- self.add_parameter_to_dict(options, 'root_volume', 'root-volume')
- self.add_parameter_to_dict(options, 'root_volume_aggregate', 'root-volume-aggregate')
- self.add_parameter_to_dict(options, 'root_volume_security_style', 'root-volume-security-style')
- self.add_parameter_to_dict(options, 'language', 'language')
- self.add_parameter_to_dict(options, 'ipspace', 'ipspace')
- self.add_parameter_to_dict(options, 'snapshot_policy', 'snapshot-policy')
- self.add_parameter_to_dict(options, 'subtype', 'vserver-subtype')
- self.add_parameter_to_dict(options, 'comment', 'comment')
- vserver_create = netapp_utils.zapi.NaElement.create_node_with_children('vserver-create', **options)
- try:
- self.server.invoke_successfully(vserver_create,
- enable_tunneling=False)
- except netapp_utils.zapi.NaApiError as e:
- self.module.fail_json(msg='Error provisioning SVM %s: %s'
- % (self.parameters['name'], to_native(e)),
- exception=traceback.format_exc())
- # add allowed-protocols, aggr-list after creation,
- # since vserver-create doesn't allow these attributes during creation
- options = dict()
- for key in ('allowed_protocols', 'aggr_list'):
- if self.parameters.get(key):
- options[key] = self.parameters[key]
- if options:
- self.modify_vserver(options)
-
- def delete_vserver(self):
- vserver_delete = netapp_utils.zapi.NaElement.create_node_with_children(
- 'vserver-destroy', **{'vserver-name': self.parameters['name']})
-
- try:
- self.server.invoke_successfully(vserver_delete,
- enable_tunneling=False)
- except netapp_utils.zapi.NaApiError as e:
- self.module.fail_json(msg='Error deleting SVM %s: %s'
- % (self.parameters['name'], to_native(e)),
- exception=traceback.format_exc())
-
- def rename_vserver(self):
- vserver_rename = netapp_utils.zapi.NaElement.create_node_with_children(
- 'vserver-rename', **{'vserver-name': self.parameters['from_name'],
- 'new-name': self.parameters['name']})
-
- try:
- self.server.invoke_successfully(vserver_rename,
- enable_tunneling=False)
- except netapp_utils.zapi.NaApiError as e:
- self.module.fail_json(msg='Error renaming SVM %s: %s'
- % (self.parameters['from_name'], to_native(e)),
- exception=traceback.format_exc())
-
- def modify_vserver(self, modify):
- '''
- Modify vserver.
- :param modify: list of modify attributes
- '''
- vserver_modify = netapp_utils.zapi.NaElement('vserver-modify')
- vserver_modify.add_new_child('vserver-name', self.parameters['name'])
- for attribute in modify:
- if attribute == 'language':
- vserver_modify.add_new_child('language', self.parameters['language'])
- if attribute == 'snapshot_policy':
- vserver_modify.add_new_child('snapshot_policy', self.parameters['snapshot_policy'])
- if attribute == 'comment':
- vserver_modify.add_new_child('comment', self.parameters['comment'])
- if attribute == 'allowed_protocols':
- allowed_protocols = netapp_utils.zapi.NaElement('allowed-protocols')
- for protocol in self.parameters['allowed_protocols']:
- allowed_protocols.add_new_child('protocol', protocol)
- vserver_modify.add_child_elem(allowed_protocols)
- if attribute == 'aggr_list':
- aggregates = netapp_utils.zapi.NaElement('aggr-list')
- for aggr in self.parameters['aggr_list']:
- aggregates.add_new_child('aggr-name', aggr)
- vserver_modify.add_child_elem(aggregates)
- try:
- self.server.invoke_successfully(vserver_modify,
- enable_tunneling=False)
- except netapp_utils.zapi.NaApiError as e:
- self.module.fail_json(msg='Error modifying SVM %s: %s'
- % (self.parameters['name'], to_native(e)),
- exception=traceback.format_exc())
-
- def add_parameter_to_dict(self, adict, name, key=None, tostr=False):
- '''
- add defined parameter (not None) to adict using key.
- :param adict: a dictionary.
- :param name: name in self.parameters.
- :param key: key in adict.
- :param tostr: boolean.
- '''
- if key is None:
- key = name
- if self.parameters.get(name) is not None:
- if tostr:
- adict[key] = str(self.parameters.get(name))
- else:
- adict[key] = self.parameters.get(name)
-
- def apply(self):
- '''Call create/modify/delete operations.'''
- self.asup_log_for_cserver("na_ontap_svm")
- current = self.get_vserver()
- cd_action, rename = None, None
- if self.parameters.get('from_name'):
- rename = self.na_helper.is_rename_action(self.get_vserver(self.parameters['from_name']), current)
- else:
- cd_action = self.na_helper.get_cd_action(current, self.parameters)
- modify = self.na_helper.get_modified_attributes(current, self.parameters)
- for attribute in modify:
- if attribute in ['root_volume', 'root_volume_aggregate', 'root_volume_security_style', 'subtype', 'ipspace']:
- self.module.fail_json(msg='Error modifying SVM %s: can not modify %s.' % (self.parameters['name'], attribute))
- if attribute == 'language':
- # Ontap documentation uses C.UTF-8, but actually stores as c.utf_8.
- if self.parameters['language'].lower() == 'c.utf-8':
- self.parameters['language'] = 'c.utf_8'
- if self.na_helper.changed:
- if self.module.check_mode:
- pass
- else:
- if rename:
- self.rename_vserver()
- # If rename is True, cd_action is None, but modify could be true or false.
- if cd_action == 'create':
- self.create_vserver()
- elif cd_action == 'delete':
- self.delete_vserver()
- elif modify:
- self.modify_vserver(modify)
- self.module.exit_json(changed=self.na_helper.changed)
-
- def asup_log_for_cserver(self, event_name):
- """
- Fetch admin vserver for the given cluster
- Create and Autosupport log event with the given module name
- :param event_name: Name of the event log
- :return: None
- """
- results = netapp_utils.get_cserver(self.server)
- cserver = netapp_utils.setup_na_ontap_zapi(module=self.module, vserver=results)
- netapp_utils.ems_log_event(event_name, cserver)
-
-
-def main():
- '''Apply vserver operations from playbook'''
- v = NetAppOntapSVM()
- v.apply()
-
-
-if __name__ == '__main__':
- main()
diff --git a/lib/ansible/modules/storage/netapp/na_ontap_svm_options.py b/lib/ansible/modules/storage/netapp/na_ontap_svm_options.py
deleted file mode 100644
index b67453a848..0000000000
--- a/lib/ansible/modules/storage/netapp/na_ontap_svm_options.py
+++ /dev/null
@@ -1,156 +0,0 @@
-#!/usr/bin/python
-
-# (c) 2018, NetApp, Inc
-# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-
-from __future__ import absolute_import, division, print_function
-__metaclass__ = type
-
-ANSIBLE_METADATA = {'metadata_version': '1.1',
- 'status': ['preview'],
- 'supported_by': 'community'}
-
-DOCUMENTATION = '''
-short_description: NetApp ONTAP Modify SVM Options
-author: NetApp Ansible Team (@carchi8py) <ng-ansibleteam@netapp.com>
-description:
- - Modify ONTAP SVM Options
- - Only Options that appear on "vserver options show" can be set
-extends_documentation_fragment:
- - netapp.na_ontap
-module: na_ontap_svm_options
-version_added: "2.7"
-options:
- name:
- description:
- - Name of the option.
- value:
- description:
- - Value of the option.
- - Value must be in quote
- vserver:
- description:
- - The name of the vserver to which this option belongs to.
- required: True
-'''
-
-EXAMPLES = """
- - name: Set SVM Options
- na_ontap_svm_options:
- vserver: "{{ netapp_vserver_name }}"
- hostname: "{{ netapp_hostname }}"
- username: "{{ netapp_username }}"
- password: "{{ netapp_password }}"
- name: snmp.enable
- value: 'on'
-"""
-
-RETURN = """
-"""
-
-import traceback
-
-from ansible.module_utils.basic import AnsibleModule
-from ansible.module_utils._text import to_native
-import ansible.module_utils.netapp as netapp_utils
-from ansible.module_utils.netapp_module import NetAppModule
-
-HAS_NETAPP_LIB = netapp_utils.has_netapp_lib()
-
-
-class NetAppONTAPSvnOptions(object):
-
- def __init__(self):
- self.argument_spec = netapp_utils.na_ontap_host_argument_spec()
- self.argument_spec.update(dict(
- name=dict(required=False, type="str", default=None),
- value=dict(required=False, type='str', default=None),
- vserver=dict(required=True, type='str')
-
- ))
-
- self.module = AnsibleModule(
- argument_spec=self.argument_spec,
- supports_check_mode=True
- )
-
- self.na_helper = NetAppModule()
- self.parameters = self.na_helper.set_parameters(self.module.params)
-
- if HAS_NETAPP_LIB is False:
- self.module.fail_json(msg="the python NetApp-Lib module is required")
- else:
- self.server = netapp_utils.setup_na_ontap_zapi(module=self.module, vserver=self.parameters['vserver'])
- return
-
- def set_options(self):
- """
- Set a specific option
- :return: None
- """
- option_obj = netapp_utils.zapi.NaElement("options-set")
- option_obj.add_new_child('name', self.parameters['name'])
- option_obj.add_new_child('value', self.parameters['value'])
- try:
- result = self.server.invoke_successfully(option_obj, True)
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg="Error setting options: %s" % to_native(error), exception=traceback.format_exc())
-
- def list_options(self):
- """
- List all Options on the Vserver
- :return: None
- """
- option_obj = netapp_utils.zapi.NaElement("options-list-info")
- try:
- result = self.server.invoke_successfully(option_obj, True)
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg="Error getting options: %s" % to_native(error), exception=traceback.format_exc())
-
- def is_option_set(self):
- """
- Checks to see if an option is set or not
- :return: If option is set return True, else return False
- """
- option_obj = netapp_utils.zapi.NaElement("options-get-iter")
- options_info = netapp_utils.zapi.NaElement("option-info")
- if self.parameters.get('name') is not None:
- options_info.add_new_child("name", self.parameters['name'])
- if self.parameters.get('value') is not None:
- options_info.add_new_child("value", self.parameters['value'])
- if "vserver" in self.parameters.keys():
- if self.parameters['vserver'] is not None:
- options_info.add_new_child("vserver", self.parameters['vserver'])
- query = netapp_utils.zapi.NaElement("query")
- query.add_child_elem(options_info)
- option_obj.add_child_elem(query)
- try:
- result = self.server.invoke_successfully(option_obj, True)
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg="Error finding option: %s" % to_native(error), exception=traceback.format_exc())
-
- if result.get_child_by_name('num-records') and int(result.get_child_content('num-records')) >= 1:
- return True
- return False
-
- def apply(self):
- changed = False
- netapp_utils.ems_log_event("na_ontap_svm_options", self.server)
- is_set = self.is_option_set()
- if not is_set:
- self.set_options()
- changed = True
- self.module.exit_json(changed=changed)
-
-
-def main():
- """
- Execute action from playbook
- :return: none
- """
- cg_obj = NetAppONTAPSvnOptions()
- cg_obj.apply()
-
-
-if __name__ == '__main__':
- main()
diff --git a/lib/ansible/modules/storage/netapp/na_ontap_ucadapter.py b/lib/ansible/modules/storage/netapp/na_ontap_ucadapter.py
deleted file mode 100644
index 75956c1676..0000000000
--- a/lib/ansible/modules/storage/netapp/na_ontap_ucadapter.py
+++ /dev/null
@@ -1,224 +0,0 @@
-#!/usr/bin/python
-
-# (c) 2018-2019, NetApp, Inc
-# GNU General Public License v3.0+
-# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-
-from __future__ import absolute_import, division, print_function
-__metaclass__ = type
-
-ANSIBLE_METADATA = {
- 'metadata_version': '1.1',
- 'status': ['preview'],
- 'supported_by': 'certified'
-}
-
-DOCUMENTATION = '''
----
-
-module: na_ontap_ucadapter
-short_description: NetApp ONTAP UC adapter configuration
-extends_documentation_fragment:
- - netapp.na_ontap
-version_added: '2.6'
-author: NetApp Ansible Team (@carchi8py) <ng-ansibleteam@netapp.com>
-
-description:
- - modify the UC adapter mode and type taking pending type and mode into account.
-
-options:
- state:
- description:
- - Whether the specified adapter should exist.
- required: false
- choices: ['present']
- default: 'present'
-
- adapter_name:
- description:
- - Specifies the adapter name.
- required: true
-
- node_name:
- description:
- - Specifies the adapter home node.
- required: true
-
- mode:
- description:
- - Specifies the mode of the adapter.
-
- type:
- description:
- - Specifies the fc4 type of the adapter.
-
-'''
-
-EXAMPLES = '''
- - name: Modify adapter
- na_ontap_adapter:
- state: present
- adapter_name: data2
- node_name: laurentn-vsim1
- mode: fc
- type: target
- hostname: "{{ netapp_hostname }}"
- username: "{{ netapp_username }}"
- password: "{{ netapp_password }}"
-
-'''
-
-RETURN = '''
-'''
-
-import traceback
-
-from ansible.module_utils.basic import AnsibleModule
-from ansible.module_utils._text import to_native
-import ansible.module_utils.netapp as netapp_utils
-from ansible.module_utils.netapp_module import NetAppModule
-
-HAS_NETAPP_LIB = netapp_utils.has_netapp_lib()
-
-
-class NetAppOntapadapter(object):
- ''' object to describe adapter info '''
-
- def __init__(self):
-
- self.argument_spec = netapp_utils.na_ontap_host_argument_spec()
- self.argument_spec.update(dict(
- state=dict(required=False, choices=['present'], default='present'),
- adapter_name=dict(required=True, type='str'),
- node_name=dict(required=True, type='str'),
- mode=dict(required=False, type='str'),
- type=dict(required=False, type='str'),
- ))
-
- self.module = AnsibleModule(
- argument_spec=self.argument_spec,
- supports_check_mode=True
- )
-
- self.na_helper = NetAppModule()
- self.parameters = self.na_helper.set_parameters(self.module.params)
-
- if HAS_NETAPP_LIB is False:
- self.module.fail_json(msg="the python NetApp-Lib module is required")
- else:
- self.server = netapp_utils.setup_na_ontap_zapi(module=self.module)
-
- def get_adapter(self):
- """
- Return details about the adapter
- :param:
- name : Name of the name of the adapter
-
- :return: Details about the adapter. None if not found.
- :rtype: dict
- """
- adapter_info = netapp_utils.zapi.NaElement('ucm-adapter-get')
- adapter_info.add_new_child('adapter-name', self.parameters['adapter_name'])
- adapter_info.add_new_child('node-name', self.parameters['node_name'])
- try:
- result = self.server.invoke_successfully(adapter_info, True)
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg='Error fetching ucadapter details: %s: %s'
- % (self.parameters['node_name'], to_native(error)),
- exception=traceback.format_exc())
- if result.get_child_by_name('attributes'):
- adapter_attributes = result.get_child_by_name('attributes').\
- get_child_by_name('uc-adapter-info')
- return_value = {
- 'mode': adapter_attributes.get_child_content('mode'),
- 'pending-mode': adapter_attributes.get_child_content('pending-mode'),
- 'type': adapter_attributes.get_child_content('fc4-type'),
- 'pending-type': adapter_attributes.get_child_content('pending-fc4-type'),
- 'status': adapter_attributes.get_child_content('status'),
- }
- return return_value
- return None
-
- def modify_adapter(self):
- """
- Modify the adapter.
- """
- params = {'adapter-name': self.parameters['adapter_name'],
- 'node-name': self.parameters['node_name']}
- if self.parameters['type'] is not None:
- params['fc4-type'] = self.parameters['type']
- if self.parameters['mode'] is not None:
- params['mode'] = self.parameters['mode']
- adapter_modify = netapp_utils.zapi.NaElement.create_node_with_children(
- 'ucm-adapter-modify', ** params)
- try:
- self.server.invoke_successfully(adapter_modify,
- enable_tunneling=True)
- except netapp_utils.zapi.NaApiError as e:
- self.module.fail_json(msg='Error modifying adapter %s: %s' % (self.parameters['adapter_name'], to_native(e)),
- exception=traceback.format_exc())
-
- def online_or_offline_adapter(self, status):
- """
- Bring a Fibre Channel target adapter offline/online.
- """
- if status == 'down':
- adapter = netapp_utils.zapi.NaElement('fcp-adapter-config-down')
- elif status == 'up':
- adapter = netapp_utils.zapi.NaElement('fcp-adapter-config-up')
- adapter.add_new_child('fcp-adapter', self.parameters['adapter_name'])
- adapter.add_new_child('node', self.parameters['node_name'])
- try:
- self.server.invoke_successfully(adapter,
- enable_tunneling=True)
- except netapp_utils.zapi.NaApiError as e:
- self.module.fail_json(msg='Error trying to %s fc-adapter %s: %s' % (status, self.parameters['adapter_name'], to_native(e)),
- exception=traceback.format_exc())
-
- def autosupport_log(self):
- """
- Autosupport log for ucadater
- :return:
- """
- results = netapp_utils.get_cserver(self.server)
- cserver = netapp_utils.setup_na_ontap_zapi(module=self.module, vserver=results)
- netapp_utils.ems_log_event("na_ontap_ucadapter", cserver)
-
- def apply(self):
- ''' calling all adapter features '''
- changed = False
- adapter_detail = self.get_adapter()
-
- def need_to_change(expected, pending, current):
- if expected is None:
- return False
- elif pending is not None:
- return pending != expected
- elif current is not None:
- return current != expected
- return False
-
- if adapter_detail:
- changed = need_to_change(self.parameters.get('type'), adapter_detail['pending-type'],
- adapter_detail['type']) or need_to_change(self.parameters.get('mode'),
- adapter_detail['pending-mode'],
- adapter_detail['mode'])
-
- if changed:
- if self.module.check_mode:
- pass
- else:
- self.online_or_offline_adapter('down')
- self.modify_adapter()
- self.online_or_offline_adapter('up')
-
- self.module.exit_json(changed=changed)
-
-
-def main():
- adapter = NetAppOntapadapter()
- adapter.apply()
-
-
-if __name__ == '__main__':
- main()
diff --git a/lib/ansible/modules/storage/netapp/na_ontap_unix_group.py b/lib/ansible/modules/storage/netapp/na_ontap_unix_group.py
deleted file mode 100644
index 2d24b7bdb4..0000000000
--- a/lib/ansible/modules/storage/netapp/na_ontap_unix_group.py
+++ /dev/null
@@ -1,348 +0,0 @@
-#!/usr/bin/python
-"""
-create Autosupport module to enable, disable or modify
-"""
-
-# (c) 2019, NetApp, Inc
-# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-from __future__ import absolute_import, division, print_function
-__metaclass__ = type
-
-
-ANSIBLE_METADATA = {'metadata_version': '1.1',
- 'status': ['preview'],
- 'supported_by': 'community'}
-
-
-DOCUMENTATION = """
-author: NetApp Ansible Team (@carchi8py) <ng-ansibleteam@netapp.com>
-description:
- - "Create/Delete Unix user group"
-extends_documentation_fragment:
- - netapp.na_ontap
-module: na_ontap_unix_group
-options:
- state:
- description:
- - Whether the specified group should exist or not.
- choices: ['present', 'absent']
- default: 'present'
-
- name:
- description:
- - Specifies UNIX group's name, unique for each group.
- - Non-modifiable.
- required: true
-
- id:
- description:
- - Specifies an identification number for the UNIX group.
- - Group ID is unique for each UNIX group.
- - Required for create, modifiable.
-
- vserver:
- description:
- - Specifies the Vserver for the UNIX group.
- - Non-modifiable.
- required: true
-
- skip_name_validation:
- description:
- - Specifies if group name validation is skipped.
- type: bool
-
- users:
- description:
- - Specifies the users associated with this group. Should be comma separated.
- - It represents the expected state of a list of users at any time.
- - Add a user into group if it is specified in expected state but not in current state.
- - Delete a user from group if it is specified in current state but not in expected state.
- - To delete all current users, use '' as value.
- type: list
- version_added: "2.9"
-
-short_description: NetApp ONTAP UNIX Group
-version_added: "2.8"
-
-"""
-
-EXAMPLES = """
- - name: Create UNIX group
- na_ontap_unix_group:
- state: present
- name: SampleGroup
- vserver: ansibleVServer
- id: 2
- users: user1,user2
- hostname: "{{ netapp_hostname }}"
- username: "{{ netapp_username }}"
- password: "{{ netapp_password }}"
-
- - name: Delete all users in UNIX group
- na_ontap_unix_group:
- state: present
- name: SampleGroup
- vserver: ansibleVServer
- users: ''
- hostname: "{{ netapp_hostname }}"
- username: "{{ netapp_username }}"
- password: "{{ netapp_password }}"
-
- - name: Delete UNIX group
- na_ontap_unix_group:
- state: absent
- name: SampleGroup
- vserver: ansibleVServer
- hostname: "{{ netapp_hostname }}"
- username: "{{ netapp_username }}"
- password: "{{ netapp_password }}"
-
-"""
-
-RETURN = """
-"""
-import traceback
-
-from ansible.module_utils.basic import AnsibleModule
-from ansible.module_utils._text import to_native
-import ansible.module_utils.netapp as netapp_utils
-from ansible.module_utils.netapp_module import NetAppModule
-
-HAS_NETAPP_LIB = netapp_utils.has_netapp_lib()
-
-
-class NetAppOntapUnixGroup(object):
- """
- Common operations to manage UNIX groups
- """
-
- def __init__(self):
- self.argument_spec = netapp_utils.na_ontap_host_argument_spec()
- self.argument_spec.update(dict(
- state=dict(required=False, choices=['present', 'absent'], default='present'),
- name=dict(required=True, type='str'),
- id=dict(required=False, type='int'),
- skip_name_validation=dict(required=False, type='bool'),
- vserver=dict(required=True, type='str'),
- users=dict(required=False, type='list')
- ))
-
- self.module = AnsibleModule(
- argument_spec=self.argument_spec,
- supports_check_mode=True
- )
-
- self.na_helper = NetAppModule()
- self.parameters = self.na_helper.set_parameters(self.module.params)
- self.set_playbook_zapi_key_map()
-
- if HAS_NETAPP_LIB is False:
- self.module.fail_json(msg="the python NetApp-Lib module is required")
- else:
- self.server = netapp_utils.setup_na_ontap_zapi(module=self.module, vserver=self.parameters['vserver'])
-
- def set_playbook_zapi_key_map(self):
- self.na_helper.zapi_string_keys = {
- 'name': 'group-name'
- }
- self.na_helper.zapi_int_keys = {
- 'id': 'group-id'
- }
- self.na_helper.zapi_bool_keys = {
- 'skip_name_validation': 'skip-name-validation'
- }
-
- def get_unix_group(self):
- """
- Checks if the UNIX group exists.
-
- :return:
- dict() if group found
- None if group is not found
- """
-
- get_unix_group = netapp_utils.zapi.NaElement('name-mapping-unix-group-get-iter')
- attributes = {
- 'query': {
- 'unix-group-info': {
- 'group-name': self.parameters['name'],
- 'vserver': self.parameters['vserver'],
- }
- }
- }
- get_unix_group.translate_struct(attributes)
- try:
- result = self.server.invoke_successfully(get_unix_group, enable_tunneling=True)
- if result.get_child_by_name('num-records') and int(result.get_child_content('num-records')) >= 1:
- group_info = result['attributes-list']['unix-group-info']
- group_details = dict()
- else:
- return None
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg='Error getting UNIX group %s: %s' % (self.parameters['name'], to_native(error)),
- exception=traceback.format_exc())
- for item_key, zapi_key in self.na_helper.zapi_string_keys.items():
- group_details[item_key] = group_info[zapi_key]
- for item_key, zapi_key in self.na_helper.zapi_int_keys.items():
- group_details[item_key] = self.na_helper.get_value_for_int(from_zapi=True,
- value=group_info[zapi_key])
- if group_info.get_child_by_name('users') is not None:
- group_details['users'] = [user.get_child_content('user-name')
- for user in group_info.get_child_by_name('users').get_children()]
- else:
- group_details['users'] = None
- return group_details
-
- def create_unix_group(self):
- """
- Creates an UNIX group in the specified Vserver
-
- :return: None
- """
- if self.parameters.get('id') is None:
- self.module.fail_json(msg='Error: Missing a required parameter for create: (id)')
-
- group_create = netapp_utils.zapi.NaElement('name-mapping-unix-group-create')
- group_details = {}
- for item in self.parameters:
- if item in self.na_helper.zapi_string_keys:
- zapi_key = self.na_helper.zapi_string_keys.get(item)
- group_details[zapi_key] = self.parameters[item]
- elif item in self.na_helper.zapi_bool_keys:
- zapi_key = self.na_helper.zapi_bool_keys.get(item)
- group_details[zapi_key] = self.na_helper.get_value_for_bool(from_zapi=False,
- value=self.parameters[item])
- elif item in self.na_helper.zapi_int_keys:
- zapi_key = self.na_helper.zapi_int_keys.get(item)
- group_details[zapi_key] = self.na_helper.get_value_for_int(from_zapi=True,
- value=self.parameters[item])
- group_create.translate_struct(group_details)
- try:
- self.server.invoke_successfully(group_create, enable_tunneling=True)
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg='Error creating UNIX group %s: %s' % (self.parameters['name'], to_native(error)),
- exception=traceback.format_exc())
- if self.parameters.get('users') is not None:
- self.modify_users_in_group()
-
- def delete_unix_group(self):
- """
- Deletes an UNIX group from a vserver
-
- :return: None
- """
- group_delete = netapp_utils.zapi.NaElement.create_node_with_children(
- 'name-mapping-unix-group-destroy', **{'group-name': self.parameters['name']})
-
- try:
- self.server.invoke_successfully(group_delete, enable_tunneling=True)
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg='Error removing UNIX group %s: %s' % (self.parameters['name'], to_native(error)),
- exception=traceback.format_exc())
-
- def modify_unix_group(self, params):
- """
- Modify an UNIX group from a vserver
- :param params: modify parameters
- :return: None
- """
- # modify users requires separate zapi.
- if 'users' in params:
- self.modify_users_in_group()
- if len(params) == 1:
- return
-
- group_modify = netapp_utils.zapi.NaElement('name-mapping-unix-group-modify')
- group_details = {'group-name': self.parameters['name']}
- for key in params:
- if key in self.na_helper.zapi_int_keys:
- zapi_key = self.na_helper.zapi_int_keys.get(key)
- group_details[zapi_key] = self.na_helper.get_value_for_int(from_zapi=True,
- value=params[key])
- group_modify.translate_struct(group_details)
-
- try:
- self.server.invoke_successfully(group_modify, enable_tunneling=True)
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg='Error modifying UNIX group %s: %s' % (self.parameters['name'], to_native(error)),
- exception=traceback.format_exc())
-
- def modify_users_in_group(self):
- """
- Add/delete one or many users in a UNIX group
-
- :return: None
- """
- current_users = self.get_unix_group().get('users')
- expect_users = self.parameters.get('users')
-
- if current_users is None:
- current_users = []
- if expect_users[0] == '' and len(expect_users) == 1:
- expect_users = []
-
- users_to_remove = list(set(current_users) - set(expect_users))
- users_to_add = list(set(expect_users) - set(current_users))
-
- if len(users_to_add) > 0:
- for user in users_to_add:
- add_user = netapp_utils.zapi.NaElement('name-mapping-unix-group-add-user')
- group_details = {'group-name': self.parameters['name'], 'user-name': user}
- add_user.translate_struct(group_details)
- try:
- self.server.invoke_successfully(add_user, enable_tunneling=True)
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(
- msg='Error adding user %s to UNIX group %s: %s' % (user, self.parameters['name'], to_native(error)),
- exception=traceback.format_exc())
-
- if len(users_to_remove) > 0:
- for user in users_to_remove:
- delete_user = netapp_utils.zapi.NaElement('name-mapping-unix-group-delete-user')
- group_details = {'group-name': self.parameters['name'], 'user-name': user}
- delete_user.translate_struct(group_details)
- try:
- self.server.invoke_successfully(delete_user, enable_tunneling=True)
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(
- msg='Error deleting user %s from UNIX group %s: %s' % (user, self.parameters['name'], to_native(error)),
- exception=traceback.format_exc())
-
- def autosupport_log(self):
- """
- Autosupport log for unix_group
- :return: None
- """
- netapp_utils.ems_log_event("na_ontap_unix_group", self.server)
-
- def apply(self):
- """
- Invoke appropriate action based on playbook parameters
-
- :return: None
- """
- self.autosupport_log()
- current = self.get_unix_group()
- cd_action = self.na_helper.get_cd_action(current, self.parameters)
- if self.parameters['state'] == 'present' and cd_action is None:
- modify = self.na_helper.get_modified_attributes(current, self.parameters)
- if self.na_helper.changed:
- if self.module.check_mode:
- pass
- else:
- if cd_action == 'create':
- self.create_unix_group()
- elif cd_action == 'delete':
- self.delete_unix_group()
- else:
- self.modify_unix_group(modify)
- self.module.exit_json(changed=self.na_helper.changed)
-
-
-def main():
- obj = NetAppOntapUnixGroup()
- obj.apply()
-
-
-if __name__ == '__main__':
- main()
diff --git a/lib/ansible/modules/storage/netapp/na_ontap_unix_user.py b/lib/ansible/modules/storage/netapp/na_ontap_unix_user.py
deleted file mode 100644
index ee620f47fa..0000000000
--- a/lib/ansible/modules/storage/netapp/na_ontap_unix_user.py
+++ /dev/null
@@ -1,253 +0,0 @@
-#!/usr/bin/python
-
-# (c) 2018-2019, NetApp, Inc
-# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-
-from __future__ import absolute_import, division, print_function
-__metaclass__ = type
-
-
-ANSIBLE_METADATA = {'metadata_version': '1.1',
- 'status': ['preview'],
- 'supported_by': 'community'}
-
-
-DOCUMENTATION = '''
-
-module: na_ontap_unix_user
-
-short_description: NetApp ONTAP UNIX users
-extends_documentation_fragment:
- - netapp.na_ontap
-version_added: '2.8'
-author: NetApp Ansible Team (@carchi8py) <ng-ansibleteam@netapp.com>
-
-description:
-- Create, delete or modify UNIX users local to ONTAP.
-
-options:
-
- state:
- description:
- - Whether the specified user should exist or not.
- choices: ['present', 'absent']
- default: 'present'
-
- name:
- description:
- - Specifies user's UNIX account name.
- - Non-modifiable.
- required: true
-
- group_id:
- description:
- - Specifies the primary group identification number for the UNIX user
- - Required for create, modifiable.
-
- vserver:
- description:
- - Specifies the Vserver for the UNIX user.
- - Non-modifiable.
- required: true
-
- id:
- description:
- - Specifies an identification number for the UNIX user.
- - Required for create, modifiable.
-
- full_name:
- description:
- - Specifies the full name of the UNIX user
- - Optional for create, modifiable.
-'''
-
-EXAMPLES = """
-
- - name: Create UNIX User
- na_ontap_unix_user:
- state: present
- name: SampleUser
- vserver: ansibleVServer
- group_id: 1
- id: 2
- full_name: Test User
- hostname: "{{ netapp_hostname }}"
- username: "{{ netapp_username }}"
- password: "{{ netapp_password }}"
-
- - name: Delete UNIX User
- na_ontap_unix_user:
- state: absent
- name: SampleUser
- vserver: ansibleVServer
- hostname: "{{ netapp_hostname }}"
- username: "{{ netapp_username }}"
- password: "{{ netapp_password }}"
-
-"""
-
-RETURN = """
-
-"""
-import traceback
-
-from ansible.module_utils.basic import AnsibleModule
-from ansible.module_utils._text import to_native
-import ansible.module_utils.netapp as netapp_utils
-from ansible.module_utils.netapp_module import NetAppModule
-
-HAS_NETAPP_LIB = netapp_utils.has_netapp_lib()
-
-
-class NetAppOntapUnixUser(object):
- """
- Common operations to manage users and roles.
- """
-
- def __init__(self):
- self.argument_spec = netapp_utils.na_ontap_host_argument_spec()
- self.argument_spec.update(dict(
- state=dict(required=False, choices=['present', 'absent'], default='present'),
- name=dict(required=True, type='str'),
- group_id=dict(required=False, type='int'),
- id=dict(required=False, type='int'),
- full_name=dict(required=False, type='str'),
- vserver=dict(required=True, type='str'),
- ))
-
- self.module = AnsibleModule(
- argument_spec=self.argument_spec,
- supports_check_mode=True
- )
-
- self.na_helper = NetAppModule()
- self.parameters = self.na_helper.set_parameters(self.module.params)
-
- if HAS_NETAPP_LIB is False:
- self.module.fail_json(msg="the python NetApp-Lib module is required")
- else:
- self.server = netapp_utils.setup_na_ontap_zapi(module=self.module, vserver=self.parameters['vserver'])
-
- def get_unix_user(self):
- """
- Checks if the UNIX user exists.
-
- :return:
- dict() if user found
- None if user is not found
- """
-
- get_unix_user = netapp_utils.zapi.NaElement('name-mapping-unix-user-get-iter')
- attributes = {
- 'query': {
- 'unix-user-info': {
- 'user-name': self.parameters['name'],
- 'vserver': self.parameters['vserver'],
- }
- }
- }
- get_unix_user.translate_struct(attributes)
- try:
- result = self.server.invoke_successfully(get_unix_user, enable_tunneling=True)
- if result.get_child_by_name('num-records') and int(result.get_child_content('num-records')) >= 1:
- user_info = result['attributes-list']['unix-user-info']
- return {'group_id': int(user_info['group-id']),
- 'id': int(user_info['user-id']),
- 'full_name': user_info['full-name']}
- return None
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg='Error getting UNIX user %s: %s' % (self.parameters['name'], to_native(error)),
- exception=traceback.format_exc())
-
- def create_unix_user(self):
- """
- Creates an UNIX user in the specified Vserver
-
- :return: None
- """
- if self.parameters.get('group_id') is None or self.parameters.get('id') is None:
- self.module.fail_json(msg='Error: Missing one or more required parameters for create: (group_id, id)')
-
- user_create = netapp_utils.zapi.NaElement.create_node_with_children(
- 'name-mapping-unix-user-create', **{'user-name': self.parameters['name'],
- 'group-id': str(self.parameters['group_id']),
- 'user-id': str(self.parameters['id'])})
- if self.parameters.get('full_name') is not None:
- user_create.add_new_child('full-name', self.parameters['full_name'])
-
- try:
- self.server.invoke_successfully(user_create, enable_tunneling=True)
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg='Error creating UNIX user %s: %s' % (self.parameters['name'], to_native(error)),
- exception=traceback.format_exc())
-
- def delete_unix_user(self):
- """
- Deletes an UNIX user from a vserver
-
- :return: None
- """
- user_delete = netapp_utils.zapi.NaElement.create_node_with_children(
- 'name-mapping-unix-user-destroy', **{'user-name': self.parameters['name']})
-
- try:
- self.server.invoke_successfully(user_delete, enable_tunneling=True)
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg='Error removing UNIX user %s: %s' % (self.parameters['name'], to_native(error)),
- exception=traceback.format_exc())
-
- def modify_unix_user(self, params):
- user_modify = netapp_utils.zapi.NaElement.create_node_with_children(
- 'name-mapping-unix-user-modify', **{'user-name': self.parameters['name']})
- for key in params:
- if key == 'group_id':
- user_modify.add_new_child('group-id', str(params['group_id']))
- if key == 'id':
- user_modify.add_new_child('user-id', str(params['id']))
- if key == 'full_name':
- user_modify.add_new_child('full-name', params['full_name'])
-
- try:
- self.server.invoke_successfully(user_modify, enable_tunneling=True)
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg='Error modifying UNIX user %s: %s' % (self.parameters['name'], to_native(error)),
- exception=traceback.format_exc())
-
- def autosupport_log(self):
- """
- Autosupport log for unix_user
- :return: None
- """
- netapp_utils.ems_log_event("na_ontap_unix_user", self.server)
-
- def apply(self):
- """
- Invoke appropriate action based on playbook parameters
-
- :return: None
- """
- self.autosupport_log()
- current = self.get_unix_user()
- cd_action = self.na_helper.get_cd_action(current, self.parameters)
- if self.parameters['state'] == 'present' and cd_action is None:
- modify = self.na_helper.get_modified_attributes(current, self.parameters)
- if self.na_helper.changed:
- if self.module.check_mode:
- pass
- else:
- if cd_action == 'create':
- self.create_unix_user()
- elif cd_action == 'delete':
- self.delete_unix_user()
- else:
- self.modify_unix_user(modify)
- self.module.exit_json(changed=self.na_helper.changed)
-
-
-def main():
- obj = NetAppOntapUnixUser()
- obj.apply()
-
-
-if __name__ == '__main__':
- main()
diff --git a/lib/ansible/modules/storage/netapp/na_ontap_user.py b/lib/ansible/modules/storage/netapp/na_ontap_user.py
deleted file mode 100644
index dcb69acffb..0000000000
--- a/lib/ansible/modules/storage/netapp/na_ontap_user.py
+++ /dev/null
@@ -1,389 +0,0 @@
-#!/usr/bin/python
-
-# (c) 2018-2019, NetApp, Inc
-# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-
-from __future__ import absolute_import, division, print_function
-__metaclass__ = type
-
-
-ANSIBLE_METADATA = {'metadata_version': '1.1',
- 'status': ['preview'],
- 'supported_by': 'certified'}
-
-
-DOCUMENTATION = '''
-
-module: na_ontap_user
-
-short_description: NetApp ONTAP user configuration and management
-extends_documentation_fragment:
- - netapp.na_ontap
-version_added: '2.6'
-author: NetApp Ansible Team (@carchi8py) <ng-ansibleteam@netapp.com>
-
-description:
-- Create or destroy users.
-
-options:
- state:
- description:
- - Whether the specified user should exist or not.
- choices: ['present', 'absent']
- default: 'present'
- name:
- description:
- - The name of the user to manage.
- required: true
- applications:
- description:
- - List of application to grant access to.
- required: true
- type: list
- choices: ['console', 'http','ontapi','rsh','snmp','service-processor','sp','ssh','telnet']
- aliases:
- - application
- authentication_method:
- description:
- - Authentication method for the application.
- - Not all authentication methods are valid for an application.
- - Valid authentication methods for each application are as denoted in I(authentication_choices_description).
- - Password for console application
- - Password, domain, nsswitch, cert for http application.
- - Password, domain, nsswitch, cert for ontapi application.
- - Community for snmp application (when creating SNMPv1 and SNMPv2 users).
- - The usm and community for snmp application (when creating SNMPv3 users).
- - Password for sp application.
- - Password for rsh application.
- - Password for telnet application.
- - Password, publickey, domain, nsswitch for ssh application.
- required: true
- choices: ['community', 'password', 'publickey', 'domain', 'nsswitch', 'usm', 'cert']
- set_password:
- description:
- - Password for the user account.
- - It is ignored for creating snmp users, but is required for creating non-snmp users.
- - For an existing user, this value will be used as the new password.
- role_name:
- description:
- - The name of the role. Required when C(state=present)
- lock_user:
- description:
- - Whether the specified user account is locked.
- type: bool
- vserver:
- description:
- - The name of the vserver to use.
- required: true
-'''
-
-EXAMPLES = """
-
- - name: Create User
- na_ontap_user:
- state: present
- name: SampleUser
- applications: ssh,console
- authentication_method: password
- set_password: apn1242183u1298u41
- lock_user: True
- role_name: vsadmin
- vserver: ansibleVServer
- hostname: "{{ netapp_hostname }}"
- username: "{{ netapp_username }}"
- password: "{{ netapp_password }}"
-
- - name: Delete User
- na_ontap_user:
- state: absent
- name: SampleUser
- applications: ssh
- authentication_method: password
- vserver: ansibleVServer
- hostname: "{{ netapp_hostname }}"
- username: "{{ netapp_username }}"
- password: "{{ netapp_password }}"
-
-"""
-
-RETURN = """
-
-"""
-import traceback
-
-from ansible.module_utils.basic import AnsibleModule
-from ansible.module_utils._text import to_native
-import ansible.module_utils.netapp as netapp_utils
-from ansible.module_utils.netapp_module import NetAppModule
-
-HAS_NETAPP_LIB = netapp_utils.has_netapp_lib()
-
-
-class NetAppOntapUser(object):
- """
- Common operations to manage users and roles.
- """
-
- def __init__(self):
- self.argument_spec = netapp_utils.na_ontap_host_argument_spec()
- self.argument_spec.update(dict(
- state=dict(required=False, choices=['present', 'absent'], default='present'),
- name=dict(required=True, type='str'),
-
- applications=dict(required=True, type='list', aliases=['application'],
- choices=['console', 'http', 'ontapi', 'rsh', 'snmp',
- 'sp', 'service-processor', 'ssh', 'telnet'],),
- authentication_method=dict(required=True, type='str',
- choices=['community', 'password', 'publickey', 'domain', 'nsswitch', 'usm', 'cert']),
- set_password=dict(required=False, type='str', no_log=True),
- role_name=dict(required=False, type='str'),
- lock_user=dict(required=False, type='bool'),
- vserver=dict(required=True, type='str'),
- ))
-
- self.module = AnsibleModule(
- argument_spec=self.argument_spec,
- required_if=[
- ('state', 'present', ['role_name'])
- ],
- supports_check_mode=True
- )
-
- self.na_helper = NetAppModule()
- self.parameters = self.na_helper.set_parameters(self.module.params)
-
- if HAS_NETAPP_LIB is False:
- self.module.fail_json(msg="the python NetApp-Lib module is required")
- else:
- self.server = netapp_utils.setup_na_ontap_zapi(module=self.module, vserver=self.parameters['vserver'])
-
- def get_user(self, application=None):
- """
- Checks if the user exists.
- :param: application: application to grant access to
- :return:
- Dictionary if user found
- None if user is not found
- """
- security_login_get_iter = netapp_utils.zapi.NaElement('security-login-get-iter')
- query_details = netapp_utils.zapi.NaElement.create_node_with_children(
- 'security-login-account-info', **{'vserver': self.parameters['vserver'],
- 'user-name': self.parameters['name'],
- 'authentication-method': self.parameters['authentication_method']})
- if application is not None:
- query_details.add_new_child('application', application)
- query = netapp_utils.zapi.NaElement('query')
- query.add_child_elem(query_details)
- security_login_get_iter.add_child_elem(query)
- try:
- result = self.server.invoke_successfully(security_login_get_iter,
- enable_tunneling=False)
- if result.get_child_by_name('num-records') and \
- int(result.get_child_content('num-records')) >= 1:
- interface_attributes = result.get_child_by_name('attributes-list').\
- get_child_by_name('security-login-account-info')
- return_value = {
- 'lock_user': interface_attributes.get_child_content('is-locked'),
- 'role_name': interface_attributes.get_child_content('role-name')
- }
- return return_value
- return None
- except netapp_utils.zapi.NaApiError as error:
- # Error 16034 denotes a user not being found.
- if to_native(error.code) == "16034":
- return None
- # Error 16043 denotes the user existing, but the application missing
- elif to_native(error.code) == "16043":
- return None
- else:
- self.module.fail_json(msg='Error getting user %s: %s' % (self.parameters['name'], to_native(error)),
- exception=traceback.format_exc())
-
- def create_user(self, application):
- """
- creates the user for the given application and authentication_method
- :param: application: application to grant access to
- """
- user_create = netapp_utils.zapi.NaElement.create_node_with_children(
- 'security-login-create', **{'vserver': self.parameters['vserver'],
- 'user-name': self.parameters['name'],
- 'application': application,
- 'authentication-method': self.parameters['authentication_method'],
- 'role-name': self.parameters.get('role_name')})
- if self.parameters.get('set_password') is not None:
- user_create.add_new_child('password', self.parameters.get('set_password'))
-
- try:
- self.server.invoke_successfully(user_create,
- enable_tunneling=False)
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg='Error creating user %s: %s' % (self.parameters['name'], to_native(error)),
- exception=traceback.format_exc())
-
- def lock_given_user(self):
- """
- locks the user
-
- :return:
- True if user locked
- False if lock user is not performed
- :rtype: bool
- """
- user_lock = netapp_utils.zapi.NaElement.create_node_with_children(
- 'security-login-lock', **{'vserver': self.parameters['vserver'],
- 'user-name': self.parameters['name']})
-
- try:
- self.server.invoke_successfully(user_lock,
- enable_tunneling=False)
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg='Error locking user %s: %s' % (self.parameters['name'], to_native(error)),
- exception=traceback.format_exc())
-
- def unlock_given_user(self):
- """
- unlocks the user
-
- :return:
- True if user unlocked
- False if unlock user is not performed
- :rtype: bool
- """
- user_unlock = netapp_utils.zapi.NaElement.create_node_with_children(
- 'security-login-unlock', **{'vserver': self.parameters['vserver'],
- 'user-name': self.parameters['name']})
-
- try:
- self.server.invoke_successfully(user_unlock,
- enable_tunneling=False)
- except netapp_utils.zapi.NaApiError as error:
- if to_native(error.code) == '13114':
- return False
- else:
- self.module.fail_json(msg='Error unlocking user %s: %s' % (self.parameters['name'], to_native(error)),
- exception=traceback.format_exc())
- return True
-
- def delete_user(self, application):
- """
- deletes the user for the given application and authentication_method
- :param: application: application to grant access to
- """
- user_delete = netapp_utils.zapi.NaElement.create_node_with_children(
- 'security-login-delete', **{'vserver': self.parameters['vserver'],
- 'user-name': self.parameters['name'],
- 'application': application,
- 'authentication-method': self.parameters['authentication_method']})
-
- try:
- self.server.invoke_successfully(user_delete,
- enable_tunneling=False)
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg='Error removing user %s: %s' % (self.parameters['name'], to_native(error)),
- exception=traceback.format_exc())
-
- def change_password(self):
- """
- Changes the password
-
- :return:
- True if password updated
- False if password is not updated
- :rtype: bool
- """
- # self.server.set_vserver(self.parameters['vserver'])
- modify_password = netapp_utils.zapi.NaElement.create_node_with_children(
- 'security-login-modify-password', **{
- 'new-password': str(self.parameters.get('set_password')),
- 'user-name': self.parameters['name']})
- try:
- self.server.invoke_successfully(modify_password,
- enable_tunneling=True)
- except netapp_utils.zapi.NaApiError as error:
- if to_native(error.code) == '13114':
- return False
- # if the user give the same password, instead of returning an error, return ok
- if to_native(error.code) == '13214' and \
- (error.message.startswith('New password must be different than last 6 passwords.')
- or error.message.startswith('New password must be different than the old password.')):
- return False
- self.module.fail_json(msg='Error setting password for user %s: %s' % (self.parameters['name'], to_native(error)),
- exception=traceback.format_exc())
-
- self.server.set_vserver(None)
- return True
-
- def modify_user(self, application):
- """
- Modify user
- """
- user_modify = netapp_utils.zapi.NaElement.create_node_with_children(
- 'security-login-modify', **{'vserver': self.parameters['vserver'],
- 'user-name': self.parameters['name'],
- 'application': application,
- 'authentication-method': self.parameters['authentication_method'],
- 'role-name': self.parameters.get('role_name')})
-
- try:
- self.server.invoke_successfully(user_modify,
- enable_tunneling=False)
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg='Error modifying user %s: %s' % (self.parameters['name'], to_native(error)),
- exception=traceback.format_exc())
-
- def apply(self):
- create_delete_decision = {}
- modify_decision = {}
-
- netapp_utils.ems_log_event("na_ontap_user", self.server)
- for application in self.parameters['applications']:
- current = self.get_user(application)
- if current is not None:
- current['lock_user'] = self.na_helper.get_value_for_bool(True, current['lock_user'])
-
- cd_action = self.na_helper.get_cd_action(current, self.parameters)
-
- if cd_action is not None:
- create_delete_decision[application] = cd_action
- else:
- modify_decision[application] = self.na_helper.get_modified_attributes(current, self.parameters)
-
- if not create_delete_decision and self.parameters.get('state') == 'present':
- if self.parameters.get('set_password') is not None:
- self.na_helper.changed = True
-
- if self.na_helper.changed:
-
- if self.module.check_mode:
- pass
- else:
- for application in create_delete_decision:
- if create_delete_decision[application] == 'create':
- self.create_user(application)
- elif create_delete_decision[application] == 'delete':
- self.delete_user(application)
- lock_user = False
- for application in modify_decision:
- if 'role_name' in modify_decision[application]:
- self.modify_user(application)
- if 'lock_user' in modify_decision[application]:
- lock_user = True
-
- if lock_user:
- if self.parameters.get('lock_user'):
- self.lock_given_user()
- else:
- self.unlock_given_user()
- if not create_delete_decision and self.parameters.get('set_password') is not None:
- # if change password return false nothing has changed so we need to set changed to False
- self.na_helper.changed = self.change_password()
- self.module.exit_json(changed=self.na_helper.changed)
-
-
-def main():
- obj = NetAppOntapUser()
- obj.apply()
-
-
-if __name__ == '__main__':
- main()
diff --git a/lib/ansible/modules/storage/netapp/na_ontap_user_role.py b/lib/ansible/modules/storage/netapp/na_ontap_user_role.py
deleted file mode 100644
index 167e78529d..0000000000
--- a/lib/ansible/modules/storage/netapp/na_ontap_user_role.py
+++ /dev/null
@@ -1,268 +0,0 @@
-#!/usr/bin/python
-
-# (c) 2018-2019, NetApp, Inc
-# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-
-from __future__ import absolute_import, division, print_function
-__metaclass__ = type
-
-
-ANSIBLE_METADATA = {'metadata_version': '1.1',
- 'status': ['preview'],
- 'supported_by': 'certified'}
-
-
-DOCUMENTATION = '''
-
-module: na_ontap_user_role
-
-short_description: NetApp ONTAP user role configuration and management
-extends_documentation_fragment:
- - netapp.na_ontap
-version_added: '2.6'
-author: NetApp Ansible Team (@carchi8py) <ng-ansibleteam@netapp.com>
-
-description:
-- Create or destroy user roles
-
-options:
-
- state:
- description:
- - Whether the specified user should exist or not.
- choices: ['present', 'absent']
- default: present
-
- name:
- description:
- - The name of the role to manage.
- required: true
-
- command_directory_name:
- description:
- - The command or command directory to which the role has an access.
- required: true
-
- access_level:
- description:
- - The name of the role to manage.
- choices: ['none', 'readonly', 'all']
- default: all
-
- query:
- description:
- - A query for the role. The query must apply to the specified command or directory name.
- - Use double quotes "" for modifying a existing query to none.
- version_added: '2.8'
-
- vserver:
- description:
- - The name of the vserver to use.
- required: true
-
-'''
-
-EXAMPLES = """
-
- - name: Create User Role
- na_ontap_user_role:
- state: present
- name: ansibleRole
- command_directory_name: volume
- access_level: none
- query: show
- vserver: ansibleVServer
- hostname: "{{ netapp_hostname }}"
- username: "{{ netapp_username }}"
- password: "{{ netapp_password }}"
-
- - name: Modify User Role
- na_ontap_user_role:
- state: present
- name: ansibleRole
- command_directory_name: volume
- access_level: none
- query: ""
- vserver: ansibleVServer
- hostname: "{{ netapp_hostname }}"
- username: "{{ netapp_username }}"
- password: "{{ netapp_password }}"
-
-"""
-
-RETURN = """
-
-"""
-import traceback
-
-from ansible.module_utils.basic import AnsibleModule
-from ansible.module_utils._text import to_native
-from ansible.module_utils.netapp_module import NetAppModule
-import ansible.module_utils.netapp as netapp_utils
-
-
-HAS_NETAPP_LIB = netapp_utils.has_netapp_lib()
-
-
-class NetAppOntapUserRole(object):
-
- def __init__(self):
- self.argument_spec = netapp_utils.na_ontap_host_argument_spec()
- self.argument_spec.update(dict(
- state=dict(required=False, choices=['present', 'absent'], default='present'),
- name=dict(required=True, type='str'),
- command_directory_name=dict(required=True, type='str'),
- access_level=dict(required=False, type='str', default='all',
- choices=['none', 'readonly', 'all']),
- vserver=dict(required=True, type='str'),
- query=dict(required=False, type='str')
- ))
-
- self.module = AnsibleModule(
- argument_spec=self.argument_spec,
- supports_check_mode=True
- )
- self.na_helper = NetAppModule()
- self.parameters = self.na_helper.set_parameters(self.module.params)
-
- if HAS_NETAPP_LIB is False:
- self.module.fail_json(msg="the python NetApp-Lib module is required")
- else:
- self.server = netapp_utils.setup_na_ontap_zapi(module=self.module, vserver=self.parameters['vserver'])
-
- def get_role(self):
- """
- Checks if the role exists for specific command-directory-name.
-
- :return:
- True if role found
- False if role is not found
- :rtype: bool
- """
- options = {'vserver': self.parameters['vserver'],
- 'role-name': self.parameters['name'],
- 'command-directory-name': self.parameters['command_directory_name']}
-
- security_login_role_get_iter = netapp_utils.zapi.NaElement(
- 'security-login-role-get-iter')
- query_details = netapp_utils.zapi.NaElement.create_node_with_children(
- 'security-login-role-info', **options)
- query = netapp_utils.zapi.NaElement('query')
- query.add_child_elem(query_details)
- security_login_role_get_iter.add_child_elem(query)
-
- try:
- result = self.server.invoke_successfully(
- security_login_role_get_iter, enable_tunneling=False)
- except netapp_utils.zapi.NaApiError as e:
- # Error 16031 denotes a role not being found.
- if to_native(e.code) == "16031":
- return None
- # Error 16039 denotes command directory not found.
- elif to_native(e.code) == "16039":
- return None
- else:
- self.module.fail_json(msg='Error getting role %s: %s' % (self.name, to_native(e)),
- exception=traceback.format_exc())
- if (result.get_child_by_name('num-records') and
- int(result.get_child_content('num-records')) >= 1):
- role_info = result.get_child_by_name('attributes-list').get_child_by_name('security-login-role-info')
- result = {
- 'name': role_info['role-name'],
- 'access_level': role_info['access-level'],
- 'command_directory_name': role_info['command-directory-name'],
- 'query': role_info['role-query']
- }
- return result
-
- return None
-
- def create_role(self):
- options = {'vserver': self.parameters['vserver'],
- 'role-name': self.parameters['name'],
- 'command-directory-name': self.parameters['command_directory_name'],
- 'access-level': self.parameters['access_level']}
- if self.parameters.get('query'):
- options['role-query'] = self.parameters['query']
- role_create = netapp_utils.zapi.NaElement.create_node_with_children('security-login-role-create', **options)
-
- try:
- self.server.invoke_successfully(role_create,
- enable_tunneling=False)
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg='Error creating role %s: %s' % (self.parameters['name'], to_native(error)),
- exception=traceback.format_exc())
-
- def delete_role(self):
- role_delete = netapp_utils.zapi.NaElement.create_node_with_children(
- 'security-login-role-delete', **{'vserver': self.parameters['vserver'],
- 'role-name': self.parameters['name'],
- 'command-directory-name':
- self.parameters['command_directory_name']})
-
- try:
- self.server.invoke_successfully(role_delete,
- enable_tunneling=False)
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg='Error removing role %s: %s' % (self.parameters['name'], to_native(error)),
- exception=traceback.format_exc())
-
- def modify_role(self, modify):
- options = {'vserver': self.parameters['vserver'],
- 'role-name': self.parameters['name'],
- 'command-directory-name': self.parameters['command_directory_name']}
- if 'access_level' in modify.keys():
- options['access-level'] = self.parameters['access_level']
- if 'query' in modify.keys():
- options['role-query'] = self.parameters['query']
-
- role_modify = netapp_utils.zapi.NaElement.create_node_with_children('security-login-role-modify', **options)
-
- try:
- self.server.invoke_successfully(role_modify,
- enable_tunneling=False)
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg='Error modifying role %s: %s' % (self.parameters['name'], to_native(error)),
- exception=traceback.format_exc())
-
- def apply(self):
- self.asup_log_for_cserver('na_ontap_user_role')
- current = self.get_role()
- cd_action = self.na_helper.get_cd_action(current, self.parameters)
-
- # if desired state specify empty quote query and current query is None, set desired query to None.
- # otherwise na_helper.get_modified_attributes will detect a change.
- if self.parameters.get('query') == '' and current is not None:
- if current['query'] is None:
- self.parameters['query'] = None
-
- modify = self.na_helper.get_modified_attributes(current, self.parameters)
- if self.na_helper.changed:
- if self.module.check_mode:
- pass
- else:
- if cd_action == 'create':
- self.create_role()
- elif cd_action == 'delete':
- self.delete_role()
- elif modify:
- self.modify_role(modify)
- self.module.exit_json(changed=self.na_helper.changed)
-
- def asup_log_for_cserver(self, event_name):
- """
- Fetch admin vserver for the given cluster
- Create and Autosupport log event with the given module name
- :param event_name: Name of the event log
- :return: None
- """
- netapp_utils.ems_log_event(event_name, self.server)
-
-
-def main():
- obj = NetAppOntapUserRole()
- obj.apply()
-
-
-if __name__ == '__main__':
- main()
diff --git a/lib/ansible/modules/storage/netapp/na_ontap_volume.py b/lib/ansible/modules/storage/netapp/na_ontap_volume.py
deleted file mode 100644
index 9c04b28e0f..0000000000
--- a/lib/ansible/modules/storage/netapp/na_ontap_volume.py
+++ /dev/null
@@ -1,1283 +0,0 @@
-#!/usr/bin/python
-
-# (c) 2018-2019, NetApp, Inc
-# GNU General Public License v3.0+
-# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-
-from __future__ import absolute_import, division, print_function
-__metaclass__ = type
-
-ANSIBLE_METADATA = {'metadata_version': '1.1',
- 'status': ['preview'],
- 'supported_by': 'certified'}
-
-
-DOCUMENTATION = '''
-
-module: na_ontap_volume
-
-short_description: NetApp ONTAP manage volumes.
-extends_documentation_fragment:
- - netapp.na_ontap
-version_added: '2.6'
-author: NetApp Ansible Team (@carchi8py) <ng-ansibleteam@netapp.com>
-
-description:
-- Create or destroy or modify volumes on NetApp ONTAP.
-
-options:
-
- state:
- description:
- - Whether the specified volume should exist or not.
- choices: ['present', 'absent']
- default: 'present'
-
- name:
- description:
- - The name of the volume to manage.
- type: str
- required: true
-
- vserver:
- description:
- - Name of the vserver to use.
- type: str
- required: true
-
- from_name:
- description:
- - Name of the existing volume to be renamed to name.
- type: str
- version_added: '2.7'
-
- is_infinite:
- type: bool
- description:
- Set True if the volume is an Infinite Volume.
- Deleting an infinite volume is asynchronous.
-
- is_online:
- type: bool
- description:
- - Whether the specified volume is online, or not.
- default: True
-
- aggregate_name:
- description:
- - The name of the aggregate the flexvol should exist on.
- - Required when C(state=present).
- type: str
-
- size:
- description:
- - The size of the volume in (size_unit). Required when C(state=present).
- type: int
-
- size_unit:
- description:
- - The unit used to interpret the size parameter.
- choices: ['bytes', 'b', 'kb', 'mb', 'gb', 'tb', 'pb', 'eb', 'zb', 'yb']
- type: str
- default: 'gb'
-
- type:
- description:
- - The volume type, either read-write (RW) or data-protection (DP).
- type: str
-
- policy:
- description:
- - Name of the export policy.
- type: str
-
- junction_path:
- description:
- - Junction path of the volume.
- - To unmount, use junction path C('').
- type: str
-
- space_guarantee:
- description:
- - Space guarantee style for the volume.
- choices: ['none', 'file', 'volume']
- type: str
-
- percent_snapshot_space:
- description:
- - Amount of space reserved for snapshot copies of the volume.
- type: int
-
- volume_security_style:
- description:
- - The security style associated with this volume.
- choices: ['mixed', 'ntfs', 'unified', 'unix']
- default: 'mixed'
- type: str
-
- encrypt:
- type: bool
- description:
- - Whether or not to enable Volume Encryption.
- default: False
- version_added: '2.7'
-
- efficiency_policy:
- description:
- - Allows a storage efficiency policy to be set on volume creation.
- type: str
- version_added: '2.7'
-
- unix_permissions:
- description:
- - Unix permission bits in octal or symbolic format.
- - For example, 0 is equivalent to ------------, 777 is equivalent to ---rwxrwxrwx,both formats are accepted.
- - The valid octal value ranges between 0 and 777 inclusive.
- type: str
- version_added: '2.8'
-
- snapshot_policy:
- description:
- - The name of the snapshot policy.
- - the default policy name is 'default'.
- type: str
- version_added: '2.8'
-
- aggr_list:
- description:
- - an array of names of aggregates to be used for FlexGroup constituents.
- type: list
- version_added: '2.8'
-
- aggr_list_multiplier:
- description:
- - The number of times to iterate over the aggregates listed with the aggr_list parameter when creating a FlexGroup.
- type: int
- version_added: '2.8'
-
- auto_provision_as:
- description:
- - Automatically provision a FlexGroup volume.
- version_added: '2.8'
- choices: ['flexgroup']
- type: str
-
- snapdir_access:
- description:
- - This is an advanced option, the default is False.
- - Enable the visible '.snapshot' directory that is normally present at system internal mount points.
- - This value also turns on access to all other '.snapshot' directories in the volume.
- type: bool
- version_added: '2.8'
-
- atime_update:
- description:
- - This is an advanced option, the default is True.
- - If false, prevent the update of inode access times when a file is read.
- - This value is useful for volumes with extremely high read traffic,
- since it prevents writes to the inode file for the volume from contending with reads from other files.
- - This field should be used carefully.
- - That is, use this field when you know in advance that the correct access time for inodes will not be needed for files on that volume.
- type: bool
- version_added: '2.8'
-
- wait_for_completion:
- description:
- - Set this parameter to 'true' for synchronous execution during create (wait until volume status is online)
- - Set this parameter to 'false' for asynchronous execution
- - For asynchronous, execution exits as soon as the request is sent, without checking volume status
- type: bool
- default: false
- version_added: '2.8'
-
- time_out:
- description:
- - time to wait for flexGroup creation, modification, or deletion in seconds.
- - Error out if task is not completed in defined time.
- - if 0, the request is asynchronous.
- - default is set to 3 minutes.
- default: 180
- type: int
- version_added: '2.8'
-
- language:
- description:
- - Language to use for Volume
- - Default uses SVM language
- - Possible values Language
- - c POSIX
- - ar Arabic
- - cs Czech
- - da Danish
- - de German
- - en English
- - en_us English (US)
- - es Spanish
- - fi Finnish
- - fr French
- - he Hebrew
- - hr Croatian
- - hu Hungarian
- - it Italian
- - ja Japanese euc-j
- - ja_v1 Japanese euc-j
- - ja_jp.pck Japanese PCK (sjis)
- - ja_jp.932 Japanese cp932
- - ja_jp.pck_v2 Japanese PCK (sjis)
- - ko Korean
- - no Norwegian
- - nl Dutch
- - pl Polish
- - pt Portuguese
- - ro Romanian
- - ru Russian
- - sk Slovak
- - sl Slovenian
- - sv Swedish
- - tr Turkish
- - zh Simplified Chinese
- - zh.gbk Simplified Chinese (GBK)
- - zh_tw Traditional Chinese euc-tw
- - zh_tw.big5 Traditional Chinese Big 5
- - To use UTF-8 as the NFS character set, append '.UTF-8' to the language code
- type: str
- version_added: '2.8'
-
- qos_policy_group:
- description:
- - Specifies a QoS policy group to be set on volume.
- version_added: '2.9'
-
- qos_adaptive_policy_group:
- description:
- - Specifies a QoS adaptive policy group to be set on volume.
- version_added: '2.9'
-
- tiering_policy:
- description:
- - The tiering policy that is to be associated with the volume.
- - This policy decides whether the blocks of a volume will be tiered to the capacity tier.
- - snapshot-only policy allows tiering of only the volume snapshot copies not associated with the active file system.
- - auto policy allows tiering of both snapshot and active file system user data to the capacity tier.
- - backup policy on DP volumes allows all transferred user data blocks to start in the capacity tier.
- - When set to none, the Volume blocks will not be tiered to the capacity tier.
- - If no value specified, the volume is assigned snapshot only by default.
- choices: ['snapshot-only', 'auto', 'backup', 'none']
- type: str
- version_added: '2.9'
-
- space_slo:
- description:
- - Specifies the space SLO type for the volume. The space SLO type is the Service Level Objective for space management for the volume.
- - The space SLO value is used to enforce existing volume settings so that sufficient space is set aside on the aggregate to meet the space SLO.
- - This parameter is not supported on Infinite Volumes.
- choices: ['none', 'thick', 'semi-thick']
- type: str
- version_added: '2.9'
-
- nvfail_enabled:
- description:
- - If true, the controller performs additional work at boot and takeover times if it finds that there has been any potential data loss in the volume's
- constituents due to an NVRAM failure.
- - The volume's constituents would be put in a special state called 'in-nvfailed-state' such that protocol access is blocked.
- - This will cause the client applications to crash and thus prevent access to stale data.
- - To get out of this situation, the admin needs to manually clear the 'in-nvfailed-state' on the volume's constituents.
- type: bool
- version_added: '2.9'
-
- vserver_dr_protection:
- description:
- - Specifies the protection type for the volume in a Vserver DR setup.
- choices: ['protected', 'unprotected']
- type: str
- version_added: '2.9'
-
- comment:
- description:
- - Sets a comment associated with the volume.
- type: str
- version_added: '2.9'
-'''
-
-EXAMPLES = """
-
- - name: Create FlexVol
- na_ontap_volume:
- state: present
- name: ansibleVolume12
- is_infinite: False
- aggregate_name: ansible_aggr
- size: 100
- size_unit: mb
- space_guarantee: none
- tiering_policy: auto
- policy: default
- percent_snapshot_space: 60
- qos_policy_group: max_performance_gold
- vserver: ansibleVServer
- wait_for_completion: True
- space_slo: none
- nvfail_enabled: False
- comment: ansible created volume
- hostname: "{{ netapp_hostname }}"
- username: "{{ netapp_username }}"
- password: "{{ netapp_password }}"
-
- - name: Volume Delete
- na_ontap_volume:
- state: absent
- name: ansibleVolume12
- aggregate_name: ansible_aggr
- vserver: ansibleVServer
- hostname: "{{ netapp_hostname }}"
- username: "{{ netapp_username }}"
- password: "{{ netapp_password }}"
-
- - name: Make FlexVol offline
- na_ontap_volume:
- state: present
- name: ansibleVolume
- is_infinite: False
- is_online: False
- vserver: ansibleVServer
- hostname: "{{ netapp_hostname }}"
- username: "{{ netapp_username }}"
- password: "{{ netapp_password }}"
-
- - name: Create flexGroup volume manually
- na_ontap_volume:
- state: present
- name: ansibleVolume
- is_infinite: False
- aggr_list: "{{ aggr_list }}"
- aggr_list_multiplier: 2
- size: 200
- size_unit: mb
- space_guarantee: none
- policy: default
- vserver: "{{ vserver }}"
- hostname: "{{ netapp_hostname }}"
- username: "{{ netapp_username }}"
- password: "{{ netapp_password }}"
- https: False
- unix_permissions: 777
- snapshot_policy: default
- time_out: 0
-
- - name: Create flexGroup volume auto provision as flex group
- na_ontap_volume:
- state: present
- name: ansibleVolume
- is_infinite: False
- auto_provision_as: flexgroup
- size: 200
- size_unit: mb
- space_guarantee: none
- policy: default
- vserver: "{{ vserver }}"
- hostname: "{{ netapp_hostname }}"
- username: "{{ netapp_username }}"
- password: "{{ netapp_password }}"
- https: False
- unix_permissions: 777
- snapshot_policy: default
- time_out: 0
-
- - name: Create FlexVol with QoS adaptive
- na_ontap_volume:
- state: present
- name: ansibleVolume15
- is_infinite: False
- aggregate_name: ansible_aggr
- size: 100
- size_unit: gb
- space_guarantee: none
- policy: default
- percent_snapshot_space: 10
- qos_adaptive_policy_group: extreme
- vserver: ansibleVServer
- wait_for_completion: True
- hostname: "{{ netapp_hostname }}"
- username: "{{ netapp_username }}"
- password: "{{ netapp_password }}"
-
- - name: Modify volume dr protection (vserver of the volume must be in a snapmirror relationship)
- na_ontap_volume:
- state: present
- name: ansibleVolume
- vserver_dr_protection: protected
- vserver: "{{ vserver }}"
- hostname: "{{ netapp_hostname }}"
- username: "{{ netapp_username }}"
- password: "{{ netapp_password }}"
- https: False
-
-"""
-
-RETURN = """
-"""
-
-import time
-import traceback
-import ansible.module_utils.netapp as netapp_utils
-from ansible.module_utils.netapp_module import NetAppModule
-from ansible.module_utils.basic import AnsibleModule
-from ansible.module_utils._text import to_native
-
-HAS_NETAPP_LIB = netapp_utils.has_netapp_lib()
-
-
-class NetAppOntapVolume(object):
- '''Class with volume operations'''
-
- def __init__(self):
- '''Initialize module parameters'''
- self._size_unit_map = dict(
- bytes=1,
- b=1,
- kb=1024,
- mb=1024 ** 2,
- gb=1024 ** 3,
- tb=1024 ** 4,
- pb=1024 ** 5,
- eb=1024 ** 6,
- zb=1024 ** 7,
- yb=1024 ** 8
- )
-
- self.argument_spec = netapp_utils.na_ontap_host_argument_spec()
- self.argument_spec.update(dict(
- state=dict(required=False, choices=[
- 'present', 'absent'], default='present'),
- name=dict(required=True, type='str'),
- vserver=dict(required=True, type='str'),
- from_name=dict(required=False, type='str'),
- is_infinite=dict(required=False, type='bool',
- default=False),
- is_online=dict(required=False, type='bool',
- default=True),
- size=dict(type='int', default=None),
- size_unit=dict(default='gb',
- choices=['bytes', 'b', 'kb', 'mb', 'gb', 'tb',
- 'pb', 'eb', 'zb', 'yb'], type='str'),
- aggregate_name=dict(type='str', default=None),
- type=dict(type='str', default=None),
- policy=dict(type='str', default=None),
- junction_path=dict(type='str', default=None),
- space_guarantee=dict(choices=['none', 'file', 'volume'], default=None),
- percent_snapshot_space=dict(type='int', default=None),
- volume_security_style=dict(choices=['mixed',
- 'ntfs', 'unified', 'unix'],
- default='mixed'),
- encrypt=dict(required=False, type='bool', default=False),
- efficiency_policy=dict(required=False, type='str'),
- unix_permissions=dict(required=False, type='str'),
- snapshot_policy=dict(required=False, type='str'),
- aggr_list=dict(required=False, type='list'),
- aggr_list_multiplier=dict(required=False, type='int'),
- snapdir_access=dict(required=False, type='bool'),
- atime_update=dict(required=False, type='bool'),
- auto_provision_as=dict(choices=['flexgroup'], required=False, type='str'),
- wait_for_completion=dict(required=False, type='bool', default=False),
- time_out=dict(required=False, type='int', default=180),
- language=dict(type='str', required=False),
- qos_policy_group=dict(required=False, type='str'),
- qos_adaptive_policy_group=dict(required=False, type='str'),
- nvfail_enabled=dict(type='bool', required=False),
- space_slo=dict(type='str', required=False, choices=['none', 'thick', 'semi-thick']),
- tiering_policy=dict(type='str', required=False, choices=['snapshot-only', 'auto',
- 'backup', 'none']),
- vserver_dr_protection=dict(type='str', required=False, choices=['protected', 'unprotected']),
- comment=dict(type='str', required=False)
-
- ))
- self.module = AnsibleModule(
- argument_spec=self.argument_spec,
- supports_check_mode=True
- )
- self.na_helper = NetAppModule()
- self.parameters = self.na_helper.set_parameters(self.module.params)
- self.volume_style = None
-
- if self.parameters.get('size'):
- self.parameters['size'] = self.parameters['size'] * \
- self._size_unit_map[self.parameters['size_unit']]
- # ONTAP will return True and False as the string true and false.
- if 'snapdir_access' in self.parameters:
- self.parameters['snapdir_access'] = str(self.parameters['snapdir_access']).lower()
- if 'atime_update' in self.parameters:
- self.parameters['atime_update'] = str(self.parameters['atime_update']).lower()
- if HAS_NETAPP_LIB is False:
- self.module.fail_json(
- msg="the python NetApp-Lib module is required")
- else:
- self.server = netapp_utils.setup_na_ontap_zapi(
- module=self.module, vserver=self.parameters['vserver'])
- self.cluster = netapp_utils.setup_na_ontap_zapi(module=self.module)
-
- def volume_get_iter(self, vol_name=None):
- """
- Return volume-get-iter query results
- :param vol_name: name of the volume
- :return: NaElement
- """
- volume_info = netapp_utils.zapi.NaElement('volume-get-iter')
- volume_attributes = netapp_utils.zapi.NaElement('volume-attributes')
- volume_id_attributes = netapp_utils.zapi.NaElement('volume-id-attributes')
- volume_id_attributes.add_new_child('name', vol_name)
- volume_id_attributes.add_new_child('vserver', self.parameters['vserver'])
- volume_attributes.add_child_elem(volume_id_attributes)
- query = netapp_utils.zapi.NaElement('query')
- query.add_child_elem(volume_attributes)
- volume_info.add_child_elem(query)
-
- try:
- result = self.server.invoke_successfully(volume_info, True)
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg='Error fetching volume %s : %s'
- % (self.parameters['name'], to_native(error)),
- exception=traceback.format_exc())
- return result
-
- def get_volume(self, vol_name=None):
- """
- Return details about the volume
- :param:
- name : Name of the volume
- :return: Details about the volume. None if not found.
- :rtype: dict
- """
- if vol_name is None:
- vol_name = self.parameters['name']
- volume_get_iter = self.volume_get_iter(vol_name)
- return_value = None
- if volume_get_iter.get_child_by_name('num-records') and \
- int(volume_get_iter.get_child_content('num-records')) > 0:
-
- volume_attributes = volume_get_iter['attributes-list']['volume-attributes']
- volume_space_attributes = volume_attributes['volume-space-attributes']
- volume_state_attributes = volume_attributes['volume-state-attributes']
- volume_id_attributes = volume_attributes['volume-id-attributes']
- volume_export_attributes = volume_attributes['volume-export-attributes']
- volume_security_unix_attributes = volume_attributes['volume-security-attributes']['volume-security-unix-attributes']
- volume_snapshot_attributes = volume_attributes['volume-snapshot-attributes']
- volume_performance_attributes = volume_attributes['volume-performance-attributes']
- volume_comp_aggr_attributes = volume_attributes['volume-comp-aggr-attributes']
- # Get volume's state (online/offline)
- current_state = volume_state_attributes['state']
- is_online = (current_state == "online")
-
- return_value = {
- 'name': vol_name,
- 'size': int(volume_space_attributes['size']),
- 'is_online': is_online,
- 'policy': volume_export_attributes['policy'],
- 'unix_permissions': volume_security_unix_attributes['permissions'],
- 'snapshot_policy': volume_snapshot_attributes['snapshot-policy'],
- 'tiering_policy': volume_comp_aggr_attributes['tiering-policy']
- }
- if volume_space_attributes.get_child_by_name('encrypt'):
- return_value['encrypt'] = volume_attributes['encrypt']
- if volume_space_attributes.get_child_by_name('percentage-snapshot-reserve'):
- return_value['percent_snapshot_space'] = int(volume_space_attributes['percentage-snapshot-reserve'])
- if volume_space_attributes.get_child_by_name('space-slo'):
- return_value['space_slo'] = volume_space_attributes['space-slo']
- else:
- return_value['space_slo'] = None
- if volume_state_attributes.get_child_by_name('is-nvfail-enabled') is not None:
- return_value['nvfail_enabled'] = volume_state_attributes['is-nvfail-enabled'] == 'true'
- else:
- return_value['nvfail_enabled'] = None
- if volume_id_attributes.get_child_by_name('containing-aggregate-name'):
- return_value['aggregate_name'] = volume_id_attributes['containing-aggregate-name']
- else:
- return_value['aggregate_name'] = None
- if volume_id_attributes.get_child_by_name('junction-path'):
- return_value['junction_path'] = volume_id_attributes['junction-path']
- else:
- return_value['junction_path'] = ''
- if volume_id_attributes.get_child_by_name('comment'):
- return_value['comment'] = volume_id_attributes['comment']
- else:
- return_value['comment'] = None
- if volume_id_attributes.get_child_by_name('style-extended'):
- return_value['style_extended'] = volume_id_attributes['style-extended']
- else:
- return_value['style_extended'] = None
- if volume_space_attributes.get_child_by_name('space-guarantee'):
- return_value['space_guarantee'] = volume_space_attributes['space-guarantee']
- else:
- return_value['space_guarantee'] = None
- if volume_snapshot_attributes.get_child_by_name('snapdir-access-enabled'):
- return_value['snapdir_access'] = volume_snapshot_attributes['snapdir-access-enabled']
- else:
- return_value['snapdir_access'] = None
- if volume_performance_attributes.get_child_by_name('is-atime-update-enabled'):
- return_value['atime_update'] = volume_performance_attributes['is-atime-update-enabled']
- else:
- return_value['atime_update'] = None
- if volume_attributes.get_child_by_name('volume-qos-attributes'):
- volume_qos_attributes = volume_attributes['volume-qos-attributes']
- if volume_qos_attributes.get_child_by_name('policy-group-name'):
- return_value['qos_policy_group'] = volume_qos_attributes['policy-group-name']
- else:
- return_value['qos_policy_group'] = None
- if volume_qos_attributes.get_child_by_name('adaptive-policy-group-name'):
- return_value['qos_adaptive_policy_group'] = volume_qos_attributes['adaptive-policy-group-name']
- else:
- return_value['qos_adaptive_policy_group'] = None
- else:
- return_value['qos_policy_group'] = None
- return_value['qos_adaptive_policy_group'] = None
- if volume_attributes.get_child_by_name('volume-vserver-dr-protection-attributes'):
- volume_vserver_dr_protection_attributes = volume_attributes['volume-vserver-dr-protection-attributes']
- if volume_vserver_dr_protection_attributes.get_child_by_name('vserver-dr-protection'):
- return_value['vserver_dr_protection'] = volume_vserver_dr_protection_attributes['vserver-dr-protection']
- else:
- return_value['vserver_dr_protection'] = None
-
- return return_value
-
- def create_volume(self):
- '''Create ONTAP volume'''
- if self.volume_style == 'flexGroup':
- self.create_volume_async()
- else:
- options = self.create_volume_options()
- volume_create = netapp_utils.zapi.NaElement.create_node_with_children('volume-create', **options)
- try:
- self.server.invoke_successfully(volume_create, enable_tunneling=True)
- if self.parameters.get('wait_for_completion'):
- # round off time_out
- retries = (self.parameters['time_out'] + 5) // 10
- current = self.get_volume()
- is_online = None if current is None else current['is_online']
- while not is_online and retries > 0:
- time.sleep(10)
- retries = retries - 1
- current = self.get_volume()
- is_online = None if current is None else current['is_online']
- self.ems_log_event("volume-create")
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg='Error provisioning volume %s of size %s: %s'
- % (self.parameters['name'], self.parameters['size'], to_native(error)),
- exception=traceback.format_exc())
-
- if self.parameters.get('efficiency_policy'):
- self.assign_efficiency_policy()
-
- def create_volume_async(self):
- '''
- create volume async.
- '''
- options = self.create_volume_options()
- volume_create = netapp_utils.zapi.NaElement.create_node_with_children('volume-create-async', **options)
- if self.parameters.get('aggr_list'):
- aggr_list_obj = netapp_utils.zapi.NaElement('aggr-list')
- volume_create.add_child_elem(aggr_list_obj)
- for aggr in self.parameters['aggr_list']:
- aggr_list_obj.add_new_child('aggr-name', aggr)
- try:
- result = self.server.invoke_successfully(volume_create, enable_tunneling=True)
- self.ems_log_event("volume-create")
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg='Error provisioning volume %s of size %s: %s'
- % (self.parameters['name'], self.parameters['size'], to_native(error)),
- exception=traceback.format_exc())
- self.check_invoke_result(result, 'create')
-
- if self.parameters.get('efficiency_policy'):
- self.assign_efficiency_policy_async()
-
- def create_volume_options(self):
- '''Set volume options for create operation'''
- options = {}
- if self.volume_style == 'flexGroup':
- options['volume-name'] = self.parameters['name']
- if self.parameters.get('aggr_list_multiplier'):
- options['aggr-list-multiplier'] = str(self.parameters['aggr_list_multiplier'])
- if self.parameters.get('auto_provision_as'):
- options['auto-provision-as'] = self.parameters['auto_provision_as']
- if self.parameters.get('space_guarantee'):
- options['space-guarantee'] = self.parameters['space_guarantee']
- else:
- options['volume'] = self.parameters['name']
- if self.parameters.get('aggregate_name') is None:
- self.module.fail_json(msg='Error provisioning volume %s: aggregate_name is required'
- % self.parameters['name'])
- options['containing-aggr-name'] = self.parameters['aggregate_name']
- if self.parameters.get('space_guarantee'):
- options['space-reserve'] = self.parameters['space_guarantee']
-
- if self.parameters.get('size'):
- options['size'] = str(self.parameters['size'])
- if self.parameters.get('snapshot_policy'):
- options['snapshot-policy'] = self.parameters['snapshot_policy']
- if self.parameters.get('unix_permissions'):
- options['unix-permissions'] = self.parameters['unix_permissions']
- if self.parameters.get('volume_security_style'):
- options['volume-security-style'] = self.parameters['volume_security_style']
- if self.parameters.get('policy'):
- options['export-policy'] = self.parameters['policy']
- if self.parameters.get('junction_path'):
- options['junction-path'] = self.parameters['junction_path']
- if self.parameters.get('comment'):
- options['volume-comment'] = self.parameters['comment']
- if self.parameters.get('type'):
- options['volume-type'] = self.parameters['type']
- if self.parameters.get('percent_snapshot_space') is not None:
- options['percentage-snapshot-reserve'] = str(self.parameters['percent_snapshot_space'])
- if self.parameters.get('language'):
- options['language-code'] = self.parameters['language']
- if self.parameters.get('qos_policy_group'):
- options['qos-policy-group-name'] = self.parameters['qos_policy_group']
- if self.parameters.get('qos_adaptive_policy_group'):
- options['qos-adaptive-policy-group-name'] = self.parameters['qos_adaptive_policy_group']
- if self.parameters.get('nvfail_enabled') is not None:
- options['is-nvfail-enabled'] = str(self.parameters['nvfail_enabled'])
- if self.parameters.get('space_slo'):
- options['space-slo'] = self.parameters['space_slo']
- if self.parameters.get('tiering_policy'):
- options['tiering-policy'] = self.parameters['tiering_policy']
- if self.parameters.get('encrypt'):
- options['encrypt'] = str(self.parameters['encrypt'])
- if self.parameters.get('vserver_dr_protection'):
- options['vserver-dr-protection'] = self.parameters['vserver_dr_protection']
- return options
-
- def delete_volume(self):
- '''Delete ONTAP volume'''
- if self.parameters.get('is_infinite') or self.volume_style == 'flexGroup':
- volume_delete = netapp_utils.zapi\
- .NaElement.create_node_with_children(
- 'volume-destroy-async', **{'volume-name': self.parameters['name'], 'unmount-and-offline': 'true'})
- else:
- volume_delete = netapp_utils.zapi\
- .NaElement.create_node_with_children(
- 'volume-destroy', **{'name': self.parameters['name'],
- 'unmount-and-offline': 'true'})
- try:
- result = self.server.invoke_successfully(volume_delete, enable_tunneling=True)
- if self.parameters.get('is_infinite') or self.volume_style == 'flexGroup':
- self.check_invoke_result(result, 'delete')
- self.ems_log_event("volume-delete")
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg='Error deleting volume %s: %s'
- % (self.parameters['name'], to_native(error)),
- exception=traceback.format_exc())
-
- def move_volume(self):
- '''Move volume from source aggregate to destination aggregate'''
- volume_move = netapp_utils.zapi.NaElement.create_node_with_children(
- 'volume-move-start', **{'source-volume': self.parameters['name'],
- 'vserver': self.parameters['vserver'],
- 'dest-aggr': self.parameters['aggregate_name']})
- try:
- self.cluster.invoke_successfully(volume_move,
- enable_tunneling=True)
- self.ems_log_event("volume-move")
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg='Error moving volume %s: %s'
- % (self.parameters['name'], to_native(error)),
- exception=traceback.format_exc())
-
- def rename_volume(self):
- """
- Rename the volume.
-
- Note: 'is_infinite' needs to be set to True in order to rename an
- Infinite Volume. Use time_out parameter to set wait time for rename completion.
- """
- vol_rename_zapi, vol_name_zapi = ['volume-rename-async', 'volume-name'] if self.parameters['is_infinite']\
- else ['volume-rename', 'volume']
- volume_rename = netapp_utils.zapi.NaElement.create_node_with_children(
- vol_rename_zapi, **{vol_name_zapi: self.parameters['from_name'],
- 'new-volume-name': str(self.parameters['name'])})
- try:
- result = self.server.invoke_successfully(volume_rename, enable_tunneling=True)
- if vol_rename_zapi == 'volume-rename-async':
- self.check_invoke_result(result, 'rename')
- self.ems_log_event("volume-rename")
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg='Error renaming volume %s: %s'
- % (self.parameters['name'], to_native(error)),
- exception=traceback.format_exc())
-
- def resize_volume(self):
- """
- Re-size the volume.
-
- Note: 'is_infinite' needs to be set to True in order to rename an
- Infinite Volume.
- """
- vol_size_zapi, vol_name_zapi = ['volume-size-async', 'volume-name']\
- if (self.parameters['is_infinite'] or self.volume_style == 'flexGroup')\
- else ['volume-size', 'volume']
- volume_resize = netapp_utils.zapi.NaElement.create_node_with_children(
- vol_size_zapi, **{vol_name_zapi: self.parameters['name'],
- 'new-size': str(self.parameters['size'])})
- try:
- result = self.server.invoke_successfully(volume_resize, enable_tunneling=True)
- if vol_size_zapi == 'volume-size-async':
- self.check_invoke_result(result, 'resize')
- self.ems_log_event("volume-resize")
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg='Error re-sizing volume %s: %s'
- % (self.parameters['name'], to_native(error)),
- exception=traceback.format_exc())
-
- def change_volume_state(self):
- """
- Change volume's state (offline/online).
- """
- if self.parameters['is_online']: # Desired state is online, setup zapi APIs respectively
- vol_state_zapi, vol_name_zapi, action = ['volume-online-async', 'volume-name', 'online']\
- if (self.parameters['is_infinite'] or self.volume_style == 'flexGroup')\
- else ['volume-online', 'name', 'online']
- else: # Desired state is offline, setup zapi APIs respectively
- vol_state_zapi, vol_name_zapi, action = ['volume-offline-async', 'volume-name', 'offline']\
- if (self.parameters['is_infinite'] or self.volume_style == 'flexGroup')\
- else ['volume-offline', 'name', 'offline']
- volume_unmount = netapp_utils.zapi.NaElement.create_node_with_children(
- 'volume-unmount', **{'volume-name': self.parameters['name']})
- volume_change_state = netapp_utils.zapi.NaElement.create_node_with_children(
- vol_state_zapi, **{vol_name_zapi: self.parameters['name']})
- try:
- if not self.parameters['is_online']: # Unmount before offline
- self.server.invoke_successfully(volume_unmount, enable_tunneling=True)
- result = self.server.invoke_successfully(volume_change_state, enable_tunneling=True)
- if self.volume_style == 'flexGroup' or self.parameters['is_infinite']:
- self.check_invoke_result(result, action)
- self.ems_log_event("change-state")
- except netapp_utils.zapi.NaApiError as error:
- state = "online" if self.parameters['is_online'] else "offline"
- self.module.fail_json(msg='Error changing the state of volume %s to %s: %s'
- % (self.parameters['name'], state, to_native(error)),
- exception=traceback.format_exc())
-
- def create_volume_attribute(self, zapi_object, parent_attribute, attribute, value):
- """
-
- :param parent_attribute:
- :param child_attribute:
- :param value:
- :return:
- """
- if isinstance(parent_attribute, str):
- vol_attribute = netapp_utils.zapi.NaElement(parent_attribute)
- vol_attribute.add_new_child(attribute, value)
- zapi_object.add_child_elem(vol_attribute)
- else:
- zapi_object.add_new_child(attribute, value)
- parent_attribute.add_child_elem(zapi_object)
-
- def volume_modify_attributes(self, params):
- """
- modify volume parameter 'policy','unix_permissions','snapshot_policy','space_guarantee', 'percent_snapshot_space',
- 'qos_policy_group', 'qos_adaptive_policy_group'
- """
- # TODO: refactor this method
- if self.volume_style == 'flexGroup' or self.parameters['is_infinite']:
- vol_mod_iter = netapp_utils.zapi.NaElement('volume-modify-iter-async')
- else:
- vol_mod_iter = netapp_utils.zapi.NaElement('volume-modify-iter')
- attributes = netapp_utils.zapi.NaElement('attributes')
- vol_mod_attributes = netapp_utils.zapi.NaElement('volume-attributes')
- # Volume-attributes is split in to 25 sub categories
- # volume-space-attributes
- vol_space_attributes = netapp_utils.zapi.NaElement('volume-space-attributes')
- if self.parameters.get('space_guarantee'):
- self.create_volume_attribute(vol_space_attributes, vol_mod_attributes,
- 'space-guarantee', self.parameters['space_guarantee'])
- if self.parameters.get('percent_snapshot_space') is not None:
- self.create_volume_attribute(vol_space_attributes, vol_mod_attributes,
- 'percentage-snapshot-reserve', str(self.parameters['percent_snapshot_space']))
- if self.parameters.get('space_slo'):
- self.create_volume_attribute(vol_space_attributes, vol_mod_attributes, 'space-slo', self.parameters['space_slo'])
- # volume-snapshot-attributes
- vol_snapshot_attributes = netapp_utils.zapi.NaElement('volume-snapshot-attributes')
- if self.parameters.get('snapshot_policy'):
- self.create_volume_attribute(vol_snapshot_attributes, vol_mod_attributes,
- 'snapshot-policy', self.parameters['snapshot_policy'])
- if self.parameters.get('snapdir_access'):
- self.create_volume_attribute(vol_snapshot_attributes, vol_mod_attributes,
- 'snapdir-access-enabled', self.parameters['snapdir_access'])
- # volume-export-attributes
- if self.parameters.get('policy'):
- self.create_volume_attribute(vol_mod_attributes, 'volume-export-attributes',
- 'policy', self.parameters['policy'])
- # volume-security-attributes
- if self.parameters.get('unix_permissions'):
- vol_security_attributes = netapp_utils.zapi.NaElement('volume-security-attributes')
- self.create_volume_attribute(vol_security_attributes, 'volume-security-unix-attributes',
- 'permissions', self.parameters['unix_permissions'])
- vol_mod_attributes.add_child_elem(vol_security_attributes)
- # volume-performance-attributes
- if self.parameters.get('atime_update'):
- self.create_volume_attribute(vol_mod_attributes, 'volume-performance-attributes',
- 'is-atime-update-enabled', self.parameters['atime_update'])
- # volume-qos-attributes
- if self.parameters.get('qos_policy_group'):
- self.create_volume_attribute(vol_mod_attributes, 'volume-qos-attributes',
- 'policy-group-name', self.parameters['qos_policy_group'])
- if self.parameters.get('qos_adaptive_policy_group'):
- self.create_volume_attribute(vol_mod_attributes, 'volume-qos-attributes',
- 'adaptive-policy-group-name', self.parameters['qos_adaptive_policy_group'])
- # volume-comp-aggr-attributes
- if params and params.get('tiering_policy'):
- self.create_volume_attribute(vol_mod_attributes, 'volume-comp-aggr-attributes',
- 'tiering-policy', self.parameters['tiering_policy'])
- # volume-state-attributes
- if self.parameters.get('nvfail_enabled') is not None:
- self.create_volume_attribute(vol_mod_attributes, 'volume-state-attributes', 'is-nvfail-enabled', str(self.parameters['nvfail_enabled']))
- # volume-dr-protection-attributes
- if self.parameters.get('vserver_dr_protection') is not None:
- self.create_volume_attribute(vol_mod_attributes, 'volume-vserver-dr-protection-attributes',
- 'vserver-dr-protection', self.parameters['vserver_dr_protection'])
- # volume-id-attributes
- if self.parameters.get('comment') is not None:
- self.create_volume_attribute(vol_mod_attributes, 'volume-id-attributes',
- 'comment', self.parameters['comment'])
- # End of Volume-attributes sub attributes
- attributes.add_child_elem(vol_mod_attributes)
- query = netapp_utils.zapi.NaElement('query')
- vol_query_attributes = netapp_utils.zapi.NaElement('volume-attributes')
- self.create_volume_attribute(vol_query_attributes, 'volume-id-attributes',
- 'name', self.parameters['name'])
- query.add_child_elem(vol_query_attributes)
- vol_mod_iter.add_child_elem(attributes)
- vol_mod_iter.add_child_elem(query)
- try:
- result = self.server.invoke_successfully(vol_mod_iter, enable_tunneling=True)
- failures = result.get_child_by_name('failure-list')
- if self.volume_style == 'flexGroup' or self.parameters['is_infinite']:
- success = result.get_child_by_name('success-list')
- success = success.get_child_by_name('volume-modify-iter-async-info')
- results = dict()
- for key in ('status', 'jobid'):
- if success.get_child_by_name(key):
- results[key] = success[key]
- status = results.get('status')
- if status == 'in_progress' and 'jobid' in results:
- if self.parameters['time_out'] == 0:
- return
- error = self.check_job_status(results['jobid'])
- if error is None:
- return
- else:
- self.module.fail_json(msg='Error when modify volume: %s' % error)
- self.module.fail_json(msg='Unexpected error when modify volume: results is: %s' % repr(results))
- # handle error if modify space, policy, or unix-permissions parameter fails
- if failures is not None:
- if failures.get_child_by_name('volume-modify-iter-info') is not None:
- return_info = 'volume-modify-iter-info'
- error_msg = failures.get_child_by_name(return_info).get_child_content('error-message')
- self.module.fail_json(msg="Error modifying volume %s: %s"
- % (self.parameters['name'], error_msg),
- exception=traceback.format_exc())
- elif failures.get_child_by_name('volume-modify-iter-async-info') is not None:
- return_info = 'volume-modify-iter-async-info'
- error_msg = failures.get_child_by_name(return_info).get_child_content('error-message')
- self.module.fail_json(msg="Error modifying volume %s: %s"
- % (self.parameters['name'], error_msg),
- exception=traceback.format_exc())
- self.ems_log_event("volume-modify")
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg='Error modifying volume %s: %s'
- % (self.parameters['name'], to_native(error)),
- exception=traceback.format_exc())
-
- def volume_mount(self):
- """
- Mount an existing volume in specified junction_path
- :return: None
- """
- vol_mount = netapp_utils.zapi.NaElement('volume-mount')
- vol_mount.add_new_child('volume-name', self.parameters['name'])
- vol_mount.add_new_child('junction-path', self.parameters['junction_path'])
- try:
- self.server.invoke_successfully(vol_mount, enable_tunneling=True)
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg='Error mounting volume %s on path %s: %s'
- % (self.parameters['name'], self.parameters['junction_path'],
- to_native(error)), exception=traceback.format_exc())
-
- def volume_unmount(self):
- """
- Unmount an existing volume
- :return: None
- """
- vol_unmount = netapp_utils.zapi.NaElement.create_node_with_children(
- 'volume-unmount', **{'volume-name': self.parameters['name']})
- try:
- self.server.invoke_successfully(vol_unmount, enable_tunneling=True)
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg='Error unmounting volume %s: %s'
- % (self.parameters['name'], to_native(error)), exception=traceback.format_exc())
-
- def modify_volume(self, modify):
- '''Modify volume action'''
- for attribute in modify.keys():
- if attribute == 'size':
- self.resize_volume()
- if attribute == 'is_online':
- self.change_volume_state()
- if attribute == 'aggregate_name':
- self.move_volume()
- if attribute in ['space_guarantee', 'policy', 'unix_permissions', 'tiering_policy',
- 'snapshot_policy', 'percent_snapshot_space', 'snapdir_access', 'atime_update',
- 'nvfail_enabled', 'space_slo', 'qos_policy_group', 'qos_adaptive_policy_group', 'vserver_dr_protection', 'comment']:
- self.volume_modify_attributes(modify)
- if attribute == 'junction_path':
- if modify.get('junction_path') == '':
- self.volume_unmount()
- else:
- self.volume_mount()
-
- def compare_chmod_value(self, current):
- """
- compare current unix_permissions to desire unix_permissions.
- :return: True if the same, False it not the same or desire unix_permissions is not valid.
- """
- desire = self.parameters
- if current is None:
- return False
- octal_value = ''
- unix_permissions = desire['unix_permissions']
- if unix_permissions.isdigit():
- return int(current['unix_permissions']) == int(unix_permissions)
- else:
- if len(unix_permissions) != 12:
- return False
- if unix_permissions[:3] != '---':
- return False
- for i in range(3, len(unix_permissions), 3):
- if unix_permissions[i] not in ['r', '-'] or unix_permissions[i + 1] not in ['w', '-']\
- or unix_permissions[i + 2] not in ['x', '-']:
- return False
- group_permission = self.char_to_octal(unix_permissions[i:i + 3])
- octal_value += str(group_permission)
- return int(current['unix_permissions']) == int(octal_value)
-
- def char_to_octal(self, chars):
- """
- :param chars: Characters to be converted into octal values.
- :return: octal value of the individual group permission.
- """
- total = 0
- if chars[0] == 'r':
- total += 4
- if chars[1] == 'w':
- total += 2
- if chars[2] == 'x':
- total += 1
- return total
-
- def get_volume_style(self, current):
- '''Get volume style, infinite or standard flexvol'''
- if current is None:
- if self.parameters.get('aggr_list') or self.parameters.get('aggr_list_multiplier') or self.parameters.get('auto_provision_as'):
- return 'flexGroup'
- else:
- if current.get('style_extended'):
- if current['style_extended'] == 'flexgroup':
- return 'flexGroup'
- else:
- return current['style_extended']
- return None
-
- def get_job(self, jobid, server):
- """
- Get job details by id
- """
- job_get = netapp_utils.zapi.NaElement('job-get')
- job_get.add_new_child('job-id', jobid)
- try:
- result = server.invoke_successfully(job_get, enable_tunneling=True)
- except netapp_utils.zapi.NaApiError as error:
- if to_native(error.code) == "15661":
- # Not found
- return None
- self.module.fail_json(msg='Error fetching job info: %s' % to_native(error),
- exception=traceback.format_exc())
- job_info = result.get_child_by_name('attributes').get_child_by_name('job-info')
- results = {
- 'job-progress': job_info['job-progress'],
- 'job-state': job_info['job-state']
- }
- if job_info.get_child_by_name('job-completion') is not None:
- results['job-completion'] = job_info['job-completion']
- else:
- results['job-completion'] = None
- return results
-
- def check_job_status(self, jobid):
- """
- Loop until job is complete
- """
- server = self.server
- sleep_time = 5
- time_out = self.parameters['time_out']
- results = self.get_job(jobid, server)
- error = 'timeout'
-
- while time_out > 0:
- results = self.get_job(jobid, server)
- # If running as cluster admin, the job is owned by cluster vserver
- # rather than the target vserver.
- if results is None and server == self.server:
- results = netapp_utils.get_cserver(self.server)
- server = netapp_utils.setup_na_ontap_zapi(module=self.module, vserver=results)
- continue
- if results is None:
- error = 'cannot locate job with id: %d' % int(jobid)
- break
- if results['job-state'] in ('queued', 'running'):
- time.sleep(sleep_time)
- time_out -= sleep_time
- continue
- if results['job-state'] in ('success', 'failure'):
- break
- else:
- self.module.fail_json(msg='Unexpected job status in: %s' % repr(results))
-
- if results is not None:
- if results['job-state'] == 'success':
- error = None
- elif results['job-state'] in ('queued', 'running'):
- error = 'job completion exceeded expected timer of: %s seconds' % \
- self.parameters['time_out']
- else:
- if results['job-completion'] is not None:
- error = results['job-completion']
- else:
- error = results['job-progress']
- return error
-
- def check_invoke_result(self, result, action):
- '''
- check invoked api call back result.
- '''
- results = dict()
- for key in ('result-status', 'result-jobid'):
- if result.get_child_by_name(key):
- results[key] = result[key]
- status = results.get('result-status')
- if status == 'in_progress' and 'result-jobid' in results:
- if self.parameters['time_out'] == 0:
- return
- error = self.check_job_status(results['result-jobid'])
- if error is None:
- return
- else:
- self.module.fail_json(msg='Error when %s volume: %s' % (action, error))
- if status == 'failed':
- self.module.fail_json(msg='Operation failed when %s volume.' % action)
-
- def assign_efficiency_policy(self):
- '''Set efficiency policy'''
- options = {'path': '/vol/' + self.parameters['name']}
- efficiency_enable = netapp_utils.zapi.NaElement.create_node_with_children('sis-enable', **options)
- try:
- self.server.invoke_successfully(efficiency_enable, enable_tunneling=True)
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg='Error enable efficiency on volume %s: %s'
- % (self.parameters['name'], to_native(error)),
- exception=traceback.format_exc())
-
- options['policy-name'] = self.parameters['efficiency_policy']
- efficiency_start = netapp_utils.zapi.NaElement.create_node_with_children('sis-set-config', **options)
- try:
- self.server.invoke_successfully(efficiency_start, enable_tunneling=True)
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg='Error setting up an efficiency policy %s on volume %s: %s'
- % (self.parameters['efficiency_policy'], self.parameters['name'], to_native(error)),
- exception=traceback.format_exc())
-
- def assign_efficiency_policy_async(self):
- '''Set efficiency policy in asynchronous mode'''
- options = {'volume-name': self.parameters['name']}
- efficiency_enable = netapp_utils.zapi.NaElement.create_node_with_children('sis-enable-async', **options)
- try:
- result = self.server.invoke_successfully(efficiency_enable, enable_tunneling=True)
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg='Error enable efficiency on volume %s: %s'
- % (self.parameters['name'], to_native(error)),
- exception=traceback.format_exc())
- self.check_invoke_result(result, 'enable efficiency on')
-
- options['policy-name'] = self.parameters['efficiency_policy']
- efficiency_start = netapp_utils.zapi.NaElement.create_node_with_children('sis-set-config-async', **options)
- try:
- result = self.server.invoke_successfully(efficiency_start, enable_tunneling=True)
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg='Error setting up an efficiency policy on volume %s: %s'
- % (self.parameters['name'], to_native(error)),
- exception=traceback.format_exc())
- self.check_invoke_result(result, 'set efficiency policy on')
-
- def apply(self):
- '''Call create/modify/delete operations'''
- current = self.get_volume()
- self.volume_style = self.get_volume_style(current)
- # rename and create are mutually exclusive
- rename, cd_action, modify = None, None, None
- if self.parameters.get('from_name'):
- rename = self.na_helper.is_rename_action(self.get_volume(self.parameters['from_name']), current)
- else:
- cd_action = self.na_helper.get_cd_action(current, self.parameters)
- if self.parameters.get('unix_permissions'):
- # current stores unix_permissions' numeric value.
- # unix_permission in self.parameter can be either numeric or character.
- if self.compare_chmod_value(current):
- del self.parameters['unix_permissions']
- if cd_action is None and self.parameters['state'] == 'present':
- modify = self.na_helper.get_modified_attributes(current, self.parameters)
- if self.na_helper.changed:
- if self.module.check_mode:
- pass
- else:
- if rename:
- self.rename_volume()
- if cd_action == 'create':
- self.create_volume()
- # if we create, and modify only variable are set (snapdir_access or atime_update) we need to run a modify
- if 'snapdir_access' in self.parameters or 'atime_update' in self.parameters:
- self.volume_modify_attributes({'snapdir_access': self.parameters['snapdir_access'],
- 'atime_update': self.parameters['atime_update']})
- elif cd_action == 'delete':
- self.delete_volume()
- elif modify:
- self.modify_volume(modify)
- self.module.exit_json(changed=self.na_helper.changed)
-
- def ems_log_event(self, state):
- '''Autosupport log event'''
- if state == 'create':
- message = "A Volume has been created, size: " + \
- str(self.parameters['size']) + str(self.parameters['size_unit'])
- elif state == 'volume-delete':
- message = "A Volume has been deleted"
- elif state == 'volume-move':
- message = "A Volume has been moved"
- elif state == 'volume-rename':
- message = "A Volume has been renamed"
- elif state == 'volume-resize':
- message = "A Volume has been resized to: " + \
- str(self.parameters['size']) + str(self.parameters['size_unit'])
- elif state == 'volume-change':
- message = "A Volume state has been changed"
- else:
- message = "na_ontap_volume has been called"
- netapp_utils.ems_log_event(
- "na_ontap_volume", self.server, event=message)
-
-
-def main():
- '''Apply volume operations from playbook'''
- obj = NetAppOntapVolume()
- obj.apply()
-
-
-if __name__ == '__main__':
- main()
diff --git a/lib/ansible/modules/storage/netapp/na_ontap_volume_autosize.py b/lib/ansible/modules/storage/netapp/na_ontap_volume_autosize.py
deleted file mode 100644
index b0adab1c47..0000000000
--- a/lib/ansible/modules/storage/netapp/na_ontap_volume_autosize.py
+++ /dev/null
@@ -1,361 +0,0 @@
-#!/usr/bin/python
-
-# (c) 2019, NetApp, Inc
-# GNU General Public License v3.0+
-# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-
-from __future__ import absolute_import, division, print_function
-__metaclass__ = type
-
-ANSIBLE_METADATA = {'metadata_version': '1.1',
- 'status': ['preview'],
- 'supported_by': 'community'}
-
-DOCUMENTATION = '''
-module: na_ontap_volume_autosize
-short_description: NetApp ONTAP manage volume autosize
-extends_documentation_fragment:
- - netapp.na_ontap
-version_added: '2.9'
-author: NetApp Ansible Team (@carchi8py) <ng-ansibleteam@netapp.com>
-description:
-- Modify Volume AutoSize
-options:
- volume:
- description:
- - The name of the flexible volume for which we want to set autosize.
- type: str
- required: true
-
- mode:
- description:
- - Specify the flexible volume's autosize mode of operation.
- type: str
- choices: ['grow', 'grow_shrink', 'off']
-
- vserver:
- description:
- - Name of the vserver to use.
- required: true
- type: str
-
- grow_threshold_percent:
- description:
- - Specifies the percentage of the flexible volume's capacity at which autogrow is initiated.
- - The default grow threshold varies from 85% to 98%, depending on the volume size.
- - It is an error for the grow threshold to be less than or equal to the shrink threshold.
- - Range between 0 and 100
- type: int
-
- increment_size:
- description:
- - Specify the flexible volume's increment size using the following format < number > [k|m|g|t]
- - The amount is the absolute size to set.
- - The trailing 'k', 'm', 'g', and 't' indicates the desired units, namely 'kilobytes', 'megabytes', 'gigabytes', and 'terabytes' (respectively).
- type: str
-
- maximum_size:
- description:
- - Specify the flexible volume's maximum allowed size using the following format < number > [k|m|g|t]
- - The amount is the absolute size to set.
- - The trailing 'k', 'm', 'g', and 't' indicates the desired units, namely 'kilobytes', 'megabytes', 'gigabytes', and 'terabytes' (respectively).
- - The default value is 20% greater than the volume size at the time autosize was enabled.
- - It is an error for the maximum volume size to be less than the current volume size.
- - It is also an error for the maximum size to be less than or equal to the minimum size.
- type: str
-
- minimum_size:
- description:
- - Specify the flexible volume's minimum allowed size using the following format < number > [k|m|g|t] The amount is the absolute size to set.
- - The trailing 'k', 'm', 'g', and 't' indicates the desired units, namely 'kilobytes', 'megabytes', 'gigabytes', and 'terabytes' (respectively).
- - The default value is the size of the volume at the time the 'grow_shrink' mode was enabled.
- - It is an error for the minimum size to be greater than or equal to the maximum size.
- type: str
-
- reset:
- description:
- - "Sets the values of maximum_size, increment_size, minimum_size, grow_threshold_percent, shrink_threshold_percent and mode to their defaults"
- type: bool
-
- shrink_threshold_percent:
- description:
- - Specifies the percentage of the flexible volume's capacity at which autoshrink is initiated.
- - The default shrink threshold is 50%. It is an error for the shrink threshold to be greater than or equal to the grow threshold.
- - Range between 0 and 100
- type: int
-'''
-
-EXAMPLES = """
- - name: Modify volume autosize
- na_ontap_volume_autosize:
- hostname: 10.193.79.189
- username: admin
- password: netapp1!
- volume: ansibleVolumesize12
- mode: grow
- grow_threshold_percent: 99
- increment_size: 50m
- maximum_size: 10g
- minimum_size: 21m
- shrink_threshold_percent: 40
- vserver: ansible_vserver
-
- - name: Reset volume autosize
- na_ontap_volume_autosize:
- hostname: 10.193.79.189
- username: admin
- password: netapp1!
- volume: ansibleVolumesize12
- reset: true
- vserver: ansible_vserver
-"""
-
-RETURN = """
-"""
-import sys
-import copy
-import traceback
-import ansible.module_utils.netapp as netapp_utils
-from ansible.module_utils.netapp_module import NetAppModule
-from ansible.module_utils.basic import AnsibleModule
-from ansible.module_utils.netapp import OntapRestAPI
-from ansible.module_utils._text import to_native
-
-HAS_NETAPP_LIB = netapp_utils.has_netapp_lib()
-
-
-class NetAppOntapVolumeAutosize(object):
- def __init__(self):
- self.use_rest = False
- # Volume_autosize returns KB and not B like Volume so values are shifted down 1
- self._size_unit_map = dict(
- k=1,
- m=1024,
- g=1024 ** 2,
- t=1024 ** 3,
- )
- self.argument_spec = netapp_utils.na_ontap_host_argument_spec()
- self.argument_spec.update(dict(
- volume=dict(required=True, type="str"),
- mode=dict(required=False, choices=['grow', 'grow_shrink', 'off']),
- vserver=dict(required=True, type='str'),
- grow_threshold_percent=dict(required=False, type='int'),
- increment_size=dict(required=False, type='str'),
- maximum_size=dict(required=False, type='str'),
- minimum_size=dict(required=False, type='str'),
- reset=dict(required=False, type='bool'),
- shrink_threshold_percent=dict(required=False, type='int')
- ))
- self.module = AnsibleModule(
- argument_spec=self.argument_spec,
- supports_check_mode=True,
- mutually_exclusive=[
- ['reset', 'maximum_size'],
- ['reset', 'increment_size'],
- ['reset', 'minimum_size'],
- ['reset', 'grow_threshold_percent'],
- ['reset', 'shrink_threshold_percent'],
- ['reset', 'mode']
- ]
- )
- self.na_helper = NetAppModule()
- self.parameters = self.na_helper.set_parameters(self.module.params)
- # API should be used for ONTAP 9.6 or higher, ZAPI for lower version
- self.restApi = OntapRestAPI(self.module)
- if self.restApi.is_rest():
- self.use_rest = True
- # increment size and reset are not supported with rest api
- if self.parameters.get('increment_size'):
- self.module.fail_json(msg="Rest API does not support increment size, please switch to ZAPI")
- if self.parameters.get('reset'):
- self.module.fail_json(msg="Rest API does not support reset, please switch to ZAPI")
- else:
- if HAS_NETAPP_LIB is False:
- self.module.fail_json(msg="the python NetApp-Lib module is required")
- else:
- self.server = netapp_utils.setup_na_ontap_zapi(module=self.module, vserver=self.parameters['vserver'])
-
- def get_volume_autosize(self, uuid=None):
- """
- Get volume_autosize information from the ONTAP system
- :return:
- """
- if self.use_rest:
- params = {'fields': 'autosize'}
- api = 'storage/volumes/' + uuid
- message, error = self.restApi.get(api, params)
- if error is not None:
- self.module.fail_json(msg="%s" % error)
- return self._create_get_volume_return(message['autosize'])
- else:
- volume_autosize_info = netapp_utils.zapi.NaElement('volume-autosize-get')
- volume_autosize_info.add_new_child('volume', self.parameters['volume'])
- try:
- result = self.server.invoke_successfully(volume_autosize_info, True)
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg='Error fetching volume autosize infor for %s : %s' % (self.parameters['volume'],
- to_native(error)),
- exception=traceback.format_exc())
- return self._create_get_volume_return(result)
-
- def _create_get_volume_return(self, results):
- """
- Create a return value from volume-autosize-get info file
- :param results:
- :return:
- """
- return_value = {}
- if self.use_rest:
- if 'mode' in results:
- return_value['mode'] = results['mode']
- if 'grow_threshold' in results:
- return_value['grow_threshold_percent'] = results['grow_threshold']
- if 'maximum' in results:
- return_value['maximum_size'] = results['maximum']
- if 'minimum' in results:
- return_value['minimum_size'] = results['minimum']
- if 'shrink_threshold' in results:
- return_value['shrink_threshold_percent'] = results['shrink_threshold']
- else:
- if results.get_child_by_name('mode'):
- return_value['mode'] = results.get_child_content('mode')
- if results.get_child_by_name('grow-threshold-percent'):
- return_value['grow_threshold_percent'] = int(results.get_child_content('grow-threshold-percent'))
- if results.get_child_by_name('increment-size'):
- return_value['increment_size'] = results.get_child_content('increment-size')
- if results.get_child_by_name('maximum-size'):
- return_value['maximum_size'] = results.get_child_content('maximum-size')
- if results.get_child_by_name('minimum-size'):
- return_value['minimum_size'] = results.get_child_content('minimum-size')
- if results.get_child_by_name('shrink-threshold-percent'):
- return_value['shrink_threshold_percent'] = int(results.get_child_content('shrink-threshold-percent'))
- if return_value == {}:
- return_value = None
- return return_value
-
- def modify_volume_autosize(self, uuid=None):
- """
- Modify a Volumes autosize
- :return:
- """
- if self.use_rest:
- params = {}
- data = {}
- autosize = {}
- if self.parameters.get('mode'):
- autosize['mode'] = self.parameters['mode']
- if self.parameters.get('grow_threshold_percent'):
- autosize['grow_threshold'] = self.parameters['grow_threshold_percent']
- if self.parameters.get('maximum_size'):
- autosize['maximum'] = self.parameters['maximum_size']
- if self.parameters.get('minimum_size'):
- autosize['minimum'] = self.parameters['minimum_size']
- if self.parameters.get('shrink_threshold_percent'):
- autosize['shrink_threshold'] = self.parameters['shrink_threshold_percent']
- data['autosize'] = autosize
- api = "storage/volumes/" + uuid
- message, error = self.restApi.patch(api, data, params)
- if error is not None:
- self.module.fail_json(msg="%s" % error)
-
- else:
- volume_autosize_info = netapp_utils.zapi.NaElement('volume-autosize-set')
- volume_autosize_info.add_new_child('volume', self.parameters['volume'])
- if self.parameters.get('mode'):
- volume_autosize_info.add_new_child('mode', self.parameters['mode'])
- if self.parameters.get('grow_threshold_percent'):
- volume_autosize_info.add_new_child('grow-threshold-percent', str(self.parameters['grow_threshold_percent']))
- if self.parameters.get('increment_size'):
- volume_autosize_info.add_new_child('increment-size', self.parameters['increment_size'])
- if self.parameters.get('reset') is not None:
- volume_autosize_info.add_new_child('reset', str(self.parameters['reset']))
- if self.parameters.get('maximum_size'):
- volume_autosize_info.add_new_child('maximum-size', self.parameters['maximum_size'])
- if self.parameters.get('minimum_size'):
- volume_autosize_info.add_new_child('minimum-size', self.parameters['minimum_size'])
- if self.parameters.get('shrink_threshold_percent'):
- volume_autosize_info.add_new_child('shrink-threshold-percent', str(self.parameters['shrink_threshold_percent']))
- try:
- self.server.invoke_successfully(volume_autosize_info, True)
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg="Error modify volume autosize for %s: %s" % (self.parameters["volume"], to_native(error)),
- exception=traceback.format_exc())
-
- def modify_to_kb(self, converted_parameters):
- """
- Save a converted parameter
- :param converted_parameters: Dic of all parameters
- :return:
- """
- for attr in ['maximum_size', 'minimum_size', 'increment_size']:
- if converted_parameters.get(attr):
- if self.use_rest:
- converted_parameters[attr] = self.convert_to_byte(attr, converted_parameters)
- else:
- converted_parameters[attr] = str(self.convert_to_kb(attr, converted_parameters))
- return converted_parameters
-
- def convert_to_kb(self, variable, converted_parameters):
- """
- Convert a number 10m in to its correct KB size
- :param variable: the Parameter we are going to covert
- :param converted_parameters: Dic of all parameters
- :return:
- """
- if converted_parameters.get(variable)[-1] not in ['k', 'm', 'g', 't']:
- self.module.fail_json(msg="%s must end with a k, m, g or t" % variable)
- return self._size_unit_map[converted_parameters.get(variable)[-1]] * int(converted_parameters.get(variable)[:-1])
-
- def convert_to_byte(self, variable, converted_parameters):
- if converted_parameters.get(variable)[-1] not in ['k', 'm', 'g', 't']:
- self.module.fail_json(msg="%s must end with a k, m, g or t" % variable)
- return (self._size_unit_map[converted_parameters.get(variable)[-1]] * int(converted_parameters.get(variable)[:-1])) * 1024
-
- def get_volume_uuid(self):
- """
- Get a volume's UUID
- :return: uuid of the volume
- """
- params = {'fields': '*',
- 'name': self.parameters['volume'],
- 'svm.name': self.parameters['vserver']}
- api = "storage/volumes"
- message, error = self.restApi.get(api, params)
- if error is not None:
- self.module.fail_json(msg="%s" % error)
- return message['records'][0]['uuid']
-
- def apply(self):
- # TODO Logging for rest
- uuid = None
- if not self.use_rest:
- netapp_utils.ems_log_event("na_ontap_volume_autosize", self.server)
- if self.use_rest:
- # we only have the volume name, we need to the uuid for the volume
- uuid = self.get_volume_uuid()
- current = self.get_volume_autosize(uuid=uuid)
- converted_parameters = copy.deepcopy(self.parameters)
- converted_parameters = self.modify_to_kb(converted_parameters)
- self.na_helper.get_modified_attributes(current, converted_parameters)
- if self.na_helper.changed:
- if self.module.check_mode:
- pass
- else:
- self.modify_volume_autosize(uuid=uuid)
- if self.parameters.get('reset') is True:
- self.modify_volume_autosize(uuid=uuid)
- self.na_helper.changed = True
- self.module.exit_json(changed=self.na_helper.changed)
-
-
-def main():
- """
- Apply volume autosize operations from playbook
- :return:
- """
- obj = NetAppOntapVolumeAutosize()
- obj.apply()
-
-
-if __name__ == '__main__':
- main()
diff --git a/lib/ansible/modules/storage/netapp/na_ontap_volume_clone.py b/lib/ansible/modules/storage/netapp/na_ontap_volume_clone.py
deleted file mode 100644
index 5fbe4e86d5..0000000000
--- a/lib/ansible/modules/storage/netapp/na_ontap_volume_clone.py
+++ /dev/null
@@ -1,228 +0,0 @@
-#!/usr/bin/python
-
-# (c) 2018-2019, NetApp, Inc
-# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-from __future__ import absolute_import, division, print_function
-__metaclass__ = type
-
-
-ANSIBLE_METADATA = {'metadata_version': '1.1',
- 'status': ['preview'],
- 'supported_by': 'community'}
-
-DOCUMENTATION = '''
-module: na_ontap_volume_clone
-short_description: NetApp ONTAP manage volume clones.
-extends_documentation_fragment:
- - netapp.na_ontap
-version_added: '2.6'
-author: NetApp Ansible Team (@carchi8py) <ng-ansibleteam@netapp.com>
-description:
-- Create NetApp ONTAP volume clones.
-- A FlexClone License is required to use this module
-options:
- state:
- description:
- - Whether volume clone should be created.
- choices: ['present']
- default: 'present'
- parent_volume:
- description:
- - The parent volume of the volume clone being created.
- required: true
- type: str
- name:
- description:
- - The name of the volume clone being created.
- required: true
- type: str
- aliases:
- - volume
- vserver:
- description:
- - Vserver in which the volume clone should be created.
- required: true
- type: str
- parent_snapshot:
- description:
- - Parent snapshot in which volume clone is created off.
- type: str
- parent_vserver:
- description:
- - Vserver of parent volume in which clone is created off.
- type: str
- qos_policy_group_name:
- description:
- - The qos-policy-group-name which should be set for volume clone.
- type: str
- space_reserve:
- description:
- - The space_reserve setting which should be used for the volume clone.
- choices: ['volume', 'none']
- volume_type:
- description:
- - The volume-type setting which should be used for the volume clone.
- choices: ['rw', 'dp']
- junction_path:
- version_added: '2.8'
- description:
- - Junction path of the volume.
- type: str
- uid:
- version_added: '2.9'
- description:
- - The UNIX user ID for the clone volume.
- type: int
- gid:
- version_added: '2.9'
- description:
- - The UNIX group ID for the clone volume.
- type: int
-'''
-
-EXAMPLES = """
- - name: create volume clone
- na_ontap_volume_clone:
- state: present
- username: "{{ netapp username }}"
- password: "{{ netapp password }}"
- hostname: "{{ netapp hostname }}"
- vserver: vs_hack
- parent_volume: normal_volume
- name: clone_volume_7
- space_reserve: none
- parent_snapshot: backup1
- junction_path: /clone_volume_7
- uid: 1
- gid: 1
-"""
-
-RETURN = """
-"""
-
-import traceback
-from ansible.module_utils.basic import AnsibleModule
-import ansible.module_utils.netapp as netapp_utils
-from ansible.module_utils.netapp_module import NetAppModule
-from ansible.module_utils._text import to_native
-
-HAS_NETAPP_LIB = netapp_utils.has_netapp_lib()
-
-
-class NetAppONTAPVolumeClone(object):
- """
- Creates a volume clone
- """
-
- def __init__(self):
- """
- Initialize the NetAppOntapVolumeClone class
- """
- self.argument_spec = netapp_utils.na_ontap_host_argument_spec()
- self.argument_spec.update(dict(
- state=dict(required=False, choices=['present'], default='present'),
- parent_volume=dict(required=True, type='str'),
- name=dict(required=True, type='str', aliases=["volume"]),
- vserver=dict(required=True, type='str'),
- parent_snapshot=dict(required=False, type='str', default=None),
- parent_vserver=dict(required=False, type='str', default=None),
- qos_policy_group_name=dict(required=False, type='str', default=None),
- space_reserve=dict(required=False, choices=['volume', 'none'], default=None),
- volume_type=dict(required=False, choices=['rw', 'dp']),
- junction_path=dict(required=False, type='str', default=None),
- uid=dict(required=False, type='int'),
- gid=dict(required=False, type='int')
- ))
-
- self.module = AnsibleModule(
- argument_spec=self.argument_spec,
- supports_check_mode=True,
- required_together=[
- ['uid', 'gid']
- ]
- )
-
- self.na_helper = NetAppModule()
- self.parameters = self.na_helper.set_parameters(self.module.params)
-
- if HAS_NETAPP_LIB is False:
- self.module.fail_json(msg="the python NetApp-Lib module is required")
- else:
- self.server = netapp_utils.setup_na_ontap_zapi(module=self.module, vserver=self.parameters['vserver'])
- return
-
- def create_volume_clone(self):
- """
- Creates a new volume clone
- """
- clone_obj = netapp_utils.zapi.NaElement('volume-clone-create')
- clone_obj.add_new_child("parent-volume", self.parameters['parent_volume'])
- clone_obj.add_new_child("volume", self.parameters['volume'])
- if self.parameters.get('qos_policy_group_name'):
- clone_obj.add_new_child("qos-policy-group-name", self.parameters['qos_policy_group_name'])
- if self.parameters.get('space_reserve'):
- clone_obj.add_new_child("space-reserve", self.parameters['space_reserve'])
- if self.parameters.get('parent_snapshot'):
- clone_obj.add_new_child("parent-snapshot", self.parameters['parent_snapshot'])
- if self.parameters.get('parent_vserver'):
- clone_obj.add_new_child("parent-vserver", self.parameters['parent_vserver'])
- if self.parameters.get('volume_type'):
- clone_obj.add_new_child("volume-type", self.parameters['volume_type'])
- if self.parameters.get('junction_path'):
- clone_obj.add_new_child("junction-path", self.parameters['junction_path'])
- if self.parameters.get('uid'):
- clone_obj.add_new_child("uid", str(self.parameters['uid']))
- clone_obj.add_new_child("gid", str(self.parameters['gid']))
- try:
- self.server.invoke_successfully(clone_obj, True)
- except netapp_utils.zapi.NaApiError as exc:
- self.module.fail_json(msg='Error creating volume clone: %s: %s' %
- (self.parameters['volume'], to_native(exc)), exception=traceback.format_exc())
-
- def get_volume_clone(self):
- clone_obj = netapp_utils.zapi.NaElement('volume-clone-get')
- clone_obj.add_new_child("volume", self.parameters['volume'])
- try:
- results = self.server.invoke_successfully(clone_obj, True)
- if results.get_child_by_name('attributes'):
- attributes = results.get_child_by_name('attributes')
- info = attributes.get_child_by_name('volume-clone-info')
- parent_volume = info.get_child_content('parent-volume')
- # checking if clone volume name already used to create by same parent volume
- if parent_volume == self.parameters['parent_volume']:
- return results
- except netapp_utils.zapi.NaApiError as error:
- # Error 15661 denotes an volume clone not being found.
- if to_native(error.code) == "15661":
- pass
- else:
- self.module.fail_json(msg='Error fetching volume clone information %s: %s' %
- (self.parameters['volume'], to_native(error)), exception=traceback.format_exc())
- return None
-
- def apply(self):
- """
- Run Module based on play book
- """
- netapp_utils.ems_log_event("na_ontap_volume_clone", self.server)
- current = self.get_volume_clone()
- cd_action = self.na_helper.get_cd_action(current, self.parameters)
- if self.na_helper.changed:
- if self.module.check_mode:
- pass
- else:
- if cd_action == 'create':
- self.create_volume_clone()
- self.module.exit_json(changed=self.na_helper.changed)
-
-
-def main():
- """
- Creates the NetApp Ontap Volume Clone object and runs the correct play task
- """
- obj = NetAppONTAPVolumeClone()
- obj.apply()
-
-
-if __name__ == '__main__':
- main()
diff --git a/lib/ansible/modules/storage/netapp/na_ontap_vscan.py b/lib/ansible/modules/storage/netapp/na_ontap_vscan.py
deleted file mode 100644
index 4eee6329ec..0000000000
--- a/lib/ansible/modules/storage/netapp/na_ontap_vscan.py
+++ /dev/null
@@ -1,178 +0,0 @@
-#!/usr/bin/python
-
-# (c) 2018-2019, NetApp Inc.
-# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-
-from __future__ import absolute_import, division, print_function
-
-__metaclass__ = type
-
-ANSIBLE_METADATA = {'metadata_version': '1.1',
- 'status': ['preview'],
- 'supported_by': 'certified'}
-
-DOCUMENTATION = '''
-module: na_ontap_vscan
-short_description: NetApp ONTAP Vscan enable/disable.
-extends_documentation_fragment:
- - netapp.na_ontap
-version_added: '2.9'
-author: NetApp Ansible Team (@carchi8py) <ng-ansibleteam@netapp.com>
-notes:
-- on demand task, on_access_policy and scanner_pools must be set up before running this module
-description:
-- Enable and Disable Vscan
-options:
- enable:
- description:
- - Whether to enable to disable a Vscan
- type: bool
- default: True
-
- vserver:
- description:
- - the name of the data vserver to use.
- required: true
- type: str
-'''
-
-EXAMPLES = """
- - name: Enable Vscan
- na_ontap_vscan:
- enable: True
- username: '{{ netapp_username }}'
- password: '{{ netapp_password }}'
- hostname: '{{ netapp_hostname }}'
- vserver: trident_svm
-
- - name: Disable Vscan
- na_ontap_vscan:
- enable: False
- username: '{{ netapp_username }}'
- password: '{{ netapp_password }}'
- hostname: '{{ netapp_hostname }}'
- vserver: trident_svm
-"""
-
-RETURN = """
-
-"""
-
-import traceback
-
-from ansible.module_utils.basic import AnsibleModule
-from ansible.module_utils._text import to_native
-import ansible.module_utils.netapp as netapp_utils
-from ansible.module_utils.netapp import OntapRestAPI
-from ansible.module_utils.netapp_module import NetAppModule
-
-HAS_NETAPP_LIB = netapp_utils.has_netapp_lib()
-
-
-class NetAppOntapVscan(object):
- def __init__(self):
- self.use_rest = False
- self.argument_spec = netapp_utils.na_ontap_host_argument_spec()
- self.argument_spec.update(dict(
- enable=dict(type='bool', default=True),
- vserver=dict(required=True, type='str'),
- ))
- self.module = AnsibleModule(
- argument_spec=self.argument_spec,
- supports_check_mode=True
- )
- self.na_helper = NetAppModule()
- self.parameters = self.na_helper.set_parameters(self.module.params)
-
- # API should be used for ONTAP 9.6 or higher, Zapi for lower version
- self.restApi = OntapRestAPI(self.module)
- if self.restApi.is_rest():
- self.use_rest = True
- else:
- if HAS_NETAPP_LIB is False:
- self.module.fail_json(msg="the python NetApp-Lib module is required")
- else:
- self.server = netapp_utils.setup_na_ontap_zapi(module=self.module, vserver=self.parameters['vserver'])
-
- def get_vscan(self):
- if self.use_rest:
- params = {'fields': 'svm,enabled',
- "svm.name": self.parameters['vserver']}
- api = "protocols/vscan"
- message, error = self.restApi.get(api, params)
- if error:
- self.module.fail_json(msg=error)
- return message['records'][0]
- else:
- vscan_status_iter = netapp_utils.zapi.NaElement('vscan-status-get-iter')
- vscan_status_info = netapp_utils.zapi.NaElement('vscan-status-info')
- vscan_status_info.add_new_child('vserver', self.parameters['vserver'])
- query = netapp_utils.zapi.NaElement('query')
- query.add_child_elem(vscan_status_info)
- vscan_status_iter.add_child_elem(query)
- try:
- result = self.server.invoke_successfully(vscan_status_iter, True)
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg='Error getting Vscan info for Vserver %s: %s' %
- (self.parameters['vserver'], to_native(error)),
- exception=traceback.format_exc())
- if result.get_child_by_name('num-records') and int(result.get_child_content('num-records')) >= 1:
- return result.get_child_by_name('attributes-list').get_child_by_name('vscan-status-info')
-
- def enable_vscan(self, uuid=None):
- if self.use_rest:
- params = {"svm.name": self.parameters['vserver']}
- data = {"enabled": self.parameters['enable']}
- api = "protocols/vscan/" + uuid
- message, error = self.restApi.patch(api, data, params)
- if error is not None:
- self.module.fail_json(msg=error)
- # self.module.fail_json(msg=repr(self.restApi.errors), log=repr(self.restApi.debug_logs))
- else:
- vscan_status_obj = netapp_utils.zapi.NaElement("vscan-status-modify")
- vscan_status_obj.add_new_child('is-vscan-enabled', str(self.parameters['enable']))
- try:
- self.server.invoke_successfully(vscan_status_obj, True)
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg="Error Enable/Disabling Vscan: %s" % to_native(error), exception=traceback.format_exc())
-
- def asup_log(self):
- if self.use_rest:
- # TODO: logging for Rest
- return
- else:
- # Either we are using ZAPI, or REST failed when it should not
- try:
- netapp_utils.ems_log_event("na_ontap_vscan", self.server)
- except Exception:
- # TODO: we may fail to connect to REST or ZAPI, the line below shows REST issues only
- # self.module.fail_json(msg=repr(self.restApi.errors), log=repr(self.restApi.debug_logs))
- pass
-
- def apply(self):
- changed = False
- self.asup_log()
- current = self.get_vscan()
- if self.use_rest:
- if current['enabled'] != self.parameters['enable']:
- if not self.module.check_mode:
- self.enable_vscan(current['svm']['uuid'])
- changed = True
- else:
- if current.get_child_content('is-vscan-enabled') != str(self.parameters['enable']).lower():
- if not self.module.check_mode:
- self.enable_vscan()
- changed = True
- self.module.exit_json(changed=changed)
-
-
-def main():
- """
- Execute action from playbook
- """
- command = NetAppOntapVscan()
- command.apply()
-
-
-if __name__ == '__main__':
- main()
diff --git a/lib/ansible/modules/storage/netapp/na_ontap_vscan_on_access_policy.py b/lib/ansible/modules/storage/netapp/na_ontap_vscan_on_access_policy.py
deleted file mode 100644
index bbe19a6536..0000000000
--- a/lib/ansible/modules/storage/netapp/na_ontap_vscan_on_access_policy.py
+++ /dev/null
@@ -1,366 +0,0 @@
-#!/usr/bin/python
-
-# (c) 2018-2019, NetApp Inc.
-# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-
-from __future__ import absolute_import, division, print_function
-
-__metaclass__ = type
-
-ANSIBLE_METADATA = {'metadata_version': '1.1',
- 'status': ['preview'],
- 'supported_by': 'certified'}
-
-DOCUMENTATION = '''
-module: na_ontap_vscan_on_access_policy
-short_description: NetApp ONTAP Vscan on access policy configuration.
-extends_documentation_fragment:
- - netapp.na_ontap
-version_added: '2.8'
-author: NetApp Ansible Team (@carchi8py) <ng-ansibleteam@netapp.com>
-description:
-- Configure on access policy for Vscan (virus scan)
-options:
- state:
- description:
- - Whether a Vscan on Access policy is present or not
- choices: ['present', 'absent']
- default: present
-
- vserver:
- description:
- - the name of the data vserver to use.
- required: true
-
- policy_name:
- description:
- - The name of the policy
- required: true
-
- file_ext_to_exclude:
- description:
- - File extensions for which On-Access scanning must not be performed.
-
- file_ext_to_include:
- description:
- - File extensions for which On-Access scanning is considered. The default value is '*', which means that all files are considered for scanning except
- - those which are excluded from scanning.
-
- filters:
- description:
- - A list of filters which can be used to define the scope of the On-Access policy more precisely. The filters can be added in any order. Possible values
- - scan_ro_volume Enable scans for read-only volume,
- - scan_execute_access Scan only files opened with execute-access (CIFS only)
-
- is_scan_mandatory:
- description:
- - Specifies whether access to a file is allowed if there are no external virus-scanning servers available for virus scanning. It is true if not provided at
- the time of creating a policy.
- type: bool
-
- max_file_size:
- description:
- - Max file-size (in bytes) allowed for scanning. The default value of 2147483648 (2GB) is taken if not provided at the time of creating a policy.
-
- paths_to_exclude:
- description:
- - File paths for which On-Access scanning must not be performed.
-
- scan_files_with_no_ext:
- description:
- - Specifies whether files without any extension are considered for scanning or not.
- default: True
-'''
-
-EXAMPLES = """
- - name: Create Vscan On Access Policy
- na_ontap_vscan_on_access_policy:
- state: present
- username: '{{ netapp_username }}'
- password: '{{ netapp_password }}'
- hostname: '{{ netapp_hostname }}'
- vserver: carchi-vsim2
- policy_name: carchi_policy
- file_ext_to_exclude: ['exe', 'yml']
- - name: modify Vscan on Access Policy
- na_ontap_vscan_on_access_policy:
- state: present
- username: '{{ netapp_username }}'
- password: '{{ netapp_password }}'
- hostname: '{{ netapp_hostname }}'
- vserver: carchi-vsim2
- policy_name: carchi_policy
- file_ext_to_exclude: ['exe', 'yml', 'py']
- - name: Delete On Access Policy
- na_ontap_vscan_on_access_policy:
- state: absent
- username: '{{ netapp_username }}'
- password: '{{ netapp_password }}'
- hostname: '{{ netapp_hostname }}'
- vserver: carchi-vsim2
- policy_name: carchi_policy
-"""
-
-RETURN = """
-
-"""
-
-import traceback
-
-from ansible.module_utils.basic import AnsibleModule
-from ansible.module_utils._text import to_native
-import ansible.module_utils.netapp as netapp_utils
-
-HAS_NETAPP_LIB = netapp_utils.has_netapp_lib()
-
-
-class NetAppOntapVscanOnAccessPolicy(object):
- """
- Create/Modify/Delete a Vscan OnAccess policy
- """
- def __init__(self):
- self.argument_spec = netapp_utils.na_ontap_host_argument_spec()
- self.argument_spec.update(dict(
- state=dict(choices=['present', 'absent'], default='present'),
- vserver=dict(required=True, type='str'),
- policy_name=dict(required=True, type='str'),
- file_ext_to_exclude=dict(required=False, type="list"),
- file_ext_to_include=dict(required=False, type="list"),
- filters=dict(required=False, type="list"),
- is_scan_mandatory=dict(required=False, type='bool', default=False),
- max_file_size=dict(required=False, type="int"),
- paths_to_exclude=dict(required=False, type="list"),
- scan_files_with_no_ext=dict(required=False, type=bool, default=True)
- ))
- self.module = AnsibleModule(
- argument_spec=self.argument_spec,
- supports_check_mode=True
- )
- parameters = self.module.params
- self.state = parameters['state']
- self.vserver = parameters['vserver']
- self.policy_name = parameters['policy_name']
- self.file_ext_to_exclude = parameters['file_ext_to_exclude']
- self.file_ext_to_include = parameters['file_ext_to_include']
- self.filters = parameters['filters']
- self.is_scan_mandatory = parameters['is_scan_mandatory']
- self.max_file_size = parameters['max_file_size']
- self.paths_to_exclude = parameters['paths_to_exclude']
- self.scan_files_with_no_ext = parameters['scan_files_with_no_ext']
-
- if HAS_NETAPP_LIB is False:
- self.module.fail_json(msg="the python NetApp-Lib module is required")
- else:
- self.server = netapp_utils.setup_na_ontap_zapi(module=self.module, vserver=self.vserver)
-
- def exists_access_policy(self, policy_obj=None):
- """
- Check if a Vscan Access policy exists
- :return: True if Exist, False if it does not
- """
- if policy_obj is None:
- policy_obj = self.return_on_access_policy()
- if policy_obj:
- return True
- else:
- return False
-
- def return_on_access_policy(self):
- """
- Return a Vscan on Access Policy
- :return: None if there is no access policy, return the policy if there is
- """
- access_policy_obj = netapp_utils.zapi.NaElement('vscan-on-access-policy-get-iter')
- access_policy_info = netapp_utils.zapi.NaElement('vscan-on-access-policy-info')
- access_policy_info.add_new_child('policy-name', self.policy_name)
- query = netapp_utils.zapi.NaElement('query')
- query.add_child_elem(access_policy_info)
- access_policy_obj.add_child_elem(query)
- try:
- result = self.server.invoke_successfully(access_policy_obj, True)
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg='Error searching Vscan on Access Policy %s: %s' %
- (self.policy_name, to_native(error)), exception=traceback.format_exc())
- if result.get_child_by_name('num-records'):
- if int(result.get_child_content('num-records')) == 1:
- return result
- elif int(result.get_child_content('num-records')) > 1:
- self.module.fail_json(msg='Multiple Vscan on Access Policy matching %s:' % self.policy_name)
- return None
-
- def create_on_access_policy(self):
- """
- Create a Vscan on Access policy
- :return: none
- """
- access_policy_obj = netapp_utils.zapi.NaElement('vscan-on-access-policy-create')
- access_policy_obj.add_new_child('policy-name', self.policy_name)
- access_policy_obj.add_new_child('protocol', 'cifs')
- access_policy_obj = self._fill_in_access_policy(access_policy_obj)
-
- try:
- self.server.invoke_successfully(access_policy_obj, True)
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg='Error creating Vscan on Access Policy %s: %s' %
- (self.policy_name, to_native(error)), exception=traceback.format_exc())
-
- def delete_on_access_policy(self):
- """
- Delete a Vscan On Access Policy
- :return:
- """
- access_policy_obj = netapp_utils.zapi.NaElement('vscan-on-access-policy-delete')
- access_policy_obj.add_new_child('policy-name', self.policy_name)
- try:
- self.server.invoke_successfully(access_policy_obj, True)
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg='Error Deleting Vscan on Access Policy %s: %s' %
- (self.policy_name, to_native(error)), exception=traceback.format_exc())
-
- def modify_on_access_policy(self):
- """
- Modify a Vscan On Access policy
- :return: nothing
- """
- access_policy_obj = netapp_utils.zapi.NaElement('vscan-on-access-policy-modify')
- access_policy_obj.add_new_child('policy-name', self.policy_name)
- access_policy_obj = self._fill_in_access_policy(access_policy_obj)
- try:
- self.server.invoke_successfully(access_policy_obj, True)
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg='Error Modifying Vscan on Access Policy %s: %s' %
- (self.policy_name, to_native(error)), exception=traceback.format_exc())
-
- def _fill_in_access_policy(self, access_policy_obj):
- if self.is_scan_mandatory is not None:
- access_policy_obj.add_new_child('is-scan-mandatory', str(self.is_scan_mandatory).lower())
- if self.max_file_size:
- access_policy_obj.add_new_child('max-file-size', str(self.max_file_size))
- if self.scan_files_with_no_ext is not None:
- access_policy_obj.add_new_child('scan-files-with-no-ext', str(self.scan_files_with_no_ext))
- if self.file_ext_to_exclude:
- ext_obj = netapp_utils.zapi.NaElement('file-ext-to-exclude')
- access_policy_obj.add_child_elem(ext_obj)
- for extension in self.file_ext_to_exclude:
- ext_obj.add_new_child('file-extension', extension)
- if self.file_ext_to_include:
- ext_obj = netapp_utils.zapi.NaElement('file-ext-to-include')
- access_policy_obj.add_child_elem(ext_obj)
- for extension in self.file_ext_to_include:
- ext_obj.add_new_child('file-extension', extension)
- if self.filters:
- ui_filter_obj = netapp_utils.zapi.NaElement('filters')
- access_policy_obj.add_child_elem(ui_filter_obj)
- for filter in self.filters:
- ui_filter_obj.add_new_child('vscan-on-access-policy-ui-filter', filter)
- if self.paths_to_exclude:
- path_obj = netapp_utils.zapi.NaElement('paths-to-exclude')
- access_policy_obj.add_child_elem(path_obj)
- for path in self.paths_to_exclude:
- path_obj.add_new_child('file-path', path)
- return access_policy_obj
-
- def has_policy_changed(self):
- results = self.return_on_access_policy()
- if results is None:
- return False
- try:
- policy_obj = results.get_child_by_name('attributes-list').get_child_by_name('vscan-on-access-policy-info')
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg='Error Accessing on access policy %s: %s' %
- (self.policy_name, to_native(error)), exception=traceback.format_exc())
- if self.is_scan_mandatory is not None:
- if str(self.is_scan_mandatory).lower() != policy_obj.get_child_content('is-scan-mandatory'):
- return True
- if self.max_file_size:
- if self.max_file_size != int(policy_obj.get_child_content('max-file-size')):
- return True
- if self.scan_files_with_no_ext is not None:
- if str(self.scan_files_with_no_ext).lower() != policy_obj.get_child_content('scan-files-with-no-ext'):
- return True
- if self.file_ext_to_exclude:
- # if no file-ext-to-exclude are given at creation, XML will not have a file-ext-to-exclude
- if policy_obj.get_child_by_name('file-ext-to-exclude') is None:
- return True
- current_to_exclude = []
- for each in policy_obj.get_child_by_name('file-ext-to-exclude').get_children():
- current_to_exclude.append(each.get_content())
- k = self._diff(self.file_ext_to_exclude, current_to_exclude)
- # If the diff returns something the lists don't match and the policy has changed
- if k:
- return True
- if self.file_ext_to_include:
- # if no file-ext-to-include are given at creation, XML will not have a file-ext-to-include
- if policy_obj.get_child_by_name('file-ext-to-include') is None:
- return True
- current_to_include = []
- for each in policy_obj.get_child_by_name('file-ext-to-include').get_children():
- current_to_include.append(each.get_content())
- k = self._diff(self.file_ext_to_include, current_to_include)
- # If the diff returns something the lists don't match and the policy has changed
- if k:
- return True
- if self.filters:
- if policy_obj.get_child_by_name('filters') is None:
- return True
- current_filters = []
- for each in policy_obj.get_child_by_name('filters').get_children():
- current_filters.append(each.get_content())
- k = self._diff(self.filters, current_filters)
- # If the diff returns something the lists don't match and the policy has changed
- if k:
- return True
- if self.paths_to_exclude:
- if policy_obj.get_child_by_name('paths-to-exclude') is None:
- return True
- current_paths_to_exlude = []
- for each in policy_obj.get_child_by_name('paths-to-exclude').get_children():
- current_paths_to_exlude.append(each.get_content())
- k = self._diff(self.paths_to_exclude, current_paths_to_exlude)
- # If the diff returns something the lists don't match and the policy has changed
- if k:
- return True
- return False
-
- def _diff(self, li1, li2):
- """
- :param li1: list 1
- :param li2: list 2
- :return: a list contain items that are not on both lists
- """
- li_dif = [i for i in li1 + li2 if i not in li1 or i not in li2]
- return li_dif
-
- def apply(self):
- netapp_utils.ems_log_event("na_ontap_vscan_on_access_policy", self.server)
- changed = False
- policy_obj = self.return_on_access_policy()
- if self.state == 'present':
- if not self.exists_access_policy(policy_obj):
- if not self.module.check_mode:
- self.create_on_access_policy()
- changed = True
- else:
- # Check if anything has changed first.
- if self.has_policy_changed():
- if not self.module.check_mode:
- self.modify_on_access_policy()
- changed = True
- if self.state == 'absent':
- if self.exists_access_policy(policy_obj):
- if not self.module.check_mode:
- self.delete_on_access_policy()
- changed = True
- self.module.exit_json(changed=changed)
-
-
-def main():
- """
- Execute action from playbook
- """
- command = NetAppOntapVscanOnAccessPolicy()
- command.apply()
-
-
-if __name__ == '__main__':
- main()
diff --git a/lib/ansible/modules/storage/netapp/na_ontap_vscan_on_demand_task.py b/lib/ansible/modules/storage/netapp/na_ontap_vscan_on_demand_task.py
deleted file mode 100644
index 63b65a211c..0000000000
--- a/lib/ansible/modules/storage/netapp/na_ontap_vscan_on_demand_task.py
+++ /dev/null
@@ -1,313 +0,0 @@
-#!/usr/bin/python
-
-# (c) 2018-2019, NetApp, Inc
-# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-
-from __future__ import absolute_import, division, print_function
-
-__metaclass__ = type
-
-ANSIBLE_METADATA = {'metadata_version': '1.1',
- 'status': ['preview'],
- 'supported_by': 'certified'}
-
-DOCUMENTATION = '''
-module: na_ontap_vscan_on_demand_task
-short_description: NetApp ONTAP Vscan on demand task configuration.
-extends_documentation_fragment:
- - netapp.na_ontap
-version_added: '2.8'
-author: NetApp Ansible Team (@carchi8py) <ng-ansibleteam@netapp.com>
-description:
-- Configure on demand task for Vscan
-options:
- state:
- description:
- - Whether a Vscan on demand task is present or not
- choices: ['present', 'absent']
- default: present
-
- vserver:
- description:
- - the name of the data vserver to use.
- required: true
-
- cross_junction:
- description:
- - Specifies whether the On-Demand task is allowed to cross volume junctions
- type: bool
- default: False
-
- directory_recursion:
- description:
- - Specifies whether the On-Demand task is allowed to recursively scan through sub-directories.
- type: bool
- default: False
-
- file_ext_to_exclude:
- description:
- - File-Extensions for which scanning must not be performed.
- - File whose extension matches with both inclusion and exclusion list is not considered for scanning.
- type: list
-
- file_ext_to_include:
- description:
- - File extensions for which scanning is considered.
- - The default value is '*', which means that all files are considered for scanning except those which are excluded from scanning.
- - File whose extension matches with both inclusion and exclusion list is not considered for scanning.
- type: list
-
- max_file_size:
- description:
- - Max file-size (in bytes) allowed for scanning. The default value of 10737418240 (10GB) is taken if not provided at the time of creating a task.
-
- paths_to_exclude:
- description:
- - File-paths for which scanning must not be performed.
- type: list
-
- report_directory:
- description:
- - Path from the vserver root where task report is created. The path must be a directory and provided in unix-format from the root of the Vserver.
- - Example /vol1/on-demand-reports.
-
- report_log_level:
- description:
- - Log level for the On-Demand report.
- choices: ['verbose', 'info', 'error']
- default: error
-
- request_timeout:
- description:
- - Total request-service time-limit in seconds. If the virus-scanner does not respond within the provided time, scan will be timed out.
-
- scan_files_with_no_ext:
- description:
- - Specifies whether files without any extension are considered for scanning or not.
- type: bool
- default: True
-
- scan_paths:
- description:
- - List of paths that need to be scanned. The path must be provided in unix-format and from the root of the Vserver.
- - Example /vol1/large_files.
- type: list
-
- scan_priority:
- description:
- - Priority of the On-Demand scan requests generated by this task.
- choices: ['low', 'normal']
- default: low
-
- schedule:
- description:
- - Schedule of the task. The task will be run as per the schedule.
- - For running the task immediately, vscan-on-demand-task-run api must be used after creating a task.
-
- task_name:
- description:
- - Name of the task.
- required: True
-'''
-
-
-EXAMPLES = """
- - name: Create Vscan On Demand Task
- na_ontap_vscan_on_demand_task:
- state: present
- username: '{{ netapp_username }}'
- password: '{{ netapp_password }}'
- hostname: '{{ netapp_hostname }}'
- vserver: carchi-vsim2
- task_name: carchiOnDemand
- scan_paths: /
- report_directory: /
- file_ext_to_exclude: ['py', 'yml']
- max_file_size: 10737418241
- paths_to_exclude: ['/tmp', '/var']
- report_log_level: info
- request_timeout: 60
-
- - name: Delete Vscan On Demand Task
- na_ontap_vscan_on_demand_task:
- state: absent
- username: '{{ netapp_username }}'
- password: '{{ netapp_password }}'
- hostname: '{{ netapp_hostname }}'
- vserver: carchi-vsim2
- task_name: carchiOnDemand
-"""
-
-RETURN = """
-
-"""
-
-import traceback
-
-from ansible.module_utils.basic import AnsibleModule
-from ansible.module_utils._text import to_native
-import ansible.module_utils.netapp as netapp_utils
-from ansible.module_utils.netapp_module import NetAppModule
-
-HAS_NETAPP_LIB = netapp_utils.has_netapp_lib()
-
-
-class NetAppOntapVscanOnDemandTask(object):
- def __init__(self):
- self.argument_spec = netapp_utils.na_ontap_host_argument_spec()
- self.argument_spec.update(dict(
- state=dict(choices=['present', 'absent'], default='present'),
- vserver=dict(required=True, type='str'),
- cross_junction=dict(required=False, type='bool', default=False),
- directory_recursion=dict(required=False, type='bool', default=False),
- file_ext_to_exclude=dict(required=False, type="list"),
- file_ext_to_include=dict(required=False, type="list"),
- max_file_size=dict(required=False, type="str"),
- paths_to_exclude=dict(required=False, type="list"),
- report_directory=dict(required=False, type='str'),
- report_log_level=dict(required=False, choices=['verbose', 'info', 'error'], default='error'),
- request_timeout=dict(required=False, type='str'),
- scan_files_with_no_ext=dict(required=False, type='bool', default=True),
- scan_paths=dict(required=False, type="list"),
- scan_priority=dict(required=False, choices=['low', 'normal'], default='low'),
- schedule=dict(required=False, type="str"),
- task_name=dict(required=True, type="str")
- ))
- self.module = AnsibleModule(
- argument_spec=self.argument_spec,
- supports_check_mode=True,
- required_if=[
- ["state", "present", ["report_directory", "scan_paths"]]
- ]
- )
- self.na_helper = NetAppModule()
- self.parameters = self.na_helper.set_parameters(self.module.params)
-
- if HAS_NETAPP_LIB is False:
- self.module.fail_json(msg="the python NetApp-Lib module is required")
- else:
- self.server = netapp_utils.setup_na_ontap_zapi(module=self.module, vserver=self.parameters['vserver'])
-
- def get_demand_task(self):
- """
- Get a demand task
- :return: A vscan-on-demand-task-info or None
- """
- demand_task_iter = netapp_utils.zapi.NaElement("vscan-on-demand-task-get-iter")
- demand_task_info = netapp_utils.zapi.NaElement("vscan-on-demand-task-info")
- demand_task_info.add_new_child('task-name', self.parameters['task_name'])
- query = netapp_utils.zapi.NaElement('query')
- query.add_child_elem(demand_task_info)
- demand_task_iter.add_child_elem(query)
- try:
- result = self.server.invoke_successfully(demand_task_iter, True)
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg='Error searching for Vscan on demand task %s: %s' %
- (self.parameters['task_name'], to_native(error)),
- exception=traceback.format_exc())
- if result.get_child_by_name('num-records') and int(result.get_child_content('num-records')) >= 1:
- return result.get_child_by_name('attributes-list').get_child_by_name('vscan-on-demand-task-info')
- return None
-
- def create_demand_task(self):
- """
- Create a Demand Task
- :return: None
- """
- demand_task_obj = netapp_utils.zapi.NaElement("vscan-on-demand-task-create")
- # Required items first
- demand_task_obj.add_new_child('report-directory', self.parameters['report_directory'])
- demand_task_obj.add_new_child('task-name', self.parameters['task_name'])
- scan_paths = netapp_utils.zapi.NaElement("scan-paths")
- for scan_path in self.parameters['scan_paths']:
- scan_paths.add_new_child('string', scan_path)
- demand_task_obj.add_child_elem(scan_paths)
- # Optional items next
- if self.parameters.get('cross_junction'):
- demand_task_obj.add_new_child('cross-junction', str(self.parameters['cross_junction']).lower())
- if self.parameters.get('directory_recursion'):
- demand_task_obj.add_new_child('directory-recursion', str(self.parameters['directory_recursion']).lower())
- if self.parameters.get('file_ext_to_exclude'):
- ext_to_exclude_obj = netapp_utils.zapi.NaElement('file-ext-to-exclude')
- for exclude_file in self.parameters['file_ext_to_exclude']:
- ext_to_exclude_obj.add_new_child('file-extension', exclude_file)
- demand_task_obj.add_child_elem(ext_to_exclude_obj)
- if self.parameters.get('file_ext_to_include'):
- ext_to_include_obj = netapp_utils.zapi.NaElement('file-ext-to-include')
- for include_file in self.parameters['file_ext_to_exclude']:
- ext_to_include_obj.add_child_elem('file-extension', include_file)
- demand_task_obj.add_child_elem(ext_to_include_obj)
- if self.parameters.get('max_file_size'):
- demand_task_obj.add_new_child('max-file-size', self.parameters['max_file_size'])
- if self.parameters.get('paths_to_exclude'):
- exclude_paths = netapp_utils.zapi.NaElement('paths-to-exclude')
- for path in self.parameters['paths_to_exclude']:
- exclude_paths.add_new_child('string', path)
- demand_task_obj.add_child_elem(exclude_paths)
- if self.parameters.get('report_log_level'):
- demand_task_obj.add_new_child('report-log-level', self.parameters['report_log_level'])
- if self.parameters.get('request_timeout'):
- demand_task_obj.add_new_child('request-timeout', self.parameters['request_timeout'])
- if self.parameters.get('scan_files_with_no_ext'):
- demand_task_obj.add_new_child('scan-files-with-no-ext', str(self.parameters['scan_files_with_no_ext']).lower())
- if self.parameters.get('scan_priority'):
- demand_task_obj.add_new_child('scan-priority', self.parameters['scan_priority'].lower())
- if self.parameters.get('schedule'):
- demand_task_obj.add_new_child('schedule', self.parameters['schedule'])
- try:
- result = self.server.invoke_successfully(demand_task_obj, True)
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg='Error creating on demand task %s: %s' %
- (self.parameters['task_name'], to_native(error)),
- exception=traceback.format_exc())
-
- def delete_demand_task(self):
- """
- Delete a Demand Task"
- :return:
- """
- demand_task_obj = netapp_utils.zapi.NaElement('vscan-on-demand-task-delete')
- demand_task_obj.add_new_child('task-name', self.parameters['task_name'])
- try:
- self.server.invoke_successfully(demand_task_obj, True)
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg='Error deleting on demand task, %s: %s' %
- (self.parameters['task_name'], to_native(error)),
- exception=traceback.format_exc())
-
- def asup_log_for_cserver(self, event_name):
- """
- Fetch admin vserver for the given cluster
- Create and Autosupport log event with the given module name
- :param event_name: Name of the event log
- :return: None
- """
- results = netapp_utils.get_cserver(self.server)
- cserver = netapp_utils.setup_na_ontap_zapi(module=self.module, vserver=results)
- netapp_utils.ems_log_event(event_name, cserver)
-
- def apply(self):
- self.asup_log_for_cserver("na_ontap_vscan_on_demand_task")
- current = self.get_demand_task()
- cd_action = self.na_helper.get_cd_action(current, self.parameters)
- if self.na_helper.changed:
- if self.module.check_mode:
- pass
- else:
- if cd_action == 'create':
- self.create_demand_task()
- elif cd_action == 'delete':
- self.delete_demand_task()
- self.module.exit_json(changed=self.na_helper.changed)
-
-
-def main():
- """
- Execute action from playbook
- """
- command = NetAppOntapVscanOnDemandTask()
- command.apply()
-
-
-if __name__ == '__main__':
- main()
diff --git a/lib/ansible/modules/storage/netapp/na_ontap_vscan_scanner_pool.py b/lib/ansible/modules/storage/netapp/na_ontap_vscan_scanner_pool.py
deleted file mode 100644
index b5e14f62df..0000000000
--- a/lib/ansible/modules/storage/netapp/na_ontap_vscan_scanner_pool.py
+++ /dev/null
@@ -1,240 +0,0 @@
-#!/usr/bin/python
-
-# (c) 2018-2019, NetApp, Inc
-# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-
-from __future__ import absolute_import, division, print_function
-
-__metaclass__ = type
-
-ANSIBLE_METADATA = {'metadata_version': '1.1',
- 'status': ['preview'],
- 'supported_by': 'community'}
-
-DOCUMENTATION = '''
-module: na_ontap_vscan_scanner_pool
-short_description: NetApp ONTAP Vscan Scanner Pools Configuration.
-extends_documentation_fragment:
- - netapp.na_ontap
-version_added: '2.8'
-author: NetApp Ansible Team (@carchi8py) <ng-ansibleteam@netapp.com>
-description:
-- Configure a Vscan Scanner Pool
-options:
- state:
- description:
- - Whether a Vscan Scanner pool is present or not
- choices: ['present', 'absent']
- default: present
-
- vserver:
- description:
- - the name of the data vserver to use.
- required: true
-
- hostnames:
- description:
- - List of hostnames of Vscan servers which are allowed to connect to Data ONTAP
-
- privileged_users:
- description:
- - List of privileged usernames. Username must be in the form "domain-name\\user-name"
-
- scanner_pool:
- description:
- - the name of the virus scanner pool
- required: true
-
- scanner_policy:
- description:
- - The name of the Virus scanner Policy
- choices: ['primary', 'secondary', 'idle']
-'''
-
-EXAMPLES = """
-- name: Create and enable Scanner pool
- na_ontap_vscan_scanner_pool:
- state: present
- username: '{{ netapp_username }}'
- password: '{{ netapp_password }}'
- hostname: '{{ netapp_hostname }}'
- vserver: carchi-vsim2
- hostnames: ['name', 'name2']
- privileged_users: ['sim.rtp.openeng.netapp.com\\admin', 'sim.rtp.openeng.netapp.com\\carchi']
- scanner_pool: Scanner1
- scanner_policy: primary
-
-- name: Delete a scanner pool
- na_ontap_vscan_scanner_pool:
- state: absent
- username: '{{ netapp_username }}'
- password: '{{ netapp_password }}'
- hostname: '{{ netapp_hostname }}'
- vserver: carchi-vsim2
- scanner_pool: Scanner1
-"""
-
-RETURN = """
-
-"""
-
-import traceback
-
-from ansible.module_utils.basic import AnsibleModule
-from ansible.module_utils._text import to_native
-import ansible.module_utils.netapp as netapp_utils
-
-HAS_NETAPP_LIB = netapp_utils.has_netapp_lib()
-
-
-class NetAppOntapVscanScannerPool(object):
-
- def __init__(self):
- self.argument_spec = netapp_utils.na_ontap_host_argument_spec()
- self.argument_spec.update(dict(
- state=dict(choices=['present', 'absent'], default='present'),
- vserver=dict(required=True, type='str'),
- hostnames=dict(required=False, type='list'),
- privileged_users=dict(required=False, type='list'),
- scanner_pool=dict(required=True, type='str'),
- scanner_policy=dict(required=False, choices=['primary', 'secondary', 'idle'])
- ))
- self.module = AnsibleModule(
- argument_spec=self.argument_spec,
- supports_check_mode=True
- )
- parameters = self.module.params
- self.hostnames = parameters['hostnames']
- self.vserver = parameters['vserver']
- self.privileged_users = parameters['privileged_users']
- self.scanner_pool = parameters['scanner_pool']
- self.state = parameters['state']
- self.scanner_policy = parameters['scanner_policy']
-
- if HAS_NETAPP_LIB is False:
- self.module.fail_json(msg="the python NetApp-Lib module is required")
- else:
- self.server = netapp_utils.setup_na_ontap_zapi(module=self.module, vserver=self.vserver)
-
- def create_scanner_pool(self):
- """
- Create a Vscan Scanner Pool
- :return: nothing
- """
- scanner_pool_obj = netapp_utils.zapi.NaElement('vscan-scanner-pool-create')
- if self.hostnames:
- string_obj = netapp_utils.zapi.NaElement('hostnames')
- scanner_pool_obj.add_child_elem(string_obj)
- for hostname in self.hostnames:
- string_obj.add_new_child('string', hostname)
- if self.privileged_users:
- users_obj = netapp_utils.zapi.NaElement('privileged-users')
- scanner_pool_obj.add_child_elem(users_obj)
- for user in self.privileged_users:
- users_obj.add_new_child('privileged-user', user)
- scanner_pool_obj.add_new_child('scanner-pool', self.scanner_pool)
- try:
- self.server.invoke_successfully(scanner_pool_obj, True)
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg='Error creating Vscan Scanner Pool %s: %s' %
- (self.scanner_pool, to_native(error)),
- exception=traceback.format_exc())
-
- def apply_policy(self):
- """
- Apply a Scanner policy to a Scanner pool
- :return: nothing
- """
- apply_policy_obj = netapp_utils.zapi.NaElement('vscan-scanner-pool-apply-policy')
- apply_policy_obj.add_new_child('scanner-policy', self.scanner_policy)
- apply_policy_obj.add_new_child('scanner-pool', self.scanner_pool)
- try:
- self.server.invoke_successfully(apply_policy_obj, True)
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg='Error applying policy %s to pool %s: %s' %
- (self.scanner_policy, self.scanner_pool, to_native(error)),
- exception=traceback.format_exc())
-
- def get_scanner_pool(self):
- """
- Check to see if a scanner pool exist or not
- :return: True if it exist, False if it does not
- """
- scanner_pool_obj = netapp_utils.zapi.NaElement('vscan-scanner-pool-get-iter')
- scanner_pool_info = netapp_utils.zapi.NaElement('scan-scanner-pool-info')
- scanner_pool_info.add_new_child('scanner-pool', self.scanner_pool)
- scanner_pool_info.add_new_child('vserver', self.vserver)
- query = netapp_utils.zapi.NaElement('query')
- query.add_child_elem(scanner_pool_info)
- scanner_pool_obj.add_child_elem(query)
- try:
- result = self.server.invoke_successfully(scanner_pool_obj, True)
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg='Error searching for Vscan Scanner Pool %s: %s' %
- (self.scanner_pool, to_native(error)),
- exception=traceback.format_exc())
- if result.get_child_by_name('num-records') and int(result.get_child_content('num-records')) >= 1:
- if result.get_child_by_name('attributes-list').get_child_by_name('vscan-scanner-pool-info').get_child_content(
- 'scanner-pool') == self.scanner_pool:
- return result.get_child_by_name('attributes-list').get_child_by_name('vscan-scanner-pool-info')
- return False
- return False
-
- def delete_scanner_pool(self):
- """
- Delete a Scanner pool
- :return: nothing
- """
- scanner_pool_obj = netapp_utils.zapi.NaElement('vscan-scanner-pool-delete')
- scanner_pool_obj.add_new_child('scanner-pool', self.scanner_pool)
- try:
- self.server.invoke_successfully(scanner_pool_obj, True)
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg='Error deleting Vscan Scanner Pool %s: %s' %
- (self.scanner_pool, to_native(error)),
- exception=traceback.format_exc())
-
- def asup_log_for_cserver(self, event_name):
- """
- Fetch admin vserver for the given cluster
- Create and Autosupport log event with the given module name
- :param event_name: Name of the event log
- :return: None
- """
- results = netapp_utils.get_cserver(self.server)
- cserver = netapp_utils.setup_na_ontap_zapi(module=self.module, vserver=results)
- netapp_utils.ems_log_event(event_name, cserver)
-
- def apply(self):
- self.asup_log_for_cserver("na_ontap_vscan_scanner_pool")
- changed = False
- scanner_pool_obj = self.get_scanner_pool()
- if self.state == 'present':
- if not scanner_pool_obj:
- self.create_scanner_pool()
- if self.scanner_policy:
- self.apply_policy()
- changed = True
- # apply Scanner policy
- if scanner_pool_obj:
- if self.scanner_policy:
- if scanner_pool_obj.get_child_content('scanner-policy') != self.scanner_policy:
- self.apply_policy()
- changed = True
- if self.state == 'absent':
- if scanner_pool_obj:
- self.delete_scanner_pool()
- changed = True
- self.module.exit_json(changed=changed)
-
-
-def main():
- """
- Execute action from playbook
- """
- command = NetAppOntapVscanScannerPool()
- command.apply()
-
-
-if __name__ == '__main__':
- main()
diff --git a/lib/ansible/modules/storage/netapp/na_ontap_vserver_cifs_security.py b/lib/ansible/modules/storage/netapp/na_ontap_vserver_cifs_security.py
deleted file mode 100644
index afb66d2ee2..0000000000
--- a/lib/ansible/modules/storage/netapp/na_ontap_vserver_cifs_security.py
+++ /dev/null
@@ -1,282 +0,0 @@
-#!/usr/bin/python
-
-# (c) 2018-2019, NetApp, Inc
-# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-from __future__ import absolute_import, division, print_function
-__metaclass__ = type
-
-ANSIBLE_METADATA = {'metadata_version': '1.1',
- 'status': ['preview'],
- 'supported_by': 'certified'}
-
-DOCUMENTATION = '''
----
-module: na_ontap_vserver_cifs_security
-short_description: NetApp ONTAP vserver CIFS security modification
-extends_documentation_fragment:
- - netapp.na_ontap
-version_added: '2.9'
-author: NetApp Ansible Team (@carchi8py) <ng-ansibleteam@netapp.com>
-
-description:
- - modify vserver CIFS security.
-
-options:
-
- vserver:
- description:
- - name of the vserver.
- required: true
- type: str
-
- kerberos_clock_skew:
- description:
- - The clock skew in minutes is the tolerance for accepting tickets with time stamps that do not exactly match the host's system clock.
- type: int
-
- kerberos_ticket_age:
- description:
- - Determine the maximum amount of time in hours that a user's ticket may be used for the purpose of Kerberos authentication.
- type: int
-
- kerberos_renew_age:
- description:
- - Determine the maximum amount of time in days for which a ticket can be renewed.
- type: int
-
- kerberos_kdc_timeout:
- description:
- - Determine the timeout value in seconds for KDC connections.
- type: int
-
- is_signing_required:
- description:
- - Determine whether signing is required for incoming CIFS traffic.
- type: bool
-
- is_password_complexity_required:
- description:
- - Determine whether password complexity is required for local users.
- type: bool
-
- is_aes_encryption_enabled:
- description:
- - Determine whether AES-128 and AES-256 encryption mechanisms are enabled for Kerberos-related CIFS communication.
- type: bool
-
- is_smb_encryption_required:
- description:
- - Determine whether SMB encryption is required for incoming CIFS traffic.
- type: bool
-
- lm_compatibility_level:
- description:
- - Determine the LM compatibility level.
- choices: ['lm_ntlm_ntlmv2_krb', 'ntlm_ntlmv2_krb', 'ntlmv2_krb', 'krb']
- type: str
-
- referral_enabled_for_ad_ldap:
- description:
- - Determine whether LDAP referral chasing is enabled or not for AD LDAP connections.
- type: bool
-
- session_security_for_ad_ldap:
- description:
- - Determine the level of security required for LDAP communications.
- choices: ['none', 'sign', 'seal']
- type: str
-
- smb1_enabled_for_dc_connections:
- description:
- - Determine if SMB version 1 is used for connections to domain controllers.
- choices: ['false', 'true', 'system_default']
- type: str
-
- smb2_enabled_for_dc_connections:
- description:
- - Determine if SMB version 2 is used for connections to domain controllers.
- choices: ['false', 'true', 'system_default']
- type: str
-
- use_start_tls_for_ad_ldap:
- description:
- - Determine whether to use start_tls for AD LDAP connections.
- type: bool
-
-'''
-
-EXAMPLES = '''
- - name: modify cifs security
- na_ontap_vserver_cifs_security:
- vserver: ansible
- hostname: "{{ hostname }}"
- kerberos_clock_skew: 5
- kerberos_ticket_age: 5
- kerberos_renew_age: 10
- kerberos_kdc_timeout: 5
- is_signing_required: true
- is_password_complexity_required: true
- is_aes_encryption_enabled: true
- is_smb_encryption_required: true
- lm_compatibility_level: krb
- smb1_enabled_for_dc_connections: true
- smb2_enabled_for_dc_connections: true
- use_start_tls_for_ad_ldap: true
- username: username
- password: password
-
- - name: modify cifs security
- na_ontap_vserver_cifs_security:
- vserver: ansible
- hostname: "{{ hostname }}"
- referral_enabled_for_ad_ldap: true
- username: username
- password: password
-
- - name: modify cifs security
- na_ontap_vserver_cifs_security:
- vserver: ansible
- hostname: "{{ hostname }}"
- session_security_for_ad_ldap: true
- username: username
- password: password
-'''
-
-RETURN = '''
-'''
-
-import traceback
-from ansible.module_utils.basic import AnsibleModule
-from ansible.module_utils._text import to_native
-import ansible.module_utils.netapp as netapp_utils
-from ansible.module_utils.netapp_module import NetAppModule
-
-HAS_NETAPP_LIB = netapp_utils.has_netapp_lib()
-
-
-class NetAppONTAPCifsSecurity(object):
- '''
- modify vserver cifs security
- '''
- def __init__(self):
-
- self.argument_spec = netapp_utils.na_ontap_host_argument_spec()
- self.argument_spec.update(dict(
- vserver=dict(required=True, type='str'),
- kerberos_clock_skew=dict(required=False, type='int'),
- kerberos_ticket_age=dict(required=False, type='int'),
- kerberos_renew_age=dict(required=False, type='int'),
- kerberos_kdc_timeout=dict(required=False, type='int'),
- is_signing_required=dict(required=False, type='bool'),
- is_password_complexity_required=dict(required=False, type='bool'),
- is_aes_encryption_enabled=dict(required=False, type='bool'),
- is_smb_encryption_required=dict(required=False, type='bool'),
- lm_compatibility_level=dict(required=False, choices=['lm_ntlm_ntlmv2_krb', 'ntlm_ntlmv2_krb', 'ntlmv2_krb', 'krb']),
- referral_enabled_for_ad_ldap=dict(required=False, type='bool'),
- session_security_for_ad_ldap=dict(required=False, choices=['none', 'sign', 'seal']),
- smb1_enabled_for_dc_connections=dict(required=False, choices=['false', 'true', 'system_default']),
- smb2_enabled_for_dc_connections=dict(required=False, choices=['false', 'true', 'system_default']),
- use_start_tls_for_ad_ldap=dict(required=False, type='bool')
- ))
-
- self.module = AnsibleModule(
- argument_spec=self.argument_spec,
- supports_check_mode=True
- )
-
- self.na_helper = NetAppModule()
- self.parameters = self.na_helper.set_parameters(self.module.params)
-
- if HAS_NETAPP_LIB is False:
- self.module.fail_json(msg="the python NetApp-Lib module is required")
- else:
- self.server = netapp_utils.setup_na_ontap_zapi(module=self.module, vserver=self.parameters['vserver'])
-
- def cifs_security_get_iter(self):
- """
- get current vserver cifs security.
- :return: a dict of vserver cifs security
- """
- cifs_security_get = netapp_utils.zapi.NaElement('cifs-security-get-iter')
- query = netapp_utils.zapi.NaElement('query')
- cifs_security = netapp_utils.zapi.NaElement('cifs-security')
- cifs_security.add_new_child('vserver', self.parameters['vserver'])
- query.add_child_elem(cifs_security)
- cifs_security_get.add_child_elem(query)
- cifs_security_details = dict()
- try:
- result = self.server.invoke_successfully(cifs_security_get, enable_tunneling=True)
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg='Error fetching cifs security from %s: %s'
- % (self.parameters['vserver'], to_native(error)),
- exception=traceback.format_exc())
- if result.get_child_by_name('num-records') and int(result.get_child_content('num-records')) > 0:
- cifs_security_info = result.get_child_by_name('attributes-list').get_child_by_name('cifs-security')
- cifs_security_details['kerberos_clock_skew'] = cifs_security_info.get_child_content('kerberos-clock-skew')
- cifs_security_details['kerberos_ticket_age'] = cifs_security_info.get_child_content('kerberos-ticket-age')
- cifs_security_details['kerberos_renew_age'] = cifs_security_info.get_child_content('kerberos-renew-age')
- cifs_security_details['kerberos_kdc_timeout'] = cifs_security_info.get_child_content('kerberos-kdc-timeout')
- cifs_security_details['is_signing_required'] = bool(cifs_security_info.get_child_content('is-signing-required'))
- cifs_security_details['is_password_complexity_required'] = bool(cifs_security_info.get_child_content('is-password-complexity-required'))
- cifs_security_details['is_aes_encryption_enabled'] = bool(cifs_security_info.get_child_content('is-aes-encryption-enabled'))
- cifs_security_details['is_smb_encryption_required'] = bool(cifs_security_info.get_child_content('is-smb-encryption-required'))
- cifs_security_details['lm_compatibility_level'] = cifs_security_info.get_child_content('lm-compatibility-level')
- cifs_security_details['referral_enabled_for_ad_ldap'] = bool(cifs_security_info.get_child_content('referral-enabled-for-ad-ldap'))
- cifs_security_details['session_security_for_ad_ldap'] = cifs_security_info.get_child_content('session-security-for-ad-ldap')
- cifs_security_details['smb1_enabled_for_dc_connections'] = cifs_security_info.get_child_content('smb1-enabled-for-dc-connections')
- cifs_security_details['smb2_enabled_for_dc_connections'] = cifs_security_info.get_child_content('smb2-enabled-for-dc-connections')
- cifs_security_details['use_start_tls_for_ad_ldap'] = bool(cifs_security_info.get_child_content('use-start-tls-for-ad-ldap'))
- return cifs_security_details
- return None
-
- def cifs_security_modify(self, modify):
- """
- :param modify: A list of attributes to modify
- :return: None
- """
- cifs_security_modify = netapp_utils.zapi.NaElement('cifs-security-modify')
- for attribute in modify:
- cifs_security_modify.add_new_child(self.attribute_to_name(attribute), str(self.parameters[attribute]))
- try:
- self.server.invoke_successfully(cifs_security_modify, enable_tunneling=True)
- except netapp_utils.zapi.NaApiError as e:
- self.module.fail_json(msg='Error modifying cifs security on %s: %s'
- % (self.parameters['vserver'], to_native(e)),
- exception=traceback.format_exc())
-
- @staticmethod
- def attribute_to_name(attribute):
- return str.replace(attribute, '_', '-')
-
- def apply(self):
- """Call modify operations."""
- self.asup_log_for_cserver("na_ontap_vserver_cifs_security")
- current = self.cifs_security_get_iter()
- modify = self.na_helper.get_modified_attributes(current, self.parameters)
- if self.na_helper.changed:
- if self.module.check_mode:
- pass
- else:
- if modify:
- self.cifs_security_modify(modify)
- self.module.exit_json(changed=self.na_helper.changed)
-
- def asup_log_for_cserver(self, event_name):
- """
- Fetch admin vserver for the given cluster
- Create and Autosupport log event with the given module name
- :param event_name: Name of the event log
- :return: None
- """
- results = netapp_utils.get_cserver(self.server)
- cserver = netapp_utils.setup_na_ontap_zapi(module=self.module, vserver=results)
- netapp_utils.ems_log_event(event_name, cserver)
-
-
-def main():
- obj = NetAppONTAPCifsSecurity()
- obj.apply()
-
-
-if __name__ == '__main__':
- main()
diff --git a/lib/ansible/modules/storage/netapp/na_ontap_vserver_peer.py b/lib/ansible/modules/storage/netapp/na_ontap_vserver_peer.py
deleted file mode 100644
index c0c9db3e4a..0000000000
--- a/lib/ansible/modules/storage/netapp/na_ontap_vserver_peer.py
+++ /dev/null
@@ -1,276 +0,0 @@
-#!/usr/bin/python
-
-# (c) 2018-2019, NetApp, Inc
-# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-from __future__ import absolute_import, division, print_function
-__metaclass__ = type
-
-ANSIBLE_METADATA = {'metadata_version': '1.1',
- 'status': ['preview'],
- 'supported_by': 'certified'}
-
-
-DOCUMENTATION = '''
-author: NetApp Ansible Team (@carchi8py) <ng-ansibleteam@netapp.com>
-description:
- - Create/Delete vserver peer
-extends_documentation_fragment:
- - netapp.na_ontap
-module: na_ontap_vserver_peer
-options:
- state:
- choices: ['present', 'absent']
- description:
- - Whether the specified vserver peer should exist or not.
- default: present
- vserver:
- description:
- - Specifies name of the source Vserver in the relationship.
- applications:
- choices: ['snapmirror', 'file_copy', 'lun_copy', 'flexcache']
- description:
- - List of applications which can make use of the peering relationship.
- - FlexCache supported from ONTAP 9.5 onwards.
- peer_vserver:
- description:
- - Specifies name of the peer Vserver in the relationship.
- peer_cluster:
- description:
- - Specifies name of the peer Cluster.
- - Required for creating the vserver peer relationship with a remote cluster
- dest_hostname:
- description:
- - Destination hostname or IP address.
- - Required for creating the vserver peer relationship with a remote cluster
- dest_username:
- description:
- - Destination username.
- - Optional if this is same as source username.
- dest_password:
- description:
- - Destination password.
- - Optional if this is same as source password.
-short_description: NetApp ONTAP Vserver peering
-version_added: "2.7"
-'''
-
-EXAMPLES = """
-
- - name: Source vserver peer create
- na_ontap_vserver_peer:
- state: present
- peer_vserver: ansible2
- peer_cluster: ansibleCluster
- vserver: ansible
- applications: snapmirror
- hostname: "{{ netapp_hostname }}"
- username: "{{ netapp_username }}"
- password: "{{ netapp_password }}"
- dest_hostname: "{{ netapp_dest_hostname }}"
-
- - name: vserver peer delete
- na_ontap_vserver_peer:
- state: absent
- peer_vserver: ansible2
- vserver: ansible
- hostname: "{{ netapp_hostname }}"
- username: "{{ netapp_username }}"
- password: "{{ netapp_password }}"
-"""
-
-RETURN = """
-"""
-
-import traceback
-from ansible.module_utils.basic import AnsibleModule
-from ansible.module_utils._text import to_native
-import ansible.module_utils.netapp as netapp_utils
-from ansible.module_utils.netapp_module import NetAppModule
-
-HAS_NETAPP_LIB = netapp_utils.has_netapp_lib()
-
-
-class NetAppONTAPVserverPeer(object):
- """
- Class with vserver peer methods
- """
-
- def __init__(self):
-
- self.argument_spec = netapp_utils.na_ontap_host_argument_spec()
- self.argument_spec.update(dict(
- state=dict(required=False, type='str', choices=['present', 'absent'], default='present'),
- vserver=dict(required=True, type='str'),
- peer_vserver=dict(required=True, type='str'),
- peer_cluster=dict(required=False, type='str'),
- applications=dict(required=False, type='list', choices=['snapmirror', 'file_copy', 'lun_copy', 'flexcache']),
- dest_hostname=dict(required=False, type='str'),
- dest_username=dict(required=False, type='str'),
- dest_password=dict(required=False, type='str', no_log=True)
- ))
-
- self.module = AnsibleModule(
- argument_spec=self.argument_spec,
- supports_check_mode=True
- )
-
- self.na_helper = NetAppModule()
- self.parameters = self.na_helper.set_parameters(self.module.params)
-
- if HAS_NETAPP_LIB is False:
- self.module.fail_json(msg="the python NetApp-Lib module is required")
- else:
- self.server = netapp_utils.setup_na_ontap_zapi(module=self.module)
- if self.parameters.get('dest_hostname'):
- self.module.params['hostname'] = self.parameters['dest_hostname']
- if self.parameters.get('dest_username'):
- self.module.params['username'] = self.parameters['dest_username']
- if self.parameters.get('dest_password'):
- self.module.params['password'] = self.parameters['dest_password']
- self.dest_server = netapp_utils.setup_na_ontap_zapi(module=self.module)
- # reset to source host connection for asup logs
- self.module.params['hostname'] = self.parameters['hostname']
-
- def vserver_peer_get_iter(self):
- """
- Compose NaElement object to query current vserver using peer-vserver and vserver parameters
- :return: NaElement object for vserver-get-iter with query
- """
- vserver_peer_get = netapp_utils.zapi.NaElement('vserver-peer-get-iter')
- query = netapp_utils.zapi.NaElement('query')
- vserver_peer_info = netapp_utils.zapi.NaElement('vserver-peer-info')
- vserver_peer_info.add_new_child('peer-vserver', self.parameters['peer_vserver'])
- vserver_peer_info.add_new_child('vserver', self.parameters['vserver'])
- query.add_child_elem(vserver_peer_info)
- vserver_peer_get.add_child_elem(query)
- return vserver_peer_get
-
- def vserver_peer_get(self):
- """
- Get current vserver peer info
- :return: Dictionary of current vserver peer details if query successful, else return None
- """
- vserver_peer_get_iter = self.vserver_peer_get_iter()
- vserver_info = dict()
- try:
- result = self.server.invoke_successfully(vserver_peer_get_iter, enable_tunneling=True)
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg='Error fetching vserver peer %s: %s'
- % (self.parameters['vserver'], to_native(error)),
- exception=traceback.format_exc())
- # return vserver peer details
- if result.get_child_by_name('num-records') and \
- int(result.get_child_content('num-records')) > 0:
- vserver_peer_info = result.get_child_by_name('attributes-list').get_child_by_name('vserver-peer-info')
- vserver_info['peer_vserver'] = vserver_peer_info.get_child_content('peer-vserver')
- vserver_info['vserver'] = vserver_peer_info.get_child_content('vserver')
- vserver_info['peer_state'] = vserver_peer_info.get_child_content('peer-state')
- return vserver_info
- return None
-
- def vserver_peer_delete(self):
- """
- Delete a vserver peer
- """
- vserver_peer_delete = netapp_utils.zapi.NaElement.create_node_with_children(
- 'vserver-peer-delete', **{'peer-vserver': self.parameters['peer_vserver'],
- 'vserver': self.parameters['vserver']})
- try:
- self.server.invoke_successfully(vserver_peer_delete,
- enable_tunneling=True)
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg='Error deleting vserver peer %s: %s'
- % (self.parameters['vserver'], to_native(error)),
- exception=traceback.format_exc())
-
- def get_peer_cluster_name(self):
- """
- Get local cluster name
- :return: cluster name
- """
- cluster_info = netapp_utils.zapi.NaElement('cluster-identity-get')
- try:
- result = self.server.invoke_successfully(cluster_info, enable_tunneling=True)
- return result.get_child_by_name('attributes').get_child_by_name(
- 'cluster-identity-info').get_child_content('cluster-name')
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg='Error fetching peer cluster name for peer vserver %s: %s'
- % (self.parameters['peer_vserver'], to_native(error)),
- exception=traceback.format_exc())
-
- def vserver_peer_create(self):
- """
- Create a vserver peer
- """
- if self.parameters.get('applications') is None:
- self.module.fail_json(msg='applications parameter is missing')
- if self.parameters.get('peer_cluster') is not None and self.parameters.get('dest_hostname') is None:
- self.module.fail_json(msg='dest_hostname is required for peering a vserver in remote cluster')
- if self.parameters.get('peer_cluster') is None:
- self.parameters['peer_cluster'] = self.get_peer_cluster_name()
- vserver_peer_create = netapp_utils.zapi.NaElement.create_node_with_children(
- 'vserver-peer-create', **{'peer-vserver': self.parameters['peer_vserver'],
- 'vserver': self.parameters['vserver'],
- 'peer-cluster': self.parameters['peer_cluster']})
- applications = netapp_utils.zapi.NaElement('applications')
- for application in self.parameters['applications']:
- applications.add_new_child('vserver-peer-application', application)
- vserver_peer_create.add_child_elem(applications)
- try:
- self.server.invoke_successfully(vserver_peer_create,
- enable_tunneling=True)
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg='Error creating vserver peer %s: %s'
- % (self.parameters['vserver'], to_native(error)),
- exception=traceback.format_exc())
-
- def is_remote_peer(self):
- if self.parameters.get('dest_hostname') is None or \
- (self.parameters['dest_hostname'] == self.parameters['hostname']):
- return False
- return True
-
- def vserver_peer_accept(self):
- """
- Accept a vserver peer at destination
- """
- # peer-vserver -> remote (source vserver is provided)
- # vserver -> local (destination vserver is provided)
- vserver_peer_accept = netapp_utils.zapi.NaElement.create_node_with_children(
- 'vserver-peer-accept', **{'peer-vserver': self.parameters['vserver'],
- 'vserver': self.parameters['peer_vserver']})
- try:
- self.dest_server.invoke_successfully(vserver_peer_accept, enable_tunneling=True)
- except netapp_utils.zapi.NaApiError as error:
- self.module.fail_json(msg='Error accepting vserver peer %s: %s'
- % (self.parameters['peer_vserver'], to_native(error)),
- exception=traceback.format_exc())
-
- def apply(self):
- """
- Apply action to create/delete or accept vserver peer
- """
- results = netapp_utils.get_cserver(self.server)
- cserver = netapp_utils.setup_na_ontap_zapi(module=self.module, vserver=results)
- netapp_utils.ems_log_event("na_ontap_vserver_peer", cserver)
- current = self.vserver_peer_get()
- cd_action = self.na_helper.get_cd_action(current, self.parameters)
- if cd_action == 'create':
- self.vserver_peer_create()
- # accept only if the peer relationship is on a remote cluster
- if self.is_remote_peer():
- self.vserver_peer_accept()
- elif cd_action == 'delete':
- self.vserver_peer_delete()
-
- self.module.exit_json(changed=self.na_helper.changed)
-
-
-def main():
- """Execute action"""
- community_obj = NetAppONTAPVserverPeer()
- community_obj.apply()
-
-
-if __name__ == '__main__':
- main()
diff --git a/lib/ansible/plugins/doc_fragments/netapp.py b/lib/ansible/plugins/doc_fragments/netapp.py
deleted file mode 100644
index 80d8dcb154..0000000000
--- a/lib/ansible/plugins/doc_fragments/netapp.py
+++ /dev/null
@@ -1,226 +0,0 @@
-# -*- coding: utf-8 -*-
-
-# Copyright: (c) 2018, Sumit Kumar <sumit4@netapp.com>, chris Archibald <carchi@netapp.com>
-# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-
-
-class ModuleDocFragment(object):
-
- DOCUMENTATION = r'''
-options:
- - See respective platform section for more details
-requirements:
- - See respective platform section for more details
-notes:
- - Ansible modules are available for the following NetApp Storage Platforms: E-Series, ONTAP, SolidFire
-'''
-
- # Documentation fragment for Cloud Volume Services on Azure NetApp (azure_rm_netapp)
- AZURE_RM_NETAPP = r'''
-options:
- resource_group:
- description:
- - Name of the resource group.
- required: true
- type: str
-requirements:
- - python >= 2.7
- - azure >= 2.0.0
- - Python netapp-mgmt. Install using 'pip install netapp-mgmt'
- - Python netapp-mgmt-netapp. Install using 'pip install netapp-mgmt-netapp'
- - For authentication with Azure NetApp log in before you run your tasks or playbook with C(az login).
-
-notes:
- - The modules prefixed with azure_rm_netapp are built to support the Cloud Volume Services for Azure NetApp Files.
-
-seealso:
- - name: Sign in with Azure CLI
- link: https://docs.microsoft.com/en-us/cli/azure/authenticate-azure-cli?view=azure-cli-latest
- description: How to authenticate using the C(az login) command.
- '''
-
- # Documentation fragment for ONTAP (na_ontap)
- NA_ONTAP = r'''
-options:
- hostname:
- description:
- - The hostname or IP address of the ONTAP instance.
- type: str
- required: true
- username:
- description:
- - This can be a Cluster-scoped or SVM-scoped account, depending on whether a Cluster-level or SVM-level API is required.
- For more information, please read the documentation U(https://mysupport.netapp.com/NOW/download/software/nmsdk/9.4/).
- type: str
- required: true
- aliases: [ user ]
- password:
- description:
- - Password for the specified user.
- type: str
- required: true
- aliases: [ pass ]
- https:
- description:
- - Enable and disable https
- type: bool
- default: no
- validate_certs:
- description:
- - If set to C(no), the SSL certificates will not be validated.
- - This should only set to C(False) used on personally controlled sites using self-signed certificates.
- type: bool
- default: yes
- http_port:
- description:
- - Override the default port (80 or 443) with this port
- type: int
- ontapi:
- description:
- - The ontap api version to use
- type: int
- use_rest:
- description:
- - REST API if supported by the target system for all the resources and attributes the module requires. Otherwise will revert to ZAPI.
- - Always -- will always use the REST API
- - Never -- will always use the ZAPI
- - Auto -- will try to use the REST Api
- default: Auto
- choices: ['Never', 'Always', 'Auto']
- type: str
-
-
-requirements:
- - A physical or virtual clustered Data ONTAP system. The modules support Data ONTAP 9.1 and onward
- - Ansible 2.6
- - Python2 netapp-lib (2017.10.30) or later. Install using 'pip install netapp-lib'
- - Python3 netapp-lib (2018.11.13) or later. Install using 'pip install netapp-lib'
- - To enable http on the cluster you must run the following commands 'set -privilege advanced;' 'system services web modify -http-enabled true;'
-
-notes:
- - The modules prefixed with na\\_ontap are built to support the ONTAP storage platform.
-
-'''
-
- # Documentation fragment for ONTAP (na_cdot)
- ONTAP = r'''
-options:
- hostname:
- required: true
- description:
- - The hostname or IP address of the ONTAP instance.
- username:
- required: true
- description:
- - This can be a Cluster-scoped or SVM-scoped account, depending on whether a Cluster-level or SVM-level API is required.
- For more information, please read the documentation U(https://mysupport.netapp.com/NOW/download/software/nmsdk/9.4/).
- aliases: ['user']
- password:
- required: true
- description:
- - Password for the specified user.
- aliases: ['pass']
-requirements:
- - A physical or virtual clustered Data ONTAP system. The modules were developed with Clustered Data ONTAP 8.3
- - Ansible 2.2
- - netapp-lib (2015.9.25). Install using 'pip install netapp-lib'
-
-notes:
- - The modules prefixed with na\\_cdot are built to support the ONTAP storage platform.
-
-'''
-
- # Documentation fragment for SolidFire
- SOLIDFIRE = r'''
-options:
- hostname:
- required: true
- description:
- - The hostname or IP address of the SolidFire cluster.
- username:
- required: true
- description:
- - Please ensure that the user has the adequate permissions. For more information, please read the official documentation
- U(https://mysupport.netapp.com/documentation/docweb/index.html?productID=62636&language=en-US).
- aliases: ['user']
- password:
- required: true
- description:
- - Password for the specified user.
- aliases: ['pass']
-
-requirements:
- - The modules were developed with SolidFire 10.1
- - solidfire-sdk-python (1.1.0.92) or greater. Install using 'pip install solidfire-sdk-python'
-
-notes:
- - The modules prefixed with na\\_elementsw are built to support the SolidFire storage platform.
-
-'''
-
- # Documentation fragment for E-Series
- ESERIES = r'''
-options:
- api_username:
- required: true
- type: str
- description:
- - The username to authenticate with the SANtricity Web Services Proxy or Embedded Web Services API.
- api_password:
- required: true
- type: str
- description:
- - The password to authenticate with the SANtricity Web Services Proxy or Embedded Web Services API.
- api_url:
- required: true
- type: str
- description:
- - The url to the SANtricity Web Services Proxy or Embedded Web Services API.
- Example https://prod-1.wahoo.acme.com/devmgr/v2
- validate_certs:
- required: false
- default: true
- description:
- - Should https certificates be validated?
- type: bool
- ssid:
- required: false
- type: str
- default: 1
- description:
- - The ID of the array to manage. This value must be unique for each array.
-
-notes:
- - The E-Series Ansible modules require either an instance of the Web Services Proxy (WSP), to be available to manage
- the storage-system, or an E-Series storage-system that supports the Embedded Web Services API.
- - Embedded Web Services is currently available on the E2800, E5700, EF570, and newer hardware models.
- - M(netapp_e_storage_system) may be utilized for configuring the systems managed by a WSP instance.
-'''
-
- # Documentation fragment for AWSCVS
- AWSCVS = """
-options:
- api_key:
- required: true
- type: str
- description:
- - The access key to authenticate with the AWSCVS Web Services Proxy or Embedded Web Services API.
- secret_key:
- required: true
- type: str
- description:
- - The secret_key to authenticate with the AWSCVS Web Services Proxy or Embedded Web Services API.
- api_url:
- required: true
- type: str
- description:
- - The url to the AWSCVS Web Services Proxy or Embedded Web Services API.
- validate_certs:
- required: false
- default: true
- description:
- - Should https certificates be validated?
- type: bool
-notes:
- - The modules prefixed with aws\\_cvs\\_netapp are built to Manage AWS Cloud Volume Service .
-"""
diff --git a/test/sanity/ignore.txt b/test/sanity/ignore.txt
index 553372c58d..6566eba00f 100644
--- a/test/sanity/ignore.txt
+++ b/test/sanity/ignore.txt
@@ -86,12 +86,6 @@ lib/ansible/module_utils/gcp_utils.py future-import-boilerplate
lib/ansible/module_utils/gcp_utils.py metaclass-boilerplate
lib/ansible/module_utils/json_utils.py future-import-boilerplate
lib/ansible/module_utils/json_utils.py metaclass-boilerplate
-lib/ansible/module_utils/netapp.py future-import-boilerplate
-lib/ansible/module_utils/netapp.py metaclass-boilerplate
-lib/ansible/module_utils/netapp_elementsw_module.py future-import-boilerplate
-lib/ansible/module_utils/netapp_elementsw_module.py metaclass-boilerplate
-lib/ansible/module_utils/netapp_module.py future-import-boilerplate
-lib/ansible/module_utils/netapp_module.py metaclass-boilerplate
lib/ansible/module_utils/network/asa/asa.py future-import-boilerplate
lib/ansible/module_utils/network/asa/asa.py metaclass-boilerplate
lib/ansible/module_utils/network/checkpoint/checkpoint.py metaclass-boilerplate
@@ -3324,160 +3318,6 @@ lib/ansible/modules/source_control/git.py validate-modules:parameter-type-not-in
lib/ansible/modules/source_control/subversion.py validate-modules:doc-required-mismatch
lib/ansible/modules/source_control/subversion.py validate-modules:parameter-type-not-in-doc
lib/ansible/modules/source_control/subversion.py validate-modules:undocumented-parameter
-lib/ansible/modules/storage/netapp/na_ontap_aggregate.py validate-modules:doc-missing-type
-lib/ansible/modules/storage/netapp/na_ontap_aggregate.py validate-modules:parameter-list-no-elements
-lib/ansible/modules/storage/netapp/na_ontap_aggregate.py validate-modules:parameter-type-not-in-doc
-lib/ansible/modules/storage/netapp/na_ontap_autosupport.py validate-modules:doc-missing-type
-lib/ansible/modules/storage/netapp/na_ontap_autosupport.py validate-modules:parameter-list-no-elements
-lib/ansible/modules/storage/netapp/na_ontap_autosupport.py validate-modules:parameter-type-not-in-doc
-lib/ansible/modules/storage/netapp/na_ontap_broadcast_domain.py validate-modules:doc-missing-type
-lib/ansible/modules/storage/netapp/na_ontap_broadcast_domain.py validate-modules:parameter-list-no-elements
-lib/ansible/modules/storage/netapp/na_ontap_broadcast_domain.py validate-modules:parameter-type-not-in-doc
-lib/ansible/modules/storage/netapp/na_ontap_broadcast_domain_ports.py validate-modules:doc-missing-type
-lib/ansible/modules/storage/netapp/na_ontap_broadcast_domain_ports.py validate-modules:doc-required-mismatch
-lib/ansible/modules/storage/netapp/na_ontap_broadcast_domain_ports.py validate-modules:parameter-list-no-elements
-lib/ansible/modules/storage/netapp/na_ontap_broadcast_domain_ports.py validate-modules:parameter-type-not-in-doc
-lib/ansible/modules/storage/netapp/na_ontap_cg_snapshot.py validate-modules:doc-missing-type
-lib/ansible/modules/storage/netapp/na_ontap_cg_snapshot.py validate-modules:parameter-list-no-elements
-lib/ansible/modules/storage/netapp/na_ontap_cg_snapshot.py validate-modules:parameter-type-not-in-doc
-lib/ansible/modules/storage/netapp/na_ontap_cifs.py validate-modules:parameter-list-no-elements
-lib/ansible/modules/storage/netapp/na_ontap_cifs.py validate-modules:parameter-type-not-in-doc
-lib/ansible/modules/storage/netapp/na_ontap_cifs_acl.py validate-modules:parameter-type-not-in-doc
-lib/ansible/modules/storage/netapp/na_ontap_cifs_server.py validate-modules:doc-missing-type
-lib/ansible/modules/storage/netapp/na_ontap_cifs_server.py validate-modules:parameter-type-not-in-doc
-lib/ansible/modules/storage/netapp/na_ontap_cluster.py validate-modules:doc-missing-type
-lib/ansible/modules/storage/netapp/na_ontap_cluster.py validate-modules:parameter-type-not-in-doc
-lib/ansible/modules/storage/netapp/na_ontap_cluster_ha.py validate-modules:doc-missing-type
-lib/ansible/modules/storage/netapp/na_ontap_cluster_peer.py validate-modules:parameter-list-no-elements
-lib/ansible/modules/storage/netapp/na_ontap_cluster_peer.py validate-modules:parameter-type-not-in-doc
-lib/ansible/modules/storage/netapp/na_ontap_command.py validate-modules:parameter-list-no-elements
-lib/ansible/modules/storage/netapp/na_ontap_command.py validate-modules:parameter-type-not-in-doc
-lib/ansible/modules/storage/netapp/na_ontap_disks.py validate-modules:parameter-type-not-in-doc
-lib/ansible/modules/storage/netapp/na_ontap_dns.py validate-modules:doc-missing-type
-lib/ansible/modules/storage/netapp/na_ontap_dns.py validate-modules:parameter-list-no-elements
-lib/ansible/modules/storage/netapp/na_ontap_dns.py validate-modules:parameter-type-not-in-doc
-lib/ansible/modules/storage/netapp/na_ontap_export_policy.py validate-modules:parameter-type-not-in-doc
-lib/ansible/modules/storage/netapp/na_ontap_export_policy_rule.py validate-modules:parameter-list-no-elements
-lib/ansible/modules/storage/netapp/na_ontap_export_policy_rule.py validate-modules:parameter-type-not-in-doc
-lib/ansible/modules/storage/netapp/na_ontap_fcp.py validate-modules:doc-missing-type
-lib/ansible/modules/storage/netapp/na_ontap_fcp.py validate-modules:parameter-type-not-in-doc
-lib/ansible/modules/storage/netapp/na_ontap_firewall_policy.py validate-modules:doc-missing-type
-lib/ansible/modules/storage/netapp/na_ontap_firewall_policy.py validate-modules:parameter-list-no-elements
-lib/ansible/modules/storage/netapp/na_ontap_firewall_policy.py validate-modules:parameter-type-not-in-doc
-lib/ansible/modules/storage/netapp/na_ontap_firmware_upgrade.py validate-modules:doc-required-mismatch
-lib/ansible/modules/storage/netapp/na_ontap_flexcache.py validate-modules:parameter-list-no-elements
-lib/ansible/modules/storage/netapp/na_ontap_flexcache.py validate-modules:parameter-type-not-in-doc
-lib/ansible/modules/storage/netapp/na_ontap_igroup.py validate-modules:doc-missing-type
-lib/ansible/modules/storage/netapp/na_ontap_igroup.py validate-modules:parameter-list-no-elements
-lib/ansible/modules/storage/netapp/na_ontap_igroup.py validate-modules:parameter-type-not-in-doc
-lib/ansible/modules/storage/netapp/na_ontap_igroup_initiator.py validate-modules:doc-missing-type
-lib/ansible/modules/storage/netapp/na_ontap_igroup_initiator.py validate-modules:parameter-list-no-elements
-lib/ansible/modules/storage/netapp/na_ontap_igroup_initiator.py validate-modules:parameter-type-not-in-doc
-lib/ansible/modules/storage/netapp/na_ontap_info.py validate-modules:parameter-list-no-elements
-lib/ansible/modules/storage/netapp/na_ontap_info.py validate-modules:parameter-state-invalid-choice
-lib/ansible/modules/storage/netapp/na_ontap_interface.py validate-modules:doc-missing-type
-lib/ansible/modules/storage/netapp/na_ontap_interface.py validate-modules:parameter-list-no-elements
-lib/ansible/modules/storage/netapp/na_ontap_interface.py validate-modules:parameter-type-not-in-doc
-lib/ansible/modules/storage/netapp/na_ontap_ipspace.py validate-modules:doc-missing-type
-lib/ansible/modules/storage/netapp/na_ontap_ipspace.py validate-modules:parameter-type-not-in-doc
-lib/ansible/modules/storage/netapp/na_ontap_iscsi.py validate-modules:doc-missing-type
-lib/ansible/modules/storage/netapp/na_ontap_iscsi.py validate-modules:parameter-type-not-in-doc
-lib/ansible/modules/storage/netapp/na_ontap_job_schedule.py validate-modules:doc-missing-type
-lib/ansible/modules/storage/netapp/na_ontap_job_schedule.py validate-modules:parameter-list-no-elements
-lib/ansible/modules/storage/netapp/na_ontap_job_schedule.py validate-modules:parameter-type-not-in-doc
-lib/ansible/modules/storage/netapp/na_ontap_kerberos_realm.py validate-modules:invalid-ansiblemodule-schema
-lib/ansible/modules/storage/netapp/na_ontap_ldap_client.py validate-modules:invalid-ansiblemodule-schema
-lib/ansible/modules/storage/netapp/na_ontap_ldap_client.py validate-modules:parameter-list-no-elements
-lib/ansible/modules/storage/netapp/na_ontap_license.py validate-modules:doc-missing-type
-lib/ansible/modules/storage/netapp/na_ontap_license.py validate-modules:parameter-list-no-elements
-lib/ansible/modules/storage/netapp/na_ontap_license.py validate-modules:parameter-type-not-in-doc
-lib/ansible/modules/storage/netapp/na_ontap_lun.py validate-modules:doc-missing-type
-lib/ansible/modules/storage/netapp/na_ontap_lun.py validate-modules:parameter-type-not-in-doc
-lib/ansible/modules/storage/netapp/na_ontap_lun_copy.py validate-modules:doc-missing-type
-lib/ansible/modules/storage/netapp/na_ontap_lun_copy.py validate-modules:parameter-type-not-in-doc
-lib/ansible/modules/storage/netapp/na_ontap_lun_map.py validate-modules:doc-missing-type
-lib/ansible/modules/storage/netapp/na_ontap_lun_map.py validate-modules:parameter-type-not-in-doc
-lib/ansible/modules/storage/netapp/na_ontap_motd.py validate-modules:doc-missing-type
-lib/ansible/modules/storage/netapp/na_ontap_motd.py validate-modules:invalid-argument-name
-lib/ansible/modules/storage/netapp/na_ontap_motd.py validate-modules:nonexistent-parameter-documented
-lib/ansible/modules/storage/netapp/na_ontap_ndmp.py validate-modules:parameter-list-no-elements
-lib/ansible/modules/storage/netapp/na_ontap_net_ifgrp.py validate-modules:doc-missing-type
-lib/ansible/modules/storage/netapp/na_ontap_net_ifgrp.py validate-modules:parameter-list-no-elements
-lib/ansible/modules/storage/netapp/na_ontap_net_ifgrp.py validate-modules:parameter-type-not-in-doc
-lib/ansible/modules/storage/netapp/na_ontap_net_port.py validate-modules:doc-missing-type
-lib/ansible/modules/storage/netapp/na_ontap_net_port.py validate-modules:parameter-list-no-elements
-lib/ansible/modules/storage/netapp/na_ontap_net_port.py validate-modules:parameter-type-not-in-doc
-lib/ansible/modules/storage/netapp/na_ontap_net_routes.py validate-modules:doc-missing-type
-lib/ansible/modules/storage/netapp/na_ontap_net_routes.py validate-modules:parameter-type-not-in-doc
-lib/ansible/modules/storage/netapp/na_ontap_net_subnet.py validate-modules:doc-missing-type
-lib/ansible/modules/storage/netapp/na_ontap_net_subnet.py validate-modules:doc-required-mismatch
-lib/ansible/modules/storage/netapp/na_ontap_net_subnet.py validate-modules:parameter-type-not-in-doc
-lib/ansible/modules/storage/netapp/na_ontap_net_vlan.py validate-modules:doc-missing-type
-lib/ansible/modules/storage/netapp/na_ontap_net_vlan.py validate-modules:parameter-type-not-in-doc
-lib/ansible/modules/storage/netapp/na_ontap_nfs.py validate-modules:doc-missing-type
-lib/ansible/modules/storage/netapp/na_ontap_nfs.py validate-modules:parameter-invalid
-lib/ansible/modules/storage/netapp/na_ontap_nfs.py validate-modules:parameter-type-not-in-doc
-lib/ansible/modules/storage/netapp/na_ontap_node.py validate-modules:parameter-type-not-in-doc
-lib/ansible/modules/storage/netapp/na_ontap_ntp.py validate-modules:doc-missing-type
-lib/ansible/modules/storage/netapp/na_ontap_ntp.py validate-modules:parameter-type-not-in-doc
-lib/ansible/modules/storage/netapp/na_ontap_nvme.py validate-modules:parameter-type-not-in-doc
-lib/ansible/modules/storage/netapp/na_ontap_nvme_namespace.py validate-modules:doc-required-mismatch
-lib/ansible/modules/storage/netapp/na_ontap_nvme_namespace.py validate-modules:parameter-type-not-in-doc
-lib/ansible/modules/storage/netapp/na_ontap_nvme_subsystem.py validate-modules:parameter-list-no-elements
-lib/ansible/modules/storage/netapp/na_ontap_nvme_subsystem.py validate-modules:parameter-type-not-in-doc
-lib/ansible/modules/storage/netapp/na_ontap_ports.py validate-modules:parameter-list-no-elements
-lib/ansible/modules/storage/netapp/na_ontap_portset.py validate-modules:doc-missing-type
-lib/ansible/modules/storage/netapp/na_ontap_portset.py validate-modules:parameter-list-no-elements
-lib/ansible/modules/storage/netapp/na_ontap_portset.py validate-modules:parameter-type-not-in-doc
-lib/ansible/modules/storage/netapp/na_ontap_qos_policy_group.py validate-modules:doc-required-mismatch
-lib/ansible/modules/storage/netapp/na_ontap_qos_policy_group.py validate-modules:parameter-type-not-in-doc
-lib/ansible/modules/storage/netapp/na_ontap_qtree.py validate-modules:doc-missing-type
-lib/ansible/modules/storage/netapp/na_ontap_qtree.py validate-modules:doc-required-mismatch
-lib/ansible/modules/storage/netapp/na_ontap_security_key_manager.py validate-modules:doc-missing-type
-lib/ansible/modules/storage/netapp/na_ontap_security_key_manager.py validate-modules:parameter-type-not-in-doc
-lib/ansible/modules/storage/netapp/na_ontap_service_processor_network.py validate-modules:doc-missing-type
-lib/ansible/modules/storage/netapp/na_ontap_service_processor_network.py validate-modules:parameter-type-not-in-doc
-lib/ansible/modules/storage/netapp/na_ontap_snapmirror.py validate-modules:parameter-type-not-in-doc
-lib/ansible/modules/storage/netapp/na_ontap_snapshot.py validate-modules:doc-missing-type
-lib/ansible/modules/storage/netapp/na_ontap_snapshot.py validate-modules:parameter-type-not-in-doc
-lib/ansible/modules/storage/netapp/na_ontap_snapshot_policy.py validate-modules:doc-elements-mismatch
-lib/ansible/modules/storage/netapp/na_ontap_snapshot_policy.py validate-modules:doc-missing-type
-lib/ansible/modules/storage/netapp/na_ontap_snapshot_policy.py validate-modules:parameter-type-not-in-doc
-lib/ansible/modules/storage/netapp/na_ontap_snmp.py validate-modules:parameter-type-not-in-doc
-lib/ansible/modules/storage/netapp/na_ontap_software_update.py validate-modules:parameter-list-no-elements
-lib/ansible/modules/storage/netapp/na_ontap_software_update.py validate-modules:parameter-type-not-in-doc
-lib/ansible/modules/storage/netapp/na_ontap_svm.py validate-modules:doc-missing-type
-lib/ansible/modules/storage/netapp/na_ontap_svm.py validate-modules:parameter-list-no-elements
-lib/ansible/modules/storage/netapp/na_ontap_svm.py validate-modules:parameter-type-not-in-doc
-lib/ansible/modules/storage/netapp/na_ontap_svm_options.py validate-modules:parameter-type-not-in-doc
-lib/ansible/modules/storage/netapp/na_ontap_ucadapter.py validate-modules:doc-missing-type
-lib/ansible/modules/storage/netapp/na_ontap_ucadapter.py validate-modules:parameter-type-not-in-doc
-lib/ansible/modules/storage/netapp/na_ontap_unix_group.py validate-modules:doc-missing-type
-lib/ansible/modules/storage/netapp/na_ontap_unix_group.py validate-modules:parameter-list-no-elements
-lib/ansible/modules/storage/netapp/na_ontap_unix_group.py validate-modules:parameter-type-not-in-doc
-lib/ansible/modules/storage/netapp/na_ontap_unix_user.py validate-modules:doc-missing-type
-lib/ansible/modules/storage/netapp/na_ontap_unix_user.py validate-modules:parameter-type-not-in-doc
-lib/ansible/modules/storage/netapp/na_ontap_user.py validate-modules:doc-missing-type
-lib/ansible/modules/storage/netapp/na_ontap_user.py validate-modules:parameter-list-no-elements
-lib/ansible/modules/storage/netapp/na_ontap_user.py validate-modules:parameter-type-not-in-doc
-lib/ansible/modules/storage/netapp/na_ontap_user_role.py validate-modules:doc-missing-type
-lib/ansible/modules/storage/netapp/na_ontap_user_role.py validate-modules:parameter-type-not-in-doc
-lib/ansible/modules/storage/netapp/na_ontap_volume.py validate-modules:doc-missing-type
-lib/ansible/modules/storage/netapp/na_ontap_volume.py validate-modules:parameter-list-no-elements
-lib/ansible/modules/storage/netapp/na_ontap_volume.py validate-modules:parameter-type-not-in-doc
-lib/ansible/modules/storage/netapp/na_ontap_volume_clone.py validate-modules:doc-missing-type
-lib/ansible/modules/storage/netapp/na_ontap_vscan_on_access_policy.py validate-modules:doc-missing-type
-lib/ansible/modules/storage/netapp/na_ontap_vscan_on_access_policy.py validate-modules:parameter-list-no-elements
-lib/ansible/modules/storage/netapp/na_ontap_vscan_on_access_policy.py validate-modules:parameter-type-not-in-doc
-lib/ansible/modules/storage/netapp/na_ontap_vscan_on_demand_task.py validate-modules:doc-missing-type
-lib/ansible/modules/storage/netapp/na_ontap_vscan_on_demand_task.py validate-modules:parameter-list-no-elements
-lib/ansible/modules/storage/netapp/na_ontap_vscan_on_demand_task.py validate-modules:parameter-type-not-in-doc
-lib/ansible/modules/storage/netapp/na_ontap_vscan_scanner_pool.py validate-modules:doc-missing-type
-lib/ansible/modules/storage/netapp/na_ontap_vscan_scanner_pool.py validate-modules:parameter-list-no-elements
-lib/ansible/modules/storage/netapp/na_ontap_vscan_scanner_pool.py validate-modules:parameter-type-not-in-doc
-lib/ansible/modules/storage/netapp/na_ontap_vserver_peer.py validate-modules:doc-required-mismatch
-lib/ansible/modules/storage/netapp/na_ontap_vserver_peer.py validate-modules:parameter-list-no-elements
-lib/ansible/modules/storage/netapp/na_ontap_vserver_peer.py validate-modules:parameter-type-not-in-doc
lib/ansible/modules/storage/purestorage/purefa_alert.py validate-modules:doc-required-mismatch
lib/ansible/modules/storage/purestorage/purefa_arrayname.py validate-modules:doc-required-mismatch
lib/ansible/modules/storage/purestorage/purefa_banner.py validate-modules:doc-required-mismatch
@@ -3655,8 +3495,6 @@ lib/ansible/plugins/doc_fragments/junos.py future-import-boilerplate
lib/ansible/plugins/doc_fragments/junos.py metaclass-boilerplate
lib/ansible/plugins/doc_fragments/meraki.py future-import-boilerplate
lib/ansible/plugins/doc_fragments/meraki.py metaclass-boilerplate
-lib/ansible/plugins/doc_fragments/netapp.py future-import-boilerplate
-lib/ansible/plugins/doc_fragments/netapp.py metaclass-boilerplate
lib/ansible/plugins/doc_fragments/nxos.py future-import-boilerplate
lib/ansible/plugins/doc_fragments/nxos.py metaclass-boilerplate
lib/ansible/plugins/doc_fragments/openstack.py future-import-boilerplate
@@ -3948,104 +3786,6 @@ test/units/modules/packaging/os/test_yum.py future-import-boilerplate
test/units/modules/packaging/os/test_yum.py metaclass-boilerplate
test/units/modules/remote_management/oneview/conftest.py future-import-boilerplate
test/units/modules/remote_management/oneview/conftest.py metaclass-boilerplate
-test/units/modules/storage/netapp/test_na_ontap_aggregate.py future-import-boilerplate
-test/units/modules/storage/netapp/test_na_ontap_aggregate.py metaclass-boilerplate
-test/units/modules/storage/netapp/test_na_ontap_autosupport.py future-import-boilerplate
-test/units/modules/storage/netapp/test_na_ontap_autosupport.py metaclass-boilerplate
-test/units/modules/storage/netapp/test_na_ontap_broadcast_domain.py future-import-boilerplate
-test/units/modules/storage/netapp/test_na_ontap_broadcast_domain.py metaclass-boilerplate
-test/units/modules/storage/netapp/test_na_ontap_cifs.py future-import-boilerplate
-test/units/modules/storage/netapp/test_na_ontap_cifs.py metaclass-boilerplate
-test/units/modules/storage/netapp/test_na_ontap_cifs_server.py future-import-boilerplate
-test/units/modules/storage/netapp/test_na_ontap_cifs_server.py metaclass-boilerplate
-test/units/modules/storage/netapp/test_na_ontap_cluster.py future-import-boilerplate
-test/units/modules/storage/netapp/test_na_ontap_cluster.py metaclass-boilerplate
-test/units/modules/storage/netapp/test_na_ontap_cluster_peer.py future-import-boilerplate
-test/units/modules/storage/netapp/test_na_ontap_cluster_peer.py metaclass-boilerplate
-test/units/modules/storage/netapp/test_na_ontap_command.py future-import-boilerplate
-test/units/modules/storage/netapp/test_na_ontap_command.py metaclass-boilerplate
-test/units/modules/storage/netapp/test_na_ontap_export_policy_rule.py future-import-boilerplate
-test/units/modules/storage/netapp/test_na_ontap_export_policy_rule.py metaclass-boilerplate
-test/units/modules/storage/netapp/test_na_ontap_firewall_policy.py future-import-boilerplate
-test/units/modules/storage/netapp/test_na_ontap_firewall_policy.py metaclass-boilerplate
-test/units/modules/storage/netapp/test_na_ontap_flexcache.py future-import-boilerplate
-test/units/modules/storage/netapp/test_na_ontap_flexcache.py metaclass-boilerplate
-test/units/modules/storage/netapp/test_na_ontap_igroup.py future-import-boilerplate
-test/units/modules/storage/netapp/test_na_ontap_igroup.py metaclass-boilerplate
-test/units/modules/storage/netapp/test_na_ontap_igroup_initiator.py future-import-boilerplate
-test/units/modules/storage/netapp/test_na_ontap_igroup_initiator.py metaclass-boilerplate
-test/units/modules/storage/netapp/test_na_ontap_info.py future-import-boilerplate
-test/units/modules/storage/netapp/test_na_ontap_info.py metaclass-boilerplate
-test/units/modules/storage/netapp/test_na_ontap_interface.py future-import-boilerplate
-test/units/modules/storage/netapp/test_na_ontap_interface.py metaclass-boilerplate
-test/units/modules/storage/netapp/test_na_ontap_ipspace.py future-import-boilerplate
-test/units/modules/storage/netapp/test_na_ontap_ipspace.py metaclass-boilerplate
-test/units/modules/storage/netapp/test_na_ontap_job_schedule.py future-import-boilerplate
-test/units/modules/storage/netapp/test_na_ontap_job_schedule.py metaclass-boilerplate
-test/units/modules/storage/netapp/test_na_ontap_lun_copy.py future-import-boilerplate
-test/units/modules/storage/netapp/test_na_ontap_lun_copy.py metaclass-boilerplate
-test/units/modules/storage/netapp/test_na_ontap_lun_map.py future-import-boilerplate
-test/units/modules/storage/netapp/test_na_ontap_lun_map.py metaclass-boilerplate
-test/units/modules/storage/netapp/test_na_ontap_motd.py future-import-boilerplate
-test/units/modules/storage/netapp/test_na_ontap_motd.py metaclass-boilerplate
-test/units/modules/storage/netapp/test_na_ontap_net_ifgrp.py future-import-boilerplate
-test/units/modules/storage/netapp/test_na_ontap_net_ifgrp.py metaclass-boilerplate
-test/units/modules/storage/netapp/test_na_ontap_net_port.py future-import-boilerplate
-test/units/modules/storage/netapp/test_na_ontap_net_port.py metaclass-boilerplate
-test/units/modules/storage/netapp/test_na_ontap_net_routes.py future-import-boilerplate
-test/units/modules/storage/netapp/test_na_ontap_net_routes.py metaclass-boilerplate
-test/units/modules/storage/netapp/test_na_ontap_net_subnet.py future-import-boilerplate
-test/units/modules/storage/netapp/test_na_ontap_net_subnet.py metaclass-boilerplate
-test/units/modules/storage/netapp/test_na_ontap_nfs.py future-import-boilerplate
-test/units/modules/storage/netapp/test_na_ontap_nfs.py metaclass-boilerplate
-test/units/modules/storage/netapp/test_na_ontap_nvme.py future-import-boilerplate
-test/units/modules/storage/netapp/test_na_ontap_nvme.py metaclass-boilerplate
-test/units/modules/storage/netapp/test_na_ontap_nvme_namespace.py future-import-boilerplate
-test/units/modules/storage/netapp/test_na_ontap_nvme_namespace.py metaclass-boilerplate
-test/units/modules/storage/netapp/test_na_ontap_nvme_subsystem.py future-import-boilerplate
-test/units/modules/storage/netapp/test_na_ontap_nvme_subsystem.py metaclass-boilerplate
-test/units/modules/storage/netapp/test_na_ontap_portset.py future-import-boilerplate
-test/units/modules/storage/netapp/test_na_ontap_portset.py metaclass-boilerplate
-test/units/modules/storage/netapp/test_na_ontap_qos_policy_group.py future-import-boilerplate
-test/units/modules/storage/netapp/test_na_ontap_qos_policy_group.py metaclass-boilerplate
-test/units/modules/storage/netapp/test_na_ontap_quotas.py future-import-boilerplate
-test/units/modules/storage/netapp/test_na_ontap_quotas.py metaclass-boilerplate
-test/units/modules/storage/netapp/test_na_ontap_security_key_manager.py future-import-boilerplate
-test/units/modules/storage/netapp/test_na_ontap_security_key_manager.py metaclass-boilerplate
-test/units/modules/storage/netapp/test_na_ontap_service_processor_network.py future-import-boilerplate
-test/units/modules/storage/netapp/test_na_ontap_service_processor_network.py metaclass-boilerplate
-test/units/modules/storage/netapp/test_na_ontap_snapmirror.py future-import-boilerplate
-test/units/modules/storage/netapp/test_na_ontap_snapmirror.py metaclass-boilerplate
-test/units/modules/storage/netapp/test_na_ontap_snapshot.py future-import-boilerplate
-test/units/modules/storage/netapp/test_na_ontap_snapshot.py metaclass-boilerplate
-test/units/modules/storage/netapp/test_na_ontap_snapshot_policy.py future-import-boilerplate
-test/units/modules/storage/netapp/test_na_ontap_snapshot_policy.py metaclass-boilerplate
-test/units/modules/storage/netapp/test_na_ontap_software_update.py future-import-boilerplate
-test/units/modules/storage/netapp/test_na_ontap_software_update.py metaclass-boilerplate
-test/units/modules/storage/netapp/test_na_ontap_svm.py future-import-boilerplate
-test/units/modules/storage/netapp/test_na_ontap_svm.py metaclass-boilerplate
-test/units/modules/storage/netapp/test_na_ontap_ucadapter.py future-import-boilerplate
-test/units/modules/storage/netapp/test_na_ontap_ucadapter.py metaclass-boilerplate
-test/units/modules/storage/netapp/test_na_ontap_unix_group.py future-import-boilerplate
-test/units/modules/storage/netapp/test_na_ontap_unix_group.py metaclass-boilerplate
-test/units/modules/storage/netapp/test_na_ontap_unix_user.py future-import-boilerplate
-test/units/modules/storage/netapp/test_na_ontap_unix_user.py metaclass-boilerplate
-test/units/modules/storage/netapp/test_na_ontap_user.py future-import-boilerplate
-test/units/modules/storage/netapp/test_na_ontap_user.py metaclass-boilerplate
-test/units/modules/storage/netapp/test_na_ontap_user_role.py future-import-boilerplate
-test/units/modules/storage/netapp/test_na_ontap_user_role.py metaclass-boilerplate
-test/units/modules/storage/netapp/test_na_ontap_volume.py future-import-boilerplate
-test/units/modules/storage/netapp/test_na_ontap_volume.py metaclass-boilerplate
-test/units/modules/storage/netapp/test_na_ontap_volume_clone.py future-import-boilerplate
-test/units/modules/storage/netapp/test_na_ontap_volume_clone.py metaclass-boilerplate
-test/units/modules/storage/netapp/test_na_ontap_vscan.py future-import-boilerplate
-test/units/modules/storage/netapp/test_na_ontap_vscan.py metaclass-boilerplate
-test/units/modules/storage/netapp/test_na_ontap_vscan_on_access_policy.py future-import-boilerplate
-test/units/modules/storage/netapp/test_na_ontap_vscan_on_access_policy.py metaclass-boilerplate
-test/units/modules/storage/netapp/test_na_ontap_vscan_on_demand_task.py future-import-boilerplate
-test/units/modules/storage/netapp/test_na_ontap_vscan_on_demand_task.py metaclass-boilerplate
-test/units/modules/storage/netapp/test_na_ontap_vscan_scanner_pool.py future-import-boilerplate
-test/units/modules/storage/netapp/test_na_ontap_vscan_scanner_pool.py metaclass-boilerplate
test/units/modules/system/test_iptables.py future-import-boilerplate
test/units/modules/system/test_iptables.py metaclass-boilerplate
test/units/modules/system/test_known_hosts.py future-import-boilerplate
diff --git a/test/units/module_utils/test_netapp.py b/test/units/module_utils/test_netapp.py
deleted file mode 100644
index 828eaf96b4..0000000000
--- a/test/units/module_utils/test_netapp.py
+++ /dev/null
@@ -1,163 +0,0 @@
-# (c) 2018, NetApp Inc.
-# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-
-
-from __future__ import absolute_import, division, print_function
-__metaclass__ = type
-
-import pytest
-
-from ansible.module_utils.ansible_release import __version__ as ansible_version
-
-import ansible.module_utils.netapp as netapp_utils
-
-try:
- from unittest.mock import patch, mock_open
-except ImportError:
- from mock import patch, mock_open
-
-from ansible.module_utils.six.moves.urllib.error import URLError
-from ansible.module_utils.netapp import NetAppESeriesModule, create_multipart_formdata
-from units.modules.utils import AnsibleExitJson, AnsibleFailJson, ModuleTestCase, set_module_args
-
-
-class StubNetAppESeriesModule(NetAppESeriesModule):
- def __init__(self):
- super(StubNetAppESeriesModule, self).__init__(ansible_options={})
-
-
-class NetappTest(ModuleTestCase):
- REQUIRED_PARAMS = {"api_username": "rw",
- "api_password": "password",
- "api_url": "http://localhost",
- "ssid": "1"}
- REQ_FUNC = "ansible.module_utils.netapp.request"
-
- def _set_args(self, args=None):
- module_args = self.REQUIRED_PARAMS.copy()
- if args is not None:
- module_args.update(args)
- set_module_args(module_args)
-
- def test_is_embedded_embedded_pass(self):
- """Verify is_embedded successfully returns True when an embedded web service's rest api is inquired."""
- self._set_args()
- with patch(self.REQ_FUNC, side_effect=[(200, {"version": "03.10.9000.0009"}),
- (200, {"runningAsProxy": False})]):
- base = StubNetAppESeriesModule()
- self.assertTrue(base.is_embedded())
- with patch(self.REQ_FUNC, side_effect=[(200, {"version": "03.10.9000.0009"}),
- (200, {"runningAsProxy": True})]):
- base = StubNetAppESeriesModule()
- self.assertFalse(base.is_embedded())
-
- def test_is_embedded_fail(self):
- """Verify exception is thrown when a web service's rest api fails to return about information."""
- self._set_args()
- with patch(self.REQ_FUNC, side_effect=[(200, {"version": "03.10.9000.0009"}), Exception()]):
- with self.assertRaisesRegexp(AnsibleFailJson, r"Failed to retrieve the webservices about information!"):
- base = StubNetAppESeriesModule()
- base.is_embedded()
- with patch(self.REQ_FUNC, side_effect=[(200, {"version": "03.10.9000.0009"}), URLError(""), Exception()]):
- with self.assertRaisesRegexp(AnsibleFailJson, r"Failed to retrieve the webservices about information!"):
- base = StubNetAppESeriesModule()
- base.is_embedded()
-
- def test_check_web_services_version_fail(self):
- """Verify that an unacceptable rest api version fails."""
- minimum_required = "02.10.9000.0010"
- test_set = ["02.10.9000.0009", "02.09.9000.0010", "01.10.9000.0010"]
-
- self._set_args()
- base = StubNetAppESeriesModule()
- base.web_services_version = minimum_required
- base.is_embedded = lambda: True
- for current_version in test_set:
- with patch(self.REQ_FUNC, return_value=(200, {"version": current_version})):
- with self.assertRaisesRegexp(AnsibleFailJson, r"version does not meet minimum version required."):
- base._check_web_services_version()
-
- def test_check_web_services_version_pass(self):
- """Verify that an unacceptable rest api version fails."""
- minimum_required = "02.10.9000.0010"
- test_set = ["02.10.9000.0009", "02.09.9000.0010", "01.10.9000.0010"]
-
- self._set_args()
- base = StubNetAppESeriesModule()
- base.web_services_version = minimum_required
- base.is_embedded = lambda: True
- for current_version in test_set:
- with patch(self.REQ_FUNC, return_value=(200, {"version": current_version})):
- with self.assertRaisesRegexp(AnsibleFailJson, r"version does not meet minimum version required."):
- base._check_web_services_version()
-
- def test_check_check_web_services_version_fail(self):
- """Verify exception is thrown when api url is invalid."""
- invalid_url_forms = ["localhost:8080/devmgr/v2",
- "http:///devmgr/v2"]
-
- invalid_url_protocols = ["ssh://localhost:8080/devmgr/v2"]
-
- for url in invalid_url_forms:
- self._set_args({"api_url": url})
- with patch(self.REQ_FUNC, return_value=(200, {"runningAsProxy": True})):
- with self.assertRaisesRegexp(AnsibleFailJson, r"Failed to provide valid API URL."):
- base = StubNetAppESeriesModule()
- base._check_web_services_version()
-
- for url in invalid_url_protocols:
- self._set_args({"api_url": url})
- with patch(self.REQ_FUNC, return_value=(200, {"runningAsProxy": True})):
- with self.assertRaisesRegexp(AnsibleFailJson, r"Protocol must be http or https."):
- base = StubNetAppESeriesModule()
- base._check_web_services_version()
-
-
-class MockONTAPConnection(object):
- ''' mock a server connection to ONTAP host '''
-
- def __init__(self, kind=None, parm1=None):
- ''' save arguments '''
- self.type = kind
- self.parm1 = parm1
- self.xml_in = None
- self.xml_out = None
-
- def invoke_successfully(self, xml, enable_tunneling): # pylint: disable=unused-argument
- ''' mock invoke_successfully returning xml data '''
- self.xml_in = xml
- if self.type == 'vserver':
- xml = self.build_vserver_info(self.parm1)
- self.xml_out = xml
- return xml
-
- @staticmethod
- def build_vserver_info(vserver):
- ''' build xml data for vserser-info '''
- xml = netapp_utils.zapi.NaElement('xml')
- attributes = netapp_utils.zapi.NaElement('attributes-list')
- attributes.add_node_with_children('vserver-info',
- **{'vserver-name': vserver})
- xml.add_child_elem(attributes)
- return xml
-
-
-@pytest.mark.skipif(not netapp_utils.has_netapp_lib(), reason="skipping as missing required netapp_lib")
-def test_ems_log_event_version():
- ''' validate Ansible version is correctly read '''
- source = 'unittest'
- server = MockONTAPConnection()
- netapp_utils.ems_log_event(source, server)
- xml = server.xml_in
- version = xml.get_child_content('app-version')
- assert version == ansible_version
- print("Ansible version: %s" % ansible_version)
-
-
-@pytest.mark.skipif(not netapp_utils.has_netapp_lib(), reason="skipping as missing required netapp_lib")
-def test_get_cserver():
- ''' validate cluster vserser name is correctly retrieved '''
- svm_name = 'svm1'
- server = MockONTAPConnection('vserver', svm_name)
- cserver = netapp_utils.get_cserver(server)
- assert cserver == svm_name
diff --git a/test/units/modules/storage/netapp/test_na_ontap_aggregate.py b/test/units/modules/storage/netapp/test_na_ontap_aggregate.py
deleted file mode 100644
index e9766af468..0000000000
--- a/test/units/modules/storage/netapp/test_na_ontap_aggregate.py
+++ /dev/null
@@ -1,217 +0,0 @@
-# (c) 2018-2019, NetApp, Inc
-# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-
-""" unit tests for Ansible module: na_ontap_aggregate """
-
-from __future__ import print_function
-import json
-import pytest
-
-from units.compat import unittest
-from units.compat.mock import patch, Mock
-from ansible.module_utils import basic
-from ansible.module_utils._text import to_bytes
-import ansible.module_utils.netapp as netapp_utils
-
-from ansible.modules.storage.netapp.na_ontap_aggregate \
- import NetAppOntapAggregate as my_module # module under test
-
-if not netapp_utils.has_netapp_lib():
- pytestmark = pytest.mark.skip('skipping as missing required netapp_lib')
-
-
-def set_module_args(args):
- """prepare arguments so that they will be picked up during module creation"""
- args = json.dumps({'ANSIBLE_MODULE_ARGS': args})
- basic._ANSIBLE_ARGS = to_bytes(args) # pylint: disable=protected-access
-
-
-class AnsibleExitJson(Exception):
- """Exception class to be raised by module.exit_json and caught by the test case"""
- pass
-
-
-class AnsibleFailJson(Exception):
- """Exception class to be raised by module.fail_json and caught by the test case"""
- pass
-
-
-def exit_json(*args, **kwargs): # pylint: disable=unused-argument
- """function to patch over exit_json; package return data into an exception"""
- if 'changed' not in kwargs:
- kwargs['changed'] = False
- raise AnsibleExitJson(kwargs)
-
-
-def fail_json(*args, **kwargs): # pylint: disable=unused-argument
- """function to patch over fail_json; package return data into an exception"""
- kwargs['failed'] = True
- raise AnsibleFailJson(kwargs)
-
-
-class MockONTAPConnection(object):
- ''' mock server connection to ONTAP host '''
-
- def __init__(self, kind=None, parm1=None, parm2=None):
- ''' save arguments '''
- self.type = kind
- self.parm1 = parm1
- self.parm2 = parm2
- self.xml_in = None
- self.xml_out = None
-
- def invoke_successfully(self, xml, enable_tunneling): # pylint: disable=unused-argument
- ''' mock invoke_successfully returning xml data '''
- self.xml_in = xml
- if self.type == 'aggregate':
- xml = self.build_aggregate_info(self.parm1, self.parm2)
- elif self.type == 'aggregate_fail':
- raise netapp_utils.zapi.NaApiError(code='TEST', message="This exception is from the unit test")
- self.xml_out = xml
- return xml
-
- @staticmethod
- def build_aggregate_info(vserver, aggregate):
- ''' build xml data for aggregatte and vserser-info '''
- xml = netapp_utils.zapi.NaElement('xml')
- data = {'num-records': 2,
- 'attributes-list':
- {'aggr-attributes':
- {'aggregate-name': aggregate,
- 'aggr-raid-attributes':
- {'state': 'offline'
- }
- },
- },
- 'vserver-info':
- {'vserver-name': vserver
- }
- }
- xml.translate_struct(data)
- print(xml.to_string())
- return xml
-
-
-class TestMyModule(unittest.TestCase):
- ''' a group of related Unit Tests '''
-
- def setUp(self):
- self.mock_module_helper = patch.multiple(basic.AnsibleModule,
- exit_json=exit_json,
- fail_json=fail_json)
- self.mock_module_helper.start()
- self.addCleanup(self.mock_module_helper.stop)
- self.server = MockONTAPConnection('aggregate', '12', 'name')
- # whether to use a mock or a simulator
- self.onbox = False
-
- def set_default_args(self):
- if self.onbox:
- hostname = '10.193.74.78'
- username = 'admin'
- password = 'netapp1!'
- name = 'name'
- else:
- hostname = 'hostname'
- username = 'username'
- password = 'password'
- name = 'name'
- return dict({
- 'hostname': hostname,
- 'username': username,
- 'password': password,
- 'name': name
- })
-
- def call_command(self, module_args):
- ''' utility function to call apply '''
- module_args.update(self.set_default_args())
- set_module_args(module_args)
- my_obj = my_module()
- my_obj.asup_log_for_cserver = Mock(return_value=None)
- if not self.onbox:
- # mock the connection
- my_obj.server = MockONTAPConnection('aggregate', '12', 'test_name')
- with pytest.raises(AnsibleExitJson) as exc:
- my_obj.apply()
- return exc.value.args[0]['changed']
-
- def test_module_fail_when_required_args_missing(self):
- ''' required arguments are reported as errors '''
- with pytest.raises(AnsibleFailJson) as exc:
- set_module_args({})
- my_module()
- print('Info: %s' % exc.value.args[0]['msg'])
-
- def test_is_mirrored(self):
- module_args = {
- 'disk_count': '2',
- 'is_mirrored': 'true',
- }
- changed = self.call_command(module_args)
- assert not changed
-
- def test_disks_list(self):
- module_args = {
- 'disk_count': '2',
- 'disks': ['1', '2'],
- }
- changed = self.call_command(module_args)
- assert not changed
-
- def test_mirror_disks(self):
- module_args = {
- 'disk_count': '2',
- 'disks': ['1', '2'],
- 'mirror_disks': ['3', '4']
- }
- changed = self.call_command(module_args)
- assert not changed
-
- def test_spare_pool(self):
- module_args = {
- 'disk_count': '2',
- 'spare_pool': 'Pool1'
- }
- changed = self.call_command(module_args)
- assert not changed
-
- def test_rename(self):
- module_args = {
- 'from_name': 'test_name2'
- }
- changed = self.call_command(module_args)
- assert not changed
-
- def test_if_all_methods_catch_exception(self):
- module_args = {}
- module_args.update(self.set_default_args())
- module_args.update({'service_state': 'online'})
- module_args.update({'unmount_volumes': 'True'})
- module_args.update({'from_name': 'test_name2'})
- set_module_args(module_args)
- my_obj = my_module()
- if not self.onbox:
- my_obj.server = MockONTAPConnection('aggregate_fail')
- with pytest.raises(AnsibleFailJson) as exc:
- my_obj.aggr_get_iter(module_args.get('name'))
- assert '' in exc.value.args[0]['msg']
- with pytest.raises(AnsibleFailJson) as exc:
- my_obj.aggregate_online()
- assert 'Error changing the state of aggregate' in exc.value.args[0]['msg']
- with pytest.raises(AnsibleFailJson) as exc:
- my_obj.aggregate_offline()
- assert 'Error changing the state of aggregate' in exc.value.args[0]['msg']
- with pytest.raises(AnsibleFailJson) as exc:
- my_obj.create_aggr()
- assert 'Error provisioning aggregate' in exc.value.args[0]['msg']
- with pytest.raises(AnsibleFailJson) as exc:
- my_obj.delete_aggr()
- assert 'Error removing aggregate' in exc.value.args[0]['msg']
- with pytest.raises(AnsibleFailJson) as exc:
- my_obj.rename_aggregate()
- assert 'Error renaming aggregate' in exc.value.args[0]['msg']
- with pytest.raises(AnsibleFailJson) as exc:
- my_obj.asup_log_for_cserver = Mock(return_value=None)
- my_obj.apply()
- assert 'Error renaming: aggregate test_name2 does not exist' in exc.value.args[0]['msg']
diff --git a/test/units/modules/storage/netapp/test_na_ontap_autosupport.py b/test/units/modules/storage/netapp/test_na_ontap_autosupport.py
deleted file mode 100644
index 6f10f78f80..0000000000
--- a/test/units/modules/storage/netapp/test_na_ontap_autosupport.py
+++ /dev/null
@@ -1,244 +0,0 @@
-# (c) 2018, NetApp, Inc
-# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-
-''' unit test template for ONTAP Ansible module '''
-
-from __future__ import print_function
-import json
-import pytest
-
-from units.compat import unittest
-from units.compat.mock import patch, Mock
-from ansible.module_utils import basic
-from ansible.module_utils._text import to_bytes
-import ansible.module_utils.netapp as netapp_utils
-
-from ansible.modules.storage.netapp.na_ontap_autosupport \
- import NetAppONTAPasup as asup_module # module under test
-
-if not netapp_utils.has_netapp_lib():
- pytestmark = pytest.mark.skip('skipping as missing required netapp_lib')
-
-
-def set_module_args(args):
- """prepare arguments so that they will be picked up during module creation"""
- args = json.dumps({'ANSIBLE_MODULE_ARGS': args})
- basic._ANSIBLE_ARGS = to_bytes(args) # pylint: disable=protected-access
-
-
-class AnsibleExitJson(Exception):
- """Exception class to be raised by module.exit_json and caught by the test case"""
- pass
-
-
-class AnsibleFailJson(Exception):
- """Exception class to be raised by module.fail_json and caught by the test case"""
- pass
-
-
-def exit_json(*args, **kwargs): # pylint: disable=unused-argument
- """function to patch over exit_json; package return data into an exception"""
- if 'changed' not in kwargs:
- kwargs['changed'] = False
- raise AnsibleExitJson(kwargs)
-
-
-def fail_json(*args, **kwargs): # pylint: disable=unused-argument
- """function to patch over fail_json; package return data into an exception"""
- kwargs['failed'] = True
- raise AnsibleFailJson(kwargs)
-
-
-class MockONTAPConnection(object):
- ''' mock server connection to ONTAP host '''
-
- def __init__(self, kind=None, data=None):
- ''' save arguments '''
- self.kind = kind
- self.params = data
- self.xml_in = None
- self.xml_out = None
-
- def invoke_successfully(self, xml, enable_tunneling): # pylint: disable=unused-argument
- ''' mock invoke_successfully returning xml data '''
- self.xml_in = xml
- if self.kind == 'asup':
- xml = self.build_asup_config_info(self.params)
- self.xml_out = xml
- return xml
-
- @staticmethod
- def build_asup_config_info(asup_data):
- ''' build xml data for asup-config '''
- xml = netapp_utils.zapi.NaElement('xml')
- attributes = {'attributes': {'autosupport-config-info': {
- 'node-name': asup_data['node_name'],
- 'is-enabled': asup_data['is_enabled'],
- 'is-support-enabled': asup_data['support'],
- 'proxy-url': asup_data['proxy_url'],
- 'post-url': asup_data['post_url'],
- 'transport': asup_data['transport'],
- 'is-node-in-subject': 'false',
- 'from': 'test',
- 'mail-hosts': [{'string': '1.2.3.4'}, {'string': '4.5.6.8'}],
- 'noteto': [{'mail-address': 'abc@test.com'},
- {'mail-address': 'def@test.com'}],
- }}}
- xml.translate_struct(attributes)
- return xml
-
-
-class TestMyModule(unittest.TestCase):
- ''' a group of related Unit Tests '''
-
- def setUp(self):
- self.mock_module_helper = patch.multiple(basic.AnsibleModule,
- exit_json=exit_json,
- fail_json=fail_json)
- self.mock_module_helper.start()
- self.addCleanup(self.mock_module_helper.stop)
- self.server = MockONTAPConnection()
- self.mock_asup = {
- 'node_name': 'test-vsim1',
- 'transport': 'https',
- 'support': 'false',
- 'post_url': 'testbed.netapp.com/asupprod/post/1.0/postAsup',
- 'proxy_url': 'something.com',
- }
-
- def mock_args(self):
- return {
- 'node_name': self.mock_asup['node_name'],
- 'transport': self.mock_asup['transport'],
- 'support': self.mock_asup['support'],
- 'post_url': self.mock_asup['post_url'],
- 'proxy_url': self.mock_asup['proxy_url'],
- 'hostname': 'host',
- 'username': 'admin',
- 'password': 'password',
- }
-
- def get_asup_mock_object(self, kind=None, enabled='false'):
- """
- Helper method to return an na_ontap_volume object
- :param kind: passes this param to MockONTAPConnection()
- :return: na_ontap_volume object
- """
- asup_obj = asup_module()
- asup_obj.autosupport_log = Mock(return_value=None)
- if kind is None:
- asup_obj.server = MockONTAPConnection()
- else:
- data = self.mock_asup
- data['is_enabled'] = enabled
- asup_obj.server = MockONTAPConnection(kind='asup', data=data)
- return asup_obj
-
- def test_module_fail_when_required_args_missing(self):
- ''' required arguments are reported as errors '''
- with pytest.raises(AnsibleFailJson) as exc:
- set_module_args({})
- asup_module()
- print('Info: %s' % exc.value.args[0]['msg'])
-
- def test_enable_asup(self):
- ''' a more interesting test '''
- data = self.mock_args()
- set_module_args(data)
- with pytest.raises(AnsibleExitJson) as exc:
- self.get_asup_mock_object('asup').apply()
- assert exc.value.args[0]['changed']
-
- def test_disable_asup(self):
- ''' a more interesting test '''
- # enable asup
- data = self.mock_args()
- data['state'] = 'absent'
- set_module_args(data)
- with pytest.raises(AnsibleExitJson) as exc:
- self.get_asup_mock_object(kind='asup', enabled='true').apply()
- assert exc.value.args[0]['changed']
-
- def test_result_from_get(self):
- ''' Check boolean and service_state conversion from get '''
- data = self.mock_args()
- set_module_args(data)
- obj = self.get_asup_mock_object(kind='asup', enabled='true')
- # constructed based on valued passed in self.mock_asup and build_asup_config_info()
- expected_dict = {
- 'node_name': 'test-vsim1',
- 'service_state': 'started',
- 'support': False,
- 'hostname_in_subject': False,
- 'transport': self.mock_asup['transport'],
- 'post_url': self.mock_asup['post_url'],
- 'proxy_url': self.mock_asup['proxy_url'],
- 'from_address': 'test',
- 'mail_hosts': ['1.2.3.4', '4.5.6.8'],
- 'partner_addresses': [],
- 'to_addresses': [],
- 'noteto': ['abc@test.com', 'def@test.com']
- }
- result = obj.get_autosupport_config()
- assert result == expected_dict
-
- def test_modify_config(self):
- ''' Check boolean and service_state conversion from get '''
- data = self.mock_args()
- data['transport'] = 'http'
- data['post_url'] = 'somethingelse.com'
- data['proxy_url'] = 'somethingelse.com'
- set_module_args(data)
- with pytest.raises(AnsibleExitJson) as exc:
- self.get_asup_mock_object('asup').apply()
- assert exc.value.args[0]['changed']
-
- @patch('ansible.modules.storage.netapp.na_ontap_autosupport.NetAppONTAPasup.get_autosupport_config')
- def test_get_called(self, get_asup):
- data = self.mock_args()
- set_module_args(data)
- with pytest.raises(AnsibleExitJson) as exc:
- self.get_asup_mock_object('asup').apply()
- get_asup.assert_called_with()
-
- @patch('ansible.modules.storage.netapp.na_ontap_autosupport.NetAppONTAPasup.modify_autosupport_config')
- def test_modify_called(self, modify_asup):
- data = self.mock_args()
- data['transport'] = 'http'
- set_module_args(data)
- with pytest.raises(AnsibleExitJson) as exc:
- self.get_asup_mock_object('asup').apply()
- modify_asup.assert_called_with({'transport': 'http', 'service_state': 'started'})
-
- @patch('ansible.modules.storage.netapp.na_ontap_autosupport.NetAppONTAPasup.modify_autosupport_config')
- @patch('ansible.modules.storage.netapp.na_ontap_autosupport.NetAppONTAPasup.get_autosupport_config')
- def test_modify_not_called(self, get_asup, modify_asup):
- data = self.mock_args()
- set_module_args(data)
- with pytest.raises(AnsibleExitJson) as exc:
- self.get_asup_mock_object('asup').apply()
- get_asup.assert_called_with()
- modify_asup.assert_not_called()
-
- def test_modify_packet(self):
- '''check XML construction for nested attributes like mail-hosts, noteto, partner-address, and to'''
- data = self.mock_args()
- set_module_args(data)
- obj = self.get_asup_mock_object(kind='asup', enabled='true')
- modify_dict = {
- 'noteto': ['one@test.com'],
- 'partner_addresses': ['firstpartner@test.com'],
- 'mail_hosts': ['1.1.1.1'],
- 'to_addresses': ['first@test.com']
- }
- obj.modify_autosupport_config(modify_dict)
- xml = obj.server.xml_in
- for key in ['noteto', 'to', 'partner-address']:
- assert xml[key] is not None
- assert xml[key]['mail-address'] is not None
- assert xml['noteto']['mail-address'] == modify_dict['noteto'][0]
- assert xml['to']['mail-address'] == modify_dict['to_addresses'][0]
- assert xml['partner-address']['mail-address'] == modify_dict['partner_addresses'][0]
- assert xml['mail-hosts'] is not None
- assert xml['mail-hosts']['string'] is not None
- assert xml['mail-hosts']['string'] == modify_dict['mail_hosts'][0]
diff --git a/test/units/modules/storage/netapp/test_na_ontap_broadcast_domain.py b/test/units/modules/storage/netapp/test_na_ontap_broadcast_domain.py
deleted file mode 100644
index 98c13bea65..0000000000
--- a/test/units/modules/storage/netapp/test_na_ontap_broadcast_domain.py
+++ /dev/null
@@ -1,308 +0,0 @@
-# (c) 2018, NetApp, Inc
-# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-
-''' unit test template for ONTAP Ansible module '''
-
-from __future__ import print_function
-import json
-import pytest
-
-from units.compat import unittest
-from units.compat.mock import patch, Mock
-from ansible.module_utils import basic
-from ansible.module_utils._text import to_bytes
-import ansible.module_utils.netapp as netapp_utils
-
-from ansible.modules.storage.netapp.na_ontap_broadcast_domain \
- import NetAppOntapBroadcastDomain as broadcast_domain_module # module under test
-
-if not netapp_utils.has_netapp_lib():
- pytestmark = pytest.mark.skip('skipping as missing required netapp_lib')
-
-
-def set_module_args(args):
- """prepare arguments so that they will be picked up during module creation"""
- args = json.dumps({'ANSIBLE_MODULE_ARGS': args})
- basic._ANSIBLE_ARGS = to_bytes(args) # pylint: disable=protected-access
-
-
-class AnsibleExitJson(Exception):
- """Exception class to be raised by module.exit_json and caught by the test case"""
- pass
-
-
-class AnsibleFailJson(Exception):
- """Exception class to be raised by module.fail_json and caught by the test case"""
- pass
-
-
-def exit_json(*args, **kwargs): # pylint: disable=unused-argument
- """function to patch over exit_json; package return data into an exception"""
- if 'changed' not in kwargs:
- kwargs['changed'] = False
- raise AnsibleExitJson(kwargs)
-
-
-def fail_json(*args, **kwargs): # pylint: disable=unused-argument
- """function to patch over fail_json; package return data into an exception"""
- kwargs['failed'] = True
- raise AnsibleFailJson(kwargs)
-
-
-class MockONTAPConnection(object):
- ''' mock server connection to ONTAP host '''
-
- def __init__(self, kind=None, data=None):
- ''' save arguments '''
- self.type = kind
- self.params = data
- self.xml_in = None
- self.xml_out = None
-
- def invoke_successfully(self, xml, enable_tunneling): # pylint: disable=unused-argument
- ''' mock invoke_successfully returning xml data '''
- self.xml_in = xml
- if self.type == 'broadcast_domain':
- xml = self.build_broadcast_domain_info(self.params)
- self.xml_out = xml
- return xml
-
- @staticmethod
- def build_broadcast_domain_info(broadcast_domain_details):
- ''' build xml data for broadcast_domain info '''
- xml = netapp_utils.zapi.NaElement('xml')
- attributes = {
- 'num-records': 1,
- 'attributes-list': {
- 'net-port-broadcast-domain-info': {
- 'broadcast-domain': broadcast_domain_details['name'],
- 'ipspace': broadcast_domain_details['ipspace'],
- 'mtu': broadcast_domain_details['mtu'],
- 'ports': {
- 'port-info': {
- 'port': 'test_port_1'
- }
- }
- }
-
- }
- }
- xml.translate_struct(attributes)
- return xml
-
-
-class TestMyModule(unittest.TestCase):
- ''' a group of related Unit Tests '''
-
- def setUp(self):
- self.mock_module_helper = patch.multiple(basic.AnsibleModule,
- exit_json=exit_json,
- fail_json=fail_json)
- self.mock_module_helper.start()
- self.addCleanup(self.mock_module_helper.stop)
- self.server = MockONTAPConnection()
- self.mock_broadcast_domain = {
- 'name': 'test_broadcast_domain',
- 'mtu': '1000',
- 'ipspace': 'Default',
- 'ports': 'test_port_1'
- }
-
- def mock_args(self):
- return {
- 'name': self.mock_broadcast_domain['name'],
- 'ipspace': self.mock_broadcast_domain['ipspace'],
- 'mtu': self.mock_broadcast_domain['mtu'],
- 'ports': self.mock_broadcast_domain['ports'],
- 'hostname': 'test',
- 'username': 'test_user',
- 'password': 'test_pass!'
- }
-
- def get_broadcast_domain_mock_object(self, kind=None, data=None):
- """
- Helper method to return an na_ontap_volume object
- :param kind: passes this param to MockONTAPConnection()
- :param data: passes this param to MockONTAPConnection()
- :return: na_ontap_volume object
- """
- broadcast_domain_obj = broadcast_domain_module()
- broadcast_domain_obj.asup_log_for_cserver = Mock(return_value=None)
- broadcast_domain_obj.cluster = Mock()
- broadcast_domain_obj.cluster.invoke_successfully = Mock()
- if kind is None:
- broadcast_domain_obj.server = MockONTAPConnection()
- else:
- if data is None:
- broadcast_domain_obj.server = MockONTAPConnection(kind='broadcast_domain', data=self.mock_broadcast_domain)
- else:
- broadcast_domain_obj.server = MockONTAPConnection(kind='broadcast_domain', data=data)
- return broadcast_domain_obj
-
- def test_module_fail_when_required_args_missing(self):
- ''' required arguments are reported as errors '''
- with pytest.raises(AnsibleFailJson) as exc:
- set_module_args({})
- broadcast_domain_module()
- print('Info: %s' % exc.value.args[0]['msg'])
-
- def test_get_nonexistent_net_route(self):
- ''' Test if get_broadcast_domain returns None for non-existent broadcast_domain '''
- set_module_args(self.mock_args())
- result = self.get_broadcast_domain_mock_object().get_broadcast_domain()
- assert result is None
-
- def test_create_error_missing_broadcast_domain(self):
- ''' Test if create throws an error if broadcast_domain is not specified'''
- data = self.mock_args()
- del data['name']
- set_module_args(data)
- with pytest.raises(AnsibleFailJson) as exc:
- self.get_broadcast_domain_mock_object('broadcast_domain').create_broadcast_domain()
- msg = 'missing required arguments: name'
- assert exc.value.args[0]['msg'] == msg
-
- @patch('ansible.modules.storage.netapp.na_ontap_broadcast_domain.NetAppOntapBroadcastDomain.create_broadcast_domain')
- def test_successful_create(self, create_broadcast_domain):
- ''' Test successful create '''
- data = self.mock_args()
- set_module_args(data)
- with pytest.raises(AnsibleExitJson) as exc:
- self.get_broadcast_domain_mock_object().apply()
- assert exc.value.args[0]['changed']
- create_broadcast_domain.assert_called_with()
-
- def test_create_idempotency(self):
- ''' Test create idempotency '''
- set_module_args(self.mock_args())
- obj = self.get_broadcast_domain_mock_object('broadcast_domain')
- with pytest.raises(AnsibleExitJson) as exc:
- obj.apply()
- assert not exc.value.args[0]['changed']
-
- def test_modify_mtu(self):
- ''' Test successful modify mtu '''
- data = self.mock_args()
- data['mtu'] = '1200'
- set_module_args(data)
- with pytest.raises(AnsibleExitJson) as exc:
- self.get_broadcast_domain_mock_object('broadcast_domain').apply()
- assert exc.value.args[0]['changed']
-
- def test_modify_ipspace_idempotency(self):
- ''' Test modify ipsapce idempotency'''
- data = self.mock_args()
- data['ipspace'] = 'Cluster'
- set_module_args(data)
- with pytest.raises(AnsibleFailJson) as exc:
- self.get_broadcast_domain_mock_object('broadcast_domain').apply()
- msg = 'A domain ipspace can not be modified after the domain has been created.'
- assert exc.value.args[0]['msg'] == msg
-
- @patch('ansible.modules.storage.netapp.na_ontap_broadcast_domain.NetAppOntapBroadcastDomain.add_broadcast_domain_ports')
- def test_add_ports(self, add_broadcast_domain_ports):
- ''' Test successful modify ports '''
- data = self.mock_args()
- data['ports'] = 'test_port_1,test_port_2'
- set_module_args(data)
- with pytest.raises(AnsibleExitJson) as exc:
- self.get_broadcast_domain_mock_object('broadcast_domain').apply()
- assert exc.value.args[0]['changed']
- add_broadcast_domain_ports.assert_called_with(['test_port_2'])
-
- @patch('ansible.modules.storage.netapp.na_ontap_broadcast_domain.NetAppOntapBroadcastDomain.delete_broadcast_domain_ports')
- def test_delete_ports(self, delete_broadcast_domain_ports):
- ''' Test successful modify ports '''
- data = self.mock_args()
- data['ports'] = ''
- set_module_args(data)
- with pytest.raises(AnsibleExitJson) as exc:
- self.get_broadcast_domain_mock_object('broadcast_domain').apply()
- assert exc.value.args[0]['changed']
- delete_broadcast_domain_ports.assert_called_with(['test_port_1'])
-
- @patch('ansible.modules.storage.netapp.na_ontap_broadcast_domain.NetAppOntapBroadcastDomain.modify_broadcast_domain')
- @patch('ansible.modules.storage.netapp.na_ontap_broadcast_domain.NetAppOntapBroadcastDomain.split_broadcast_domain')
- @patch('ansible.modules.storage.netapp.na_ontap_broadcast_domain.NetAppOntapBroadcastDomain.get_broadcast_domain')
- def test_split_broadcast_domain(self, get_broadcast_domain, split_broadcast_domain, modify_broadcast_domain):
- ''' Test successful split broadcast domain '''
- data = self.mock_args()
- data['from_name'] = 'test_broadcast_domain'
- data['name'] = 'test_broadcast_domain_2'
- data['ports'] = 'test_port_2'
- set_module_args(data)
- current = {
- 'name': 'test_broadcast_domain',
- 'mtu': '1000',
- 'ipspace': 'Default',
- 'ports': ['test_port_1,test_port2']
- }
- get_broadcast_domain.side_effect = [
- None,
- current,
- current
- ]
- with pytest.raises(AnsibleExitJson) as exc:
- self.get_broadcast_domain_mock_object().apply()
- assert exc.value.args[0]['changed']
- modify_broadcast_domain.assert_not_called()
- split_broadcast_domain.assert_called_with()
-
- @patch('ansible.modules.storage.netapp.na_ontap_broadcast_domain.NetAppOntapBroadcastDomain.delete_broadcast_domain')
- @patch('ansible.modules.storage.netapp.na_ontap_broadcast_domain.NetAppOntapBroadcastDomain.modify_broadcast_domain')
- @patch('ansible.modules.storage.netapp.na_ontap_broadcast_domain.NetAppOntapBroadcastDomain.get_broadcast_domain')
- def test_split_broadcast_domain_modify_delete(self, get_broadcast_domain, modify_broadcast_domain, delete_broadcast_domain):
- ''' Test successful split broadcast domain '''
- data = self.mock_args()
- data['from_name'] = 'test_broadcast_domain'
- data['name'] = 'test_broadcast_domain_2'
- data['ports'] = 'test_port_1,test_port_2'
- data['mtu'] = '1200'
- set_module_args(data)
-
- current = {
- 'name': 'test_broadcast_domain',
- 'mtu': '1000',
- 'ipspace': 'Default',
- 'ports': ['test_port_1,test_port2']
- }
- get_broadcast_domain.side_effect = [
- None,
- current,
- current
- ]
- with pytest.raises(AnsibleExitJson) as exc:
- self.get_broadcast_domain_mock_object().apply()
- assert exc.value.args[0]['changed']
- delete_broadcast_domain.assert_called_with('test_broadcast_domain')
- modify_broadcast_domain.assert_called_with()
-
- @patch('ansible.modules.storage.netapp.na_ontap_broadcast_domain.NetAppOntapBroadcastDomain.get_broadcast_domain')
- def test_split_broadcast_domain_not_exist(self, get_broadcast_domain):
- ''' Test successful split broadcast domain '''
- data = self.mock_args()
- data['from_name'] = 'test_broadcast_domain'
- data['name'] = 'test_broadcast_domain_2'
- data['ports'] = 'test_port_2'
- set_module_args(data)
-
- get_broadcast_domain.side_effect = [
- None,
- None,
- ]
- with pytest.raises(AnsibleFailJson) as exc:
- self.get_broadcast_domain_mock_object().apply()
- msg = 'A domain can not be split if it does not exist.'
- assert exc.value.args[0]['msg'], msg
-
- @patch('ansible.modules.storage.netapp.na_ontap_broadcast_domain.NetAppOntapBroadcastDomain.split_broadcast_domain')
- def test_split_broadcast_domain_idempotency(self, split_broadcast_domain):
- ''' Test successful split broadcast domain '''
- data = self.mock_args()
- data['from_name'] = 'test_broadcast_domain'
- data['ports'] = 'test_port_1'
- set_module_args(data)
- with pytest.raises(AnsibleExitJson) as exc:
- self.get_broadcast_domain_mock_object('broadcast_domain').apply()
- assert exc.value.args[0]['changed'] is False
- split_broadcast_domain.assert_not_called()
diff --git a/test/units/modules/storage/netapp/test_na_ontap_cifs.py b/test/units/modules/storage/netapp/test_na_ontap_cifs.py
deleted file mode 100644
index 95d56a6635..0000000000
--- a/test/units/modules/storage/netapp/test_na_ontap_cifs.py
+++ /dev/null
@@ -1,227 +0,0 @@
-# (c) 2018, NetApp, Inc
-# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-
-''' unit tests ONTAP Ansible module: na_ontap_cifs '''
-
-from __future__ import print_function
-import json
-import pytest
-
-from units.compat import unittest
-from units.compat.mock import patch
-from ansible.module_utils import basic
-from ansible.module_utils._text import to_bytes
-import ansible.module_utils.netapp as netapp_utils
-
-from ansible.modules.storage.netapp.na_ontap_cifs \
- import NetAppONTAPCifsShare as my_module # module under test
-
-if not netapp_utils.has_netapp_lib():
- pytestmark = pytest.mark.skip('skipping as missing required netapp_lib')
-
-
-def set_module_args(args):
- """prepare arguments so that they will be picked up during module creation"""
- args = json.dumps({'ANSIBLE_MODULE_ARGS': args})
- basic._ANSIBLE_ARGS = to_bytes(args) # pylint: disable=protected-access
-
-
-class AnsibleExitJson(Exception):
- """Exception class to be raised by module.exit_json and caught by the test case"""
- pass
-
-
-class AnsibleFailJson(Exception):
- """Exception class to be raised by module.fail_json and caught by the test case"""
- pass
-
-
-def exit_json(*args, **kwargs): # pylint: disable=unused-argument
- """function to patch over exit_json; package return data into an exception"""
- if 'changed' not in kwargs:
- kwargs['changed'] = False
- raise AnsibleExitJson(kwargs)
-
-
-def fail_json(*args, **kwargs): # pylint: disable=unused-argument
- """function to patch over fail_json; package return data into an exception"""
- kwargs['failed'] = True
- raise AnsibleFailJson(kwargs)
-
-
-class MockONTAPConnection(object):
- ''' mock server connection to ONTAP host '''
-
- def __init__(self, kind=None):
- ''' save arguments '''
- self.type = kind
- self.xml_in = None
- self.xml_out = None
-
- def invoke_successfully(self, xml, enable_tunneling): # pylint: disable=unused-argument
- ''' mock invoke_successfully returning xml data '''
- self.xml_in = xml
- if self.type == 'cifs':
- xml = self.build_cifs_info()
- elif self.type == 'cifs_fail':
- raise netapp_utils.zapi.NaApiError(code='TEST', message="This exception is from the unit test")
- self.xml_out = xml
- return xml
-
- @staticmethod
- def build_cifs_info():
- ''' build xml data for cifs-info '''
- xml = netapp_utils.zapi.NaElement('xml')
- data = {'num-records': 1, 'attributes-list': {'cifs-share': {
- 'share-name': 'test',
- 'path': '/test',
- 'vscan-fileop-profile': 'standard',
- 'share-properties': [{'cifs-share-properties': 'browsable'},
- {'cifs-share-properties': 'oplocks'}],
- 'symlink-properties': [{'cifs-share-symlink-properties': 'enable'},
- {'cifs-share-symlink-properties': 'read_only'}],
- }}}
- xml.translate_struct(data)
- print(xml.to_string())
- return xml
-
-
-class TestMyModule(unittest.TestCase):
- ''' a group of related Unit Tests '''
-
- def setUp(self):
- self.mock_module_helper = patch.multiple(basic.AnsibleModule,
- exit_json=exit_json,
- fail_json=fail_json)
- self.mock_module_helper.start()
- self.addCleanup(self.mock_module_helper.stop)
- self.server = MockONTAPConnection()
- self.onbox = False
-
- def set_default_args(self):
- if self.onbox:
- hostname = '10.193.77.37'
- username = 'admin'
- password = 'netapp1!'
- share_name = 'test'
- path = '/test'
- share_properties = 'browsable,oplocks'
- symlink_properties = 'disable'
- vscan_fileop_profile = 'standard'
- vserver = 'abc'
- else:
- hostname = '10.193.77.37'
- username = 'admin'
- password = 'netapp1!'
- share_name = 'test'
- path = '/test'
- share_properties = 'show_previous_versions'
- symlink_properties = 'disable'
- vscan_fileop_profile = 'no_scan'
- vserver = 'abc'
- return dict({
- 'hostname': hostname,
- 'username': username,
- 'password': password,
- 'share_name': share_name,
- 'path': path,
- 'share_properties': share_properties,
- 'symlink_properties': symlink_properties,
- 'vscan_fileop_profile': vscan_fileop_profile,
- 'vserver': vserver
- })
-
- def test_module_fail_when_required_args_missing(self):
- ''' required arguments are reported as errors '''
- with pytest.raises(AnsibleFailJson) as exc:
- set_module_args({})
- my_module()
- print('Info: %s' % exc.value.args[0]['msg'])
-
- def test_ensure_cifs_get_called(self):
- ''' fetching details of cifs '''
- set_module_args(self.set_default_args())
- my_obj = my_module()
- my_obj.server = self.server
- cifs_get = my_obj.get_cifs_share()
- print('Info: test_cifs_share_get: %s' % repr(cifs_get))
- assert not bool(cifs_get)
-
- def test_ensure_apply_for_cifs_called(self):
- ''' creating cifs share and checking idempotency '''
- module_args = {}
- module_args.update(self.set_default_args())
- set_module_args(module_args)
- my_obj = my_module()
- if not self.onbox:
- my_obj.server = self.server
- with pytest.raises(AnsibleExitJson) as exc:
- my_obj.apply()
- print('Info: test_cifs_apply: %s' % repr(exc.value))
- assert exc.value.args[0]['changed']
- if not self.onbox:
- my_obj.server = MockONTAPConnection('cifs')
- with pytest.raises(AnsibleExitJson) as exc:
- my_obj.apply()
- print('Info: test_cifs_apply: %s' % repr(exc.value))
- assert exc.value.args[0]['changed']
-
- @patch('ansible.modules.storage.netapp.na_ontap_cifs.NetAppONTAPCifsShare.create_cifs_share')
- def test_cifs_create_called(self, create_cifs_share):
- ''' creating cifs'''
- module_args = {}
- module_args.update(self.set_default_args())
- set_module_args(module_args)
- my_obj = my_module()
- if not self.onbox:
- my_obj.server = MockONTAPConnection()
- with pytest.raises(AnsibleExitJson) as exc:
- my_obj.apply()
- print('Info: test_cifs_apply: %s' % repr(exc.value))
- create_cifs_share.assert_called_with()
-
- @patch('ansible.modules.storage.netapp.na_ontap_cifs.NetAppONTAPCifsShare.delete_cifs_share')
- def test_cifs_delete_called(self, delete_cifs_share):
- ''' deleting cifs'''
- module_args = {}
- module_args.update(self.set_default_args())
- module_args['state'] = 'absent'
- set_module_args(module_args)
- my_obj = my_module()
- if not self.onbox:
- my_obj.server = MockONTAPConnection('cifs')
- with pytest.raises(AnsibleExitJson) as exc:
- my_obj.apply()
- print('Info: test_cifs_apply: %s' % repr(exc.value))
- delete_cifs_share.assert_called_with()
-
- @patch('ansible.modules.storage.netapp.na_ontap_cifs.NetAppONTAPCifsShare.modify_cifs_share')
- def test_cifs_modify_called(self, modify_cifs_share):
- ''' modifying cifs'''
- module_args = {}
- module_args.update(self.set_default_args())
- set_module_args(module_args)
- my_obj = my_module()
- if not self.onbox:
- my_obj.server = MockONTAPConnection('cifs')
- with pytest.raises(AnsibleExitJson) as exc:
- my_obj.apply()
- print('Info: test_cifs_apply: %s' % repr(exc.value))
- modify_cifs_share.assert_called_with()
-
- def test_if_all_methods_catch_exception(self):
- module_args = {}
- module_args.update(self.set_default_args())
- set_module_args(module_args)
- my_obj = my_module()
- if not self.onbox:
- my_obj.server = MockONTAPConnection('cifs_fail')
- with pytest.raises(AnsibleFailJson) as exc:
- my_obj.create_cifs_share()
- assert 'Error creating cifs-share' in exc.value.args[0]['msg']
- with pytest.raises(AnsibleFailJson) as exc:
- my_obj.delete_cifs_share()
- assert 'Error deleting cifs-share' in exc.value.args[0]['msg']
- with pytest.raises(AnsibleFailJson) as exc:
- my_obj.modify_cifs_share()
- assert 'Error modifying cifs-share' in exc.value.args[0]['msg']
diff --git a/test/units/modules/storage/netapp/test_na_ontap_cifs_server.py b/test/units/modules/storage/netapp/test_na_ontap_cifs_server.py
deleted file mode 100644
index 92173c33d3..0000000000
--- a/test/units/modules/storage/netapp/test_na_ontap_cifs_server.py
+++ /dev/null
@@ -1,221 +0,0 @@
-# (c) 2018, NetApp, Inc
-# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-
-''' unit tests ONTAP Ansible module: na_ontap_cifs_server '''
-
-from __future__ import print_function
-import json
-import pytest
-
-from units.compat import unittest
-from units.compat.mock import patch
-from ansible.module_utils import basic
-from ansible.module_utils._text import to_bytes
-import ansible.module_utils.netapp as netapp_utils
-
-from ansible.modules.storage.netapp.na_ontap_cifs_server \
- import NetAppOntapcifsServer as my_module # module under test
-
-if not netapp_utils.has_netapp_lib():
- pytestmark = pytest.mark.skip('skipping as missing required netapp_lib')
-
-
-def set_module_args(args):
- """prepare arguments so that they will be picked up during module creation"""
- args = json.dumps({'ANSIBLE_MODULE_ARGS': args})
- basic._ANSIBLE_ARGS = to_bytes(args) # pylint: disable=protected-access
-
-
-class AnsibleExitJson(Exception):
- """Exception class to be raised by module.exit_json and caught by the test case"""
- pass
-
-
-class AnsibleFailJson(Exception):
- """Exception class to be raised by module.fail_json and caught by the test case"""
- pass
-
-
-def exit_json(*args, **kwargs): # pylint: disable=unused-argument
- """function to patch over exit_json; package return data into an exception"""
- if 'changed' not in kwargs:
- kwargs['changed'] = False
- raise AnsibleExitJson(kwargs)
-
-
-def fail_json(*args, **kwargs): # pylint: disable=unused-argument
- """function to patch over fail_json; package return data into an exception"""
- kwargs['failed'] = True
- raise AnsibleFailJson(kwargs)
-
-
-class MockONTAPConnection(object):
- ''' mock server connection to ONTAP host '''
-
- def __init__(self, kind=None, parm1=None, parm2=None):
- ''' save arguments '''
- self.type = kind
- self.parm1 = parm1
- self.parm2 = parm2
- self.xml_in = None
- self.xml_out = None
-
- def invoke_successfully(self, xml, enable_tunneling): # pylint: disable=unused-argument
- ''' mock invoke_successfully returning xml data '''
- self.xml_in = xml
- if self.type == 'cifs_server':
- xml = self.build_vserver_info(self.parm1, self.parm2)
- self.xml_out = xml
- return xml
-
- @staticmethod
- def build_vserver_info(cifs_server, admin_status):
- ''' build xml data for cifs-server-info '''
- xml = netapp_utils.zapi.NaElement('xml')
- data = {'num-records': 1,
- 'attributes-list': {'cifs-server-config': {'cifs-server': cifs_server,
- 'administrative-status': admin_status}}}
- xml.translate_struct(data)
- print(xml.to_string())
- return xml
-
-
-class TestMyModule(unittest.TestCase):
- ''' a group of related Unit Tests '''
-
- def setUp(self):
- self.mock_module_helper = patch.multiple(basic.AnsibleModule,
- exit_json=exit_json,
- fail_json=fail_json)
- self.mock_module_helper.start()
- self.addCleanup(self.mock_module_helper.stop)
- self.server = MockONTAPConnection()
- self.use_vsim = False
-
- def set_default_args(self):
- if self.use_vsim:
- hostname = '10.193.77.154'
- username = 'admin'
- password = 'netapp1!'
- cifs_server = 'test'
- vserver = 'ansible_test'
- else:
- hostname = 'hostname'
- username = 'username'
- password = 'password'
- cifs_server = 'name'
- vserver = 'vserver'
- return dict({
- 'hostname': hostname,
- 'username': username,
- 'password': password,
- 'cifs_server_name': cifs_server,
- 'vserver': vserver
- })
-
- def test_module_fail_when_required_args_missing(self):
- ''' required arguments are reported as errors '''
- with pytest.raises(AnsibleFailJson) as exc:
- set_module_args({})
- my_module()
- print('Info: %s' % exc.value.args[0]['msg'])
-
- def test_ensure_cifs_server_get_called(self):
- ''' a more interesting test '''
- set_module_args(self.set_default_args())
- my_obj = my_module()
- my_obj.server = self.server
- cifs_server = my_obj.get_cifs_server()
- print('Info: test_cifs_server_get: %s' % repr(cifs_server))
- assert cifs_server is None
-
- def test_ensure_cifs_server_apply_for_create_called(self):
- ''' creating cifs server and checking idempotency '''
- module_args = {}
- module_args.update(self.set_default_args())
- module_args.update({'cifs_server_name': 'create'})
- set_module_args(module_args)
- my_obj = my_module()
- if not self.use_vsim:
- my_obj.server = self.server
- with pytest.raises(AnsibleExitJson) as exc:
- my_obj.apply()
- print('Info: test_cifs_server_apply: %s' % repr(exc.value))
- assert exc.value.args[0]['changed']
- if not self.use_vsim:
- my_obj.server = MockONTAPConnection('cifs_server', 'create', 'up')
- with pytest.raises(AnsibleExitJson) as exc:
- my_obj.apply()
- print('Info: test_cifs_server_apply_for_create: %s' % repr(exc.value))
- assert not exc.value.args[0]['changed']
-
- def test_ensure_cifs_server_apply_for_delete_called(self):
- ''' deleting cifs server and checking idempotency '''
- module_args = {}
- module_args.update(self.set_default_args())
- module_args.update({'cifs_server_name': 'delete'})
- set_module_args(module_args)
- my_obj = my_module()
- if not self.use_vsim:
- my_obj.server = MockONTAPConnection('cifs_server', 'delete', 'up')
- with pytest.raises(AnsibleExitJson) as exc:
- my_obj.apply()
- print('Info: test_cifs_server_apply: %s' % repr(exc.value))
- assert not exc.value.args[0]['changed']
- module_args.update({'state': 'absent'})
- set_module_args(module_args)
- my_obj = my_module()
- if not self.use_vsim:
- my_obj.server = MockONTAPConnection('cifs_server', 'delete', 'up')
- with pytest.raises(AnsibleExitJson) as exc:
- my_obj.apply()
- print('Info: test_cifs_server_delete: %s' % repr(exc.value))
- assert exc.value.args[0]['changed']
-
- def test_ensure_start_cifs_server_called(self):
- ''' starting cifs server and checking idempotency '''
- module_args = {}
- module_args.update(self.set_default_args())
- module_args.update({'cifs_server_name': 'delete'})
- module_args.update({'service_state': 'started'})
- set_module_args(module_args)
- my_obj = my_module()
- if not self.use_vsim:
- my_obj.server = MockONTAPConnection('cifs_server', 'test', 'up')
- with pytest.raises(AnsibleExitJson) as exc:
- my_obj.apply()
- print('Info: test_ensure_start_cifs_server: %s' % repr(exc.value))
- assert not exc.value.args[0]['changed']
- module_args.update({'service_state': 'stopped'})
- set_module_args(module_args)
- my_obj = my_module()
- if not self.use_vsim:
- my_obj.server = MockONTAPConnection('cifs_server', 'test', 'up')
- with pytest.raises(AnsibleExitJson) as exc:
- my_obj.apply()
- print('Info: test_ensure_start_cifs_server: %s' % repr(exc.value))
- assert exc.value.args[0]['changed']
-
- def test_ensure_stop_cifs_server_called(self):
- ''' stopping cifs server and checking idempotency '''
- module_args = {}
- module_args.update(self.set_default_args())
- module_args.update({'cifs_server_name': 'delete'})
- module_args.update({'service_state': 'stopped'})
- set_module_args(module_args)
- my_obj = my_module()
- if not self.use_vsim:
- my_obj.server = MockONTAPConnection('cifs_server', 'test', 'down')
- with pytest.raises(AnsibleExitJson) as exc:
- my_obj.apply()
- print('Info: test_ensure_stop_cifs_server: %s' % repr(exc.value))
- assert not exc.value.args[0]['changed']
- module_args.update({'service_state': 'started'})
- set_module_args(module_args)
- my_obj = my_module()
- if not self.use_vsim:
- my_obj.server = MockONTAPConnection('cifs_server', 'test', 'down')
- with pytest.raises(AnsibleExitJson) as exc:
- my_obj.apply()
- print('Info: test_ensure_stop_cifs_server: %s' % repr(exc.value))
- assert exc.value.args[0]['changed']
diff --git a/test/units/modules/storage/netapp/test_na_ontap_cluster.py b/test/units/modules/storage/netapp/test_na_ontap_cluster.py
deleted file mode 100644
index 913344f101..0000000000
--- a/test/units/modules/storage/netapp/test_na_ontap_cluster.py
+++ /dev/null
@@ -1,198 +0,0 @@
-# (c) 2018, NetApp, Inc
-# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-
-''' unit tests ONTAP Ansible module: na_ontap_cluster '''
-
-from __future__ import print_function
-import json
-import pytest
-
-from units.compat import unittest
-from units.compat.mock import patch, Mock
-from ansible.module_utils import basic
-from ansible.module_utils._text import to_bytes
-import ansible.module_utils.netapp as netapp_utils
-
-from ansible.modules.storage.netapp.na_ontap_cluster \
- import NetAppONTAPCluster as my_module # module under test
-
-if not netapp_utils.has_netapp_lib():
- pytestmark = pytest.mark.skip('skipping as missing required netapp_lib')
-
-
-def set_module_args(args):
- """prepare arguments so that they will be picked up during module creation"""
- args = json.dumps({'ANSIBLE_MODULE_ARGS': args})
- basic._ANSIBLE_ARGS = to_bytes(args) # pylint: disable=protected-access
-
-
-class AnsibleExitJson(Exception):
- """Exception class to be raised by module.exit_json and caught by the test case"""
- pass
-
-
-class AnsibleFailJson(Exception):
- """Exception class to be raised by module.fail_json and caught by the test case"""
- pass
-
-
-def exit_json(*args, **kwargs): # pylint: disable=unused-argument
- """function to patch over exit_json; package return data into an exception"""
- if 'changed' not in kwargs:
- kwargs['changed'] = False
- raise AnsibleExitJson(kwargs)
-
-
-def fail_json(*args, **kwargs): # pylint: disable=unused-argument
- """function to patch over fail_json; package return data into an exception"""
- kwargs['failed'] = True
- raise AnsibleFailJson(kwargs)
-
-
-class MockONTAPConnection(object):
- ''' mock server connection to ONTAP host '''
-
- def __init__(self, kind=None):
- ''' save arguments '''
- self.type = kind
- self.xml_in = None
- self.xml_out = None
-
- def invoke_successfully(self, xml, enable_tunneling): # pylint: disable=unused-argument
- ''' mock invoke_successfully returning xml data '''
- self.xml_in = xml
- if self.type == 'cluster':
- xml = self.build_cluster_info()
- elif self.type == 'cluster_fail':
- raise netapp_utils.zapi.NaApiError(code='TEST', message="This exception is from the unit test")
- self.xml_out = xml
- return xml
-
- def autosupport_log(self):
- ''' mock autosupport log'''
- return None
-
- @staticmethod
- def build_cluster_info():
- ''' build xml data for cluster-info '''
- xml = netapp_utils.zapi.NaElement('xml')
- data = {'license-v2-status': {'package': 'cifs', 'method': 'site'}}
- xml.translate_struct(data)
- print(xml.to_string())
- return xml
-
-
-class TestMyModule(unittest.TestCase):
- ''' a group of related Unit Tests '''
-
- def setUp(self):
- self.mock_module_helper = patch.multiple(basic.AnsibleModule,
- exit_json=exit_json,
- fail_json=fail_json)
- self.mock_module_helper.start()
- self.addCleanup(self.mock_module_helper.stop)
- self.server = MockONTAPConnection()
- self.use_vsim = False
-
- def set_default_args(self):
- if self.use_vsim:
- hostname = '10.193.77.37'
- username = 'admin'
- password = 'netapp1!'
- license_package = 'CIFS'
- node_serial_number = '123'
- license_code = 'AAA'
- cluster_name = 'abc'
- else:
- hostname = '10.193.77.37'
- username = 'admin'
- password = 'netapp1!'
- license_package = 'CIFS'
- node_serial_number = '123'
- cluster_ip_address = '0.0.0.0'
- license_code = 'AAA'
- cluster_name = 'abc'
- return dict({
- 'hostname': hostname,
- 'username': username,
- 'password': password,
- 'license_package': license_package,
- 'node_serial_number': node_serial_number,
- 'license_code': license_code,
- 'cluster_name': cluster_name,
- 'cluster_ip_address': cluster_ip_address
- })
-
- def test_module_fail_when_required_args_missing(self):
- ''' required arguments are reported as errors '''
- with pytest.raises(AnsibleFailJson) as exc:
- set_module_args({})
- my_module()
- print('Info: %s' % exc.value.args[0]['msg'])
-
- def test_ensure_license_get_called(self):
- ''' fetching details of license '''
- set_module_args(self.set_default_args())
- my_obj = my_module()
- my_obj.server = self.server
- license_get = my_obj.get_licensing_status()
- print('Info: test_license_get: %s' % repr(license_get))
- assert not bool(license_get)
-
- def test_ensure_apply_for_cluster_called(self):
- ''' creating license and checking idempotency '''
- module_args = {}
- module_args.update(self.set_default_args())
- set_module_args(module_args)
- my_obj = my_module()
- my_obj.autosupport_log = Mock(return_value=None)
- if not self.use_vsim:
- my_obj.server = self.server
- with pytest.raises(AnsibleExitJson) as exc:
- my_obj.apply()
- print('Info: test_cluster_apply: %s' % repr(exc.value))
- assert exc.value.args[0]['changed']
- if not self.use_vsim:
- my_obj.server = MockONTAPConnection('cluster')
- with pytest.raises(AnsibleExitJson) as exc:
- my_obj.apply()
- print('Info: test_cluster_apply: %s' % repr(exc.value))
- assert exc.value.args[0]['changed']
-
- @patch('ansible.modules.storage.netapp.na_ontap_cluster.NetAppONTAPCluster.create_cluster')
- def test_cluster_create_called(self, cluster_create):
- ''' creating cluster'''
- module_args = {}
- module_args.update(self.set_default_args())
- set_module_args(module_args)
- my_obj = my_module()
- my_obj.autosupport_log = Mock(return_value=None)
- if not self.use_vsim:
- my_obj.server = MockONTAPConnection('cluster')
- with pytest.raises(AnsibleExitJson) as exc:
- my_obj.apply()
- print('Info: test_cluster_apply: %s' % repr(exc.value))
- cluster_create.assert_called_with()
-
- def test_if_all_methods_catch_exception(self):
- module_args = {}
- module_args.update(self.set_default_args())
- set_module_args(module_args)
- my_obj = my_module()
- if not self.use_vsim:
- my_obj.server = MockONTAPConnection('cluster_fail')
- with pytest.raises(AnsibleFailJson) as exc:
- my_obj.get_licensing_status()
- assert 'Error checking license status' in exc.value.args[0]['msg']
- with pytest.raises(AnsibleFailJson) as exc:
- my_obj.create_cluster()
- assert 'Error creating cluster' in exc.value.args[0]['msg']
- with pytest.raises(AnsibleFailJson) as exc:
- my_obj.cluster_join()
- assert 'Error adding node to cluster' in exc.value.args[0]['msg']
- with pytest.raises(AnsibleFailJson) as exc:
- my_obj.license_v2_add()
- assert 'Error adding license' in exc.value.args[0]['msg']
- with pytest.raises(AnsibleFailJson) as exc:
- my_obj.license_v2_delete()
- assert 'Error deleting license' in exc.value.args[0]['msg']
diff --git a/test/units/modules/storage/netapp/test_na_ontap_cluster_peer.py b/test/units/modules/storage/netapp/test_na_ontap_cluster_peer.py
deleted file mode 100644
index 7c38b30ac8..0000000000
--- a/test/units/modules/storage/netapp/test_na_ontap_cluster_peer.py
+++ /dev/null
@@ -1,211 +0,0 @@
-''' unit tests ONTAP Ansible module: na_ontap_cluster_peer '''
-
-from __future__ import print_function
-import json
-import pytest
-
-from units.compat import unittest
-from units.compat.mock import patch, Mock
-from ansible.module_utils import basic
-from ansible.module_utils._text import to_bytes
-import ansible.module_utils.netapp as netapp_utils
-
-from ansible.modules.storage.netapp.na_ontap_cluster_peer \
- import NetAppONTAPClusterPeer as my_module # module under test
-
-if not netapp_utils.has_netapp_lib():
- pytestmark = pytest.mark.skip('skipping as missing required netapp_lib')
-
-
-def set_module_args(args):
- """prepare arguments so that they will be picked up during module creation"""
- args = json.dumps({'ANSIBLE_MODULE_ARGS': args})
- basic._ANSIBLE_ARGS = to_bytes(args) # pylint: disable=protected-access
-
-
-class AnsibleExitJson(Exception):
- """Exception class to be raised by module.exit_json and caught by the test case"""
- pass
-
-
-class AnsibleFailJson(Exception):
- """Exception class to be raised by module.fail_json and caught by the test case"""
- pass
-
-
-def exit_json(*args, **kwargs): # pylint: disable=unused-argument
- """function to patch over exit_json; package return data into an exception"""
- if 'changed' not in kwargs:
- kwargs['changed'] = False
- raise AnsibleExitJson(kwargs)
-
-
-def fail_json(*args, **kwargs): # pylint: disable=unused-argument
- """function to patch over fail_json; package return data into an exception"""
- kwargs['failed'] = True
- raise AnsibleFailJson(kwargs)
-
-
-class MockONTAPConnection(object):
- ''' mock server connection to ONTAP host '''
-
- def __init__(self, kind=None, parm1=None):
- ''' save arguments '''
- self.type = kind
- self.data = parm1
- self.xml_in = None
- self.xml_out = None
-
- def invoke_successfully(self, xml, enable_tunneling): # pylint: disable=unused-argument
- ''' mock invoke_successfully returning xml data '''
- self.xml_in = xml
- if self.type == 'cluster_peer':
- xml = self.build_cluster_peer_info(self.data)
- self.xml_out = xml
- return xml
-
- @staticmethod
- def build_cluster_peer_info(parm1):
- ''' build xml data for vserser-info '''
- xml = netapp_utils.zapi.NaElement('xml')
- attributes = {
- 'num-records': 1,
- 'attributes-list': {
- 'cluster-peer-info': {
- 'cluster-name': parm1['dest_cluster_name'],
- 'peer-addresses': parm1['dest_intercluster_lifs']
- }
- }
- }
- xml.translate_struct(attributes)
- return xml
-
-
-class TestMyModule(unittest.TestCase):
- ''' a group of related Unit Tests '''
-
- def setUp(self):
- self.mock_module_helper = patch.multiple(basic.AnsibleModule,
- exit_json=exit_json,
- fail_json=fail_json)
- self.mock_module_helper.start()
- self.addCleanup(self.mock_module_helper.stop)
- self.server = MockONTAPConnection()
- self.mock_cluster_peer = {
- 'source_intercluster_lifs': '1.2.3.4,1.2.3.5',
- 'dest_intercluster_lifs': '1.2.3.6,1.2.3.7',
- 'passphrase': 'netapp123',
- 'dest_hostname': '10.20.30.40',
- 'dest_cluster_name': 'cluster2',
- 'hostname': 'hostname',
- 'username': 'username',
- 'password': 'password',
-
- }
-
- def mock_args(self):
- return {
- 'source_intercluster_lifs': self.mock_cluster_peer['source_intercluster_lifs'],
- 'dest_intercluster_lifs': self.mock_cluster_peer['dest_intercluster_lifs'],
- 'passphrase': self.mock_cluster_peer['passphrase'],
- 'dest_hostname': self.mock_cluster_peer['dest_hostname'],
- 'dest_cluster_name': 'cluster2',
- 'hostname': 'hostname',
- 'username': 'username',
- 'password': 'password',
- }
-
- def get_cluster_peer_mock_object(self, kind=None):
- """
- Helper method to return an na_ontap_cluster_peer object
- :param kind: passes this param to MockONTAPConnection()
- :return: na_ontap_cluster_peer object
- """
- cluster_peer_obj = my_module()
- cluster_peer_obj.asup_log_for_cserver = Mock(return_value=None)
- cluster_peer_obj.cluster = Mock()
- cluster_peer_obj.cluster.invoke_successfully = Mock()
- if kind is None:
- cluster_peer_obj.server = MockONTAPConnection()
- cluster_peer_obj.dest_server = MockONTAPConnection()
- else:
- cluster_peer_obj.server = MockONTAPConnection(kind=kind, parm1=self.mock_cluster_peer)
- cluster_peer_obj.dest_server = MockONTAPConnection(kind=kind, parm1=self.mock_cluster_peer)
- return cluster_peer_obj
-
- def test_module_fail_when_required_args_missing(self):
- ''' required arguments are reported as errors '''
- with pytest.raises(AnsibleFailJson) as exc:
- set_module_args({})
- my_module()
- print('Info: %s' % exc.value.args[0]['msg'])
-
- @patch('ansible.modules.storage.netapp.na_ontap_cluster_peer.NetAppONTAPClusterPeer.cluster_peer_get')
- def test_successful_create(self, cluster_peer_get):
- ''' Test successful create '''
- set_module_args(self.mock_args())
- cluster_peer_get.side_effect = [
- None,
- None
- ]
- with pytest.raises(AnsibleExitJson) as exc:
- self.get_cluster_peer_mock_object().apply()
- assert exc.value.args[0]['changed']
-
- @patch('ansible.modules.storage.netapp.na_ontap_cluster_peer.NetAppONTAPClusterPeer.cluster_peer_get')
- def test_create_idempotency(self, cluster_peer_get):
- ''' Test create idempotency '''
- set_module_args(self.mock_args())
- current1 = {
- 'cluster_name': 'cluster1',
- 'peer-addresses': '1.2.3.6,1.2.3.7'
- }
- current2 = {
- 'cluster_name': 'cluster2',
- 'peer-addresses': '1.2.3.4,1.2.3.5'
- }
- cluster_peer_get.side_effect = [
- current1,
- current2
- ]
- with pytest.raises(AnsibleExitJson) as exc:
- self.get_cluster_peer_mock_object('cluster_peer').apply()
- assert not exc.value.args[0]['changed']
-
- @patch('ansible.modules.storage.netapp.na_ontap_cluster_peer.NetAppONTAPClusterPeer.cluster_peer_get')
- def test_successful_delete(self, cluster_peer_get):
- ''' Test delete existing interface '''
- data = self.mock_args()
- data['state'] = 'absent'
- data['source_cluster_name'] = 'cluster1'
- set_module_args(data)
- current1 = {
- 'cluster_name': 'cluster1',
- 'peer-addresses': '1.2.3.6,1.2.3.7'
- }
- current2 = {
- 'cluster_name': 'cluster2',
- 'peer-addresses': '1.2.3.4,1.2.3.5'
- }
- cluster_peer_get.side_effect = [
- current1,
- current2
- ]
- with pytest.raises(AnsibleExitJson) as exc:
- self.get_cluster_peer_mock_object('cluster_peer').apply()
- assert exc.value.args[0]['changed']
-
- @patch('ansible.modules.storage.netapp.na_ontap_cluster_peer.NetAppONTAPClusterPeer.cluster_peer_get')
- def test_delete_idempotency(self, cluster_peer_get):
- ''' Test delete idempotency '''
- data = self.mock_args()
- data['state'] = 'absent'
- data['source_cluster_name'] = 'cluster2'
- set_module_args(data)
- cluster_peer_get.side_effect = [
- None,
- None
- ]
- with pytest.raises(AnsibleExitJson) as exc:
- self.get_cluster_peer_mock_object().apply()
- assert not exc.value.args[0]['changed']
diff --git a/test/units/modules/storage/netapp/test_na_ontap_command.py b/test/units/modules/storage/netapp/test_na_ontap_command.py
deleted file mode 100644
index 4b5af4abdc..0000000000
--- a/test/units/modules/storage/netapp/test_na_ontap_command.py
+++ /dev/null
@@ -1,169 +0,0 @@
-# (c) 2018, NetApp, Inc
-# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-
-''' unit test for ONTAP Command Ansible module '''
-
-from __future__ import print_function
-import json
-import pytest
-
-from units.compat import unittest
-from units.compat.mock import patch, Mock
-from ansible.module_utils import basic
-from ansible.module_utils._text import to_bytes
-import ansible.module_utils.netapp as netapp_utils
-
-from ansible.modules.storage.netapp.na_ontap_command \
- import NetAppONTAPCommand as my_module # module under test
-
-if not netapp_utils.has_netapp_lib():
- pytestmark = pytest.mark.skip('skipping as missing required netapp_lib')
-
-
-def set_module_args(args):
- """prepare arguments so that they will be picked up during module creation"""
- args = json.dumps({'ANSIBLE_MODULE_ARGS': args})
- basic._ANSIBLE_ARGS = to_bytes(args) # pylint: disable=protected-access
-
-
-class AnsibleExitJson(Exception):
- """Exception class to be raised by module.exit_json and caught by the test case"""
- pass
-
-
-class AnsibleFailJson(Exception):
- """Exception class to be raised by module.fail_json and caught by the test case"""
- pass
-
-
-def exit_json(*args, **kwargs): # pylint: disable=unused-argument
- """function to patch over exit_json; package return data into an exception"""
- if 'changed' not in kwargs:
- kwargs['changed'] = False
- raise AnsibleExitJson(kwargs)
-
-
-def fail_json(*args, **kwargs): # pylint: disable=unused-argument
- """function to patch over fail_json; package return data into an exception"""
- kwargs['failed'] = True
- raise AnsibleFailJson(kwargs)
-
-
-class MockONTAPConnection(object):
- ''' mock server connection to ONTAP host '''
-
- def __init__(self, kind=None, parm1=None):
- ''' save arguments '''
- self.type = kind
- self.parm1 = parm1
- self.xml_in = None
- self.xml_out = None
-
- def invoke_successfully(self, xml, enable_tunneling): # pylint: disable=unused-argument
- ''' mock invoke_successfully returning xml data '''
- self.xml_in = xml
- # print(xml.to_string())
-
- if self.type == 'version':
- priv = xml.get_child_content('priv')
- xml = self.build_version(priv)
- self.xml_out = xml
- return xml
-
- @staticmethod
- def build_version(priv):
- ''' build xml data for version '''
- prefix = 'NetApp Release'
- if priv == 'advanced':
- prefix = '\n' + prefix
- xml = netapp_utils.zapi.NaElement('results')
- xml.add_new_child('cli-output', prefix)
- # print(xml.to_string())
- return xml
-
-
-class TestMyModule(unittest.TestCase):
- ''' a group of related Unit Tests '''
-
- def setUp(self):
- self.mock_module_helper = patch.multiple(basic.AnsibleModule,
- exit_json=exit_json,
- fail_json=fail_json)
- self.mock_module_helper.start()
- self.addCleanup(self.mock_module_helper.stop)
- self.server = MockONTAPConnection(kind='version')
- # whether to use a mock or a simulator
- self.use_vsim = False
-
- def test_module_fail_when_required_args_missing(self):
- ''' required arguments are reported as errors '''
- with pytest.raises(AnsibleFailJson) as exc:
- set_module_args({})
- my_module()
- print('Info: %s' % exc.value.args[0]['msg'])
-
- @staticmethod
- def set_default_args(vsim=False):
- ''' populate hostname/username/password '''
- if vsim:
- hostname = '10.10.10.10'
- username = 'admin'
- password = 'admin'
- else:
- hostname = 'hostname'
- username = 'username'
- password = 'password'
- return dict({
- 'hostname': hostname,
- 'username': username,
- 'password': password,
- 'https': True,
- 'validate_certs': False
- })
-
- def call_command(self, module_args, vsim=False):
- ''' utility function to call apply '''
- module_args.update(self.set_default_args(vsim=vsim))
- set_module_args(module_args)
- my_obj = my_module()
- my_obj.asup_log_for_cserver = Mock(return_value=None)
- if not vsim:
- # mock the connection
- my_obj.server = self.server
- with pytest.raises(AnsibleExitJson) as exc:
- my_obj.apply()
- msg = exc.value.args[0]['msg']
- return msg
-
- def test_default_priv(self):
- ''' make sure privilege is not required '''
- module_args = {
- 'command': 'version',
- }
- msg = self.call_command(module_args, vsim=self.use_vsim)
- needle = b'<cli-output>NetApp Release'
- assert needle in msg
- print('Version (raw): %s' % msg)
-
- def test_admin_priv(self):
- ''' make sure admin is accepted '''
- module_args = {
- 'command': 'version',
- 'privilege': 'admin',
- }
- msg = self.call_command(module_args, vsim=self.use_vsim)
- needle = b'<cli-output>NetApp Release'
- assert needle in msg
- print('Version (raw): %s' % msg)
-
- def test_advanced_priv(self):
- ''' make sure advanced is not required '''
- module_args = {
- 'command': 'version',
- 'privilege': 'advanced',
- }
- msg = self.call_command(module_args, vsim=self.use_vsim)
- # Interestingly, the ZAPI returns a slightly different response
- needle = b'<cli-output>\nNetApp Release'
- assert needle in msg
- print('Version (raw): %s' % msg)
diff --git a/test/units/modules/storage/netapp/test_na_ontap_dns.py b/test/units/modules/storage/netapp/test_na_ontap_dns.py
deleted file mode 100644
index 1efa98a81f..0000000000
--- a/test/units/modules/storage/netapp/test_na_ontap_dns.py
+++ /dev/null
@@ -1,276 +0,0 @@
-# (c) 2018, NetApp, Inc
-# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-
-''' unit tests for Ansible module: na_ontap_dns'''
-
-from __future__ import (absolute_import, division, print_function)
-__metaclass__ = type
-import json
-import pytest
-
-from units.compat import unittest
-from units.compat.mock import patch, Mock
-from ansible.module_utils import basic
-from ansible.module_utils._text import to_bytes
-import ansible.module_utils.netapp as netapp_utils
-
-from ansible.modules.storage.netapp.na_ontap_dns \
- import NetAppOntapDns as dns_module # module under test
-
-if not netapp_utils.has_netapp_lib():
- pytestmark = pytest.mark.skip('skipping as missing required netapp_lib')
-HAS_NETAPP_ZAPI_MSG = "pip install netapp_lib is required"
-
-
-# REST API canned responses when mocking send_request
-SRR = {
- # common responses
- 'is_rest': (200, None),
- 'is_zapi': (400, "Unreachable"),
- 'empty_good': ({}, None),
- 'end_of_sequence': (None, "Unexpected call to send_request"),
- 'generic_error': (None, "Expected error"),
- 'dns_record': ({"records": [{"domains": ['0.0.0.0'],
- "servers": ['0.0.0.0'],
- "svm": {"name": "svm1", "uuid": "02c9e252-41be-11e9-81d5-00a0986138f7"}}]}, None)}
-
-
-def set_module_args(args):
- """prepare arguments so that they will be picked up during module creation"""
- args = json.dumps({'ANSIBLE_MODULE_ARGS': args})
- basic._ANSIBLE_ARGS = to_bytes(args) # pylint: disable=protected-access
-
-
-class AnsibleExitJson(Exception):
- """Exception class to be raised by module.exit_json and caught by the test case"""
- pass
-
-
-class AnsibleFailJson(Exception):
- """Exception class to be raised by module.fail_json and caught by the test case"""
- pass
-
-
-def exit_json(*args, **kwargs): # pylint: disable=unused-argument
- """function to patch over exit_json; package return data into an exception"""
- if 'changed' not in kwargs:
- kwargs['changed'] = False
- raise AnsibleExitJson(kwargs)
-
-
-def fail_json(*args, **kwargs): # pylint: disable=unused-argument
- """function to patch over fail_json; package return data into an exception"""
- kwargs['failed'] = True
- raise AnsibleFailJson(kwargs)
-
-
-class MockONTAPConnection(object):
- ''' mock server connection to ONTAP host '''
-
- def __init__(self, kind=None, data=None):
- ''' save arguments '''
- self.kind = kind
- self.params = data
- self.xml_in = None
- self.xml_out = None
-
- def invoke_successfully(self, xml, enable_tunneling): # pylint: disable=unused-argument
- ''' mock invoke_successfully returning xml data '''
- self.xml_in = xml
- request = xml.to_string().decode('utf-8')
- if request.startswith("<ems-autosupport-log>"):
- xml = None # or something that may the logger happy, and you don't need @patch anymore
- # or
- # xml = build_ems_log_response()
- elif request == "<net-dns-get/>":
- if self.kind == 'create':
- raise netapp_utils.zapi.NaApiError(code="15661")
- else:
- xml = self.build_dns_status_info()
- elif request.startswith("<net-dns-create>"):
- xml = self.build_dns_status_info()
- if self.kind == 'enable':
- xml = self.build_dns_status_info()
- self.xml_out = xml
- return xml
-
- @staticmethod
- def build_dns_status_info():
- xml = netapp_utils.zapi.NaElement('xml')
- nameservers = [{'ip-address': '0.0.0.0'}]
- domains = [{'string': '0.0.0.0'}]
- attributes = {'num-records': 1,
- 'attributes': {'net-dns-info': {'name-servers': nameservers,
- 'domains': domains,
- 'skip-config-validation': 'false'}}}
- xml.translate_struct(attributes)
- return xml
-
-
-class TestMyModule(unittest.TestCase):
- ''' Unit tests for na_ontap_job_schedule '''
-
- def setUp(self):
- self.mock_module_helper = patch.multiple(basic.AnsibleModule,
- exit_json=exit_json,
- fail_json=fail_json)
- self.mock_module_helper.start()
- self.addCleanup(self.mock_module_helper.stop)
-
- def mock_args(self):
- return {
- 'state': 'present',
- 'vserver': 'vserver',
- 'nameservers': ['0.0.0.0'],
- 'domains': ['0.0.0.0'],
- 'hostname': 'test',
- 'username': 'test_user',
- 'password': 'test_pass!'
- }
-
- def get_dns_mock_object(self, type='zapi', kind=None, status=None):
- dns_obj = dns_module()
- if type == 'zapi':
- if kind is None:
- dns_obj.server = MockONTAPConnection()
- else:
- dns_obj.server = MockONTAPConnection(kind=kind, data=status)
- return dns_obj
-
- def test_module_fail_when_required_args_missing(self):
- ''' required arguments are reported as errors '''
- with pytest.raises(AnsibleFailJson) as exc:
- set_module_args({})
- dns_module()
- print('Info: %s' % exc.value.args[0]['msg'])
-
- def test_idempotent_modify_dns(self):
- data = self.mock_args()
- set_module_args(data)
- with pytest.raises(AnsibleExitJson) as exc:
- self.get_dns_mock_object('zapi', 'enable', 'false').apply()
- assert not exc.value.args[0]['changed']
-
- def test_successfully_modify_dns(self):
- data = self.mock_args()
- data['domains'] = ['1.1.1.1']
- set_module_args(data)
- with pytest.raises(AnsibleExitJson) as exc:
- self.get_dns_mock_object('zapi', 'enable', 'false').apply()
- assert exc.value.args[0]['changed']
-
- @patch('ansible.module_utils.netapp.ems_log_event')
- def test_idempotent_create_dns(self, mock_ems_log_event):
- data = self.mock_args()
- set_module_args(data)
- with pytest.raises(AnsibleExitJson) as exc:
- self.get_dns_mock_object('zapi').apply()
- assert not exc.value.args[0]['changed']
-
- @patch('ansible.module_utils.netapp.ems_log_event')
- def test_successfully_create_dns(self, mock_ems_log_event):
- data = self.mock_args()
- print("create dns")
- data['domains'] = ['1.1.1.1']
- set_module_args(data)
- with pytest.raises(AnsibleExitJson) as exc:
- self.get_dns_mock_object('zapi', 'create').apply()
- assert exc.value.args[0]['changed']
-
- @patch('ansible.module_utils.netapp.OntapRestAPI.send_request')
- def test_rest_error(self, mock_request):
- data = self.mock_args()
- set_module_args(data)
- mock_request.side_effect = [
- SRR['is_rest'],
- SRR['generic_error'],
- SRR['end_of_sequence']
- ]
- with pytest.raises(AnsibleFailJson) as exc:
- self.get_dns_mock_object(type='rest').apply()
- assert exc.value.args[0]['msg'] == SRR['generic_error'][1]
-
- @patch('ansible.module_utils.netapp.OntapRestAPI.send_request')
- def test_rest_successfully_create(self, mock_request):
- data = self.mock_args()
- set_module_args(data)
- mock_request.side_effect = [
- SRR['is_rest'],
- SRR['empty_good'], # get
- SRR['empty_good'], # post
- SRR['end_of_sequence']
- ]
- with pytest.raises(AnsibleExitJson) as exc:
- self.get_dns_mock_object(type='rest').apply()
- assert exc.value.args[0]['changed']
-
- @patch('ansible.module_utils.netapp.OntapRestAPI.send_request')
- def test_rest_idempotent_create_dns(self, mock_request):
- data = self.mock_args()
- set_module_args(data)
- mock_request.side_effect = [
- SRR['is_rest'],
- SRR['dns_record'], # get
- SRR['end_of_sequence']
- ]
- with pytest.raises(AnsibleExitJson) as exc:
- self.get_dns_mock_object(type='rest').apply()
- assert not exc.value.args[0]['changed']
-
- @patch('ansible.module_utils.netapp.OntapRestAPI.send_request')
- def test_rest_successfully_destroy(self, mock_request):
- data = self.mock_args()
- data['state'] = 'absent'
- set_module_args(data)
- mock_request.side_effect = [
- SRR['is_rest'],
- SRR['dns_record'], # get
- SRR['empty_good'], # delete
- SRR['end_of_sequence']
- ]
- with pytest.raises(AnsibleExitJson) as exc:
- self.get_dns_mock_object(type='rest').apply()
- assert exc.value.args[0]['changed']
-
- @patch('ansible.module_utils.netapp.OntapRestAPI.send_request')
- def test_rest_idempotently_destroy(self, mock_request):
- data = self.mock_args()
- data['state'] = 'absent'
- set_module_args(data)
- mock_request.side_effect = [
- SRR['is_rest'],
- SRR['empty_good'], # get
- SRR['end_of_sequence']
- ]
- with pytest.raises(AnsibleExitJson) as exc:
- self.get_dns_mock_object(type='rest').apply()
- assert not exc.value.args[0]['changed']
-
- @patch('ansible.module_utils.netapp.OntapRestAPI.send_request')
- def test_rest_successfully_modify(self, mock_request):
- data = self.mock_args()
- data['state'] = 'present'
- set_module_args(data)
- mock_request.side_effect = [
- SRR['is_rest'],
- SRR['empty_good'], # get
- SRR['empty_good'], # patch
- SRR['end_of_sequence']
- ]
- with pytest.raises(AnsibleExitJson) as exc:
- self.get_dns_mock_object(type='rest').apply()
- assert exc.value.args[0]['changed']
-
- @patch('ansible.module_utils.netapp.OntapRestAPI.send_request')
- def test_rest_idempotently_modify(self, mock_request):
- data = self.mock_args()
- data['state'] = 'present'
- set_module_args(data)
- mock_request.side_effect = [
- SRR['is_rest'],
- SRR['dns_record'], # get
- SRR['end_of_sequence']
- ]
- with pytest.raises(AnsibleExitJson) as exc:
- self.get_dns_mock_object(type='rest').apply()
- assert not exc.value.args[0]['changed']
diff --git a/test/units/modules/storage/netapp/test_na_ontap_export_policy_rule.py b/test/units/modules/storage/netapp/test_na_ontap_export_policy_rule.py
deleted file mode 100644
index 5874603c97..0000000000
--- a/test/units/modules/storage/netapp/test_na_ontap_export_policy_rule.py
+++ /dev/null
@@ -1,264 +0,0 @@
-# (c) 2018, NetApp, Inc
-# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-
-''' unit test template for ONTAP Ansible module '''
-
-from __future__ import print_function
-import json
-import pytest
-
-from units.compat import unittest
-from units.compat.mock import patch, Mock
-from ansible.module_utils import basic
-from ansible.module_utils._text import to_bytes
-import ansible.module_utils.netapp as netapp_utils
-
-from ansible.modules.storage.netapp.na_ontap_export_policy_rule \
- import NetAppontapExportRule as policy_rule # module under test
-
-if not netapp_utils.has_netapp_lib():
- pytestmark = pytest.mark.skip('skipping as missing required netapp_lib')
-
-
-def set_module_args(args):
- """prepare arguments so that they will be picked up during module creation"""
- args = json.dumps({'ANSIBLE_MODULE_ARGS': args})
- basic._ANSIBLE_ARGS = to_bytes(args) # pylint: disable=protected-access
-
-
-class AnsibleExitJson(Exception):
- """Exception class to be raised by module.exit_json and caught by the test case"""
- pass
-
-
-class AnsibleFailJson(Exception):
- """Exception class to be raised by module.fail_json and caught by the test case"""
- pass
-
-
-def exit_json(*args, **kwargs): # pylint: disable=unused-argument
- """function to patch over exit_json; package return data into an exception"""
- if 'changed' not in kwargs:
- kwargs['changed'] = False
- raise AnsibleExitJson(kwargs)
-
-
-def fail_json(*args, **kwargs): # pylint: disable=unused-argument
- """function to patch over fail_json; package return data into an exception"""
- kwargs['failed'] = True
- raise AnsibleFailJson(kwargs)
-
-
-class MockONTAPConnection(object):
- ''' mock server connection to ONTAP host '''
-
- def __init__(self, kind=None, data=None):
- ''' save arguments '''
- self.kind = kind
- self.data = data
- self.xml_in = None
- self.xml_out = None
-
- def invoke_successfully(self, xml, enable_tunneling): # pylint: disable=unused-argument
- ''' mock invoke_successfully returning xml data '''
- self.xml_in = xml
- if self.kind == 'rule':
- xml = self.build_policy_rule(self.data)
- if self.kind == 'rules':
- xml = self.build_policy_rule(self.data, multiple=True)
- if self.kind == 'policy':
- xml = self.build_policy()
- self.xml_out = xml
- return xml
-
- @staticmethod
- def build_policy_rule(policy, multiple=False):
- ''' build xml data for vserser-info '''
- xml = netapp_utils.zapi.NaElement('xml')
- attributes = {'attributes-list': {
- 'export-rule-info': {
- 'policy-name': policy['name'],
- 'client-match': policy['client_match'],
- 'ro-rule': {
- 'security-flavor': 'any'
- },
- 'rw-rule': {
- 'security-flavor': 'any'
- },
- 'protocol': {
- 'access-protocol': policy['protocol']
- },
- 'super-user-security': {
- 'security-flavor': 'any'
- },
- 'is-allow-set-uid-enabled': 'false',
- 'rule-index': policy['rule_index']
- }
- }, 'num-records': 2 if multiple is True else 1}
- xml.translate_struct(attributes)
- return xml
-
- @staticmethod
- def build_policy():
- ''' build xml data for export-policy-get-iter '''
- xml = netapp_utils.zapi.NaElement('xml')
- attributes = {
- 'num-records': 1,
-
- }
- xml.translate_struct(attributes)
- return xml
-
-
-class TestMyModule(unittest.TestCase):
- ''' a group of related Unit Tests '''
-
- def setUp(self):
- self.mock_module_helper = patch.multiple(basic.AnsibleModule,
- exit_json=exit_json,
- fail_json=fail_json)
- self.mock_module_helper.start()
- self.addCleanup(self.mock_module_helper.stop)
- self.server = MockONTAPConnection()
- self.mock_rule = {
- 'name': 'test',
- 'protocol': 'nfs',
- 'client_match': '1.1.1.0',
- 'rule_index': 10
- }
-
- def mock_rule_args(self):
- return {
- 'name': self.mock_rule['name'],
- 'client_match': self.mock_rule['client_match'],
- 'vserver': 'test',
- 'protocol': self.mock_rule['protocol'],
- 'rule_index': self.mock_rule['rule_index'],
- 'ro_rule': 'any',
- 'rw_rule': 'any',
- 'hostname': 'test',
- 'username': 'test_user',
- 'password': 'test_pass!'
- }
-
- def get_mock_object(self, kind=None):
- """
- Helper method to return an na_ontap_firewall_policy object
- :param kind: passes this param to MockONTAPConnection()
- :return: na_ontap_firewall_policy object
- """
- obj = policy_rule()
- obj.autosupport_log = Mock(return_value=None)
- if kind is None:
- obj.server = MockONTAPConnection()
- else:
- obj.server = MockONTAPConnection(kind=kind, data=self.mock_rule_args())
- return obj
-
- def test_module_fail_when_required_args_missing(self):
- ''' required arguments are reported as errors '''
- with pytest.raises(AnsibleFailJson) as exc:
- set_module_args({})
- policy_rule()
- print('Info: %s' % exc.value.args[0]['msg'])
-
- def test_get_nonexistent_rule(self):
- ''' Test if get_export_policy_rule returns None for non-existent policy '''
- set_module_args(self.mock_rule_args())
- result = self.get_mock_object().get_export_policy_rule()
- assert result is None
-
- def test_get_nonexistent_policy(self):
- ''' Test if get_export_policy returns None for non-existent policy '''
- set_module_args(self.mock_rule_args())
- result = self.get_mock_object().get_export_policy()
- assert result is None
-
- def test_get_existing_rule(self):
- ''' Test if get_export_policy_rule returns rule details for existing policy '''
- data = self.mock_rule_args()
- set_module_args(data)
- result = self.get_mock_object('rule').get_export_policy_rule()
- assert result['name'] == data['name']
- assert result['client_match'] == data['client_match']
- assert result['ro_rule'] == ['any'] # from build_rule()
-
- def test_get_existing_policy(self):
- ''' Test if get_export_policy returns policy details for existing policy '''
- data = self.mock_rule_args()
- set_module_args(data)
- result = self.get_mock_object('policy').get_export_policy()
- assert result is not None
-
- def test_create_missing_param_error(self):
- ''' Test validation error from create '''
- data = self.mock_rule_args()
- del data['ro_rule']
- set_module_args(data)
- with pytest.raises(AnsibleFailJson) as exc:
- self.get_mock_object().apply()
- msg = 'Error: Missing required param for creating export policy rule ro_rule'
- assert exc.value.args[0]['msg'] == msg
-
- def test_successful_create(self):
- ''' Test successful create '''
- set_module_args(self.mock_rule_args())
- with pytest.raises(AnsibleExitJson) as exc:
- self.get_mock_object().apply()
- assert exc.value.args[0]['changed']
-
- def test_create_idempotency(self):
- ''' Test create idempotency '''
- set_module_args(self.mock_rule_args())
- with pytest.raises(AnsibleExitJson) as exc:
- self.get_mock_object('rule').apply()
- assert not exc.value.args[0]['changed']
-
- def test_successful_delete_without_rule_index(self):
- ''' Test delete existing job '''
- data = self.mock_rule_args()
- data['state'] = 'absent'
- del data['rule_index']
- set_module_args(data)
- with pytest.raises(AnsibleExitJson) as exc:
- self.get_mock_object('rule').apply()
- assert exc.value.args[0]['changed']
-
- def test_delete_idempotency(self):
- ''' Test delete idempotency '''
- data = self.mock_rule_args()
- data['state'] = 'absent'
- set_module_args(data)
- with pytest.raises(AnsibleExitJson) as exc:
- self.get_mock_object().apply()
- assert not exc.value.args[0]['changed']
-
- def test_successful_modify(self):
- ''' Test successful modify protocol '''
- data = self.mock_rule_args()
- data['protocol'] = ['cifs']
- data['allow_suid'] = 'true'
- set_module_args(data)
- with pytest.raises(AnsibleExitJson) as exc:
- self.get_mock_object('rule').apply()
- assert exc.value.args[0]['changed']
-
- def test_error_on_ambiguous_delete(self):
- ''' Test error if multiple entries match for a delete '''
- data = self.mock_rule_args()
- data['state'] = 'absent'
- set_module_args(data)
- with pytest.raises(AnsibleFailJson) as exc:
- self.get_mock_object('rules').apply()
- msg = "Multiple export policy rules exist.Please specify a rule_index to delete"
- assert exc.value.args[0]['msg'] == msg
-
- def test_helper_query_parameters(self):
- ''' Test helper method set_query_parameters() '''
- data = self.mock_rule_args()
- set_module_args(data)
- result = self.get_mock_object('rule').set_query_parameters()
- print(str(result))
- assert 'query' in result
- assert 'export-rule-info' in result['query']
- assert result['query']['export-rule-info']['rule-index'] == data['rule_index']
diff --git a/test/units/modules/storage/netapp/test_na_ontap_firewall_policy.py b/test/units/modules/storage/netapp/test_na_ontap_firewall_policy.py
deleted file mode 100644
index e8d48fe0db..0000000000
--- a/test/units/modules/storage/netapp/test_na_ontap_firewall_policy.py
+++ /dev/null
@@ -1,286 +0,0 @@
-# (c) 2018, NetApp, Inc
-# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-
-''' unit test template for ONTAP Ansible module '''
-
-from __future__ import print_function
-import json
-import pytest
-
-from units.compat import unittest
-from units.compat.mock import patch, Mock
-from ansible.module_utils import basic
-from ansible.module_utils._text import to_bytes
-import ansible.module_utils.netapp as netapp_utils
-
-from ansible.modules.storage.netapp.na_ontap_firewall_policy \
- import NetAppONTAPFirewallPolicy as fp_module # module under test
-
-if not netapp_utils.has_netapp_lib():
- pytestmark = pytest.mark.skip('skipping as missing required netapp_lib')
-
-
-def set_module_args(args):
- """prepare arguments so that they will be picked up during module creation"""
- args = json.dumps({'ANSIBLE_MODULE_ARGS': args})
- basic._ANSIBLE_ARGS = to_bytes(args) # pylint: disable=protected-access
-
-
-class AnsibleExitJson(Exception):
- """Exception class to be raised by module.exit_json and caught by the test case"""
- pass
-
-
-class AnsibleFailJson(Exception):
- """Exception class to be raised by module.fail_json and caught by the test case"""
- pass
-
-
-def exit_json(*args, **kwargs): # pylint: disable=unused-argument
- """function to patch over exit_json; package return data into an exception"""
- if 'changed' not in kwargs:
- kwargs['changed'] = False
- raise AnsibleExitJson(kwargs)
-
-
-def fail_json(*args, **kwargs): # pylint: disable=unused-argument
- """function to patch over fail_json; package return data into an exception"""
- kwargs['failed'] = True
- raise AnsibleFailJson(kwargs)
-
-
-class MockONTAPConnection(object):
- ''' mock server connection to ONTAP host '''
-
- def __init__(self, kind=None, data=None):
- ''' save arguments '''
- self.kind = kind
- self.data = data
- self.xml_in = None
- self.xml_out = None
-
- def invoke_successfully(self, xml, enable_tunneling): # pylint: disable=unused-argument
- ''' mock invoke_successfully returning xml data '''
- self.xml_in = xml
- if self.kind == 'policy':
- xml = self.build_policy_info(self.data)
- if self.kind == 'config':
- xml = self.build_firewall_config_info(self.data)
- self.xml_out = xml
- return xml
-
- @staticmethod
- def build_policy_info(data):
- ''' build xml data for net-firewall-policy-info '''
- xml = netapp_utils.zapi.NaElement('xml')
- attributes = {
- 'num-records': 1,
- 'attributes-list': {
- 'net-firewall-policy-info': {
- 'policy': data['policy'],
- 'service': data['service'],
- 'allow-list': [
- {'ip-and-mask': '1.2.3.0/24'}
- ]
- }
- }
- }
-
- xml.translate_struct(attributes)
- return xml
-
- @staticmethod
- def build_firewall_config_info(data):
- ''' build xml data for net-firewall-config-info '''
- xml = netapp_utils.zapi.NaElement('xml')
- attributes = {
- 'attributes': {
- 'net-firewall-config-info': {
- 'is-enabled': 'true',
- 'is-logging': 'false'
- }
- }
- }
- xml.translate_struct(attributes)
- return xml
-
-
-class TestMyModule(unittest.TestCase):
- ''' a group of related Unit Tests '''
-
- def setUp(self):
- self.mock_module_helper = patch.multiple(basic.AnsibleModule,
- exit_json=exit_json,
- fail_json=fail_json)
- self.mock_module_helper.start()
- self.addCleanup(self.mock_module_helper.stop)
- self.mock_policy = {
- 'policy': 'test',
- 'service': 'http',
- 'vserver': 'my_vserver',
- 'allow_list': '1.2.3.0/24'
- }
- self.mock_config = {
- 'node': 'test',
- 'enable': 'enable',
- 'logging': 'enable'
- }
-
- def mock_policy_args(self):
- return {
- 'policy': self.mock_policy['policy'],
- 'service': self.mock_policy['service'],
- 'vserver': self.mock_policy['vserver'],
- 'allow_list': [self.mock_policy['allow_list']],
- 'hostname': 'test',
- 'username': 'test_user',
- 'password': 'test_pass!'
- }
-
- def mock_config_args(self):
- return {
- 'node': self.mock_config['node'],
- 'enable': self.mock_config['enable'],
- 'logging': self.mock_config['logging'],
- 'hostname': 'test',
- 'username': 'test_user',
- 'password': 'test_pass!'
- }
-
- def get_mock_object(self, kind=None):
- """
- Helper method to return an na_ontap_firewall_policy object
- :param kind: passes this param to MockONTAPConnection()
- :return: na_ontap_firewall_policy object
- """
- obj = fp_module()
- obj.autosupport_log = Mock(return_value=None)
- if kind is None:
- obj.server = MockONTAPConnection()
- else:
- mock_data = self.mock_config if kind == 'config' else self.mock_policy
- obj.server = MockONTAPConnection(kind=kind, data=mock_data)
- return obj
-
- def test_module_fail_when_required_args_missing(self):
- ''' required arguments are reported as errors '''
- with pytest.raises(AnsibleFailJson) as exc:
- set_module_args({})
- fp_module()
- print('Info: %s' % exc.value.args[0]['msg'])
-
- def test_helper_firewall_policy_attributes(self):
- ''' helper returns dictionary with vserver, service and policy details '''
- data = self.mock_policy
- set_module_args(self.mock_policy_args())
- result = self.get_mock_object('policy').firewall_policy_attributes()
- del data['allow_list']
- assert data == result
-
- def test_helper_validate_ip_addresses_positive(self):
- ''' test if helper validates if IP is a network address '''
- data = self.mock_policy_args()
- data['allow_list'] = ['1.2.0.0/16', '1.2.3.0/24']
- set_module_args(data)
- result = self.get_mock_object().validate_ip_addresses()
- assert result is None
-
- def test_helper_validate_ip_addresses_negative(self):
- ''' test if helper validates if IP is a network address '''
- data = self.mock_policy_args()
- data['allow_list'] = ['1.2.0.10/16', '1.2.3.0/24']
- set_module_args(data)
- with pytest.raises(AnsibleFailJson) as exc:
- self.get_mock_object().validate_ip_addresses()
- msg = 'Error: Invalid IP address value for allow_list parameter.' \
- 'Please specify a network address without host bits set: ' \
- '1.2.0.10/16 has host bits set'
- assert exc.value.args[0]['msg'] == msg
-
- def test_get_nonexistent_policy(self):
- ''' Test if get_firewall_policy returns None for non-existent policy '''
- set_module_args(self.mock_policy_args())
- result = self.get_mock_object().get_firewall_policy()
- assert result is None
-
- def test_get_existing_policy(self):
- ''' Test if get_firewall_policy returns policy details for existing policy '''
- data = self.mock_policy_args()
- set_module_args(data)
- result = self.get_mock_object('policy').get_firewall_policy()
- assert result['service'] == data['service']
- assert result['allow_list'] == ['1.2.3.0/24'] # from build_policy_info()
-
- def test_successful_create(self):
- ''' Test successful create '''
- set_module_args(self.mock_policy_args())
- with pytest.raises(AnsibleExitJson) as exc:
- self.get_mock_object().apply()
- assert exc.value.args[0]['changed']
-
- def test_create_idempotency(self):
- ''' Test create idempotency '''
- set_module_args(self.mock_policy_args())
- with pytest.raises(AnsibleExitJson) as exc:
- self.get_mock_object('policy').apply()
- assert not exc.value.args[0]['changed']
-
- def test_successful_delete(self):
- ''' Test delete existing job '''
- data = self.mock_policy_args()
- data['state'] = 'absent'
- set_module_args(data)
- with pytest.raises(AnsibleExitJson) as exc:
- self.get_mock_object('policy').apply()
- assert exc.value.args[0]['changed']
-
- def test_delete_idempotency(self):
- ''' Test delete idempotency '''
- data = self.mock_policy_args()
- data['state'] = 'absent'
- set_module_args(data)
- with pytest.raises(AnsibleExitJson) as exc:
- self.get_mock_object().apply()
- assert not exc.value.args[0]['changed']
-
- def test_successful_modify(self):
- ''' Test successful modify allow_list '''
- data = self.mock_policy_args()
- data['allow_list'] = ['1.2.0.0/16']
- set_module_args(data)
- with pytest.raises(AnsibleExitJson) as exc:
- self.get_mock_object('policy').apply()
- assert exc.value.args[0]['changed']
-
- def test_successful_modify_multiple_ips(self):
- ''' Test successful modify allow_list '''
- data = self.mock_policy_args()
- data['allow_list'] = ['1.2.0.0/16', '1.0.0.0/8']
- set_module_args(data)
- with pytest.raises(AnsibleExitJson) as exc:
- self.get_mock_object('policy').apply()
- assert exc.value.args[0]['changed']
-
- def test_get_nonexistent_config(self):
- ''' Test if get_firewall_config returns None for non-existent node '''
- set_module_args(self.mock_config_args())
- result = self.get_mock_object().get_firewall_config_for_node()
- assert result is None
-
- def test_get_existing_config(self):
- ''' Test if get_firewall_config returns policy details for existing node '''
- data = self.mock_config_args()
- set_module_args(data)
- result = self.get_mock_object('config').get_firewall_config_for_node()
- assert result['enable'] == 'enable' # from build_config_info()
- assert result['logging'] == 'disable' # from build_config_info()
-
- def test_successful_modify_config(self):
- ''' Test successful modify allow_list '''
- data = self.mock_config_args()
- data['enable'] = 'disable'
- data['logging'] = 'enable'
- set_module_args(data)
- with pytest.raises(AnsibleExitJson) as exc:
- self.get_mock_object('config').apply()
- assert exc.value.args[0]['changed']
diff --git a/test/units/modules/storage/netapp/test_na_ontap_firmware_upgrade.py b/test/units/modules/storage/netapp/test_na_ontap_firmware_upgrade.py
deleted file mode 100644
index 5c2399f0dd..0000000000
--- a/test/units/modules/storage/netapp/test_na_ontap_firmware_upgrade.py
+++ /dev/null
@@ -1,291 +0,0 @@
-# (c) 2018, NetApp, Inc
-# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-
-''' unit tests ONTAP Ansible module: na_ontap_firmware_upgrade '''
-
-from __future__ import (absolute_import, division, print_function)
-__metaclass__ = type
-import json
-import pytest
-
-from units.compat import unittest
-from units.compat.mock import patch, Mock
-from ansible.module_utils import basic
-from ansible.module_utils._text import to_bytes
-import ansible.module_utils.netapp as netapp_utils
-
-from ansible.modules.storage.netapp.na_ontap_firmware_upgrade\
- import NetAppONTAPFirmwareUpgrade as my_module # module under test
-
-if not netapp_utils.has_netapp_lib():
- pytestmark = pytest.mark.skip('skipping as missing required netapp_lib')
-
-
-def set_module_args(args):
- """prepare arguments so that they will be picked up during module creation"""
- args = json.dumps({'ANSIBLE_MODULE_ARGS': args})
- basic._ANSIBLE_ARGS = to_bytes(args) # pylint: disable=protected-access
-
-
-class AnsibleExitJson(Exception):
- """Exception class to be raised by module.exit_json and caught by the test case"""
- pass
-
-
-class AnsibleFailJson(Exception):
- """Exception class to be raised by module.fail_json and caught by the test case"""
- pass
-
-
-def exit_json(*args, **kwargs): # pylint: disable=unused-argument
- """function to patch over exit_json; package return data into an exception"""
- if 'changed' not in kwargs:
- kwargs['changed'] = False
- raise AnsibleExitJson(kwargs)
-
-
-def fail_json(*args, **kwargs): # pylint: disable=unused-argument
- """function to patch over fail_json; package return data into an exception"""
- kwargs['failed'] = True
- raise AnsibleFailJson(kwargs)
-
-
-class MockONTAPConnection(object):
- ''' mock server connection to ONTAP host '''
-
- def __init__(self, kind=None, parm1=None, parm2=None, parm3=None):
- ''' save arguments '''
- self.type = kind
- self.parm1 = parm1
- self.parm2 = parm2
- # self.parm3 = parm3
- self.xml_in = None
- self.xml_out = None
- self.firmware_type = 'None'
-
- def invoke_successfully(self, xml, enable_tunneling): # pylint: disable=unused-argument
- ''' mock invoke_successfully returning xml data '''
- self.xml_in = xml
- if self.type == 'firmware_upgrade':
- xml = self.build_firmware_upgrade_info(self.parm1, self.parm2)
- if self.type == 'acp':
- xml = self.build_acp_firmware_info(self.firmware_type)
- self.xml_out = xml
- return xml
-
- def autosupport_log(self):
- ''' mock autosupport log'''
- return None
-
- @staticmethod
- def build_firmware_upgrade_info(version, node):
- ''' build xml data for service-processor firmware info '''
- xml = netapp_utils.zapi.NaElement('xml')
- data = {
- 'num-records': 1,
- 'attributes-list': {'service-processor-info': {'firmware-version': '3.4'}}
- }
- xml.translate_struct(data)
- print(xml.to_string())
- return xml
-
- @staticmethod
- def build_acp_firmware_info(firmware_type):
- ''' build xml data for acp firmware info '''
- xml = netapp_utils.zapi.NaElement('xml')
- data = {
- # 'num-records': 1,
- 'attributes-list': {'storage-shelf-acp-module': {'state': 'firmware_update_required'}}
- }
- xml.translate_struct(data)
- print(xml.to_string())
- return xml
-
-
-class TestMyModule(unittest.TestCase):
- ''' a group of related Unit Tests '''
-
- def setUp(self):
- self.mock_module_helper = patch.multiple(basic.AnsibleModule,
- exit_json=exit_json,
- fail_json=fail_json)
- self.mock_module_helper.start()
- self.addCleanup(self.mock_module_helper.stop)
- self.server = MockONTAPConnection()
- self.use_vsim = False
-
- def set_default_args(self):
- if self.use_vsim:
- hostname = '10.10.10.10'
- username = 'admin'
- password = 'admin'
- node = 'vsim1'
- clear_logs = True
- package = 'test1.zip'
- install_baseline_image = False
- update_type = 'serial_full'
- else:
- hostname = 'hostname'
- username = 'username'
- password = 'password'
- node = 'abc'
- package = 'test1.zip'
- clear_logs = True
- install_baseline_image = False
- update_type = 'serial_full'
-
- return dict({
- 'hostname': hostname,
- 'username': username,
- 'password': password,
- 'node': node,
- 'package': package,
- 'clear_logs': clear_logs,
- 'install_baseline_image': install_baseline_image,
- 'update_type': update_type,
- 'https': 'true'
- })
-
- def test_module_fail_when_required_args_missing(self):
- ''' required arguments are reported as errors '''
- with pytest.raises(AnsibleFailJson) as exc:
- set_module_args({})
- my_module()
- print('Info: %s' % exc.value.args[0]['msg'])
-
- def test_missing_parameters(self):
- ''' fail if firmware_type is missing '''
- module_args = {}
- module_args.update(self.set_default_args())
- set_module_args(module_args)
- with pytest.raises(AnsibleFailJson) as exc:
- set_module_args(module_args)
- my_module()
- msg = 'missing required arguments: firmware_type'
- print('Info: %s' % exc.value.args[0]['msg'])
- assert exc.value.args[0]['msg'] == msg
-
- def test_invalid_firmware_type_parameters(self):
- ''' fail if firmware_type is missing '''
- module_args = {}
- module_args.update(self.set_default_args())
- module_args.update({'firmware_type': 'service_test'})
- set_module_args(module_args)
- with pytest.raises(AnsibleFailJson) as exc:
- set_module_args(module_args)
- my_module()
- msg = 'value of firmware_type must be one of: service-processor, shelf, acp, disk, got: %s' % module_args['firmware_type']
- print('Info: %s' % exc.value.args[0]['msg'])
- assert exc.value.args[0]['msg'] == msg
-
- def test_ensure_sp_firmware_get_called(self):
- ''' a more interesting test '''
- module_args = {}
- module_args.update(self.set_default_args())
- module_args.update({'firmware_type': 'service-processor'})
- set_module_args(module_args)
- my_obj = my_module()
- my_obj.server = self.server
- firmware_image_get = my_obj.firmware_image_get('node')
- print('Info: test_firmware_upgrade_get: %s' % repr(firmware_image_get))
- assert firmware_image_get is None
-
- def test_ensure_firmware_get_with_package_baseline_called(self):
- ''' a more interesting test '''
- module_args = {}
- module_args.update(self.set_default_args())
- module_args.update({'firmware_type': 'service-processor'})
- module_args.update({'package': 'test1.zip'})
- module_args.update({'install_baseline_image': True})
- with pytest.raises(AnsibleFailJson) as exc:
- set_module_args(module_args)
- my_module()
- msg = 'Do not specify both package and install_baseline_image: true'
- print('info: ' + exc.value.args[0]['msg'])
- assert exc.value.args[0]['msg'] == msg
-
- def test_ensure_acp_firmware_required_get_called(self):
- ''' a test tp verify acp firmware upgrade is required or not '''
- module_args = {}
- module_args.update(self.set_default_args())
- module_args.update({'firmware_type': 'acp'})
- set_module_args(module_args)
- my_obj = my_module()
- # my_obj.server = self.server
- my_obj.server = MockONTAPConnection(kind='acp')
- acp_firmware_required_get = my_obj.acp_firmware_required_get()
- print('Info: test_acp_firmware_upgrade_required_get: %s' % repr(acp_firmware_required_get))
- assert acp_firmware_required_get is True
-
- @patch('ansible.modules.storage.netapp.na_ontap_firmware_upgrade.NetAppONTAPFirmwareUpgrade.sp_firmware_image_update')
- @patch('ansible.modules.storage.netapp.na_ontap_firmware_upgrade.NetAppONTAPFirmwareUpgrade.sp_firmware_image_update_progress_get')
- def test_ensure_apply_for_firmware_upgrade_called(self, get_mock, update_mock):
- ''' updgrading firmware and checking idempotency '''
- module_args = {}
- module_args.update(self.set_default_args())
- module_args.update({'package': 'test1.zip'})
- module_args.update({'firmware_type': 'service-processor'})
- set_module_args(module_args)
- my_obj = my_module()
- my_obj.autosupport_log = Mock(return_value=None)
- if not self.use_vsim:
- my_obj.server = self.server
- with pytest.raises(AnsibleExitJson) as exc:
- my_obj.apply()
- print('Info: test_firmware_upgrade_apply: %s' % repr(exc.value))
- assert not exc.value.args[0]['changed']
- if not self.use_vsim:
- my_obj.server = MockONTAPConnection('firmware_upgrade', '3.5', 'true')
- with pytest.raises(AnsibleExitJson) as exc:
- my_obj.apply()
- print('Info: test_firmware_upgrade_apply: %s' % repr(exc.value))
- assert exc.value.args[0]['changed']
- update_mock.assert_called_with()
-
- def test_shelf_firmware_upgrade(self):
- ''' Test shelf firmware upgrade '''
- module_args = {}
- module_args.update(self.set_default_args())
- module_args.update({'firmware_type': 'shelf'})
- set_module_args(module_args)
- my_obj = my_module()
- my_obj.autosupport_log = Mock(return_value=None)
- if not self.use_vsim:
- my_obj.server = self.server
- with pytest.raises(AnsibleExitJson) as exc:
- my_obj.apply()
- print('Info: test_firmware_upgrade_apply: %s' % repr(exc.value))
- assert exc.value.args[0]['changed']
-
- @patch('ansible.modules.storage.netapp.na_ontap_firmware_upgrade.NetAppONTAPFirmwareUpgrade.acp_firmware_upgrade')
- @patch('ansible.modules.storage.netapp.na_ontap_firmware_upgrade.NetAppONTAPFirmwareUpgrade.acp_firmware_required_get')
- def test_acp_firmware_upgrade(self, get_mock, update_mock):
- ''' Test ACP firmware upgrade '''
- module_args = {}
- module_args.update(self.set_default_args())
- module_args.update({'firmware_type': 'acp'})
- set_module_args(module_args)
- my_obj = my_module()
- my_obj.autosupport_log = Mock(return_value=None)
- if not self.use_vsim:
- my_obj.server = self.server
- with pytest.raises(AnsibleExitJson) as exc:
- my_obj.apply()
- print('Info: test_firmware_upgrade_apply: %s' % repr(exc.value))
- assert exc.value.args[0]['changed']
-
- @patch('ansible.modules.storage.netapp.na_ontap_firmware_upgrade.NetAppONTAPFirmwareUpgrade.disk_firmware_upgrade')
- def test_disk_firmware_upgrade(self, get_mock):
- ''' Test disk firmware upgrade '''
- module_args = {}
- module_args.update(self.set_default_args())
- module_args.update({'firmware_type': 'disk'})
- set_module_args(module_args)
- my_obj = my_module()
- my_obj.autosupport_log = Mock(return_value=None)
- if not self.use_vsim:
- my_obj.server = self.server
- with pytest.raises(AnsibleExitJson) as exc:
- my_obj.apply()
- print('Info: test_firmware_upgrade_apply: %s' % repr(exc.value))
- assert exc.value.args[0]['changed']
diff --git a/test/units/modules/storage/netapp/test_na_ontap_flexcache.py b/test/units/modules/storage/netapp/test_na_ontap_flexcache.py
deleted file mode 100644
index e7c5fb941c..0000000000
--- a/test/units/modules/storage/netapp/test_na_ontap_flexcache.py
+++ /dev/null
@@ -1,529 +0,0 @@
-# (c) 2018, NetApp, Inc
-# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-
-''' unit test for ONTAP FlexCache Ansible module '''
-
-from __future__ import print_function
-import json
-import pytest
-
-from units.compat import unittest
-from units.compat.mock import patch
-from ansible.module_utils import basic
-from ansible.module_utils._text import to_bytes
-import ansible.module_utils.netapp as netapp_utils
-
-from ansible.modules.storage.netapp.na_ontap_flexcache \
- import NetAppONTAPFlexCache as my_module # module under test
-
-if not netapp_utils.has_netapp_lib():
- pytestmark = pytest.mark.skip('skipping as missing required netapp_lib')
-
-
-def set_module_args(args):
- """prepare arguments so that they will be picked up during module creation"""
- args = json.dumps({'ANSIBLE_MODULE_ARGS': args})
- basic._ANSIBLE_ARGS = to_bytes(args) # pylint: disable=protected-access
-
-
-class AnsibleExitJson(Exception):
- """Exception class to be raised by module.exit_json and caught by the test case"""
- pass
-
-
-class AnsibleFailJson(Exception):
- """Exception class to be raised by module.fail_json and caught by the test case"""
- pass
-
-
-def exit_json(*args, **kwargs): # pylint: disable=unused-argument
- """function to patch over exit_json; package return data into an exception"""
- if 'changed' not in kwargs:
- kwargs['changed'] = False
- raise AnsibleExitJson(kwargs)
-
-
-def fail_json(*args, **kwargs): # pylint: disable=unused-argument
- """function to patch over fail_json; package return data into an exception"""
- kwargs['failed'] = True
- raise AnsibleFailJson(kwargs)
-
-
-class MockONTAPConnection(object):
- ''' mock server connection to ONTAP host '''
-
- def __init__(self, kind=None, parm1=None, api_error=None, job_error=None):
- ''' save arguments '''
- self.type = kind
- self.parm1 = parm1
- self.api_error = api_error
- self.job_error = job_error
- self.xml_in = None
- self.xml_out = None
-
- def invoke_successfully(self, xml, enable_tunneling): # pylint: disable=unused-argument
- ''' mock invoke_successfully returning xml data '''
- self.xml_in = xml
- tag = xml.get_name()
- if tag == 'flexcache-get-iter' and self.type == 'vserver':
- xml = self.build_flexcache_info(self.parm1)
- elif tag == 'flexcache-create-async':
- xml = self.build_flexcache_create_destroy_rsp()
- elif tag == 'flexcache-destroy-async':
- if self.api_error:
- code, message = self.api_error.split(':', 2)
- raise netapp_utils.zapi.NaApiError(code, message)
- xml = self.build_flexcache_create_destroy_rsp()
- elif tag == 'job-get':
- xml = self.build_job_info(self.job_error)
- self.xml_out = xml
- return xml
-
- @staticmethod
- def build_flexcache_info(vserver):
- ''' build xml data for vserser-info '''
- xml = netapp_utils.zapi.NaElement('xml')
- attributes = netapp_utils.zapi.NaElement('attributes-list')
- count = 2 if vserver == 'repeats' else 1
- for dummy in range(count):
- attributes.add_node_with_children('flexcache-info', **{
- 'vserver': vserver,
- 'origin-vserver': 'ovserver',
- 'origin-volume': 'ovolume',
- 'origin-cluster': 'ocluster',
- 'volume': 'volume',
- })
- xml.add_child_elem(attributes)
- xml.add_new_child('num-records', str(count))
- return xml
-
- @staticmethod
- def build_flexcache_create_destroy_rsp():
- ''' build xml data for a create or destroy response '''
- xml = netapp_utils.zapi.NaElement('xml')
- xml.add_new_child('result-status', 'in_progress')
- xml.add_new_child('result-jobid', '1234')
- return xml
-
- @staticmethod
- def build_job_info(error):
- ''' build xml data for a job '''
- xml = netapp_utils.zapi.NaElement('xml')
- attributes = netapp_utils.zapi.NaElement('attributes')
- if error is None:
- state = 'success'
- elif error == 'time_out':
- state = 'running'
- else:
- state = 'failure'
- attributes.add_node_with_children('job-info', **{
- 'job-state': state,
- 'job-progress': 'dummy',
- 'job-completion': error,
- })
- xml.add_child_elem(attributes)
- xml.add_new_child('result-status', 'in_progress')
- xml.add_new_child('result-jobid', '1234')
- return xml
-
-
-class TestMyModule(unittest.TestCase):
- ''' a group of related Unit Tests '''
-
- def setUp(self):
- self.mock_module_helper = patch.multiple(basic.AnsibleModule,
- exit_json=exit_json,
- fail_json=fail_json)
- self.mock_module_helper.start()
- self.addCleanup(self.mock_module_helper.stop)
- # make sure to change this to False before submitting
- self.onbox = False
- self.dummy_args = dict()
- for arg in ('hostname', 'username', 'password'):
- self.dummy_args[arg] = arg
- if self.onbox:
- self.args = {
- 'hostname': '10.193.78.219',
- 'username': 'admin',
- 'password': 'netapp1!',
- }
- else:
- self.args = self.dummy_args
- self.server = MockONTAPConnection()
-
- def create_flexcache(self, vserver, volume, junction_path):
- ''' create flexcache '''
- if not self.onbox:
- return
- args = {
- 'state': 'present',
- 'volume': volume,
- 'size': '90', # 80MB minimum
- 'size_unit': 'mb', # 80MB minimum
- 'vserver': vserver,
- 'aggr_list': 'aggr1',
- 'origin_volume': 'fc_vol_origin',
- 'origin_vserver': 'ansibleSVM',
- 'junction_path': junction_path,
- }
- args.update(self.args)
- set_module_args(args)
- my_obj = my_module()
- try:
- my_obj.apply()
- except AnsibleExitJson as exc:
- print('Create util: ' + repr(exc))
- except AnsibleFailJson as exc:
- print('Create util: ' + repr(exc))
-
- def delete_flexcache(self, vserver, volume):
- ''' delete flexcache '''
- if not self.onbox:
- return
- args = {'volume': volume, 'vserver': vserver, 'state': 'absent', 'force_offline': 'true'}
- args.update(self.args)
- set_module_args(args)
- my_obj = my_module()
- try:
- my_obj.apply()
- except AnsibleExitJson as exc:
- print('Delete util: ' + repr(exc))
- except AnsibleFailJson as exc:
- print('Delete util: ' + repr(exc))
-
- def test_module_fail_when_required_args_missing(self):
- ''' required arguments are reported as errors '''
- with pytest.raises(AnsibleFailJson) as exc:
- set_module_args({})
- my_module()
- print('Info: %s' % exc.value.args[0]['msg'])
-
- def test_missing_parameters(self):
- ''' fail if origin volume and origin verser are missing '''
- args = {
- 'vserver': 'vserver',
- 'volume': 'volume'
- }
- args.update(self.dummy_args)
- set_module_args(args)
- my_obj = my_module()
- my_obj.server = self.server
- with pytest.raises(AnsibleFailJson) as exc:
- # It may not be a good idea to start with apply
- # More atomic methods can be easier to mock
- # Hint: start with get methods, as they are called first
- my_obj.apply()
- msg = 'Missing parameters: origin_volume, origin_vserver'
- assert exc.value.args[0]['msg'] == msg
-
- def test_missing_parameter(self):
- ''' fail if origin verser parameter is missing '''
- args = {
- 'vserver': 'vserver',
- 'origin_volume': 'origin_volume',
- 'volume': 'volume'
- }
- args.update(self.dummy_args)
- set_module_args(args)
- my_obj = my_module()
- my_obj.server = self.server
- with pytest.raises(AnsibleFailJson) as exc:
- my_obj.apply()
- msg = 'Missing parameter: origin_vserver'
- assert exc.value.args[0]['msg'] == msg
-
- def test_get_flexcache(self):
- ''' get flexcache info '''
- args = {
- 'vserver': 'ansibleSVM',
- 'origin_volume': 'origin_volume',
- 'volume': 'volume'
- }
- args.update(self.args)
- set_module_args(args)
- my_obj = my_module()
- if not self.onbox:
- my_obj.server = MockONTAPConnection('vserver')
- info = my_obj.flexcache_get()
- print('info: ' + repr(info))
-
- def test_get_flexcache_double(self):
- ''' get flexcache info returns 2 entries! '''
- args = {
- 'vserver': 'ansibleSVM',
- 'origin_volume': 'origin_volume',
- 'volume': 'volume'
- }
- args.update(self.dummy_args)
- set_module_args(args)
- my_obj = my_module()
- my_obj.server = MockONTAPConnection('vserver', 'repeats')
- with pytest.raises(AnsibleFailJson) as exc:
- my_obj.flexcache_get()
- msg = 'Error fetching FlexCache info: Multiple records found for %s:' % args['volume']
- assert exc.value.args[0]['msg'] == msg
-
- def test_create_flexcache(self):
- ''' create flexcache '''
- args = {
- 'volume': 'volume',
- 'size': '90', # 80MB minimum
- 'size_unit': 'mb', # 80MB minimum
- 'vserver': 'ansibleSVM',
- 'aggr_list': 'aggr1',
- 'origin_volume': 'fc_vol_origin',
- 'origin_vserver': 'ansibleSVM',
- }
- self.delete_flexcache(args['vserver'], args['volume'])
- args.update(self.args)
- set_module_args(args)
- my_obj = my_module()
- if not self.onbox:
- my_obj.server = MockONTAPConnection()
- with patch.object(my_module, 'flexcache_create', wraps=my_obj.flexcache_create) as mock_create:
- # with patch('__main__.my_module.flexcache_create', wraps=my_obj.flexcache_create) as mock_create:
- with pytest.raises(AnsibleExitJson) as exc:
- my_obj.apply()
- print('Create: ' + repr(exc.value))
- assert exc.value.args[0]['changed']
- mock_create.assert_called_with()
-
- def test_create_flexcache_idempotent(self):
- ''' create flexcache - already exists '''
- args = {
- 'volume': 'volume',
- 'vserver': 'ansibleSVM',
- 'aggr_list': 'aggr1',
- 'origin_volume': 'fc_vol_origin',
- 'origin_vserver': 'ansibleSVM',
- }
- args.update(self.args)
- set_module_args(args)
- my_obj = my_module()
- if not self.onbox:
- my_obj.server = MockONTAPConnection('vserver')
- with pytest.raises(AnsibleExitJson) as exc:
- my_obj.apply()
- print('Create: ' + repr(exc.value))
- assert exc.value.args[0]['changed'] is False
-
- def test_create_flexcache_autoprovision(self):
- ''' create flexcache with autoprovision'''
- args = {
- 'volume': 'volume',
- 'size': '90', # 80MB minimum
- 'size_unit': 'mb', # 80MB minimum
- 'vserver': 'ansibleSVM',
- 'auto_provision_as': 'flexgroup',
- 'origin_volume': 'fc_vol_origin',
- 'origin_vserver': 'ansibleSVM',
- }
- self.delete_flexcache(args['vserver'], args['volume'])
- args.update(self.args)
- set_module_args(args)
- my_obj = my_module()
- if not self.onbox:
- my_obj.server = MockONTAPConnection()
- with patch.object(my_module, 'flexcache_create', wraps=my_obj.flexcache_create) as mock_create:
- with pytest.raises(AnsibleExitJson) as exc:
- my_obj.apply()
- print('Create: ' + repr(exc.value))
- assert exc.value.args[0]['changed']
- mock_create.assert_called_with()
-
- def test_create_flexcache_autoprovision_idempotent(self):
- ''' create flexcache with autoprovision - already exists '''
- args = {
- 'volume': 'volume',
- 'vserver': 'ansibleSVM',
- 'origin_volume': 'fc_vol_origin',
- 'origin_vserver': 'ansibleSVM',
- 'auto_provision_as': 'flexgroup',
- }
- args.update(self.args)
- set_module_args(args)
- my_obj = my_module()
- if not self.onbox:
- my_obj.server = MockONTAPConnection('vserver')
- with pytest.raises(AnsibleExitJson) as exc:
- my_obj.apply()
- print('Create: ' + repr(exc.value))
- assert exc.value.args[0]['changed'] is False
-
- def test_create_flexcache_multiplier(self):
- ''' create flexcache with aggregate multiplier'''
- args = {
- 'volume': 'volume',
- 'size': '90', # 80MB minimum
- 'size_unit': 'mb', # 80MB minimum
- 'vserver': 'ansibleSVM',
- 'aggr_list': 'aggr1',
- 'origin_volume': 'fc_vol_origin',
- 'origin_vserver': 'ansibleSVM',
- 'aggr_list_multiplier': '2',
- }
- self.delete_flexcache(args['vserver'], args['volume'])
- args.update(self.args)
- set_module_args(args)
- my_obj = my_module()
- if not self.onbox:
- my_obj.server = MockONTAPConnection()
- with patch.object(my_module, 'flexcache_create', wraps=my_obj.flexcache_create) as mock_create:
- with pytest.raises(AnsibleExitJson) as exc:
- my_obj.apply()
- print('Create: ' + repr(exc.value))
- assert exc.value.args[0]['changed']
- mock_create.assert_called_with()
-
- def test_create_flexcache_multiplier_idempotent(self):
- ''' create flexcache with aggregate multiplier - already exists '''
- args = {
- 'volume': 'volume',
- 'vserver': 'ansibleSVM',
- 'aggr_list': 'aggr1',
- 'origin_volume': 'fc_vol_origin',
- 'origin_vserver': 'ansibleSVM',
- 'aggr_list_multiplier': '2',
- }
- args.update(self.args)
- set_module_args(args)
- my_obj = my_module()
- if not self.onbox:
- my_obj.server = MockONTAPConnection('vserver')
- with pytest.raises(AnsibleExitJson) as exc:
- my_obj.apply()
- print('Create: ' + repr(exc.value))
- assert exc.value.args[0]['changed'] is False
-
- def test_delete_flexcache_exists_no_force(self):
- ''' delete flexcache '''
- args = {'volume': 'volume', 'vserver': 'ansibleSVM', 'state': 'absent'}
- args.update(self.args)
- set_module_args(args)
- my_obj = my_module()
- error = '13001:Volume volume in Vserver ansibleSVM must be offline to be deleted. ' \
- 'Use "volume offline -vserver ansibleSVM -volume volume" command to offline ' \
- 'the volume'
- if not self.onbox:
- my_obj.server = MockONTAPConnection('vserver', 'flex', api_error=error)
- with patch.object(my_module, 'flexcache_delete', wraps=my_obj.flexcache_delete) as mock_delete:
- with pytest.raises(AnsibleFailJson) as exc:
- my_obj.apply()
- print('Delete: ' + repr(exc.value))
- msg = 'Error deleting FlexCache : NetApp API failed. Reason - %s' % error
- assert exc.value.args[0]['msg'] == msg
- mock_delete.assert_called_with()
-
- def test_delete_flexcache_exists_with_force(self):
- ''' delete flexcache '''
- args = {'volume': 'volume', 'vserver': 'ansibleSVM', 'state': 'absent', 'force_offline': 'true'}
- args.update(self.args)
- set_module_args(args)
- my_obj = my_module()
- if not self.onbox:
- my_obj.server = MockONTAPConnection('vserver', 'flex')
- with patch.object(my_module, 'flexcache_delete', wraps=my_obj.flexcache_delete) as mock_delete:
- with pytest.raises(AnsibleExitJson) as exc:
- my_obj.apply()
- print('Delete: ' + repr(exc.value))
- assert exc.value.args[0]['changed']
- mock_delete.assert_called_with()
-
- def test_delete_flexcache_exists_junctionpath_no_force(self):
- ''' delete flexcache '''
- args = {'volume': 'volume', 'vserver': 'ansibleSVM', 'junction_path': 'jpath', 'state': 'absent', 'force_offline': 'true'}
- self.create_flexcache(args['vserver'], args['volume'], args['junction_path'])
- args.update(self.args)
- set_module_args(args)
- my_obj = my_module()
- error = '160:Volume volume on Vserver ansibleSVM must be unmounted before being taken offline or restricted.'
- if not self.onbox:
- my_obj.server = MockONTAPConnection('vserver', 'flex', api_error=error)
- with patch.object(my_module, 'flexcache_delete', wraps=my_obj.flexcache_delete) as mock_delete:
- with pytest.raises(AnsibleFailJson) as exc:
- my_obj.apply()
- print('Delete: ' + repr(exc.value))
- msg = 'Error deleting FlexCache : NetApp API failed. Reason - %s' % error
- assert exc.value.args[0]['msg'] == msg
- mock_delete.assert_called_with()
-
- def test_delete_flexcache_exists_junctionpath_with_force(self):
- ''' delete flexcache '''
- args = {'volume': 'volume', 'vserver': 'ansibleSVM', 'junction_path': 'jpath', 'state': 'absent', 'force_offline': 'true', 'force_unmount': 'true'}
- self.create_flexcache(args['vserver'], args['volume'], args['junction_path'])
- args.update(self.args)
- set_module_args(args)
- my_obj = my_module()
- if not self.onbox:
- my_obj.server = MockONTAPConnection('vserver', 'flex')
- with patch.object(my_module, 'flexcache_delete', wraps=my_obj.flexcache_delete) as mock_delete:
- with pytest.raises(AnsibleExitJson) as exc:
- my_obj.apply()
- print('Delete: ' + repr(exc.value))
- assert exc.value.args[0]['changed']
- mock_delete.assert_called_with()
-
- def test_delete_flexcache_not_exist(self):
- ''' delete flexcache '''
- args = {'volume': 'volume', 'vserver': 'ansibleSVM', 'state': 'absent'}
- args.update(self.args)
- set_module_args(args)
- my_obj = my_module()
- if not self.onbox:
- my_obj.server = MockONTAPConnection()
- with pytest.raises(AnsibleExitJson) as exc:
- my_obj.apply()
- print('Delete: ' + repr(exc.value))
- assert exc.value.args[0]['changed'] is False
-
- def test_create_flexcache_size_error(self):
- ''' create flexcache '''
- args = {
- 'volume': 'volume_err',
- 'size': '50', # 80MB minimum
- 'size_unit': 'mb', # 80MB minimum
- 'vserver': 'ansibleSVM',
- 'aggr_list': 'aggr1',
- 'origin_volume': 'fc_vol_origin',
- 'origin_vserver': 'ansibleSVM',
- }
- args.update(self.args)
- set_module_args(args)
- my_obj = my_module()
- error = 'Size "50MB" ("52428800B") is too small. Minimum size is "80MB" ("83886080B"). '
- if not self.onbox:
- my_obj.server = MockONTAPConnection(job_error=error)
- with patch.object(my_module, 'flexcache_create', wraps=my_obj.flexcache_create) as mock_create:
- with pytest.raises(AnsibleFailJson) as exc:
- my_obj.apply()
- print('Create: ' + repr(exc.value))
- msg = 'Error when creating flexcache: %s' % error
- assert exc.value.args[0]['msg'] == msg
- mock_create.assert_called_with()
-
- @patch('time.sleep')
- def test_create_flexcache_time_out(self, mock_sleep):
- ''' create flexcache '''
- args = {
- 'volume': 'volume_err',
- 'size': '50', # 80MB minimum
- 'size_unit': 'mb', # 80MB minimum
- 'vserver': 'ansibleSVM',
- 'aggr_list': 'aggr1',
- 'origin_volume': 'fc_vol_origin',
- 'origin_vserver': 'ansibleSVM',
- 'time_out': '2'
- }
- args.update(self.args)
- set_module_args(args)
- my_obj = my_module()
- if not self.onbox:
- my_obj.server = MockONTAPConnection(job_error='time_out')
- with patch.object(my_module, 'flexcache_create', wraps=my_obj.flexcache_create) as mock_create:
- with pytest.raises(AnsibleFailJson) as exc:
- my_obj.apply()
- print('Create: ' + repr(exc.value))
- msg = 'Error when creating flexcache: job completion exceeded expected timer of: %s seconds' \
- % args['time_out']
- assert exc.value.args[0]['msg'] == msg
- mock_create.assert_called_with()
diff --git a/test/units/modules/storage/netapp/test_na_ontap_igroup.py b/test/units/modules/storage/netapp/test_na_ontap_igroup.py
deleted file mode 100644
index f52c89caca..0000000000
--- a/test/units/modules/storage/netapp/test_na_ontap_igroup.py
+++ /dev/null
@@ -1,259 +0,0 @@
-# (c) 2018, NetApp, Inc
-# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-
-''' unit test template for ONTAP Ansible module '''
-
-from __future__ import print_function
-import json
-import pytest
-
-from units.compat import unittest
-from units.compat.mock import patch, Mock
-from ansible.module_utils import basic
-from ansible.module_utils._text import to_bytes
-import ansible.module_utils.netapp as netapp_utils
-
-from ansible.modules.storage.netapp.na_ontap_igroup \
- import NetAppOntapIgroup as igroup # module under test
-
-if not netapp_utils.has_netapp_lib():
- pytestmark = pytest.mark.skip('skipping as missing required netapp_lib')
-
-
-def set_module_args(args):
- """prepare arguments so that they will be picked up during module creation"""
- args = json.dumps({'ANSIBLE_MODULE_ARGS': args})
- basic._ANSIBLE_ARGS = to_bytes(args) # pylint: disable=protected-access
-
-
-class AnsibleExitJson(Exception):
- """Exception class to be raised by module.exit_json and caught by the test case"""
- pass
-
-
-class AnsibleFailJson(Exception):
- """Exception class to be raised by module.fail_json and caught by the test case"""
- pass
-
-
-def exit_json(*args, **kwargs): # pylint: disable=unused-argument
- """function to patch over exit_json; package return data into an exception"""
- if 'changed' not in kwargs:
- kwargs['changed'] = False
- raise AnsibleExitJson(kwargs)
-
-
-def fail_json(*args, **kwargs): # pylint: disable=unused-argument
- """function to patch over fail_json; package return data into an exception"""
- kwargs['failed'] = True
- raise AnsibleFailJson(kwargs)
-
-
-class MockONTAPConnection(object):
- ''' mock server connection to ONTAP host '''
-
- def __init__(self, kind=None, data=None):
- ''' save arguments '''
- self.kind = kind
- self.data = data
- self.xml_in = None
- self.xml_out = None
-
- def invoke_successfully(self, xml, enable_tunneling): # pylint: disable=unused-argument
- ''' mock invoke_successfully returning xml data '''
- self.xml_in = xml
- if self.kind == 'igroup':
- xml = self.build_igroup()
- if self.kind == 'igroup_no_initiators':
- xml = self.build_igroup_no_initiators()
- self.xml_out = xml
- return xml
-
- @staticmethod
- def build_igroup():
- ''' build xml data for initiator '''
- xml = netapp_utils.zapi.NaElement('xml')
- attributes = {
- 'num-records': 1,
- 'attributes-list': {
- 'initiator-group-info': {
- 'initiators': [
- {
- 'initiator-info': {
- 'initiator-name': 'init1'
- }},
- {
- 'initiator-info': {
- 'initiator-name': 'init2'
- }}
- ]
- }
- }
- }
- xml.translate_struct(attributes)
- return xml
-
- @staticmethod
- def build_igroup_no_initiators():
- ''' build xml data for igroup with no initiators '''
- xml = netapp_utils.zapi.NaElement('xml')
- attributes = {
- 'num-records': 1,
- 'attributes-list': {
- 'initiator-group-info': {
- 'vserver': 'test'
- }
- }
- }
- xml.translate_struct(attributes)
- return xml
-
-
-class TestMyModule(unittest.TestCase):
- ''' a group of related Unit Tests '''
-
- def setUp(self):
- self.mock_module_helper = patch.multiple(basic.AnsibleModule,
- exit_json=exit_json,
- fail_json=fail_json)
- self.mock_module_helper.start()
- self.addCleanup(self.mock_module_helper.stop)
- self.server = MockONTAPConnection()
-
- def mock_args(self):
- return {
- 'vserver': 'vserver',
- 'name': 'test',
- 'initiators': 'init1',
- 'ostype': 'linux',
- 'initiator_group_type': 'fcp',
- 'bind_portset': 'true',
- 'hostname': 'hostname',
- 'username': 'username',
- 'password': 'password'
- }
-
- def get_igroup_mock_object(self, kind=None):
- """
- Helper method to return an na_ontap_igroup object
- :param kind: passes this param to MockONTAPConnection()
- :return: na_ontap_igroup object
- """
- obj = igroup()
- obj.autosupport_log = Mock(return_value=None)
- if kind is None:
- obj.server = MockONTAPConnection()
- else:
- obj.server = MockONTAPConnection(kind=kind)
- return obj
-
- def test_module_fail_when_required_args_missing(self):
- ''' required arguments are reported as errors '''
- with pytest.raises(AnsibleFailJson) as exc:
- set_module_args({})
- igroup()
-
- def test_get_nonexistent_igroup(self):
- ''' Test if get_igroup returns None for non-existent igroup '''
- data = self.mock_args()
- set_module_args(data)
- result = self.get_igroup_mock_object().get_igroup('dummy')
- assert result is None
-
- def test_get_existing_igroup_with_initiators(self):
- ''' Test if get_igroup returns list of existing initiators '''
- data = self.mock_args()
- set_module_args(data)
- result = self.get_igroup_mock_object('igroup').get_igroup(data['name'])
- assert data['initiators'] in result['initiators']
- assert result['initiators'] == ['init1', 'init2']
-
- def test_get_existing_igroup_without_initiators(self):
- ''' Test if get_igroup returns empty list() '''
- data = self.mock_args()
- set_module_args(data)
- result = self.get_igroup_mock_object('igroup_no_initiators').get_igroup(data['name'])
- assert result['initiators'] == []
-
- @patch('ansible.modules.storage.netapp.na_ontap_igroup.NetAppOntapIgroup.add_initiators')
- @patch('ansible.modules.storage.netapp.na_ontap_igroup.NetAppOntapIgroup.remove_initiators')
- def test_modify_initiator_calls_add_and_remove(self, remove, add):
- '''Test remove_initiator() is called followed by add_initiator() on modify operation'''
- data = self.mock_args()
- data['initiators'] = 'replacewithme'
- set_module_args(data)
- obj = self.get_igroup_mock_object('igroup')
- with pytest.raises(AnsibleExitJson) as exc:
- current = obj.get_igroup(data['name'])
- obj.apply()
- remove.assert_called_with(current['initiators'])
- add.assert_called_with()
-
- @patch('ansible.modules.storage.netapp.na_ontap_igroup.NetAppOntapIgroup.modify_initiator')
- def test_modify_called_from_add(self, modify):
- '''Test remove_initiator() and add_initiator() calls modify'''
- data = self.mock_args()
- data['initiators'] = 'replacewithme'
- add, remove = 'igroup-add', 'igroup-remove'
- set_module_args(data)
- with pytest.raises(AnsibleExitJson) as exc:
- self.get_igroup_mock_object('igroup_no_initiators').apply()
- modify.assert_called_with('replacewithme', add)
- assert modify.call_count == 1 # remove nothing, add 1 new
-
- @patch('ansible.modules.storage.netapp.na_ontap_igroup.NetAppOntapIgroup.modify_initiator')
- def test_modify_called_from_remove(self, modify):
- '''Test remove_initiator() and add_initiator() calls modify'''
- data = self.mock_args()
- data['initiators'] = ''
- remove = 'igroup-remove'
- set_module_args(data)
- with pytest.raises(AnsibleExitJson) as exc:
- self.get_igroup_mock_object('igroup').apply()
- modify.assert_called_with('init2', remove)
- assert modify.call_count == 2 # remove existing 2, add nothing
-
- @patch('ansible.modules.storage.netapp.na_ontap_igroup.NetAppOntapIgroup.add_initiators')
- def test_successful_create(self, add):
- ''' Test successful create '''
- set_module_args(self.mock_args())
- with pytest.raises(AnsibleExitJson) as exc:
- self.get_igroup_mock_object().apply()
- assert exc.value.args[0]['changed']
- add.assert_called_with()
-
- def test_successful_delete(self):
- ''' Test successful delete '''
- data = self.mock_args()
- data['state'] = 'absent'
- set_module_args(self.mock_args())
- with pytest.raises(AnsibleExitJson) as exc:
- self.get_igroup_mock_object('igroup').apply()
- assert exc.value.args[0]['changed']
-
- def test_successful_modify(self):
- ''' Test successful modify '''
- data = self.mock_args()
- data['initiators'] = 'new'
- set_module_args(self.mock_args())
- with pytest.raises(AnsibleExitJson) as exc:
- self.get_igroup_mock_object('igroup').apply()
- assert exc.value.args[0]['changed']
-
- @patch('ansible.modules.storage.netapp.na_ontap_igroup.NetAppOntapIgroup.get_igroup')
- def test_successful_rename(self, get_vserver):
- '''Test successful rename'''
- data = self.mock_args()
- data['from_name'] = 'test'
- data['name'] = 'test_new'
- set_module_args(data)
- current = {
- 'initiators': ['init1', 'init2']
- }
- get_vserver.side_effect = [
- None,
- current
- ]
- with pytest.raises(AnsibleExitJson) as exc:
- self.get_igroup_mock_object().apply()
- assert exc.value.args[0]['changed']
diff --git a/test/units/modules/storage/netapp/test_na_ontap_igroup_initiator.py b/test/units/modules/storage/netapp/test_na_ontap_igroup_initiator.py
deleted file mode 100644
index e096273555..0000000000
--- a/test/units/modules/storage/netapp/test_na_ontap_igroup_initiator.py
+++ /dev/null
@@ -1,217 +0,0 @@
-# (c) 2018, NetApp, Inc
-# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-
-''' unit test template for ONTAP Ansible module '''
-
-from __future__ import print_function
-import json
-import pytest
-
-from units.compat import unittest
-from units.compat.mock import patch, Mock
-from ansible.module_utils import basic
-from ansible.module_utils._text import to_bytes
-import ansible.module_utils.netapp as netapp_utils
-
-from ansible.modules.storage.netapp.na_ontap_igroup_initiator \
- import NetAppOntapIgroupInitiator as initiator # module under test
-
-if not netapp_utils.has_netapp_lib():
- pytestmark = pytest.mark.skip('skipping as missing required netapp_lib')
-
-
-def set_module_args(args):
- """prepare arguments so that they will be picked up during module creation"""
- args = json.dumps({'ANSIBLE_MODULE_ARGS': args})
- basic._ANSIBLE_ARGS = to_bytes(args) # pylint: disable=protected-access
-
-
-class AnsibleExitJson(Exception):
- """Exception class to be raised by module.exit_json and caught by the test case"""
- pass
-
-
-class AnsibleFailJson(Exception):
- """Exception class to be raised by module.fail_json and caught by the test case"""
- pass
-
-
-def exit_json(*args, **kwargs): # pylint: disable=unused-argument
- """function to patch over exit_json; package return data into an exception"""
- if 'changed' not in kwargs:
- kwargs['changed'] = False
- raise AnsibleExitJson(kwargs)
-
-
-def fail_json(*args, **kwargs): # pylint: disable=unused-argument
- """function to patch over fail_json; package return data into an exception"""
- kwargs['failed'] = True
- raise AnsibleFailJson(kwargs)
-
-
-class MockONTAPConnection(object):
- ''' mock server connection to ONTAP host '''
-
- def __init__(self, kind=None, data=None):
- ''' save arguments '''
- self.kind = kind
- self.data = data
- self.xml_in = None
- self.xml_out = None
-
- def invoke_successfully(self, xml, enable_tunneling): # pylint: disable=unused-argument
- ''' mock invoke_successfully returning xml data '''
- self.xml_in = xml
- if self.kind == 'initiator':
- xml = self.build_igroup_initiator()
- elif self.kind == 'initiator_fail':
- raise netapp_utils.zapi.NaApiError(code='TEST', message="This exception is from the unit test")
- self.xml_out = xml
- return xml
-
- @staticmethod
- def build_igroup_initiator():
- ''' build xml data for initiator '''
- xml = netapp_utils.zapi.NaElement('xml')
- attributes = {
- 'num-records': 1,
- 'attributes-list': {
- 'initiator-group-info': {
- 'initiators': [
- {'initiator-info': {
- 'initiator-name': 'init1'
- }},
- {'initiator-info': {
- 'initiator-name': 'init2'
- }}
- ]
- }
- }
- }
- xml.translate_struct(attributes)
- return xml
-
-
-class TestMyModule(unittest.TestCase):
- ''' a group of related Unit Tests '''
-
- def setUp(self):
- self.mock_module_helper = patch.multiple(basic.AnsibleModule,
- exit_json=exit_json,
- fail_json=fail_json)
- self.mock_module_helper.start()
- self.addCleanup(self.mock_module_helper.stop)
- self.server = MockONTAPConnection()
-
- def mock_args(self):
- return {
- 'vserver': 'vserver',
- 'name': 'init1',
- 'initiator_group': 'test',
- 'hostname': 'hostname',
- 'username': 'username',
- 'password': 'password'
- }
-
- def get_initiator_mock_object(self, kind=None):
- """
- Helper method to return an na_ontap_initiator object
- :param kind: passes this param to MockONTAPConnection()
- :return: na_ontap_initiator object
- """
- obj = initiator()
- obj.autosupport_log = Mock(return_value=None)
- if kind is None:
- obj.server = MockONTAPConnection()
- else:
- obj.server = MockONTAPConnection(kind=kind)
- return obj
-
- def test_module_fail_when_required_args_missing(self):
- ''' required arguments are reported as errors '''
- with pytest.raises(AnsibleFailJson) as exc:
- set_module_args({})
- initiator()
-
- def test_get_nonexistent_initiator(self):
- ''' Test if get_initiators returns None for non-existent initiator '''
- data = self.mock_args()
- data['name'] = 'idontexist'
- set_module_args(data)
- result = self.get_initiator_mock_object('initiator').get_initiators()
- assert data['name'] not in result
-
- def test_get_nonexistent_igroup(self):
- ''' Test if get_initiators returns None for non-existent igroup '''
- data = self.mock_args()
- data['name'] = 'idontexist'
- set_module_args(data)
- result = self.get_initiator_mock_object().get_initiators()
- assert result == []
-
- def test_get_existing_initiator(self):
- ''' Test if get_initiator returns None for existing initiator '''
- data = self.mock_args()
- set_module_args(data)
- result = self.get_initiator_mock_object(kind='initiator').get_initiators()
- assert data['name'] in result
- assert result == ['init1', 'init2'] # from build_igroup_initiators()
-
- def test_successful_add(self):
- ''' Test successful add'''
- data = self.mock_args()
- data['name'] = 'iamnew'
- set_module_args(data)
- obj = self.get_initiator_mock_object('initiator')
- with pytest.raises(AnsibleExitJson) as exc:
- current = obj.get_initiators()
- obj.apply()
- assert data['name'] not in current
- assert exc.value.args[0]['changed']
-
- def test_successful_add_idempotency(self):
- ''' Test successful add idempotency '''
- data = self.mock_args()
- set_module_args(data)
- obj = self.get_initiator_mock_object('initiator')
- with pytest.raises(AnsibleExitJson) as exc:
- current_list = obj.get_initiators()
- obj.apply()
- assert data['name'] in current_list
- assert not exc.value.args[0]['changed']
-
- def test_successful_remove(self):
- ''' Test successful remove '''
- data = self.mock_args()
- data['state'] = 'absent'
- set_module_args(data)
- obj = self.get_initiator_mock_object('initiator')
- with pytest.raises(AnsibleExitJson) as exc:
- current_list = obj.get_initiators()
- obj.apply()
- assert data['name'] in current_list
- assert exc.value.args[0]['changed']
-
- def test_successful_remove_idempotency(self):
- ''' Test successful remove idempotency'''
- data = self.mock_args()
- data['state'] = 'absent'
- data['name'] = 'alreadyremoved'
- set_module_args(data)
- obj = self.get_initiator_mock_object('initiator')
- with pytest.raises(AnsibleExitJson) as exc:
- current_list = obj.get_initiators()
- obj.apply()
- assert data['name'] not in current_list
- assert not exc.value.args[0]['changed']
-
- def test_if_all_methods_catch_exception(self):
- data = self.mock_args()
- set_module_args(data)
- my_obj = self.get_initiator_mock_object('initiator_fail')
- with pytest.raises(AnsibleFailJson) as exc:
- my_obj.get_initiators()
- assert 'Error fetching igroup info ' in exc.value.args[0]['msg']
- with pytest.raises(AnsibleFailJson) as exc:
- my_obj.modify_initiator(data['name'], 'igroup-add')
- assert 'Error modifying igroup initiator ' in exc.value.args[0]['msg']
diff --git a/test/units/modules/storage/netapp/test_na_ontap_info.py b/test/units/modules/storage/netapp/test_na_ontap_info.py
deleted file mode 100644
index 389e2128b7..0000000000
--- a/test/units/modules/storage/netapp/test_na_ontap_info.py
+++ /dev/null
@@ -1,313 +0,0 @@
-# (c) 2018-2019, NetApp, Inc
-# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-
-''' unit tests for ONTAP Ansible module na_ontap_info '''
-
-from __future__ import print_function
-import json
-import pytest
-import sys
-
-from units.compat import unittest
-from units.compat.mock import patch
-from ansible.module_utils import basic
-from ansible.module_utils._text import to_bytes
-import ansible.module_utils.netapp as netapp_utils
-
-from ansible.modules.storage.netapp.na_ontap_info import main as info_main
-from ansible.modules.storage.netapp.na_ontap_info import __finditem as info_finditem
-from ansible.modules.storage.netapp.na_ontap_info \
- import NetAppONTAPGatherInfo as info_module # module under test
-
-if not netapp_utils.has_netapp_lib():
- pytestmark = pytest.mark.skip('skipping as missing required netapp_lib')
-
-
-def set_module_args(args):
- """prepare arguments so that they will be picked up during module creation"""
- args = json.dumps({'ANSIBLE_MODULE_ARGS': args})
- basic._ANSIBLE_ARGS = to_bytes(args) # pylint: disable=protected-access
-
-
-class AnsibleExitJson(Exception):
- """Exception class to be raised by module.exit_json and caught by the test case"""
- pass
-
-
-class AnsibleFailJson(Exception):
- """Exception class to be raised by module.fail_json and caught by the test case"""
- pass
-
-
-def exit_json(*args, **kwargs): # pylint: disable=unused-argument
- """function to patch over exit_json; package return data into an exception"""
- if 'changed' not in kwargs:
- kwargs['changed'] = False
- raise AnsibleExitJson(kwargs)
-
-
-def fail_json(*args, **kwargs): # pylint: disable=unused-argument
- """function to patch over fail_json; package return data into an exception"""
- kwargs['failed'] = True
- raise AnsibleFailJson(kwargs)
-
-
-class MockONTAPConnection(object):
- ''' mock server connection to ONTAP host '''
-
- def __init__(self, kind=None):
- ''' save arguments '''
- self.type = kind
- self.xml_in = None
- self.xml_out = None
-
- def invoke_successfully(self, xml, enable_tunneling): # pylint: disable=unused-argument
- ''' mock invoke_successfully returning xml data '''
- self.xml_in = xml
- if self.type == 'vserver':
- xml = self.build_vserver_info()
- elif self.type == 'net_port':
- xml = self.build_net_port_info()
- elif self.type == 'zapi_error':
- error = netapp_utils.zapi.NaApiError('test', 'error')
- raise error
- self.xml_out = xml
- return xml
-
- @staticmethod
- def build_vserver_info():
- ''' build xml data for vserser-info '''
- xml = netapp_utils.zapi.NaElement('xml')
- attributes = netapp_utils.zapi.NaElement('attributes-list')
- attributes.add_node_with_children('vserver-info',
- **{'vserver-name': 'test_vserver'})
- xml.add_child_elem(attributes)
- return xml
-
- @staticmethod
- def build_net_port_info():
- ''' build xml data for net-port-info '''
- xml = netapp_utils.zapi.NaElement('xml')
- attributes_list = netapp_utils.zapi.NaElement('attributes-list')
- num_net_port_info = 2
- for i in range(num_net_port_info):
- net_port_info = netapp_utils.zapi.NaElement('net-port-info')
- net_port_info.add_new_child('node', 'node_' + str(i))
- net_port_info.add_new_child('port', 'port_' + str(i))
- net_port_info.add_new_child('broadcast_domain', 'test_domain_' + str(i))
- net_port_info.add_new_child('ipspace', 'ipspace' + str(i))
- attributes_list.add_child_elem(net_port_info)
- xml.add_child_elem(attributes_list)
- return xml
-
-
-class TestMyModule(unittest.TestCase):
- ''' a group of related Unit Tests '''
-
- def setUp(self):
- self.mock_module_helper = patch.multiple(basic.AnsibleModule,
- exit_json=exit_json,
- fail_json=fail_json)
- self.mock_module_helper.start()
- self.addCleanup(self.mock_module_helper.stop)
- self.server = MockONTAPConnection()
-
- def mock_args(self):
- return {
- 'hostname': 'hostname',
- 'username': 'username',
- 'password': 'password',
- }
-
- def get_info_mock_object(self, kind=None):
- """
- Helper method to return an na_ontap_info object
- """
- module = basic.AnsibleModule(
- argument_spec=netapp_utils.na_ontap_host_argument_spec(),
- supports_check_mode=True
- )
- obj = info_module(module)
- obj.netapp_info = dict()
- if kind is None:
- obj.server = MockONTAPConnection()
- else:
- obj.server = MockONTAPConnection(kind)
- return obj
-
- def test_module_fail_when_required_args_missing(self):
- ''' required arguments are reported as errors '''
- with pytest.raises(AnsibleFailJson) as exc:
- set_module_args({})
- self.get_info_mock_object()
- print('Info: %s' % exc.value.args[0]['msg'])
-
- @patch('ansible.module_utils.netapp.ems_log_event')
- def test_ensure_command_called(self, mock_ems_log):
- ''' calling get_all will raise a KeyError exception '''
- set_module_args(self.mock_args())
- my_obj = self.get_info_mock_object('vserver')
- with pytest.raises(KeyError) as exc:
- my_obj.get_all(['net_interface_info'])
- if sys.version_info >= (2, 7):
- msg = 'net-interface-info'
- assert exc.value.args[0] == msg
-
- @patch('ansible.module_utils.netapp.ems_log_event')
- def test_get_generic_get_iter(self, mock_ems_log):
- '''calling get_generic_get_iter will return expected dict'''
- set_module_args(self.mock_args())
- obj = self.get_info_mock_object('net_port')
- result = obj.get_generic_get_iter(
- 'net-port-get-iter',
- attribute='net-port-info',
- field=('node', 'port'),
- query={'max-records': '1024'}
- )
- assert result.get('node_0:port_0')
- assert result.get('node_1:port_1')
-
- @patch('ansible.modules.storage.netapp.na_ontap_info.NetAppONTAPGatherInfo.get_all')
- def test_main(self, get_all):
- '''test main method.'''
- set_module_args(self.mock_args())
- get_all.side_effect = [
- {'test_get_all':
- {'vserver_login_banner_info': 'test_vserver_login_banner_info', 'vserver_info': 'test_vserver_info'}}
- ]
- with pytest.raises(AnsibleExitJson) as exc:
- info_main()
- assert exc.value.args[0]['state'] == 'info'
-
- @patch('ansible.modules.storage.netapp.na_ontap_info.NetAppONTAPGatherInfo.get_generic_get_iter')
- def test_get_ifgrp_info(self, get_generic_get_iter):
- '''test get_ifgrp_info with empty ifgrp_info'''
- set_module_args(self.mock_args())
- get_generic_get_iter.side_effect = [
- {}
- ]
- obj = self.get_info_mock_object()
- obj.netapp_info['net_port_info'] = {}
- result = obj.get_ifgrp_info()
- assert result == {}
-
- def test_ontapi_error(self):
- '''test ontapi will raise zapi error'''
- set_module_args(self.mock_args())
- obj = self.get_info_mock_object('zapi_error')
- with pytest.raises(AnsibleFailJson) as exc:
- obj.ontapi()
- assert exc.value.args[0]['msg'] == 'Error calling API system-get-ontapi-version: NetApp API failed. Reason - test:error'
-
- def test_call_api_error(self):
- '''test call_api will raise zapi error'''
- set_module_args(self.mock_args())
- obj = self.get_info_mock_object('zapi_error')
- with pytest.raises(AnsibleFailJson) as exc:
- obj.call_api('nvme-get-iter')
- assert exc.value.args[0]['msg'] == 'Error calling API nvme-get-iter: NetApp API failed. Reason - test:error'
-
- def test_find_item(self):
- '''test __find_item return expected key value'''
- obj = {"A": 1, "B": {"C": {"D": 2}}}
- key = "D"
- result = info_finditem(obj, key)
- assert result == 2
-
- def test_subset_return_all_complete(self):
- ''' Check all returns all of the entries if version is high enough '''
- version = '140' # change this if new ZAPIs are supported
- set_module_args(self.mock_args())
- obj = self.get_info_mock_object('vserver')
- subset = obj.get_subset(['all'], version)
- assert set(obj.info_subsets.keys()) == subset
-
- def test_subset_return_all_partial(self):
- ''' Check all returns a subset of the entries if version is low enough '''
- version = '120' # low enough so that some ZAPIs are not supported
- set_module_args(self.mock_args())
- obj = self.get_info_mock_object('vserver')
- subset = obj.get_subset(['all'], version)
- all_keys = obj.info_subsets.keys()
- assert set(all_keys) > subset
- supported_keys = filter(lambda key: obj.info_subsets[key]['min_version'] <= version, all_keys)
- assert set(supported_keys) == subset
-
- def test_subset_return_one(self):
- ''' Check single entry returns one '''
- version = '120' # low enough so that some ZAPIs are not supported
- set_module_args(self.mock_args())
- obj = self.get_info_mock_object('vserver')
- subset = obj.get_subset(['net_interface_info'], version)
- assert len(subset) == 1
-
- def test_subset_return_multiple(self):
- ''' Check that more than one entry returns the same number '''
- version = '120' # low enough so that some ZAPIs are not supported
- set_module_args(self.mock_args())
- obj = self.get_info_mock_object('vserver')
- subset_entries = ['net_interface_info', 'net_port_info']
- subset = obj.get_subset(subset_entries, version)
- assert len(subset) == len(subset_entries)
-
- def test_subset_return_bad(self):
- ''' Check that a bad subset entry will error out '''
- version = '120' # low enough so that some ZAPIs are not supported
- set_module_args(self.mock_args())
- obj = self.get_info_mock_object('vserver')
- with pytest.raises(AnsibleFailJson) as exc:
- subset = obj.get_subset(['net_interface_info', 'my_invalid_subset'], version)
- print('Info: %s' % exc.value.args[0]['msg'])
- assert exc.value.args[0]['msg'] == 'Bad subset: my_invalid_subset'
-
- def test_subset_return_unsupported(self):
- ''' Check that a new subset entry will error out on an older system '''
- version = '120' # low enough so that some ZAPIs are not supported
- key = 'nvme_info' # only supported starting at 140
- set_module_args(self.mock_args())
- obj = self.get_info_mock_object('vserver')
- with pytest.raises(AnsibleFailJson) as exc:
- subset = obj.get_subset(['net_interface_info', key], version)
- print('Info: %s' % exc.value.args[0]['msg'])
- msg = 'Remote system at version %s does not support %s' % (version, key)
- assert exc.value.args[0]['msg'] == msg
-
- def test_subset_return_none(self):
- ''' Check usable subset can be empty '''
- version = '!' # lower then 0, so that no ZAPI is supported
- set_module_args(self.mock_args())
- obj = self.get_info_mock_object('vserver')
- subset = obj.get_subset(['all'], version)
- assert len(subset) == 0
-
- def test_subset_return_all_expect_one(self):
- ''' Check !x returns all of the entries except x if version is high enough '''
- version = '140' # change this if new ZAPIs are supported
- set_module_args(self.mock_args())
- obj = self.get_info_mock_object('vserver')
- subset = obj.get_subset(['!net_interface_info'], version)
- assert len(obj.info_subsets.keys()) == len(subset) + 1
- subset.add('net_interface_info')
- assert set(obj.info_subsets.keys()) == subset
-
- def test_subset_return_all_expect_three(self):
- ''' Check !x,!y,!z returns all of the entries except x, y, z if version is high enough '''
- version = '140' # change this if new ZAPIs are supported
- set_module_args(self.mock_args())
- obj = self.get_info_mock_object('vserver')
- subset = obj.get_subset(['!net_interface_info', '!nvme_info', '!ontap_version'], version)
- assert len(obj.info_subsets.keys()) == len(subset) + 3
- subset.update(['net_interface_info', 'nvme_info', 'ontap_version'])
- assert set(obj.info_subsets.keys()) == subset
-
- def test_subset_return_none_with_exclusion(self):
- ''' Check usable subset can be empty with !x '''
- version = '!' # lower then 0, so that no ZAPI is supported
- key = 'net_interface_info'
- set_module_args(self.mock_args())
- obj = self.get_info_mock_object('vserver')
- with pytest.raises(AnsibleFailJson) as exc:
- subset = obj.get_subset(['!' + key], version)
- print('Info: %s' % exc.value.args[0]['msg'])
- msg = 'Remote system at version %s does not support %s' % (version, key)
- assert exc.value.args[0]['msg'] == msg
diff --git a/test/units/modules/storage/netapp/test_na_ontap_interface.py b/test/units/modules/storage/netapp/test_na_ontap_interface.py
deleted file mode 100644
index 18119e5a37..0000000000
--- a/test/units/modules/storage/netapp/test_na_ontap_interface.py
+++ /dev/null
@@ -1,272 +0,0 @@
-# (c) 2018, NetApp, Inc
-# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-
-''' unit test template for ONTAP Ansible module '''
-
-from __future__ import print_function
-import json
-import pytest
-
-from units.compat import unittest
-from units.compat.mock import patch, Mock
-from ansible.module_utils import basic
-from ansible.module_utils._text import to_bytes
-import ansible.module_utils.netapp as netapp_utils
-
-from ansible.modules.storage.netapp.na_ontap_interface \
- import NetAppOntapInterface as interface_module # module under test
-
-if not netapp_utils.has_netapp_lib():
- pytestmark = pytest.mark.skip('skipping as missing required netapp_lib')
-
-
-def set_module_args(args):
- """prepare arguments so that they will be picked up during module creation"""
- args = json.dumps({'ANSIBLE_MODULE_ARGS': args})
- basic._ANSIBLE_ARGS = to_bytes(args) # pylint: disable=protected-access
-
-
-class AnsibleExitJson(Exception):
- """Exception class to be raised by module.exit_json and caught by the test case"""
- pass
-
-
-class AnsibleFailJson(Exception):
- """Exception class to be raised by module.fail_json and caught by the test case"""
- pass
-
-
-def exit_json(*args, **kwargs): # pylint: disable=unused-argument
- """function to patch over exit_json; package return data into an exception"""
- if 'changed' not in kwargs:
- kwargs['changed'] = False
- raise AnsibleExitJson(kwargs)
-
-
-def fail_json(*args, **kwargs): # pylint: disable=unused-argument
- """function to patch over fail_json; package return data into an exception"""
- kwargs['failed'] = True
- raise AnsibleFailJson(kwargs)
-
-
-class MockONTAPConnection(object):
- ''' mock server connection to ONTAP host '''
-
- def __init__(self, kind=None, data=None):
- ''' save arguments '''
- self.type = kind
- self.params = data
- self.xml_in = None
- self.xml_out = None
-
- def invoke_successfully(self, xml, enable_tunneling): # pylint: disable=unused-argument
- ''' mock invoke_successfully returning xml data '''
- self.xml_in = xml
- if self.type == 'interface':
- xml = self.build_interface_info(self.params)
- self.xml_out = xml
- return xml
-
- @staticmethod
- def build_interface_info(data):
- ''' build xml data for vserser-info '''
- xml = netapp_utils.zapi.NaElement('xml')
- attributes = {
- 'num-records': 1,
- 'attributes-list': {
- 'net-interface-info': {
- 'interface-name': data['name'],
- 'administrative-status': data['administrative-status'],
- 'failover-policy': data['failover-policy'],
- 'firewall-policy': data['firewall-policy'],
- 'is-auto-revert': data['is-auto-revert'],
- 'home-node': data['home_node'],
- 'home-port': data['home_port'],
- 'address': data['address'],
- 'netmask': data['netmask'],
- 'role': data['role'],
- 'protocols': data['protocols'] if data.get('protocols') else None,
- 'dns-domain-name': data['dns_domain_name'],
- 'listen-for-dns_query': data['listen_for_dns_query'],
- 'is-dns-update-enabled': data['is_dns_update_enabled']
- }
- }
- }
- xml.translate_struct(attributes)
- return xml
-
-
-class TestMyModule(unittest.TestCase):
- ''' a group of related Unit Tests '''
-
- def setUp(self):
- self.mock_module_helper = patch.multiple(basic.AnsibleModule,
- exit_json=exit_json,
- fail_json=fail_json)
- self.mock_module_helper.start()
- self.addCleanup(self.mock_module_helper.stop)
- self.mock_interface = {
- 'name': 'test_lif',
- 'administrative-status': 'up',
- 'failover-policy': 'up',
- 'firewall-policy': 'up',
- 'is-auto-revert': 'true',
- 'home_node': 'node',
- 'role': 'test',
- 'home_port': 'e0c',
- 'address': '2.2.2.2',
- 'netmask': '1.1.1.1',
- 'dns_domain_name': 'test.com',
- 'listen_for_dns_query': True,
- 'is_dns_update_enabled': True
- }
-
- def mock_args(self):
- return {
- 'vserver': 'vserver',
- 'interface_name': self.mock_interface['name'],
- 'home_node': self.mock_interface['home_node'],
- 'role': self.mock_interface['role'],
- 'home_port': self.mock_interface['home_port'],
- 'address': self.mock_interface['address'],
- 'netmask': self.mock_interface['netmask'],
- 'hostname': 'hostname',
- 'username': 'username',
- 'password': 'password',
- }
-
- def get_interface_mock_object(self, kind=None):
- """
- Helper method to return an na_ontap_interface object
- :param kind: passes this param to MockONTAPConnection()
- :return: na_ontap_interface object
- """
- interface_obj = interface_module()
- interface_obj.autosupport_log = Mock(return_value=None)
- if kind is None:
- interface_obj.server = MockONTAPConnection()
- else:
- interface_obj.server = MockONTAPConnection(kind=kind, data=self.mock_interface)
- return interface_obj
-
- def test_module_fail_when_required_args_missing(self):
- ''' required arguments are reported as errors '''
- with pytest.raises(AnsibleFailJson) as exc:
- set_module_args({})
- interface_module()
- print('Info: %s' % exc.value.args[0]['msg'])
-
- def test_create_error_missing_param(self):
- ''' Test if create throws an error if required param 'role' is not specified'''
- data = self.mock_args()
- del data['role']
- set_module_args(data)
- with pytest.raises(AnsibleFailJson) as exc:
- self.get_interface_mock_object('interface').create_interface()
- msg = 'Error: Missing one or more required parameters for creating interface: ' \
- 'home_port, netmask, role, home_node, address'
- expected = sorted(','.split(msg))
- received = sorted(','.split(exc.value.args[0]['msg']))
- assert expected == received
-
- def test_get_nonexistent_interface(self):
- ''' Test if get_interface returns None for non-existent interface '''
- set_module_args(self.mock_args())
- result = self.get_interface_mock_object().get_interface()
- assert result is None
-
- def test_get_existing_interface(self):
- ''' Test if get_interface returns None for existing interface '''
- set_module_args(self.mock_args())
- result = self.get_interface_mock_object(kind='interface').get_interface()
- assert result['interface_name'] == self.mock_interface['name']
-
- def test_successful_create(self):
- ''' Test successful create '''
- set_module_args(self.mock_args())
- with pytest.raises(AnsibleExitJson) as exc:
- self.get_interface_mock_object().apply()
- assert exc.value.args[0]['changed']
-
- def test_successful_create_for_NVMe(self):
- ''' Test successful create for NVMe protocol'''
- data = self.mock_args()
- data['protocols'] = 'fc-nvme'
- del data['address']
- del data['netmask']
- del data['home_port']
- set_module_args(data)
- with pytest.raises(AnsibleExitJson) as exc:
- self.get_interface_mock_object().apply()
- assert exc.value.args[0]['changed']
-
- def test_create_idempotency_for_NVMe(self):
- ''' Test create idempotency for NVMe protocol '''
- data = self.mock_args()
- data['protocols'] = 'fc-nvme'
- del data['address']
- del data['netmask']
- del data['home_port']
- set_module_args(data)
- with pytest.raises(AnsibleExitJson) as exc:
- self.get_interface_mock_object('interface').apply()
- assert not exc.value.args[0]['changed']
-
- def test_create_error_for_NVMe(self):
- ''' Test if create throws an error if required param 'protocols' uses NVMe'''
- data = self.mock_args()
- data['protocols'] = 'fc-nvme'
- set_module_args(data)
- with pytest.raises(AnsibleFailJson) as exc:
- self.get_interface_mock_object('interface').create_interface()
- msg = 'Error: Following parameters for creating interface are not supported for data-protocol fc-nvme: ' \
- 'netmask, firewall_policy, address'
- expected = sorted(','.split(msg))
- received = sorted(','.split(exc.value.args[0]['msg']))
- assert expected == received
-
- def test_create_idempotency(self):
- ''' Test create idempotency '''
- set_module_args(self.mock_args())
- with pytest.raises(AnsibleExitJson) as exc:
- self.get_interface_mock_object('interface').apply()
- assert not exc.value.args[0]['changed']
-
- def test_successful_delete(self):
- ''' Test delete existing interface '''
- data = self.mock_args()
- data['state'] = 'absent'
- set_module_args(data)
- with pytest.raises(AnsibleExitJson) as exc:
- self.get_interface_mock_object('interface').apply()
- assert exc.value.args[0]['changed']
-
- def test_delete_idempotency(self):
- ''' Test delete idempotency '''
- data = self.mock_args()
- data['state'] = 'absent'
- set_module_args(data)
- with pytest.raises(AnsibleExitJson) as exc:
- self.get_interface_mock_object().apply()
- assert not exc.value.args[0]['changed']
-
- def test_successful_modify(self):
- ''' Test successful modify interface_minutes '''
- data = self.mock_args()
- data['home_port'] = 'new_port'
- data['dns_domain_name'] = 'test2.com'
- data['listen_for_dns_query'] = False
- data['is_dns_update_enabled'] = False
- set_module_args(data)
- with pytest.raises(AnsibleExitJson) as exc:
- interface_obj = self.get_interface_mock_object('interface')
- interface_obj.apply()
- assert exc.value.args[0]['changed']
-
- def test_modify_idempotency(self):
- ''' Test modify idempotency '''
- data = self.mock_args()
- set_module_args(data)
- with pytest.raises(AnsibleExitJson) as exc:
- self.get_interface_mock_object('interface').apply()
- assert not exc.value.args[0]['changed']
diff --git a/test/units/modules/storage/netapp/test_na_ontap_ipspace.py b/test/units/modules/storage/netapp/test_na_ontap_ipspace.py
deleted file mode 100644
index abaa11d358..0000000000
--- a/test/units/modules/storage/netapp/test_na_ontap_ipspace.py
+++ /dev/null
@@ -1,138 +0,0 @@
-# (c) 2018, NTT Europe Ltd.
-# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-
-""" unit test for Ansible module: na_ontap_ipspace """
-
-from __future__ import print_function
-import json
-import pytest
-
-from ansible.module_utils import basic
-from ansible.module_utils._text import to_bytes
-import ansible.module_utils.netapp as netapp_utils
-from ansible.modules.storage.netapp.na_ontap_ipspace \
- import NetAppOntapIpspace as my_module
-from units.compat import unittest
-from units.compat.mock import patch
-
-if not netapp_utils.has_netapp_lib():
- pytestmark = pytest.mark.skip('skipping as missing required netapp_lib')
-
-
-def set_module_args(args):
- """prepare arguments so that they will be picked up during module creation"""
- args = json.dumps({'ANSIBLE_MODULE_ARGS': args})
- basic._ANSIBLE_ARGS = to_bytes(args) # pylint: disable=protected-access
-
-
-class AnsibleExitJson(Exception):
- """Exception class to be raised by module.exit_json and caught by the test case"""
- pass
-
-
-class AnsibleFailJson(Exception):
- """Exception class to be raised by module.fail_json and caught by the test case"""
- pass
-
-
-def exit_json(*args, **kwargs): # pylint: disable=unused-argument
- """function to patch over exit_json; package return data into an exception"""
- if 'changed' not in kwargs:
- kwargs['changed'] = False
- raise AnsibleExitJson(kwargs)
-
-
-def fail_json(*args, **kwargs): # pylint: disable=unused-argument
- """function to patch over fail_json; package return data into an exception"""
- kwargs['failed'] = True
- raise AnsibleFailJson(kwargs)
-
-
-class MockONTAPConnection(object):
- ''' mock server connection to ONTAP host '''
- def __init__(self, kind=None, parm1=None):
- ''' save arguments '''
- self.type = kind
- self.parm1 = parm1
- self.xml_in = None
- self.xml_out = None
-
- def invoke_successfully(self, xml, enable_tunneling): # pylint: disable=unused-argument
- ''' mock invoke_successfully returning xml data '''
- self.xml_in = xml
- if self.type == 'ipspace':
- xml = self.build_ipspace_info(self.parm1)
- self.xml_out = xml
- return xml
-
- @staticmethod
- def build_ipspace_info(ipspace):
- ''' build xml data for ipspace '''
- xml = netapp_utils.zapi.NaElement('xml')
- data = {'num-records': 1,
- 'attributes-list': {'net-ipspaces-info': {'ipspace': ipspace}}}
- xml.translate_struct(data)
- print(xml.to_string())
- return xml
-
-
-class TestMyModule(unittest.TestCase):
- ''' a group of related Unit Tests '''
-
- def setUp(self):
- self.mock_module_helper = patch.multiple(basic.AnsibleModule,
- exit_json=exit_json,
- fail_json=fail_json)
- self.mock_module_helper.start()
- self.addCleanup(self.mock_module_helper.stop)
- self.server = MockONTAPConnection()
-
- def set_default_args(self):
- return dict({
- 'name': 'test_ipspace',
- 'hostname': 'hostname',
- 'username': 'username',
- 'password': 'password'
- })
-
- def test_fail_requiredargs_missing(self):
- ''' required arguments are reported as errors '''
- with pytest.raises(AnsibleFailJson) as exc:
- set_module_args({})
- my_module()
- print('Info: %s' % exc.value.args[0]['msg'])
-
- def test_get_ipspace_iscalled(self):
- ''' test if get_ipspace() is called '''
- set_module_args(self.set_default_args())
- my_obj = my_module()
- my_obj.server = self.server
- ipspace = my_obj.get_ipspace()
- print('Info: test_get_ipspace: %s' % repr(ipspace))
- assert ipspace is None
-
- def test_ipspace_apply_iscalled(self):
- ''' test if apply() is called '''
- module_args = {'name': 'test_apply_ips'}
- module_args.update(self.set_default_args())
- set_module_args(module_args)
- my_obj = my_module()
- my_obj.server = self.server
- ipspace = my_obj.get_ipspace()
- print('Info: test_get_ipspace: %s' % repr(ipspace))
- assert ipspace is None
- with pytest.raises(AnsibleExitJson) as exc:
- my_obj.apply()
- print('Info: test_ipspace_apply: %s' % repr(exc.value))
- assert exc.value.args[0]['changed']
- my_obj.server = MockONTAPConnection('ipspace', 'test_apply_ips')
- ipspace = my_obj.get_ipspace()
- print('Info: test_get_ipspace: %s' % repr(ipspace))
- assert ipspace is not None
- assert ipspace['name'] == 'test_apply_ips'
- with pytest.raises(AnsibleExitJson) as exc:
- my_obj.apply()
- print('Info: test_ipspace_apply: %s' % repr(exc.value))
- assert exc.value.args[0]['changed']
- ipspace = my_obj.get_ipspace()
- assert ipspace['name'] == 'test_apply_ips'
diff --git a/test/units/modules/storage/netapp/test_na_ontap_job_schedule.py b/test/units/modules/storage/netapp/test_na_ontap_job_schedule.py
deleted file mode 100644
index 973474036b..0000000000
--- a/test/units/modules/storage/netapp/test_na_ontap_job_schedule.py
+++ /dev/null
@@ -1,246 +0,0 @@
-# (c) 2018, NetApp, Inc
-# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-
-''' unit tests for Ansible module: na_ontap_job_schedule '''
-
-from __future__ import print_function
-import json
-import pytest
-
-from units.compat import unittest
-from units.compat.mock import patch, Mock
-from ansible.module_utils import basic
-from ansible.module_utils._text import to_bytes
-import ansible.module_utils.netapp as netapp_utils
-
-from ansible.modules.storage.netapp.na_ontap_job_schedule \
- import NetAppONTAPJob as job_module # module under test
-
-if not netapp_utils.has_netapp_lib():
- pytestmark = pytest.mark.skip('skipping as missing required netapp_lib')
-
-
-def set_module_args(args):
- """prepare arguments so that they will be picked up during module creation"""
- args = json.dumps({'ANSIBLE_MODULE_ARGS': args})
- basic._ANSIBLE_ARGS = to_bytes(args) # pylint: disable=protected-access
-
-
-class AnsibleExitJson(Exception):
- """Exception class to be raised by module.exit_json and caught by the test case"""
- pass
-
-
-class AnsibleFailJson(Exception):
- """Exception class to be raised by module.fail_json and caught by the test case"""
- pass
-
-
-def exit_json(*args, **kwargs): # pylint: disable=unused-argument
- """function to patch over exit_json; package return data into an exception"""
- if 'changed' not in kwargs:
- kwargs['changed'] = False
- raise AnsibleExitJson(kwargs)
-
-
-def fail_json(*args, **kwargs): # pylint: disable=unused-argument
- """function to patch over fail_json; package return data into an exception"""
- kwargs['failed'] = True
- raise AnsibleFailJson(kwargs)
-
-
-class MockONTAPConnection(object):
- ''' mock server connection to ONTAP host '''
-
- def __init__(self, kind=None, data=None):
- ''' save arguments '''
- self.kind = kind
- self.params = data
- self.xml_in = None
- self.xml_out = None
-
- def invoke_successfully(self, xml, enable_tunneling): # pylint: disable=unused-argument
- ''' mock invoke_successfully returning xml data '''
- self.xml_in = xml
- if self.kind == 'job':
- xml = self.build_job_schedule_cron_info(self.params)
- elif self.kind == 'job_multiple':
- xml = self.build_job_schedule_multiple_cron_info(self.params)
- # TODO: mock invoke_elem for autosupport calls
- elif self.kind == 'vserver':
- xml = self.build_vserver_info()
- self.xml_out = xml
- return xml
-
- def autosupport_log(self):
- ''' Mock autosupport log method, returns None '''
- return None
-
- @staticmethod
- def build_job_schedule_cron_info(job_details):
- ''' build xml data for vserser-info '''
- xml = netapp_utils.zapi.NaElement('xml')
- attributes = {
- 'num-records': 1,
- 'attributes-list': {
- 'job-schedule-cron-info': {
- 'job-schedule-name': job_details['name'],
- 'job-schedule-cron-minute': {
- 'cron-minute': job_details['minutes']
- }
- }
- }
- }
- xml.translate_struct(attributes)
- return xml
-
- @staticmethod
- def build_job_schedule_multiple_cron_info(job_details):
- ''' build xml data for vserser-info '''
- print("CALLED MULTIPLE BUILD")
- xml = netapp_utils.zapi.NaElement('xml')
- attributes = {
- 'num-records': 1,
- 'attributes-list': {
- 'job-schedule-cron-info': {
- 'job-schedule-name': job_details['name'],
- 'job-schedule-cron-minute': [
- {'cron-minute': '25'},
- {'cron-minute': '35'}
- ],
- 'job-schedule-cron-month': [
- {'cron-month': '5'},
- {'cron-month': '10'}
- ]
- }
- }
- }
- xml.translate_struct(attributes)
- return xml
-
-
-class TestMyModule(unittest.TestCase):
- ''' Unit tests for na_ontap_job_schedule '''
-
- def setUp(self):
- self.mock_module_helper = patch.multiple(basic.AnsibleModule,
- exit_json=exit_json,
- fail_json=fail_json)
- self.mock_module_helper.start()
- self.addCleanup(self.mock_module_helper.stop)
- self.mock_job = {
- 'name': 'test_job',
- 'minutes': '25'
- }
-
- def mock_args(self):
- return {
- 'name': self.mock_job['name'],
- 'job_minutes': [self.mock_job['minutes']],
- 'hostname': 'test',
- 'username': 'test_user',
- 'password': 'test_pass!'
- }
-
- def get_job_mock_object(self, kind=None):
- """
- Helper method to return an na_ontap_job_schedule object
- :param kind: passes this param to MockONTAPConnection()
- :return: na_ontap_job_schedule object
- """
- job_obj = job_module()
- job_obj.autosupport_log = Mock(return_value=None)
- if kind is None:
- job_obj.server = MockONTAPConnection()
- else:
- job_obj.server = MockONTAPConnection(kind=kind, data=self.mock_job)
- return job_obj
-
- def test_module_fail_when_required_args_missing(self):
- ''' required arguments are reported as errors '''
- with pytest.raises(AnsibleFailJson) as exc:
- set_module_args({})
- job_module()
- print('Info: %s' % exc.value.args[0]['msg'])
-
- def test_get_nonexistent_job(self):
- ''' Test if get_job_schedule returns None for non-existent job '''
- set_module_args(self.mock_args())
- result = self.get_job_mock_object().get_job_schedule()
- assert result is None
-
- def test_get_existing_job(self):
- ''' Test if get_job_schedule retuns job details for existing job '''
- data = self.mock_args()
- set_module_args(data)
- result = self.get_job_mock_object('job').get_job_schedule()
- assert result['name'] == self.mock_job['name']
- assert result['job_minutes'] == data['job_minutes']
-
- def test_get_existing_job_multiple_minutes(self):
- ''' Test if get_job_schedule retuns job details for existing job '''
- set_module_args(self.mock_args())
- result = self.get_job_mock_object('job_multiple').get_job_schedule()
- print(str(result))
- assert result['name'] == self.mock_job['name']
- assert result['job_minutes'] == ['25', '35']
- assert result['job_months'] == ['5', '10']
-
- def test_create_error_missing_param(self):
- ''' Test if create throws an error if job_minutes is not specified'''
- data = self.mock_args()
- del data['job_minutes']
- set_module_args(data)
- with pytest.raises(AnsibleFailJson) as exc:
- self.get_job_mock_object('job').create_job_schedule()
- msg = 'Error: missing required parameter job_minutes for create'
- assert exc.value.args[0]['msg'] == msg
-
- def test_successful_create(self):
- ''' Test successful create '''
- set_module_args(self.mock_args())
- with pytest.raises(AnsibleExitJson) as exc:
- self.get_job_mock_object().apply()
- assert exc.value.args[0]['changed']
-
- def test_create_idempotency(self):
- ''' Test create idempotency '''
- set_module_args(self.mock_args())
- with pytest.raises(AnsibleExitJson) as exc:
- self.get_job_mock_object('job').apply()
- assert not exc.value.args[0]['changed']
-
- def test_successful_delete(self):
- ''' Test delete existing job '''
- data = self.mock_args()
- data['state'] = 'absent'
- set_module_args(data)
- with pytest.raises(AnsibleExitJson) as exc:
- self.get_job_mock_object('job').apply()
- assert exc.value.args[0]['changed']
-
- def test_delete_idempotency(self):
- ''' Test delete idempotency '''
- data = self.mock_args()
- data['state'] = 'absent'
- set_module_args(data)
- with pytest.raises(AnsibleExitJson) as exc:
- self.get_job_mock_object().apply()
- assert not exc.value.args[0]['changed']
-
- def test_successful_modify(self):
- ''' Test successful modify job_minutes '''
- data = self.mock_args()
- data['job_minutes'] = ['20']
- set_module_args(data)
- with pytest.raises(AnsibleExitJson) as exc:
- self.get_job_mock_object('job').apply()
- assert exc.value.args[0]['changed']
-
- def test_modify_idempotency(self):
- ''' Test modify idempotency '''
- data = self.mock_args()
- set_module_args(data)
- with pytest.raises(AnsibleExitJson) as exc:
- self.get_job_mock_object('job').apply()
- assert not exc.value.args[0]['changed']
diff --git a/test/units/modules/storage/netapp/test_na_ontap_kerberos_realm.py b/test/units/modules/storage/netapp/test_na_ontap_kerberos_realm.py
deleted file mode 100644
index abe44766c2..0000000000
--- a/test/units/modules/storage/netapp/test_na_ontap_kerberos_realm.py
+++ /dev/null
@@ -1,239 +0,0 @@
-# (c) 2018, NetApp, Inc
-# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-
-''' unit test for ONTAP Kerberos Realm module '''
-
-from __future__ import (absolute_import, division, print_function)
-__metaclass__ = type
-
-import json
-import pytest
-import ansible.module_utils.netapp as netapp_utils
-from ansible.module_utils import basic
-from ansible.module_utils._text import to_bytes
-from ansible.modules.storage.netapp.na_ontap_kerberos_realm \
- import NetAppOntapKerberosRealm as my_module # module under test
-from units.compat import unittest
-from units.compat.mock import patch
-from units.compat.mock import Mock
-
-
-if not netapp_utils.has_netapp_lib():
- pytestmark = pytest.mark.skip('skipping as missing required netapp_lib')
-
-
-def set_module_args(args):
- """prepare arguments so that they will be picked up during module creation"""
- args = json.dumps({'ANSIBLE_MODULE_ARGS': args})
- basic._ANSIBLE_ARGS = to_bytes(args) # pylint: disable=protected-access
-
-
-class AnsibleExitJson(Exception):
- """Exception class to be raised by module.exit_json and caught by the test case"""
- print(Exception)
- # pass
-
-
-class AnsibleFailJson(Exception):
- """Exception class to be raised by module.fail_json and caught by the test case"""
- print(Exception)
- # pass
-
-
-def exit_json(*args, **kwargs): # pylint: disable=unused-argument
- """function to patch over exit_json; package return data into an exception"""
- if 'changed' not in kwargs:
- kwargs['changed'] = False
- raise AnsibleExitJson(kwargs)
-
-
-def fail_json(*args, **kwargs): # pylint: disable=unused-argument
- """function to patch over fail_json; package return data into an exception"""
- kwargs['failed'] = True
- raise AnsibleFailJson(kwargs)
-
-
-class MockONTAPConnection(object):
- ''' mock server connection to ONTAP host '''
-
- def __init__(self, kind=None):
- ''' save arguments '''
- self.type = kind
- self.xml_in = None
- self.xml_out = None
-
- def invoke_successfully(self, xml, enable_tunneling): # pylint: disable=unused-argument
- ''' mock invoke_successfully returning xml data '''
- self.xml_in = xml
-
- if self.type == 'present_realm':
- xml = self.build_realm()
- else:
- xml = self.build_empty_realm()
-
- self.xml_out = xml
- return xml
-
- @staticmethod
- def build_realm():
- ''' build xml data for kerberos realm '''
- xml = netapp_utils.zapi.NaElement('xml')
- attributes = {
- 'num-records': "1",
- 'attributes-list': {
- 'kerberos-realm': {
- 'admin-server-ip': "192.168.0.1",
- 'admin-server-port': "749",
- 'clock-skew': "5",
- 'kdc-ip': "192.168.0.1",
- 'kdc-port': "88",
- 'kdc-vendor': "other",
- 'password-server-ip': "192.168.0.1",
- 'password-server-port': "464",
- "permitted-enc-types": {
- "string": ["des", "des3", "aes_128", "aes_256"]
- },
- 'realm': "EXAMPLE.COM",
- 'vserver-name': "vserver0"
- }
- }
- }
- xml.translate_struct(attributes)
- return xml
-
- @staticmethod
- def build_empty_realm():
- ''' build xml data for kerberos realm '''
- xml = netapp_utils.zapi.NaElement('xml')
- attributes = {
- 'num-records': "0",
- }
- xml.translate_struct(attributes)
- return xml
-
-
-class TestMyModule(unittest.TestCase):
- ''' a group of related Unit Tests '''
-
- def setUp(self):
- self.mock_module_helper = patch.multiple(basic.AnsibleModule,
- exit_json=exit_json,
- fail_json=fail_json)
- self.mock_module_helper.start()
- self.addCleanup(self.mock_module_helper.stop)
- self.server = MockONTAPConnection(kind='present_realm')
-
- @staticmethod
- def get_kerberos_realm_mock_object(kind=None):
- """
- Helper method to return an na_ontap_volume object
- :param kind: passes this param to MockONTAPConnection()
- :return: na_ontap_volume object
- """
- krbrealm_obj = my_module()
- krbrealm_obj.asup_log_for_cserver = Mock(return_value=None)
- krbrealm_obj.cluster = Mock()
- krbrealm_obj.cluster.invoke_successfully = Mock()
- if kind is None:
- krbrealm_obj.server = MockONTAPConnection()
- else:
- krbrealm_obj.server = MockONTAPConnection(kind=kind)
- return krbrealm_obj
-
- @staticmethod
- def mock_args():
- '''Set default arguments'''
- return dict({
- 'hostname': 'test',
- 'username': 'test_user',
- 'password': 'test_pass!',
- 'https': True,
- 'validate_certs': False
- })
-
- @staticmethod
- def test_module_fail_when_required_args_missing():
- ''' required arguments are reported as errors '''
- with pytest.raises(AnsibleFailJson) as exc:
- set_module_args({})
- my_module()
- print('Info: %s' % exc.value.args[0]['msg'])
-
- def test_module_fail_when_state_present_required_args_missing(self):
- ''' required arguments are reported as errors '''
- data = self.mock_args()
- data['state'] = 'present'
- data['vserver'] = 'vserver1'
- data['realm'] = 'NETAPP.COM'
- with pytest.raises(AnsibleFailJson) as exc:
- set_module_args(data)
- my_module()
- print('Info: %s' % exc.value.args[0]['msg'])
-
- def test_get_nonexistent_realm(self):
- ''' Test if get_krbrealm returns None for non-existent kerberos realm '''
- data = self.mock_args()
- data['vserver'] = 'none'
- data['realm'] = 'none'
- data['state'] = 'present'
- data['kdc_ip'] = 'none'
- data['kdc_vendor'] = 'Other'
- set_module_args(data)
- result = self.get_kerberos_realm_mock_object().get_krbrealm()
- assert result is None
-
- def test_get_existing_realm(self):
- ''' Test if get_krbrealm returns details for existing kerberos realm '''
- data = self.mock_args()
- data['vserver'] = 'vserver0'
- data['realm'] = 'EXAMPLE.COM'
- data['state'] = 'present'
- data['kdc_ip'] = '10.0.0.1'
- data['kdc_vendor'] = 'Other'
- set_module_args(data)
- result = self.get_kerberos_realm_mock_object('present_realm').get_krbrealm()
- assert result['realm']
-
- def test_successfully_modify_realm(self):
- ''' Test modify realm successful for modifying kdc_ip. '''
- data = self.mock_args()
- data['vserver'] = 'vserver0'
- data['realm'] = 'EXAMPLE.COM'
- data['kdc_ip'] = '192.168.10.10'
- data['state'] = 'present'
- data['kdc_ip'] = '10.0.0.1'
- data['kdc_vendor'] = 'Other'
- set_module_args(data)
- with pytest.raises(AnsibleExitJson) as exc:
- self.get_kerberos_realm_mock_object('present_realm').apply()
- assert exc.value.args[0]['changed']
-
- @patch('ansible.modules.storage.netapp.na_ontap_kerberos_realm.NetAppOntapKerberosRealm.delete_krbrealm')
- def test_successfully_delete_realm(self, delete_krbrealm):
- ''' Test successfully delete realm '''
- data = self.mock_args()
- data['state'] = 'absent'
- data['vserver'] = 'vserver0'
- data['realm'] = 'EXAMPLE.COM'
- set_module_args(data)
- obj = self.get_kerberos_realm_mock_object('present_realm')
- with pytest.raises(AnsibleExitJson) as exc:
- obj.apply()
- assert exc.value.args[0]['changed']
- delete_krbrealm.assert_called_with()
-
- @patch('ansible.modules.storage.netapp.na_ontap_kerberos_realm.NetAppOntapKerberosRealm.create_krbrealm')
- def test_successfully_create_realm(self, create_krbrealm):
- ''' Test successfully create realm '''
- data = self.mock_args()
- data['state'] = 'present'
- data['vserver'] = 'vserver1'
- data['realm'] = 'NETAPP.COM'
- data['kdc_ip'] = '10.0.0.1'
- data['kdc_vendor'] = 'Other'
- set_module_args(data)
- obj = self.get_kerberos_realm_mock_object()
- with pytest.raises(AnsibleExitJson) as exc:
- obj.apply()
- assert exc.value.args[0]['changed']
- create_krbrealm.assert_called_with()
diff --git a/test/units/modules/storage/netapp/test_na_ontap_lun_copy.py b/test/units/modules/storage/netapp/test_na_ontap_lun_copy.py
deleted file mode 100644
index bfadcaf081..0000000000
--- a/test/units/modules/storage/netapp/test_na_ontap_lun_copy.py
+++ /dev/null
@@ -1,159 +0,0 @@
-# (c) 2018, NetApp, Inc
-# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-
-''' unit test template for ONTAP Ansible module '''
-
-from __future__ import print_function
-import json
-import pytest
-
-from units.compat import unittest
-from units.compat.mock import patch, Mock
-from ansible.module_utils import basic
-from ansible.module_utils._text import to_bytes
-import ansible.module_utils.netapp as netapp_utils
-
-from ansible.modules.storage.netapp.na_ontap_lun_copy \
- import NetAppOntapLUNCopy as my_module # module under test
-
-if not netapp_utils.has_netapp_lib():
- pytestmark = pytest.mark.skip('skipping as missing required netapp_lib')
-
-
-def set_module_args(args):
- """prepare arguments so that they will be picked up during module creation"""
- args = json.dumps({'ANSIBLE_MODULE_ARGS': args})
- basic._ANSIBLE_ARGS = to_bytes(args) # pylint: disable=protected-access
-
-
-class AnsibleExitJson(Exception):
- """Exception class to be raised by module.exit_json and caught by the test case"""
- pass
-
-
-class AnsibleFailJson(Exception):
- """Exception class to be raised by module.fail_json and caught by the test case"""
- pass
-
-
-def exit_json(*args, **kwargs): # pylint: disable=unused-argument
- """function to patch over exit_json; package return data into an exception"""
- if 'changed' not in kwargs:
- kwargs['changed'] = False
- raise AnsibleExitJson(kwargs)
-
-
-def fail_json(*args, **kwargs): # pylint: disable=unused-argument
- """function to patch over fail_json; package return data into an exception"""
- kwargs['failed'] = True
- raise AnsibleFailJson(kwargs)
-
-
-class MockONTAPConnection(object):
- ''' mock server connection to ONTAP host '''
-
- def __init__(self, kind=None, parm1=None):
- ''' save arguments '''
- self.type = kind
- self.parm1 = parm1
- self.xml_in = None
- self.xml_out = None
-
- def invoke_successfully(self, xml, enable_tunneling): # pylint: disable=unused-argument
- ''' mock invoke_successfully returning xml data '''
- self.xml_in = xml
- if self.type == 'destination_vserver':
- xml = self.build_lun_info(self.parm1)
- self.xml_out = xml
- return xml
-
- @staticmethod
- def build_lun_info(data):
- ''' build xml data for lun-info '''
- xml = netapp_utils.zapi.NaElement('xml')
- attributes = {
- 'num-records': 1,
- }
- xml.translate_struct(attributes)
- return xml
-
-
-class TestMyModule(unittest.TestCase):
- ''' a group of related Unit Tests '''
-
- def setUp(self):
- self.mock_module_helper = patch.multiple(basic.AnsibleModule,
- exit_json=exit_json,
- fail_json=fail_json)
- self.mock_module_helper.start()
- self.addCleanup(self.mock_module_helper.stop)
- self.mock_lun_copy = {
- 'source_vserver': 'ansible',
- 'destination_path': '/vol/test/test_copy_dest_dest_new_reviewd_new',
- 'source_path': '/vol/test/test_copy_1',
- 'destination_vserver': 'ansible',
- 'state': 'present'
- }
-
- def mock_args(self):
-
- return {
- 'source_vserver': self.mock_lun_copy['source_vserver'],
- 'destination_path': self.mock_lun_copy['destination_path'],
- 'source_path': self.mock_lun_copy['source_path'],
- 'destination_vserver': self.mock_lun_copy['destination_vserver'],
- 'state': self.mock_lun_copy['state'],
- 'hostname': 'hostname',
- 'username': 'username',
- 'password': 'password',
- }
- # self.server = MockONTAPConnection()
-
- def get_lun_copy_mock_object(self, kind=None):
- """
- Helper method to return an na_ontap_lun_copy object
- :param kind: passes this param to MockONTAPConnection()
- :return: na_ontap_interface object
- """
- lun_copy_obj = my_module()
- lun_copy_obj.autosupport_log = Mock(return_value=None)
- if kind is None:
- lun_copy_obj.server = MockONTAPConnection()
- else:
- lun_copy_obj.server = MockONTAPConnection(kind=kind)
- return lun_copy_obj
-
- def test_module_fail_when_required_args_missing(self):
- ''' required arguments are reported as errors '''
- with pytest.raises(AnsibleFailJson) as exc:
- set_module_args({})
- my_module()
- print('Info: %s' % exc.value.args[0]['msg'])
-
- def test_create_error_missing_param(self):
- ''' Test if create throws an error if required param 'destination_vserver' is not specified'''
- data = self.mock_args()
- del data['destination_vserver']
- set_module_args(data)
- with pytest.raises(AnsibleFailJson) as exc:
- self.get_lun_copy_mock_object('lun_copy').copy_lun()
- msg = 'Error: Missing one or more required parameters for copying lun: ' \
- 'destination_path, source_path, destination_path'
- expected = sorted(','.split(msg))
- received = sorted(','.split(exc.value.args[0]['msg']))
- assert expected == received
-
- def test_successful_copy(self):
- ''' Test successful create '''
- # data = self.mock_args()
- set_module_args(self.mock_args())
- with pytest.raises(AnsibleExitJson) as exc:
- self.get_lun_copy_mock_object().apply()
- assert exc.value.args[0]['changed']
-
- def test_copy_idempotency(self):
- ''' Test create idempotency '''
- set_module_args(self.mock_args())
- with pytest.raises(AnsibleExitJson) as exc:
- self.get_lun_copy_mock_object('destination_vserver').apply()
- assert not exc.value.args[0]['changed']
diff --git a/test/units/modules/storage/netapp/test_na_ontap_lun_map.py b/test/units/modules/storage/netapp/test_na_ontap_lun_map.py
deleted file mode 100644
index c8c5d14957..0000000000
--- a/test/units/modules/storage/netapp/test_na_ontap_lun_map.py
+++ /dev/null
@@ -1,191 +0,0 @@
-''' unit tests ONTAP Ansible module: na_ontap_lun_map '''
-
-from __future__ import print_function
-import json
-import pytest
-
-from units.compat import unittest
-from units.compat.mock import patch
-from ansible.module_utils import basic
-from ansible.module_utils._text import to_bytes
-import ansible.module_utils.netapp as netapp_utils
-
-from ansible.modules.storage.netapp.na_ontap_lun_map \
- import NetAppOntapLUNMap as my_module
-
-if not netapp_utils.has_netapp_lib():
- pytestmark = pytest.mark.skip('skipping as missing required netapp_lib')
-
-
-def set_module_args(args):
- """prepare arguments so that they will be picked up during module creation"""
- args = json.dumps({'ANSIBLE_MODULE_ARGS': args})
- basic._ANSIBLE_ARGS = to_bytes(args) # pylint: disable=protected-access
-
-
-class AnsibleExitJson(Exception):
- """Exception class to be raised by module.exit_json and caught by the test case"""
- pass
-
-
-class AnsibleFailJson(Exception):
- """Exception class to be raised by module.fail_json and caught by the test case"""
- pass
-
-
-def exit_json(*args, **kwargs): # pylint: disable=unused-argument
- """function to patch over exit_json; package return data into an exception"""
- if 'changed' not in kwargs:
- kwargs['changed'] = False
- raise AnsibleExitJson(kwargs)
-
-
-def fail_json(*args, **kwargs): # pylint: disable=unused-argument
- """function to patch over fail_json; package return data into an exception"""
- kwargs['failed'] = True
- raise AnsibleFailJson(kwargs)
-
-
-class MockONTAPConnection(object):
- ''' mock server connection to ONTAP host '''
-
- def __init__(self, kind=None):
- ''' save arguments '''
- self.type = kind
- self.xml_in = None
- self.xml_out = None
-
- def invoke_successfully(self, xml, enable_tunneling): # pylint: disable=unused-argument
- ''' mock invoke_successfully returning xml data '''
- self.xml_in = xml
- if self.type == 'lun_map':
- xml = self.build_lun_info()
- elif self.type == 'lun_map_fail':
- raise netapp_utils.zapi.NaApiError(code='TEST', message="This exception is from the unit test")
- self.xml_out = xml
- return xml
-
- @staticmethod
- def build_lun_info():
- ''' build xml data for lun-map-entry '''
- xml = netapp_utils.zapi.NaElement('xml')
- data = {'initiator-groups': [{'initiator-group-info': {'initiator-group-name': 'ansible', 'lun-id': 2}}]}
- xml.translate_struct(data)
- return xml
-
-
-class TestMyModule(unittest.TestCase):
- ''' a group of related Unit Tests '''
-
- def setUp(self):
- self.mock_module_helper = patch.multiple(basic.AnsibleModule,
- exit_json=exit_json,
- fail_json=fail_json)
- self.mock_module_helper.start()
- self.addCleanup(self.mock_module_helper.stop)
- self.server = MockONTAPConnection()
- self.onbox = False
-
- def set_default_args(self):
- if self.onbox:
- hostname = '10.10.10.10'
- username = 'admin'
- password = 'password'
- initiator_group_name = 'ansible'
- vserver = 'ansible'
- path = '/vol/ansible/test'
- lun_id = 2
- else:
- hostname = 'hostname'
- username = 'username'
- password = 'password'
- initiator_group_name = 'ansible'
- vserver = 'ansible'
- path = '/vol/ansible/test'
- lun_id = 2
- return dict({
- 'hostname': hostname,
- 'username': username,
- 'password': password,
- 'initiator_group_name': initiator_group_name,
- 'vserver': vserver,
- 'path': path,
- 'lun_id': lun_id
- })
-
- def test_module_fail_when_required_args_missing(self):
- ''' required arguments are reported as errors '''
- with pytest.raises(AnsibleFailJson) as exc:
- set_module_args({})
- my_module()
- print('Info: %s' % exc.value.args[0]['msg'])
-
- def test_ensure_get_called(self):
- ''' test get_lun_map for non-existent lun'''
- set_module_args(self.set_default_args())
- my_obj = my_module()
- my_obj.server = self.server
- assert my_obj.get_lun_map is not None
-
- def test_ensure_get_called_existing(self):
- ''' test get_lun_map for existing lun'''
- set_module_args(self.set_default_args())
- my_obj = my_module()
- my_obj.server = MockONTAPConnection(kind='lun_map')
- assert my_obj.get_lun_map()
-
- @patch('ansible.modules.storage.netapp.na_ontap_lun_map.NetAppOntapLUNMap.create_lun_map')
- def test_successful_create(self, create_lun_map):
- ''' mapping lun and testing idempotency '''
- data = self.set_default_args()
- set_module_args(data)
- my_obj = my_module()
- if not self.onbox:
- my_obj.server = self.server
- with pytest.raises(AnsibleExitJson) as exc:
- my_obj.apply()
- assert exc.value.args[0]['changed']
- create_lun_map.assert_called_with()
- # to reset na_helper from remembering the previous 'changed' value
- set_module_args(self.set_default_args())
- my_obj = my_module()
- if not self.onbox:
- my_obj.server = MockONTAPConnection('lun_map')
- with pytest.raises(AnsibleExitJson) as exc:
- my_obj.apply()
- assert not exc.value.args[0]['changed']
-
- @patch('ansible.modules.storage.netapp.na_ontap_lun_map.NetAppOntapLUNMap.delete_lun_map')
- def test_successful_delete(self, delete_lun_map):
- ''' unmapping lun and testing idempotency '''
- data = self.set_default_args()
- data['state'] = 'absent'
- set_module_args(data)
- my_obj = my_module()
- if not self.onbox:
- my_obj.server = MockONTAPConnection('lun_map')
- with pytest.raises(AnsibleExitJson) as exc:
- my_obj.apply()
- assert exc.value.args[0]['changed']
- delete_lun_map.assert_called_with()
- # to reset na_helper from remembering the previous 'changed' value
- my_obj = my_module()
- if not self.onbox:
- my_obj.server = self.server
- with pytest.raises(AnsibleExitJson) as exc:
- my_obj.apply()
- assert not exc.value.args[0]['changed']
-
- def test_if_all_methods_catch_exception(self):
- module_args = {}
- module_args.update(self.set_default_args())
- set_module_args(module_args)
- my_obj = my_module()
- if not self.onbox:
- my_obj.server = MockONTAPConnection('lun_map_fail')
- with pytest.raises(AnsibleFailJson) as exc:
- my_obj.create_lun_map()
- assert 'Error mapping lun' in exc.value.args[0]['msg']
- with pytest.raises(AnsibleFailJson) as exc:
- my_obj.delete_lun_map()
- assert 'Error unmapping lun' in exc.value.args[0]['msg']
diff --git a/test/units/modules/storage/netapp/test_na_ontap_motd.py b/test/units/modules/storage/netapp/test_na_ontap_motd.py
deleted file mode 100644
index dec9d2e91d..0000000000
--- a/test/units/modules/storage/netapp/test_na_ontap_motd.py
+++ /dev/null
@@ -1,181 +0,0 @@
-# (c) 2019, NetApp, Inc
-# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-
-""" unit tests for Ansible module: na_ontap_motd """
-
-from __future__ import print_function
-import json
-import pytest
-
-from units.compat import unittest
-from units.compat.mock import patch
-from ansible.module_utils import basic
-from ansible.module_utils._text import to_bytes
-import ansible.module_utils.netapp as netapp_utils
-
-from ansible.modules.storage.netapp.na_ontap_motd \
- import NetAppONTAPMotd as my_module # module under test
-
-if not netapp_utils.has_netapp_lib():
- pytestmark = pytest.mark.skip('skipping as missing required netapp_lib')
-
-
-def set_module_args(args):
- """prepare arguments so that they will be picked up during module creation"""
- args = json.dumps({'ANSIBLE_MODULE_ARGS': args})
- basic._ANSIBLE_ARGS = to_bytes(args) # pylint: disable=protected-access
-
-
-class AnsibleExitJson(Exception):
- """Exception class to be raised by module.exit_json and caught by the test case"""
- pass
-
-
-class AnsibleFailJson(Exception):
- """Exception class to be raised by module.fail_json and caught by the test case"""
- pass
-
-
-def exit_json(*args, **kwargs): # pylint: disable=unused-argument
- """function to patch over exit_json; package return data into an exception"""
- if 'changed' not in kwargs:
- kwargs['changed'] = False
- raise AnsibleExitJson(kwargs)
-
-
-def fail_json(*args, **kwargs): # pylint: disable=unused-argument
- """function to patch over fail_json; package return data into an exception"""
- kwargs['failed'] = True
- raise AnsibleFailJson(kwargs)
-
-
-class MockONTAPConnection(object):
- ''' mock server connection to ONTAP host '''
-
- def __init__(self, kind=None):
- ''' save arguments '''
- self.type = kind
- self.xml_in = None
- self.xml_out = None
-
- def invoke_successfully(self, xml, enable_tunneling): # pylint: disable=unused-argument
- ''' mock invoke_successfully returning xml data '''
- self.xml_in = xml
- if self.type == 'motd':
- xml = self.build_motd_info()
- elif self.type == 'motd_fail':
- raise netapp_utils.zapi.NaApiError(code='TEST', message="This exception is from the unit test")
- self.xml_out = xml
- return xml
-
- @staticmethod
- def build_motd_info():
- ''' build xml data for motd '''
- xml = netapp_utils.zapi.NaElement('xml')
- data = {'num-records': 1,
- 'attributes-list': {'vserver-motd-info': {'message': 'ansible',
- 'vserver': 'ansible',
- 'is-cluster-message-enabled': 'true'}}}
- xml.translate_struct(data)
- print(xml.to_string())
- return xml
-
-
-class TestMyModule(unittest.TestCase):
- ''' a group of related Unit Tests '''
-
- def setUp(self):
- self.mock_module_helper = patch.multiple(basic.AnsibleModule,
- exit_json=exit_json,
- fail_json=fail_json)
- self.mock_module_helper.start()
- self.addCleanup(self.mock_module_helper.stop)
- self.server = MockONTAPConnection()
- # whether to use a mock or a simulator
- self.onbox = False
-
- def set_default_args(self):
- if self.onbox:
- hostname = '10.10.10.10'
- username = 'admin'
- password = 'password'
- message = 'ansible'
- vserver = 'ansible'
- show_cluster_motd = 'true'
- else:
- hostname = 'hostname'
- username = 'username'
- password = 'password'
- message = 'ansible'
- vserver = 'ansible'
- show_cluster_motd = 'true'
- return dict({
- 'hostname': hostname,
- 'username': username,
- 'password': password,
- 'message': message,
- 'vserver': vserver,
- 'show_cluster_motd': show_cluster_motd
- })
-
- def call_command(self, module_args):
- ''' utility function to call apply '''
- module_args.update(self.set_default_args())
- set_module_args(module_args)
- my_obj = my_module()
- if not self.onbox:
- my_obj.server = MockONTAPConnection('motd')
- with pytest.raises(AnsibleExitJson) as exc:
- my_obj.apply()
- return exc.value.args[0]['changed']
-
- def test_module_fail_when_required_args_missing(self):
- ''' required arguments are reported as errors '''
- with pytest.raises(AnsibleFailJson) as exc:
- set_module_args({})
- my_module()
- print('Info: %s' % exc.value.args[0]['msg'])
-
- def test_ensure_motd_get_called(self):
- ''' fetching details of motd '''
- set_module_args(self.set_default_args())
- my_obj = my_module()
- my_obj.server = self.server
- assert my_obj.motd_get() is None
-
- def test_ensure_get_called_existing(self):
- ''' test for existing motd'''
- set_module_args(self.set_default_args())
- my_obj = my_module()
- my_obj.server = MockONTAPConnection(kind='motd')
- assert my_obj.motd_get()
-
- def test_motd_create(self):
- ''' test for creating motd'''
- set_module_args(self.set_default_args())
- my_obj = my_module()
- if not self.onbox:
- my_obj.server = MockONTAPConnection(kind='motd')
- with pytest.raises(AnsibleExitJson) as exc:
- my_obj.apply()
- assert not exc.value.args[0]['changed']
-
- def test_motd_delete(self):
- ''' test for deleting motd'''
- module_args = {
- 'state': 'absent',
- }
- changed = self.call_command(module_args)
- assert changed
-
- def test_if_all_methods_catch_exception(self):
- set_module_args(self.set_default_args())
- my_obj = my_module()
- if not self.onbox:
- my_obj.server = MockONTAPConnection('motd_fail')
- with pytest.raises(AnsibleFailJson) as exc:
- my_obj.motd_get()
- assert '' in exc.value.args[0]['msg']
- with pytest.raises(AnsibleFailJson) as exc:
- my_obj.modify_motd()
- assert 'Error creating motd: ' in exc.value.args[0]['msg']
diff --git a/test/units/modules/storage/netapp/test_na_ontap_ndmp.py b/test/units/modules/storage/netapp/test_na_ontap_ndmp.py
deleted file mode 100644
index 69cd58049f..0000000000
--- a/test/units/modules/storage/netapp/test_na_ontap_ndmp.py
+++ /dev/null
@@ -1,166 +0,0 @@
-# (c) 2019, NetApp, Inc
-# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-
-''' unit test template for ONTAP Ansible module '''
-
-from __future__ import (absolute_import, division, print_function)
-__metaclass__ = type
-import json
-import pytest
-
-from units.compat import unittest
-from units.compat.mock import patch, Mock
-from ansible.module_utils import basic
-from ansible.module_utils._text import to_bytes
-import ansible.module_utils.netapp as netapp_utils
-
-from ansible.modules.storage.netapp.na_ontap_ndmp \
- import NetAppONTAPNdmp as ndmp_module # module under test
-
-if not netapp_utils.has_netapp_lib():
- pytestmark = pytest.mark.skip('skipping as missing required netapp_lib')
-
-
-def set_module_args(args):
- """prepare arguments so that they will be picked up during module creation"""
- args = json.dumps({'ANSIBLE_MODULE_ARGS': args})
- basic._ANSIBLE_ARGS = to_bytes(args) # pylint: disable=protected-access
-
-
-class AnsibleExitJson(Exception):
- """Exception class to be raised by module.exit_json and caught by the test case"""
- pass
-
-
-class AnsibleFailJson(Exception):
- """Exception class to be raised by module.fail_json and caught by the test case"""
- pass
-
-
-def exit_json(*args, **kwargs): # pylint: disable=unused-argument
- """function to patch over exit_json; package return data into an exception"""
- if 'changed' not in kwargs:
- kwargs['changed'] = False
- raise AnsibleExitJson(kwargs)
-
-
-def fail_json(*args, **kwargs): # pylint: disable=unused-argument
- """function to patch over fail_json; package return data into an exception"""
- kwargs['failed'] = True
- raise AnsibleFailJson(kwargs)
-
-
-class MockONTAPConnection(object):
- ''' mock server connection to ONTAP host '''
-
- def __init__(self, kind=None, data=None):
- ''' save arguments '''
- self.type = kind
- self.data = data
- self.xml_in = None
- self.xml_out = None
-
- def invoke_successfully(self, xml, enable_tunneling): # pylint: disable=unused-argument
- ''' mock invoke_successfully returning xml data '''
- self.xml_in = xml
- if self.type == 'ndmp':
- xml = self.build_ndmp_info(self.data)
- if self.type == 'error':
- error = netapp_utils.zapi.NaApiError('test', 'error')
- raise error
- self.xml_out = xml
- return xml
-
- @staticmethod
- def build_ndmp_info(ndmp_details):
- ''' build xml data for ndmp '''
- xml = netapp_utils.zapi.NaElement('xml')
- attributes = {
- 'num-records': 1,
- 'attributes-list': {
- 'ndmp-vserver-attributes-info': {
- 'ignore_ctime_enabled': ndmp_details['ignore_ctime_enabled'],
- 'backup_log_enable': ndmp_details['backup_log_enable'],
- 'authtype': [
- {'ndmpd-authtypes': 'plaintext'},
- {'ndmpd-authtypes': 'challenge'}
- ]
- }
- }
- }
- xml.translate_struct(attributes)
- return xml
-
-
-class TestMyModule(unittest.TestCase):
- ''' a group of related Unit Tests '''
-
- def setUp(self):
- self.mock_module_helper = patch.multiple(basic.AnsibleModule,
- exit_json=exit_json,
- fail_json=fail_json)
- self.mock_module_helper.start()
- self.addCleanup(self.mock_module_helper.stop)
- self.mock_ndmp = {
- 'ignore_ctime_enabled': 'true',
- 'backup_log_enable': 'false'
- }
-
- def mock_args(self):
- return {
- 'ignore_ctime_enabled': self.mock_ndmp['ignore_ctime_enabled'],
- 'backup_log_enable': self.mock_ndmp['backup_log_enable'],
- 'vserver': 'ansible',
- 'hostname': 'test',
- 'username': 'test_user',
- 'password': 'test_pass!',
- 'https': 'False'
- }
-
- def get_ndmp_mock_object(self, kind=None):
- """
- Helper method to return an na_ontap_ndmp object
- :param kind: passes this param to MockONTAPConnection()
- :return: na_ontap_ndmp object
- """
- obj = ndmp_module()
- obj.asup_log_for_cserver = Mock(return_value=None)
- obj.server = Mock()
- obj.server.invoke_successfully = Mock()
- if kind is None:
- obj.server = MockONTAPConnection()
- else:
- obj.server = MockONTAPConnection(kind=kind, data=self.mock_ndmp)
- return obj
-
- @patch('ansible.modules.storage.netapp.na_ontap_ndmp.NetAppONTAPNdmp.ndmp_get_iter')
- def test_successful_modify(self, ger_ndmp):
- ''' Test successful modify ndmp'''
- data = self.mock_args()
- set_module_args(data)
- current = {
- 'ignore_ctime_enabled': False,
- 'backup_log_enable': True
- }
- ger_ndmp.side_effect = [
- current
- ]
- with pytest.raises(AnsibleExitJson) as exc:
- self.get_ndmp_mock_object('ndmp').apply()
- assert exc.value.args[0]['changed']
-
- @patch('ansible.modules.storage.netapp.na_ontap_ndmp.NetAppONTAPNdmp.ndmp_get_iter')
- def test_modify_error(self, ger_ndmp):
- ''' Test modify error '''
- data = self.mock_args()
- set_module_args(data)
- current = {
- 'ignore_ctime_enabled': False,
- 'backup_log_enable': True
- }
- ger_ndmp.side_effect = [
- current
- ]
- with pytest.raises(AnsibleFailJson) as exc:
- self.get_ndmp_mock_object('error').apply()
- assert exc.value.args[0]['msg'] == 'Error modifying ndmp on ansible: NetApp API failed. Reason - test:error'
diff --git a/test/units/modules/storage/netapp/test_na_ontap_net_ifgrp.py b/test/units/modules/storage/netapp/test_na_ontap_net_ifgrp.py
deleted file mode 100644
index 498dc46930..0000000000
--- a/test/units/modules/storage/netapp/test_na_ontap_net_ifgrp.py
+++ /dev/null
@@ -1,298 +0,0 @@
-# (c) 2018, NetApp, Inc
-# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-
-''' unit test template for ONTAP Ansible module '''
-
-from __future__ import print_function
-import json
-import pytest
-
-from units.compat import unittest
-from units.compat.mock import patch, Mock
-from ansible.module_utils import basic
-from ansible.module_utils._text import to_bytes
-import ansible.module_utils.netapp as netapp_utils
-
-from ansible.modules.storage.netapp.na_ontap_net_ifgrp \
- import NetAppOntapIfGrp as ifgrp_module # module under test
-
-if not netapp_utils.has_netapp_lib():
- pytestmark = pytest.mark.skip('skipping as missing required netapp_lib')
-
-
-def set_module_args(args):
- """prepare arguments so that they will be picked up during module creation"""
- args = json.dumps({'ANSIBLE_MODULE_ARGS': args})
- basic._ANSIBLE_ARGS = to_bytes(args) # pylint: disable=protected-access
-
-
-class AnsibleExitJson(Exception):
- """Exception class to be raised by module.exit_json and caught by the test case"""
- pass
-
-
-class AnsibleFailJson(Exception):
- """Exception class to be raised by module.fail_json and caught by the test case"""
- pass
-
-
-def exit_json(*args, **kwargs): # pylint: disable=unused-argument
- """function to patch over exit_json; package return data into an exception"""
- if 'changed' not in kwargs:
- kwargs['changed'] = False
- raise AnsibleExitJson(kwargs)
-
-
-def fail_json(*args, **kwargs): # pylint: disable=unused-argument
- """function to patch over fail_json; package return data into an exception"""
- kwargs['failed'] = True
- raise AnsibleFailJson(kwargs)
-
-
-class MockONTAPConnection(object):
- ''' mock server connection to ONTAP host '''
-
- def __init__(self, kind=None, data=None):
- ''' save arguments '''
- self.kind = kind
- self.params = data
- self.xml_in = None
- self.xml_out = None
-
- def invoke_successfully(self, xml, enable_tunneling): # pylint: disable=unused-argument
- ''' mock invoke_successfully returning xml data '''
- self.xml_in = xml
- if self.kind == 'ifgrp':
- xml = self.build_ifgrp_info(self.params)
- elif self.kind == 'ifgrp-ports':
- xml = self.build_ifgrp_ports_info(self.params)
- elif self.kind == 'ifgrp-fail':
- raise netapp_utils.zapi.NaApiError(code='TEST', message="This exception is from the unit test")
- self.xml_out = xml
- return xml
-
- @staticmethod
- def build_ifgrp_info(ifgrp_details):
- ''' build xml data for ifgrp-info '''
- xml = netapp_utils.zapi.NaElement('xml')
- attributes = {
- 'num-records': 1,
- 'attributes-list': {
- 'net-port-info': {
- 'port': ifgrp_details['name'],
- 'ifgrp-distribution-function': 'mac',
- 'ifgrp-mode': ifgrp_details['mode'],
- 'node': ifgrp_details['node']
- }
- }
- }
- xml.translate_struct(attributes)
- return xml
-
- @staticmethod
- def build_ifgrp_ports_info(data):
- ''' build xml data for ifgrp-ports '''
- xml = netapp_utils.zapi.NaElement('xml')
- attributes = {
- 'attributes': {
- 'net-ifgrp-info': {
- 'ports': [
- {'lif-bindable': data['ports'][0]},
- {'lif-bindable': data['ports'][1]},
- {'lif-bindable': data['ports'][2]}
- ]
- }
- }
- }
- xml.translate_struct(attributes)
- return xml
-
-
-class TestMyModule(unittest.TestCase):
- ''' a group of related Unit Tests '''
-
- def setUp(self):
- self.mock_module_helper = patch.multiple(basic.AnsibleModule,
- exit_json=exit_json,
- fail_json=fail_json)
- self.mock_module_helper.start()
- self.addCleanup(self.mock_module_helper.stop)
- self.mock_ifgrp = {
- 'name': 'test',
- 'port': 'a1',
- 'node': 'test_vserver',
- 'mode': 'something'
- }
-
- def mock_args(self):
- return {
- 'name': self.mock_ifgrp['name'],
- 'distribution_function': 'mac',
- 'ports': [self.mock_ifgrp['port']],
- 'node': self.mock_ifgrp['node'],
- 'mode': self.mock_ifgrp['mode'],
- 'hostname': 'test',
- 'username': 'test_user',
- 'password': 'test_pass!'
- }
-
- def get_ifgrp_mock_object(self, kind=None, data=None):
- """
- Helper method to return an na_ontap_net_ifgrp object
- :param kind: passes this param to MockONTAPConnection()
- :return: na_ontap_net_ifgrp object
- """
- obj = ifgrp_module()
- obj.autosupport_log = Mock(return_value=None)
- if data is None:
- data = self.mock_ifgrp
- obj.server = MockONTAPConnection(kind=kind, data=data)
- return obj
-
- def test_module_fail_when_required_args_missing(self):
- ''' required arguments are reported as errors '''
- with pytest.raises(AnsibleFailJson) as exc:
- set_module_args({})
- ifgrp_module()
- print('Info: %s' % exc.value.args[0]['msg'])
-
- def test_get_nonexistent_ifgrp(self):
- ''' Test if get_ifgrp returns None for non-existent ifgrp '''
- set_module_args(self.mock_args())
- result = self.get_ifgrp_mock_object().get_if_grp()
- assert result is None
-
- def test_get_existing_ifgrp(self):
- ''' Test if get_ifgrp returns details for existing ifgrp '''
- set_module_args(self.mock_args())
- result = self.get_ifgrp_mock_object('ifgrp').get_if_grp()
- assert result['name'] == self.mock_ifgrp['name']
-
- def test_successful_create(self):
- ''' Test successful create '''
- data = self.mock_args()
- set_module_args(data)
- with pytest.raises(AnsibleExitJson) as exc:
- self.get_ifgrp_mock_object().apply()
- assert exc.value.args[0]['changed']
-
- def test_successful_delete(self):
- ''' Test delete existing volume '''
- data = self.mock_args()
- data['state'] = 'absent'
- set_module_args(data)
- with pytest.raises(AnsibleExitJson) as exc:
- self.get_ifgrp_mock_object('ifgrp').apply()
- assert exc.value.args[0]['changed']
-
- def test_successful_modify(self):
- ''' Test delete existing volume '''
- data = self.mock_args()
- data['ports'] = ['1', '2', '3']
- set_module_args(data)
- with pytest.raises(AnsibleExitJson) as exc:
- self.get_ifgrp_mock_object('ifgrp').apply()
- assert exc.value.args[0]['changed']
-
- @patch('ansible.modules.storage.netapp.na_ontap_net_ifgrp.NetAppOntapIfGrp.get_if_grp')
- @patch('ansible.modules.storage.netapp.na_ontap_net_ifgrp.NetAppOntapIfGrp.create_if_grp')
- def test_create_called(self, create_ifgrp, get_ifgrp):
- data = self.mock_args()
- set_module_args(data)
- get_ifgrp.return_value = None
- with pytest.raises(AnsibleExitJson) as exc:
- self.get_ifgrp_mock_object().apply()
- get_ifgrp.assert_called_with()
- create_ifgrp.assert_called_with()
-
- @patch('ansible.modules.storage.netapp.na_ontap_net_ifgrp.NetAppOntapIfGrp.add_port_to_if_grp')
- def test_if_ports_are_added_after_create(self, add_ports):
- ''' Test successful create '''
- data = self.mock_args()
- set_module_args(data)
- self.get_ifgrp_mock_object().create_if_grp()
- add_ports.assert_called_with('a1')
-
- @patch('ansible.modules.storage.netapp.na_ontap_net_ifgrp.NetAppOntapIfGrp.get_if_grp')
- @patch('ansible.modules.storage.netapp.na_ontap_net_ifgrp.NetAppOntapIfGrp.delete_if_grp')
- def test_delete_called(self, delete_ifgrp, get_ifgrp):
- data = self.mock_args()
- data['state'] = 'absent'
- set_module_args(data)
- get_ifgrp.return_value = Mock()
- with pytest.raises(AnsibleExitJson) as exc:
- self.get_ifgrp_mock_object().apply()
- get_ifgrp.assert_called_with()
- delete_ifgrp.assert_called_with()
-
- def test_get_return_value(self):
- data = self.mock_args()
- set_module_args(data)
- result = self.get_ifgrp_mock_object('ifgrp').get_if_grp()
- assert result['name'] == data['name']
- assert result['mode'] == data['mode']
- assert result['node'] == data['node']
-
- def test_get_ports_list(self):
- data = self.mock_args()
- data['ports'] = ['e0a', 'e0b', 'e0c']
- set_module_args(data)
- result = self.get_ifgrp_mock_object('ifgrp-ports', data).get_if_grp_ports()
- assert result['ports'] == data['ports']
-
- def test_add_port_packet(self):
- data = self.mock_args()
- set_module_args(data)
- obj = self.get_ifgrp_mock_object('ifgrp')
- obj.add_port_to_if_grp('addme')
- assert obj.server.xml_in['port'] == 'addme'
-
- @patch('ansible.modules.storage.netapp.na_ontap_net_ifgrp.NetAppOntapIfGrp.remove_port_to_if_grp')
- @patch('ansible.modules.storage.netapp.na_ontap_net_ifgrp.NetAppOntapIfGrp.add_port_to_if_grp')
- def test_modify_ports_calls_remove_existing_ports(self, add_port, remove_port):
- ''' Test if already existing ports are not being added again '''
- data = self.mock_args()
- data['ports'] = ['1', '2']
- set_module_args(data)
- self.get_ifgrp_mock_object('ifgrp').modify_ports(current_ports=['1', '2', '3'])
- assert remove_port.call_count == 1
- assert add_port.call_count == 0
-
- @patch('ansible.modules.storage.netapp.na_ontap_net_ifgrp.NetAppOntapIfGrp.remove_port_to_if_grp')
- @patch('ansible.modules.storage.netapp.na_ontap_net_ifgrp.NetAppOntapIfGrp.add_port_to_if_grp')
- def test_modify_ports_calls_add_new_ports(self, add_port, remove_port):
- ''' Test new ports are added '''
- data = self.mock_args()
- data['ports'] = ['1', '2', '3', '4']
- set_module_args(data)
- self.get_ifgrp_mock_object('ifgrp').modify_ports(current_ports=['1', '2'])
- assert remove_port.call_count == 0
- assert add_port.call_count == 2
-
- def test_get_ports_returns_none(self):
- set_module_args(self.mock_args())
- result = self.get_ifgrp_mock_object().get_if_grp_ports()
- assert result['ports'] == []
- result = self.get_ifgrp_mock_object().get_if_grp()
- assert result is None
-
- def test_if_all_methods_catch_exception(self):
- set_module_args(self.mock_args())
- with pytest.raises(AnsibleFailJson) as exc:
- self.get_ifgrp_mock_object('ifgrp-fail').get_if_grp()
- assert 'Error getting if_group test' in exc.value.args[0]['msg']
- with pytest.raises(AnsibleFailJson) as exc:
- self.get_ifgrp_mock_object('ifgrp-fail').create_if_grp()
- assert 'Error creating if_group test' in exc.value.args[0]['msg']
- with pytest.raises(AnsibleFailJson) as exc:
- self.get_ifgrp_mock_object('ifgrp-fail').get_if_grp_ports()
- assert 'Error getting if_group ports test' in exc.value.args[0]['msg']
- with pytest.raises(AnsibleFailJson) as exc:
- self.get_ifgrp_mock_object('ifgrp-fail').add_port_to_if_grp('test-port')
- assert 'Error adding port test-port to if_group test' in exc.value.args[0]['msg']
- with pytest.raises(AnsibleFailJson) as exc:
- self.get_ifgrp_mock_object('ifgrp-fail').remove_port_to_if_grp('test-port')
- assert 'Error removing port test-port to if_group test' in exc.value.args[0]['msg']
- with pytest.raises(AnsibleFailJson) as exc:
- self.get_ifgrp_mock_object('ifgrp-fail').delete_if_grp()
- assert 'Error deleting if_group test' in exc.value.args[0]['msg']
diff --git a/test/units/modules/storage/netapp/test_na_ontap_net_port.py b/test/units/modules/storage/netapp/test_na_ontap_net_port.py
deleted file mode 100644
index b557a964b6..0000000000
--- a/test/units/modules/storage/netapp/test_na_ontap_net_port.py
+++ /dev/null
@@ -1,179 +0,0 @@
-# (c) 2018, NetApp, Inc
-# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-
-''' unit test template for ONTAP Ansible module '''
-
-from __future__ import print_function
-import json
-import pytest
-
-from units.compat import unittest
-from units.compat.mock import patch, Mock
-from ansible.module_utils import basic
-from ansible.module_utils._text import to_bytes
-import ansible.module_utils.netapp as netapp_utils
-
-from ansible.modules.storage.netapp.na_ontap_net_port \
- import NetAppOntapNetPort as port_module # module under test
-
-if not netapp_utils.has_netapp_lib():
- pytestmark = pytest.mark.skip('skipping as missing required netapp_lib')
-
-
-def set_module_args(args):
- """prepare arguments so that they will be picked up during module creation"""
- args = json.dumps({'ANSIBLE_MODULE_ARGS': args})
- basic._ANSIBLE_ARGS = to_bytes(args) # pylint: disable=protected-access
-
-
-class AnsibleExitJson(Exception):
- """Exception class to be raised by module.exit_json and caught by the test case"""
- pass
-
-
-class AnsibleFailJson(Exception):
- """Exception class to be raised by module.fail_json and caught by the test case"""
- pass
-
-
-def exit_json(*args, **kwargs): # pylint: disable=unused-argument
- """function to patch over exit_json; package return data into an exception"""
- if 'changed' not in kwargs:
- kwargs['changed'] = False
- raise AnsibleExitJson(kwargs)
-
-
-def fail_json(*args, **kwargs): # pylint: disable=unused-argument
- """function to patch over fail_json; package return data into an exception"""
- kwargs['failed'] = True
- raise AnsibleFailJson(kwargs)
-
-
-class MockONTAPConnection(object):
- ''' mock server connection to ONTAP host '''
-
- def __init__(self, kind=None, data=None):
- ''' save arguments '''
- self.type = kind
- self.data = data
- self.xml_in = None
- self.xml_out = None
-
- def invoke_successfully(self, xml, enable_tunneling): # pylint: disable=unused-argument
- ''' mock invoke_successfully returning xml data '''
- self.xml_in = xml
- if self.type == 'port':
- xml = self.build_port_info(self.data)
- self.xml_out = xml
- return xml
-
- @staticmethod
- def build_port_info(port_details):
- ''' build xml data for net-port-info '''
- xml = netapp_utils.zapi.NaElement('xml')
- attributes = {
- 'num-records': 1,
- 'attributes-list': {
- 'net-port-info': {
- # 'port': port_details['port'],
- 'mtu': port_details['mtu'],
- 'is-administrative-auto-negotiate': 'true',
- 'ipspace': 'default',
- 'administrative-flowcontrol': port_details['flowcontrol_admin'],
- 'node': port_details['node']
- }
- }
- }
- xml.translate_struct(attributes)
- return xml
-
-
-class TestMyModule(unittest.TestCase):
- ''' a group of related Unit Tests '''
-
- def setUp(self):
- self.mock_module_helper = patch.multiple(basic.AnsibleModule,
- exit_json=exit_json,
- fail_json=fail_json)
- self.mock_module_helper.start()
- self.addCleanup(self.mock_module_helper.stop)
- self.server = MockONTAPConnection()
- self.mock_port = {
- 'node': 'test',
- 'ports': 'a1',
- 'flowcontrol_admin': 'something',
- 'mtu': '1000'
- }
-
- def mock_args(self):
- return {
- 'node': self.mock_port['node'],
- 'flowcontrol_admin': self.mock_port['flowcontrol_admin'],
- 'ports': [self.mock_port['ports']],
- 'mtu': self.mock_port['mtu'],
- 'hostname': 'test',
- 'username': 'test_user',
- 'password': 'test_pass!'
- }
-
- def get_port_mock_object(self, kind=None, data=None):
- """
- Helper method to return an na_ontap_net_port object
- :param kind: passes this param to MockONTAPConnection()
- :return: na_ontap_net_port object
- """
- obj = port_module()
- obj.autosupport_log = Mock(return_value=None)
- if data is None:
- data = self.mock_port
- obj.server = MockONTAPConnection(kind=kind, data=data)
- return obj
-
- def test_module_fail_when_required_args_missing(self):
- ''' required arguments are reported as errors '''
- with pytest.raises(AnsibleFailJson) as exc:
- set_module_args({})
- port_module()
- print('Info: %s' % exc.value.args[0]['msg'])
-
- def test_get_nonexistent_port(self):
- ''' Test if get_net_port returns None for non-existent port '''
- set_module_args(self.mock_args())
- result = self.get_port_mock_object().get_net_port('test')
- assert result is None
-
- def test_get_existing_port(self):
- ''' Test if get_net_port returns details for existing port '''
- set_module_args(self.mock_args())
- result = self.get_port_mock_object('port').get_net_port('test')
- assert result['mtu'] == self.mock_port['mtu']
- assert result['flowcontrol_admin'] == self.mock_port['flowcontrol_admin']
-
- def test_successful_modify(self):
- ''' Test modify_net_port '''
- data = self.mock_args()
- data['mtu'] = '2000'
- set_module_args(data)
- with pytest.raises(AnsibleExitJson) as exc:
- self.get_port_mock_object('port').apply()
- assert exc.value.args[0]['changed']
-
- def test_successful_modify_multiple_ports(self):
- ''' Test modify_net_port '''
- data = self.mock_args()
- data['ports'] = ['a1', 'a2']
- data['mtu'] = '2000'
- set_module_args(data)
- with pytest.raises(AnsibleExitJson) as exc:
- self.get_port_mock_object('port').apply()
- assert exc.value.args[0]['changed']
-
- @patch('ansible.modules.storage.netapp.na_ontap_net_port.NetAppOntapNetPort.get_net_port')
- def test_get_called(self, get_port):
- ''' Test get_net_port '''
- data = self.mock_args()
- data['ports'] = ['a1', 'a2']
- set_module_args(data)
- with pytest.raises(AnsibleExitJson) as exc:
- self.get_port_mock_object('port').apply()
- assert get_port.call_count == 2
diff --git a/test/units/modules/storage/netapp/test_na_ontap_net_routes.py b/test/units/modules/storage/netapp/test_na_ontap_net_routes.py
deleted file mode 100644
index 6e383098f9..0000000000
--- a/test/units/modules/storage/netapp/test_na_ontap_net_routes.py
+++ /dev/null
@@ -1,309 +0,0 @@
-# (c) 2018, NetApp, Inc
-# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-
-''' unit test template for ONTAP Ansible module '''
-
-from __future__ import print_function
-import json
-import pytest
-
-from units.compat import unittest
-from units.compat.mock import patch, Mock
-from ansible.module_utils import basic
-from ansible.module_utils._text import to_bytes
-import ansible.module_utils.netapp as netapp_utils
-
-from ansible.modules.storage.netapp.na_ontap_net_routes \
- import NetAppOntapNetRoutes as net_route_module # module under test
-
-if not netapp_utils.has_netapp_lib():
- pytestmark = pytest.mark.skip('skipping as missing required netapp_lib')
-
-
-def set_module_args(args):
- """prepare arguments so that they will be picked up during module creation"""
- args = json.dumps({'ANSIBLE_MODULE_ARGS': args})
- basic._ANSIBLE_ARGS = to_bytes(args) # pylint: disable=protected-access
-
-
-class AnsibleExitJson(Exception):
- """Exception class to be raised by module.exit_json and caught by the test case"""
- pass
-
-
-class AnsibleFailJson(Exception):
- """Exception class to be raised by module.fail_json and caught by the test case"""
- pass
-
-
-def exit_json(*args, **kwargs): # pylint: disable=unused-argument
- """function to patch over exit_json; package return data into an exception"""
- if 'changed' not in kwargs:
- kwargs['changed'] = False
- raise AnsibleExitJson(kwargs)
-
-
-def fail_json(*args, **kwargs): # pylint: disable=unused-argument
- """function to patch over fail_json; package return data into an exception"""
- kwargs['failed'] = True
- raise AnsibleFailJson(kwargs)
-
-
-class MockONTAPConnection(object):
- ''' mock server connection to ONTAP host '''
-
- def __init__(self, kind=None, data=None):
- ''' save arguments '''
- self.kind = kind
- self.params = data
- self.xml_in = None
- self.xml_out = None
-
- def invoke_successfully(self, xml, enable_tunneling): # pylint: disable=unused-argument
- ''' mock invoke_successfully returning xml data '''
- self.xml_in = xml
- if self.kind == 'net_route':
- xml = self.build_net_route_info(self.params)
- self.xml_out = xml
- return xml
-
- @staticmethod
- def build_net_route_info(net_route_details):
- ''' build xml data for net_route-attributes '''
- xml = netapp_utils.zapi.NaElement('xml')
- attributes = {
- 'attributes': {
- 'net-vs-routes-info': {
- 'address-family': 'ipv4',
- 'destination': net_route_details['destination'],
- 'gateway': net_route_details['gateway'],
- 'metric': net_route_details['metric'],
- 'vserver': net_route_details['vserver']
- }
- }
- }
- xml.translate_struct(attributes)
- return xml
-
-
-class TestMyModule(unittest.TestCase):
- ''' a group of related Unit Tests '''
-
- def setUp(self):
- self.mock_module_helper = patch.multiple(basic.AnsibleModule,
- exit_json=exit_json,
- fail_json=fail_json)
- self.mock_module_helper.start()
- self.addCleanup(self.mock_module_helper.stop)
- self.server = MockONTAPConnection()
- self.mock_net_route = {
- 'destination': '176.0.0.0/24',
- 'gateway': '10.193.72.1',
- 'vserver': 'test_vserver',
- 'metric': '70'
- }
-
- def mock_args(self):
- return {
- 'vserver': self.mock_net_route['vserver'],
- 'destination': self.mock_net_route['destination'],
- 'gateway': self.mock_net_route['gateway'],
- 'metric': self.mock_net_route['metric'],
- 'hostname': 'test',
- 'username': 'test_user',
- 'password': 'test_pass!'
- }
-
- def get_net_route_mock_object(self, kind=None, data=None):
- """
- Helper method to return an na_ontap_net_route object
- :param kind: passes this param to MockONTAPConnection()
- :param data: passes this data to MockONTAPConnection()
- :return: na_ontap_net_route object
- """
- net_route_obj = net_route_module()
- net_route_obj.ems_log_event = Mock(return_value=None)
- net_route_obj.cluster = Mock()
- net_route_obj.cluster.invoke_successfully = Mock()
- if kind is None:
- net_route_obj.server = MockONTAPConnection()
- else:
- if data is None:
- net_route_obj.server = MockONTAPConnection(kind='net_route', data=self.mock_net_route)
- else:
- net_route_obj.server = MockONTAPConnection(kind='net_route', data=data)
- return net_route_obj
-
- def test_module_fail_when_required_args_missing(self):
- ''' required arguments are reported as errors '''
- with pytest.raises(AnsibleFailJson) as exc:
- set_module_args({})
- net_route_module()
- print('Info: %s' % exc.value.args[0]['msg'])
-
- def test_get_nonexistent_net_route(self):
- ''' Test if get_net_route returns None for non-existent net_route '''
- set_module_args(self.mock_args())
- result = self.get_net_route_mock_object().get_net_route()
- assert result is None
-
- def test_get_existing_job(self):
- ''' Test if get_net_route returns details for existing net_route '''
- set_module_args(self.mock_args())
- result = self.get_net_route_mock_object('net_route').get_net_route()
- assert result['destination'] == self.mock_net_route['destination']
- assert result['gateway'] == self.mock_net_route['gateway']
-
- def test_create_error_missing_param(self):
- ''' Test if create throws an error if destination is not specified'''
- data = self.mock_args()
- del data['destination']
- set_module_args(data)
- with pytest.raises(AnsibleFailJson) as exc:
- self.get_net_route_mock_object('net_route').create_net_route()
- msg = 'missing required arguments: destination'
- assert exc.value.args[0]['msg'] == msg
-
- @patch('ansible.modules.storage.netapp.na_ontap_net_routes.NetAppOntapNetRoutes.create_net_route')
- def test_successful_create(self, create_net_route):
- ''' Test successful create '''
- data = self.mock_args()
- set_module_args(data)
- with pytest.raises(AnsibleExitJson) as exc:
- self.get_net_route_mock_object().apply()
- assert exc.value.args[0]['changed']
- create_net_route.assert_called_with()
-
- def test_create_idempotency(self):
- ''' Test create idempotency '''
- set_module_args(self.mock_args())
- obj = self.get_net_route_mock_object('net_route')
- with pytest.raises(AnsibleExitJson) as exc:
- obj.apply()
- assert not exc.value.args[0]['changed']
-
- def test_successful_delete(self):
- ''' Test successful delete '''
- data = self.mock_args()
- data['state'] = 'absent'
- set_module_args(data)
- with pytest.raises(AnsibleExitJson) as exc:
- self.get_net_route_mock_object('net_route').apply()
- assert exc.value.args[0]['changed']
-
- def test_delete_idempotency(self):
- ''' Test delete idempotency '''
- data = self.mock_args()
- data['state'] = 'absent'
- set_module_args(data)
- with pytest.raises(AnsibleExitJson) as exc:
- self.get_net_route_mock_object().apply()
- assert not exc.value.args[0]['changed']
-
- def test_successful_modify_metric(self):
- ''' Test successful modify metric '''
- data = self.mock_args()
- del data['metric']
- data['from_metric'] = '70'
- data['metric'] = '40'
- set_module_args(data)
- with pytest.raises(AnsibleExitJson) as exc:
- self.get_net_route_mock_object('net_route').apply()
- assert exc.value.args[0]['changed']
-
- def test_modify_metric_idempotency(self):
- ''' Test modify metric idempotency'''
- data = self.mock_args()
- data['metric'] = '70'
- set_module_args(data)
- with pytest.raises(AnsibleExitJson) as exc:
- self.get_net_route_mock_object('net_route').apply()
- assert not exc.value.args[0]['changed']
-
- @patch('ansible.modules.storage.netapp.na_ontap_net_routes.NetAppOntapNetRoutes.get_net_route')
- def test_successful_modify_gateway(self, get_net_route):
- ''' Test successful modify gateway '''
- data = self.mock_args()
- del data['gateway']
- data['from_gateway'] = '10.193.72.1'
- data['gateway'] = '10.193.0.1'
- set_module_args(data)
- current = {
- 'destination': '176.0.0.0/24',
- 'gateway': '10.193.72.1',
- 'metric': '70',
- 'vserver': 'test_server'
- }
- get_net_route.side_effect = [
- None,
- current
- ]
- with pytest.raises(AnsibleExitJson) as exc:
- self.get_net_route_mock_object().apply()
- assert exc.value.args[0]['changed']
-
- @patch('ansible.modules.storage.netapp.na_ontap_net_routes.NetAppOntapNetRoutes.get_net_route')
- def test__modify_gateway_idempotency(self, get_net_route):
- ''' Test modify gateway idempotency '''
- data = self.mock_args()
- del data['gateway']
- data['from_gateway'] = '10.193.72.1'
- data['gateway'] = '10.193.0.1'
- set_module_args(data)
- current = {
- 'destination': '178.0.0.1/24',
- 'gateway': '10.193.72.1',
- 'metric': '70',
- 'vserver': 'test_server'
- }
- get_net_route.side_effect = [
- current,
- None
- ]
- with pytest.raises(AnsibleExitJson) as exc:
- self.get_net_route_mock_object('net_route', current).apply()
- assert not exc.value.args[0]['changed']
-
- @patch('ansible.modules.storage.netapp.na_ontap_net_routes.NetAppOntapNetRoutes.get_net_route')
- def test_successful_modify_destination(self, get_net_route):
- ''' Test successful modify destination '''
- data = self.mock_args()
- del data['destination']
- data['from_destination'] = '176.0.0.0/24'
- data['destination'] = '178.0.0.1/24'
- set_module_args(data)
- current = {
- 'destination': '176.0.0.0/24',
- 'gateway': '10.193.72.1',
- 'metric': '70',
- 'vserver': 'test_server'
- }
- get_net_route.side_effect = [
- None,
- current
- ]
- with pytest.raises(AnsibleExitJson) as exc:
- self.get_net_route_mock_object().apply()
- assert exc.value.args[0]['changed']
-
- @patch('ansible.modules.storage.netapp.na_ontap_net_routes.NetAppOntapNetRoutes.get_net_route')
- def test__modify_destination_idempotency(self, get_net_route):
- ''' Test modify destination idempotency'''
- data = self.mock_args()
- del data['destination']
- data['from_destination'] = '176.0.0.0/24'
- data['destination'] = '178.0.0.1/24'
- set_module_args(data)
- current = {
- 'destination': '178.0.0.1/24',
- 'gateway': '10.193.72.1',
- 'metric': '70',
- 'vserver': 'test_server'
- }
- get_net_route.side_effect = [
- current,
- None
- ]
- with pytest.raises(AnsibleExitJson) as exc:
- self.get_net_route_mock_object('net_route', current).apply()
- assert not exc.value.args[0]['changed']
diff --git a/test/units/modules/storage/netapp/test_na_ontap_net_subnet.py b/test/units/modules/storage/netapp/test_na_ontap_net_subnet.py
deleted file mode 100644
index d18498143f..0000000000
--- a/test/units/modules/storage/netapp/test_na_ontap_net_subnet.py
+++ /dev/null
@@ -1,264 +0,0 @@
-# (c) 2018, NetApp, Inc
-# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-
-''' unit test template for ONTAP Ansible module '''
-
-from __future__ import print_function
-import json
-import pytest
-
-from units.compat import unittest
-from units.compat.mock import patch
-from ansible.module_utils import basic
-from ansible.module_utils._text import to_bytes
-import ansible.module_utils.netapp as netapp_utils
-
-from ansible.modules.storage.netapp.na_ontap_net_subnet \
- import NetAppOntapSubnet as my_module # module under test
-
-if not netapp_utils.has_netapp_lib():
- pytestmark = pytest.mark.skip('skipping as missing required netapp_lib')
-
-
-def set_module_args(args):
- """prepare arguments so that they will be picked up during module creation"""
- args = json.dumps({'ANSIBLE_MODULE_ARGS': args})
- basic._ANSIBLE_ARGS = to_bytes(args) # pylint: disable=protected-access
-
-
-class AnsibleExitJson(Exception):
- """Exception class to be raised by module.exit_json and caught by the test case"""
- pass
-
-
-class AnsibleFailJson(Exception):
- """Exception class to be raised by module.fail_json and caught by the test case"""
- pass
-
-
-def exit_json(*args, **kwargs): # pylint: disable=unused-argument
- """function to patch over exit_json; package return data into an exception"""
- if 'changed' not in kwargs:
- kwargs['changed'] = False
- raise AnsibleExitJson(kwargs)
-
-
-def fail_json(*args, **kwargs): # pylint: disable=unused-argument
- """function to patch over fail_json; package return data into an exception"""
- kwargs['failed'] = True
- raise AnsibleFailJson(kwargs)
-
-
-class MockONTAPConnection(object):
- ''' mock server connection to ONTAP host '''
-
- def __init__(self, kind=None, data=None):
- ''' save arguments '''
- self.type = kind
- self.params = data
- self.xml_in = None
- self.xml_out = None
-
- def invoke_successfully(self, xml, enable_tunneling): # pylint: disable=unused-argument
- ''' mock invoke_successfully returning xml data '''
- self.xml_in = xml
- if xml.get_child_by_name('query') is not None and \
- xml.get_child_by_name('query').get_child_by_name('vserver-info') is not None:
- # assume this a a cserver request
- xml = self.build_cserver_info()
- elif self.type == 'subnet':
- if xml.get_child_by_name('query'):
- name_obj = xml.get_child_by_name('query').get_child_by_name('net-subnet-info').get_child_by_name('subnet-name')
- xml_name = name_obj.get_content()
- if xml_name == self.params.get('name'):
- xml = self.build_subnet_info(self.params)
- elif self.type == 'subnet_fail':
- raise netapp_utils.zapi.NaApiError(code='TEST', message="This exception is from the unit test")
- self.xml_out = xml
- return xml
-
- @staticmethod
- def build_cserver_info():
- ''' build xml data for vserver-info '''
- xml = netapp_utils.zapi.NaElement('xml')
- attributes = {
- 'num-records': 1,
- 'attributes-list': {
- 'vserver-info': {
- 'vserver-name': 'cserver',
- }
- }
- }
- xml.translate_struct(attributes)
- return xml
-
- @staticmethod
- def build_subnet_info(data):
- ''' build xml data for subnet-info '''
- xml = netapp_utils.zapi.NaElement('xml')
- ip_ranges = []
- for elem in data['ip_ranges']:
- ip_ranges.append({'ip-range': elem})
- attributes = {
- 'num-records': 1,
- 'attributes-list': {
- 'net-subnet-info': {
- 'broadcast-domain': data['broadcast_domain'],
- 'gateway': data['gateway'],
- 'ip-ranges': ip_ranges,
- 'ipspace': data['ipspace'],
- 'subnet': data['subnet'],
- 'subnet-name': data['name'],
- }
- }
- }
- xml.translate_struct(attributes)
- return xml
-
-
-class TestMyModule(unittest.TestCase):
- ''' a group of related Unit Tests '''
-
- def setUp(self):
- self.mock_module_helper = patch.multiple(basic.AnsibleModule,
- exit_json=exit_json,
- fail_json=fail_json)
- self.mock_module_helper.start()
- self.addCleanup(self.mock_module_helper.stop)
- self.server = MockONTAPConnection()
-
- def set_default_args(self):
- return dict({
- 'name': 'test_subnet',
- 'hostname': 'hostname',
- 'username': 'username',
- 'password': 'password',
- 'broadcast_domain': 'Default',
- 'gateway': '10.0.0.1',
- 'ipspace': 'Default',
- 'subnet': '10.0.0.0/24',
- 'ip_ranges': ['10.0.0.10-10.0.0.20', '10.0.0.30']
- })
-
- def test_module_fail_when_required_args_missing(self):
- ''' required arguments are reported as errors '''
- with pytest.raises(AnsibleFailJson) as exc:
- set_module_args({})
- my_module()
- print('Info: %s' % exc.value.args[0]['msg'])
-
- def test_ensure_get_called(self):
- ''' test get_subnet for non-existent subnet'''
- set_module_args(self.set_default_args())
- my_obj = my_module()
- my_obj.server = self.server
- assert my_obj.get_subnet() is None
-
- def test_ensure_get_called_existing(self):
- ''' test get_subnet for existing subnet'''
- data = self.set_default_args()
- set_module_args(data)
- my_obj = my_module()
- my_obj.server = MockONTAPConnection(kind='subnet', data=data)
- assert my_obj.get_subnet() is not None
-
- @patch('ansible.module_utils.netapp.ems_log_event')
- def test_fail_broadcast_domain_modify(self, mock_ems_log):
- ''' test that boradcast_domain is not alterable '''
- data = self.set_default_args()
- data.update({'broadcast_domain': 'Test'})
- set_module_args(data)
- my_obj = my_module()
- my_obj.server = MockONTAPConnection(kind='subnet', data=self.set_default_args())
- with pytest.raises(AnsibleFailJson) as exc:
- my_obj.apply()
- assert 'cannot modify broadcast_domain parameter' in exc.value.args[0]['msg']
-
- @patch('ansible.module_utils.netapp.ems_log_event')
- @patch('ansible.modules.storage.netapp.na_ontap_net_subnet.NetAppOntapSubnet.create_subnet')
- def test_successful_create(self, create_subnet, mock_ems_log):
- ''' creating subnet and testing idempotency '''
- print("Create:")
- data = self.set_default_args()
- set_module_args(data)
- my_obj = my_module()
- my_obj.server = self.server
- with pytest.raises(AnsibleExitJson) as exc:
- my_obj.apply()
- assert exc.value.args[0]['changed']
- create_subnet.assert_called_with()
-
- # to reset na_helper from remembering the previous 'changed' value
- print("reset:")
- data = self.set_default_args()
- set_module_args(data)
- my_obj = my_module()
- my_obj.server = MockONTAPConnection(kind='subnet', data=data)
- with pytest.raises(AnsibleExitJson) as exc:
- my_obj.apply()
- assert not exc.value.args[0]['changed']
-
- @patch('ansible.module_utils.netapp.ems_log_event')
- @patch('ansible.modules.storage.netapp.na_ontap_net_subnet.NetAppOntapSubnet.rename_subnet')
- def test_successful_rename(self, rename_subnet, mock_ems_log):
- ''' renaming subnet '''
- data = self.set_default_args()
- data.update({'from_name': data['name'], 'name': 'new_test_subnet'})
- set_module_args(data)
- my_obj = my_module()
- my_obj.server = MockONTAPConnection(kind='subnet', data=self.set_default_args())
- with pytest.raises(AnsibleExitJson) as exc:
- my_obj.apply()
- assert exc.value.args[0]['changed']
-
- @patch('ansible.module_utils.netapp.ems_log_event')
- @patch('ansible.modules.storage.netapp.na_ontap_net_subnet.NetAppOntapSubnet.delete_subnet')
- def test_successful_delete(self, delete_subnet, mock_ems_log):
- ''' deleting subnet and testing idempotency '''
- data = self.set_default_args()
- data['state'] = 'absent'
- set_module_args(data)
- my_obj = my_module()
- my_obj.server = MockONTAPConnection(kind='subnet', data=data)
- with pytest.raises(AnsibleExitJson) as exc:
- my_obj.apply()
- assert exc.value.args[0]['changed']
- delete_subnet.assert_called_with()
-
- # to reset na_helper from remembering the previous 'changed' value
- my_obj = my_module()
- my_obj.server = self.server
- with pytest.raises(AnsibleExitJson) as exc:
- my_obj.apply()
- assert not exc.value.args[0]['changed']
-
- @patch('ansible.module_utils.netapp.ems_log_event')
- def test_successful_modify(self, mock_ems_log):
- ''' modifying subnet and testing idempotency '''
- data = self.set_default_args()
- data.update({'ip_ranges': ['10.0.0.10-10.0.0.25', '10.0.0.30']})
- set_module_args(data)
- my_obj = my_module()
- my_obj.server = MockONTAPConnection(kind='subnet', data=self.set_default_args())
- with pytest.raises(AnsibleExitJson) as exc:
- my_obj.apply()
- assert exc.value.args[0]['changed']
-
- @patch('ansible.module_utils.netapp.ems_log_event')
- def test_if_all_methods_catch_exception(self, mock_ems_log):
- data = self.set_default_args()
- set_module_args(data)
- my_obj = my_module()
- my_obj.server = MockONTAPConnection(kind='subnet_fail', data=data)
- with pytest.raises(AnsibleFailJson) as exc:
- my_obj.create_subnet()
- assert 'Error creating subnet' in exc.value.args[0]['msg']
- with pytest.raises(AnsibleFailJson) as exc:
- my_obj.delete_subnet()
- assert 'Error deleting subnet' in exc.value.args[0]['msg']
- with pytest.raises(AnsibleFailJson) as exc:
- my_obj.modify_subnet()
- assert 'Error modifying subnet' in exc.value.args[0]['msg']
- with pytest.raises(AnsibleFailJson) as exc:
- my_obj.rename_subnet()
- assert 'Error renaming subnet' in exc.value.args[0]['msg']
diff --git a/test/units/modules/storage/netapp/test_na_ontap_nfs.py b/test/units/modules/storage/netapp/test_na_ontap_nfs.py
deleted file mode 100644
index c1083894dd..0000000000
--- a/test/units/modules/storage/netapp/test_na_ontap_nfs.py
+++ /dev/null
@@ -1,308 +0,0 @@
-# (c) 2018, NetApp, Inc
-# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-
-''' unit test template for ONTAP Ansible module '''
-
-from __future__ import print_function
-import json
-import pytest
-
-from units.compat import unittest
-from units.compat.mock import patch, Mock
-from ansible.module_utils import basic
-from ansible.module_utils._text import to_bytes
-import ansible.module_utils.netapp as netapp_utils
-
-from ansible.modules.storage.netapp.na_ontap_nfs \
- import NetAppONTAPNFS as nfs_module # module under test
-
-if not netapp_utils.has_netapp_lib():
- pytestmark = pytest.mark.skip('skipping as missing required netapp_lib')
-
-
-def set_module_args(args):
- """prepare arguments so that they will be picked up during module creation"""
- args = json.dumps({'ANSIBLE_MODULE_ARGS': args})
- basic._ANSIBLE_ARGS = to_bytes(args) # pylint: disable=protected-access
-
-
-class AnsibleExitJson(Exception):
- """Exception class to be raised by module.exit_json and caught by the test case"""
- pass
-
-
-class AnsibleFailJson(Exception):
- """Exception class to be raised by module.fail_json and caught by the test case"""
- pass
-
-
-def exit_json(*args, **kwargs): # pylint: disable=unused-argument
- """function to patch over exit_json; package return data into an exception"""
- if 'changed' not in kwargs:
- kwargs['changed'] = False
- raise AnsibleExitJson(kwargs)
-
-
-def fail_json(*args, **kwargs): # pylint: disable=unused-argument
- """function to patch over fail_json; package return data into an exception"""
- kwargs['failed'] = True
- raise AnsibleFailJson(kwargs)
-
-
-class MockONTAPConnection(object):
- ''' mock server connection to ONTAP host '''
-
- def __init__(self, kind=None, data=None, job_error=None):
- ''' save arguments '''
- self.kind = kind
- self.params = data
- self.xml_in = None
- self.xml_out = None
-
- def invoke_successfully(self, xml, enable_tunneling): # pylint: disable=unused-argument
- ''' mock invoke_successfully returning xml data '''
- self.xml_in = xml
- if self.kind == 'nfs':
- xml = self.build_nfs_info(self.params)
- self.xml_out = xml
- if self.kind == 'nfs_status':
- xml = self.build_nfs_status_info(self.params)
- return xml
-
- @staticmethod
- def build_nfs_info(nfs_details):
- ''' build xml data for volume-attributes '''
- xml = netapp_utils.zapi.NaElement('xml')
- attributes = {
- "attributes-list": {
- "nfs-info": {
- "auth-sys-extended-groups": "false",
- "cached-cred-harvest-timeout": "86400000",
- "cached-cred-negative-ttl": "7200000",
- "cached-cred-positive-ttl": "86400000",
- "cached-transient-err-ttl": "30000",
- "chown-mode": "use_export_policy",
- "enable-ejukebox": "true",
- "extended-groups-limit": "32",
- "file-session-io-grouping-count": "5000",
- "file-session-io-grouping-duration": "120",
- "ignore-nt-acl-for-root": "false",
- "is-checksum-enabled-for-replay-cache": "true",
- "is-mount-rootonly-enabled": "true",
- "is-netgroup-dns-domain-search": "true",
- "is-nfs-access-enabled": "false",
- "is-nfs-rootonly-enabled": "false",
- "is-nfsv2-enabled": "false",
- "is-nfsv3-64bit-identifiers-enabled": "false",
- "is-nfsv3-connection-drop-enabled": "true",
- "is-nfsv3-enabled": "true",
- "is-nfsv3-fsid-change-enabled": "true",
- "is-nfsv4-fsid-change-enabled": "true",
- "is-nfsv4-numeric-ids-enabled": "true",
- "is-nfsv40-acl-enabled": "false",
- "is-nfsv40-enabled": "true",
- "is-nfsv40-migration-enabled": "false",
- "is-nfsv40-read-delegation-enabled": "false",
- "is-nfsv40-referrals-enabled": "false",
- "is-nfsv40-req-open-confirm-enabled": "false",
- "is-nfsv40-write-delegation-enabled": "false",
- "is-nfsv41-acl-enabled": "false",
- "is-nfsv41-acl-preserve-enabled": "true",
- "is-nfsv41-enabled": "true",
- "is-nfsv41-migration-enabled": "false",
- "is-nfsv41-pnfs-enabled": "true",
- "is-nfsv41-read-delegation-enabled": "false",
- "is-nfsv41-referrals-enabled": "false",
- "is-nfsv41-state-protection-enabled": "true",
- "is-nfsv41-write-delegation-enabled": "false",
- "is-qtree-export-enabled": "false",
- "is-rquota-enabled": "false",
- "is-tcp-enabled": "false",
- "is-udp-enabled": "false",
- "is-v3-ms-dos-client-enabled": "false",
- "is-validate-qtree-export-enabled": "true",
- "is-vstorage-enabled": "false",
- "map-unknown-uid-to-default-windows-user": "true",
- "mountd-port": "635",
- "name-service-lookup-protocol": "udp",
- "netgroup-trust-any-ns-switch-no-match": "false",
- "nfsv4-acl-max-aces": "400",
- "nfsv4-grace-seconds": "45",
- "nfsv4-id-domain": "defaultv4iddomain.com",
- "nfsv4-lease-seconds": "30",
- "nfsv41-implementation-id-domain": "netapp.com",
- "nfsv41-implementation-id-name": "NetApp Release Kalyaniblack__9.4.0",
- "nfsv41-implementation-id-time": "1541070767",
- "nfsv4x-session-num-slots": "180",
- "nfsv4x-session-slot-reply-cache-size": "640",
- "nlm-port": "4045",
- "nsm-port": "4046",
- "ntacl-display-permissive-perms": "false",
- "ntfs-unix-security-ops": "use_export_policy",
- "permitted-enc-types": {
- "string": ["des", "des3", "aes_128", "aes_256"]
- },
- "rpcsec-ctx-high": "0",
- "rpcsec-ctx-idle": "0",
- "rquotad-port": "4049",
- "showmount": "true",
- "showmount-timestamp": "1548372452",
- "skip-root-owner-write-perm-check": "false",
- "tcp-max-xfer-size": "1048576",
- "udp-max-xfer-size": "32768",
- "v3-search-unconverted-filename": "false",
- "v4-inherited-acl-preserve": "false",
- "vserver": "ansible"
- }
- },
- "num-records": "1"
- }
- xml.translate_struct(attributes)
- return xml
-
- @staticmethod
- def build_nfs_status_info(nfs_status_details):
- ''' build xml data for volume-attributes '''
- xml = netapp_utils.zapi.NaElement('xml')
- attributes = {
- 'is-enabled': "true"
- }
- xml.translate_struct(attributes)
- return xml
-
-
-class TestMyModule(unittest.TestCase):
- ''' a group of related Unit Tests '''
-
- def setUp(self):
- self.mock_module_helper = patch.multiple(basic.AnsibleModule,
- exit_json=exit_json,
- fail_json=fail_json)
- self.mock_module_helper.start()
- self.addCleanup(self.mock_module_helper.stop)
- self.mock_nfs_group = {
- 'vserver': 'nfs_vserver',
- }
-
- def mock_args(self):
- return {
- 'vserver': self.mock_nfs_group['vserver'],
- 'hostname': 'test',
- 'username': 'test_user',
- 'password': 'test_pass!',
- 'https': 'False'
- }
-
- def get_nfs_mock_object(self, kind=None):
- """
- Helper method to return an na_ontap_volume object
- :param kind: passes this param to MockONTAPConnection()
- :return: na_ontap_volume object
- """
- nfsy_obj = nfs_module()
- nfsy_obj.asup_log_for_cserver = Mock(return_value=None)
- nfsy_obj.cluster = Mock()
- nfsy_obj.cluster.invoke_successfully = Mock()
- if kind is None:
- nfsy_obj.server = MockONTAPConnection()
- else:
- nfsy_obj.server = MockONTAPConnection(kind=kind, data=self.mock_nfs_group)
- return nfsy_obj
-
- def test_module_fail_when_required_args_missing(self):
- ''' required arguments are reported as errors '''
- with pytest.raises(AnsibleFailJson) as exc:
- set_module_args({})
- nfs_module()
- print('Info: %s' % exc.value.args[0]['msg'])
-
- def test_get_nonexistent_nfs(self):
- ''' Test if get_nfs_service returns None for non-existent nfs '''
- set_module_args(self.mock_args())
- result = self.get_nfs_mock_object().get_nfs_service()
- assert result is None
-
- def test_get_existing_nfs(self):
- ''' Test if get_policy_group returns details for existing nfs '''
- set_module_args(self.mock_args())
- result = self.get_nfs_mock_object('nfs').get_nfs_service()
- assert result['is_nfsv3_enabled']
-
- def test_get_nonexistent_nfs_status(self):
- ''' Test if get__nfs_status returns None for non-existent nfs '''
- set_module_args(self.mock_args())
- result = self.get_nfs_mock_object().get_nfs_status()
- assert result is None
-
- def test_get_existing_nfs_status(self):
- ''' Test if get__nfs_status returns details for nfs '''
- set_module_args(self.mock_args())
- result = self.get_nfs_mock_object('nfs_status').get_nfs_status()
- assert result
-
- def test_modify_nfs(self):
- ''' Test if modify_nfs runs for existing nfs '''
- data = self.mock_args()
- data['nfsv3'] = 'enabled'
- data['nfsv3_fsid_change'] = 'enabled'
- data['nfsv4'] = 'enabled'
- data['nfsv41'] = 'enabled'
- data['vstorage_state'] = 'enabled'
- data['tcp'] = 'enabled'
- data['udp'] = 'enabled'
- data['nfsv4_id_domain'] = 'nfsv4_id_domain'
- data['nfsv40_acl'] = 'enabled'
- data['nfsv40_read_delegation'] = 'enabled'
- data['nfsv40_write_delegation'] = 'enabled'
- data['nfsv41_acl'] = 'enabled'
- data['nfsv41_read_delegation'] = 'enabled'
- data['nfsv41_write_delegation'] = 'enabled'
- data['showmount'] = 'enabled'
- data['tcp_max_xfer_size'] = '1048576'
- set_module_args(data)
- self.get_nfs_mock_object('nfs_status').modify_nfs()
-
- def test_successfully_modify_nfs(self):
- ''' Test modify nfs successful for modifying tcp max xfer size. '''
- data = self.mock_args()
- data['tcp_max_xfer_size'] = '8192'
- set_module_args(data)
- with pytest.raises(AnsibleExitJson) as exc:
- self.get_nfs_mock_object('nfs').apply()
- assert exc.value.args[0]['changed']
-
- def test_modify_nfs_idempotency(self):
- ''' Test modify nfs idempotency '''
- data = self.mock_args()
- data['tcp_max_xfer_size'] = '1048576'
- set_module_args(data)
- with pytest.raises(AnsibleExitJson) as exc:
- self.get_nfs_mock_object('nfs').apply()
- assert not exc.value.args[0]['changed']
-
- @patch('ansible.modules.storage.netapp.na_ontap_nfs.NetAppONTAPNFS.delete_nfs')
- def test_successfully_delete_nfs(self, delete_nfs):
- ''' Test successfully delete nfs '''
- data = self.mock_args()
- data['state'] = 'absent'
- set_module_args(data)
- obj = self.get_nfs_mock_object('nfs')
- with pytest.raises(AnsibleExitJson) as exc:
- obj.apply()
- assert exc.value.args[0]['changed']
- delete_nfs.assert_called_with()
-
- @patch('ansible.modules.storage.netapp.na_ontap_nfs.NetAppONTAPNFS.get_nfs_service')
- def test_successfully_enable_nfs(self, get_nfs_service):
- ''' Test successfully enable nfs on non-existent nfs '''
- data = self.mock_args()
- data['state'] = 'present'
- set_module_args(data)
- get_nfs_service.side_effect = [
- None,
- {}
- ]
- obj = self.get_nfs_mock_object('nfs')
- with pytest.raises(AnsibleExitJson) as exc:
- obj.apply()
- assert exc.value.args[0]['changed']
diff --git a/test/units/modules/storage/netapp/test_na_ontap_nvme.py b/test/units/modules/storage/netapp/test_na_ontap_nvme.py
deleted file mode 100644
index 190cd8ab7f..0000000000
--- a/test/units/modules/storage/netapp/test_na_ontap_nvme.py
+++ /dev/null
@@ -1,216 +0,0 @@
-# (c) 2018, NetApp, Inc
-# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-
-''' unit tests ONTAP Ansible module: na_ontap_nvme'''
-
-from __future__ import print_function
-import json
-import pytest
-
-from units.compat import unittest
-from units.compat.mock import patch
-from ansible.module_utils import basic
-from ansible.module_utils._text import to_bytes
-import ansible.module_utils.netapp as netapp_utils
-
-from ansible.modules.storage.netapp.na_ontap_nvme \
- import NetAppONTAPNVMe as my_module
-
-if not netapp_utils.has_netapp_lib():
- pytestmark = pytest.mark.skip('skipping as missing required netapp_lib')
-
-
-def set_module_args(args):
- """prepare arguments so that they will be picked up during module creation"""
- args = json.dumps({'ANSIBLE_MODULE_ARGS': args})
- basic._ANSIBLE_ARGS = to_bytes(args) # pylint: disable=protected-access
-
-
-class AnsibleExitJson(Exception):
- """Exception class to be raised by module.exit_json and caught by the test case"""
- pass
-
-
-class AnsibleFailJson(Exception):
- """Exception class to be raised by module.fail_json and caught by the test case"""
- pass
-
-
-def exit_json(*args, **kwargs): # pylint: disable=unused-argument
- """function to patch over exit_json; package return data into an exception"""
- if 'changed' not in kwargs:
- kwargs['changed'] = False
- raise AnsibleExitJson(kwargs)
-
-
-def fail_json(*args, **kwargs): # pylint: disable=unused-argument
- """function to patch over fail_json; package return data into an exception"""
- kwargs['failed'] = True
- raise AnsibleFailJson(kwargs)
-
-
-class MockONTAPConnection(object):
- ''' mock server connection to ONTAP host '''
-
- def __init__(self, kind=None):
- ''' save arguments '''
- self.type = kind
- self.xml_in = None
- self.xml_out = None
-
- def invoke_successfully(self, xml, enable_tunneling): # pylint: disable=unused-argument
- ''' mock invoke_successfully returning xml data '''
- self.xml_in = xml
- if self.type == 'nvme':
- xml = self.build_nvme_info()
- elif self.type == 'nvme_fail':
- raise netapp_utils.zapi.NaApiError(code='TEST', message="This exception is from the unit test")
- self.xml_out = xml
- return xml
-
- @staticmethod
- def build_nvme_info():
- ''' build xml data for nvme-info '''
- xml = netapp_utils.zapi.NaElement('xml')
- data = {'num-records': 1,
- 'attributes-list': [{'nvme-target-service-info': {'is-available': 'true'}}]}
- xml.translate_struct(data)
- return xml
-
-
-class TestMyModule(unittest.TestCase):
- ''' a group of related Unit Tests '''
-
- def setUp(self):
- self.mock_module_helper = patch.multiple(basic.AnsibleModule,
- exit_json=exit_json,
- fail_json=fail_json)
- self.mock_module_helper.start()
- self.addCleanup(self.mock_module_helper.stop)
- self.server = MockONTAPConnection()
- self.onbox = False
-
- def set_default_args(self):
- if self.onbox:
- hostname = '10.193.75.3'
- username = 'admin'
- password = 'netapp1!'
- vserver = 'ansible'
- status_admin = True
- else:
- hostname = 'hostname'
- username = 'username'
- password = 'password'
- vserver = 'vserver'
- status_admin = True
- return dict({
- 'hostname': hostname,
- 'username': username,
- 'password': password,
- 'vserver': vserver,
- 'status_admin': status_admin
- })
-
- def test_module_fail_when_required_args_missing(self):
- ''' required arguments are reported as errors '''
- with pytest.raises(AnsibleFailJson) as exc:
- set_module_args({})
- my_module()
- print('Info: %s' % exc.value.args[0]['msg'])
-
- def test_ensure_get_called(self):
- ''' test get_nvme() for non-existent nvme'''
- set_module_args(self.set_default_args())
- my_obj = my_module()
- my_obj.server = self.server
- assert my_obj.get_nvme() is None
-
- def test_ensure_get_called_existing(self):
- ''' test get_nvme() for existing nvme'''
- set_module_args(self.set_default_args())
- my_obj = my_module()
- my_obj.server = MockONTAPConnection(kind='nvme')
- assert my_obj.get_nvme()
-
- @patch('ansible.modules.storage.netapp.na_ontap_nvme.NetAppONTAPNVMe.create_nvme')
- def test_successful_create(self, create_nvme):
- ''' creating nvme and testing idempotency '''
- set_module_args(self.set_default_args())
- my_obj = my_module()
- if not self.onbox:
- my_obj.server = self.server
- with pytest.raises(AnsibleExitJson) as exc:
- my_obj.apply()
- assert exc.value.args[0]['changed']
- create_nvme.assert_called_with()
- # to reset na_helper from remembering the previous 'changed' value
- my_obj = my_module()
- if not self.onbox:
- my_obj.server = MockONTAPConnection('nvme')
- with pytest.raises(AnsibleExitJson) as exc:
- my_obj.apply()
- assert not exc.value.args[0]['changed']
-
- @patch('ansible.modules.storage.netapp.na_ontap_nvme.NetAppONTAPNVMe.delete_nvme')
- def test_successful_delete(self, delete_nvme):
- ''' deleting nvme and testing idempotency '''
- data = self.set_default_args()
- data['state'] = 'absent'
- set_module_args(data)
- my_obj = my_module()
- if not self.onbox:
- my_obj.server = MockONTAPConnection('nvme')
- with pytest.raises(AnsibleExitJson) as exc:
- my_obj.apply()
- assert exc.value.args[0]['changed']
- delete_nvme.assert_called_with()
- # to reset na_helper from remembering the previous 'changed' value
- my_obj = my_module()
- if not self.onbox:
- my_obj.server = self.server
- with pytest.raises(AnsibleExitJson) as exc:
- my_obj.apply()
- assert not exc.value.args[0]['changed']
-
- @patch('ansible.modules.storage.netapp.na_ontap_nvme.NetAppONTAPNVMe.modify_nvme')
- def test_successful_modify(self, modify_nvme):
- ''' modifying nvme and testing idempotency '''
- data = self.set_default_args()
- data['status_admin'] = False
- set_module_args(data)
- my_obj = my_module()
- if not self.onbox:
- my_obj.server = MockONTAPConnection('nvme')
- with pytest.raises(AnsibleExitJson) as exc:
- my_obj.apply()
- assert exc.value.args[0]['changed']
- modify_nvme.assert_called_with()
- # to reset na_helper from remembering the previous 'changed' value
- data = self.set_default_args()
- set_module_args(data)
- my_obj = my_module()
- if not self.onbox:
- my_obj.server = MockONTAPConnection('nvme')
- with pytest.raises(AnsibleExitJson) as exc:
- my_obj.apply()
- assert not exc.value.args[0]['changed']
-
- def test_if_all_methods_catch_exception(self):
- module_args = {}
- module_args.update(self.set_default_args())
- set_module_args(module_args)
- my_obj = my_module()
- if not self.onbox:
- my_obj.server = MockONTAPConnection('nvme_fail')
- with pytest.raises(AnsibleFailJson) as exc:
- my_obj.get_nvme()
- assert 'Error fetching nvme info:' in exc.value.args[0]['msg']
- with pytest.raises(AnsibleFailJson) as exc:
- my_obj.create_nvme()
- assert 'Error creating nvme' in exc.value.args[0]['msg']
- with pytest.raises(AnsibleFailJson) as exc:
- my_obj.delete_nvme()
- assert 'Error deleting nvme' in exc.value.args[0]['msg']
- with pytest.raises(AnsibleFailJson) as exc:
- my_obj.modify_nvme()
- assert 'Error modifying nvme' in exc.value.args[0]['msg']
diff --git a/test/units/modules/storage/netapp/test_na_ontap_nvme_namespace.py b/test/units/modules/storage/netapp/test_na_ontap_nvme_namespace.py
deleted file mode 100644
index fd669112fa..0000000000
--- a/test/units/modules/storage/netapp/test_na_ontap_nvme_namespace.py
+++ /dev/null
@@ -1,197 +0,0 @@
-# (c) 2018, NetApp, Inc
-# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-
-''' unit tests ONTAP Ansible module: na_ontap_nvme_namespace'''
-
-from __future__ import print_function
-import json
-import pytest
-
-from units.compat import unittest
-from units.compat.mock import patch
-from ansible.module_utils import basic
-from ansible.module_utils._text import to_bytes
-import ansible.module_utils.netapp as netapp_utils
-
-from ansible.modules.storage.netapp.na_ontap_nvme_namespace \
- import NetAppONTAPNVMENamespace as my_module
-
-if not netapp_utils.has_netapp_lib():
- pytestmark = pytest.mark.skip('skipping as missing required netapp_lib')
-
-
-def set_module_args(args):
- """prepare arguments so that they will be picked up during module creation"""
- args = json.dumps({'ANSIBLE_MODULE_ARGS': args})
- basic._ANSIBLE_ARGS = to_bytes(args) # pylint: disable=protected-access
-
-
-class AnsibleExitJson(Exception):
- """Exception class to be raised by module.exit_json and caught by the test case"""
- pass
-
-
-class AnsibleFailJson(Exception):
- """Exception class to be raised by module.fail_json and caught by the test case"""
- pass
-
-
-def exit_json(*args, **kwargs): # pylint: disable=unused-argument
- """function to patch over exit_json; package return data into an exception"""
- if 'changed' not in kwargs:
- kwargs['changed'] = False
- raise AnsibleExitJson(kwargs)
-
-
-def fail_json(*args, **kwargs): # pylint: disable=unused-argument
- """function to patch over fail_json; package return data into an exception"""
- kwargs['failed'] = True
- raise AnsibleFailJson(kwargs)
-
-
-class MockONTAPConnection(object):
- ''' mock server connection to ONTAP host '''
-
- def __init__(self, kind=None):
- ''' save arguments '''
- self.type = kind
- self.xml_in = None
- self.xml_out = None
-
- def invoke_successfully(self, xml, enable_tunneling): # pylint: disable=unused-argument
- ''' mock invoke_successfully returning xml data '''
- self.xml_in = xml
- if self.type == 'namespace':
- xml = self.build_namespace_info()
- elif self.type == 'quota_fail':
- raise netapp_utils.zapi.NaApiError(code='TEST', message="This exception is from the unit test")
- self.xml_out = xml
- return xml
-
- @staticmethod
- def build_namespace_info():
- ''' build xml data for namespace-info '''
- xml = netapp_utils.zapi.NaElement('xml')
- data = {'num-records': 2,
- 'attributes-list': [{'nvme-namespace-info': {'path': 'abcd/vol'}},
- {'nvme-namespace-info': {'path': 'xyz/vol'}}]}
- xml.translate_struct(data)
- return xml
-
-
-class TestMyModule(unittest.TestCase):
- ''' a group of related Unit Tests '''
-
- def setUp(self):
- self.mock_module_helper = patch.multiple(basic.AnsibleModule,
- exit_json=exit_json,
- fail_json=fail_json)
- self.mock_module_helper.start()
- self.addCleanup(self.mock_module_helper.stop)
- self.server = MockONTAPConnection()
- self.onbox = False
-
- def set_default_args(self):
- if self.onbox:
- hostname = '10.193.75.3'
- username = 'admin'
- password = 'netapp1!'
- vserver = 'ansible'
- ostype = 'linux'
- path = 'abcd/vol'
- size = 20
- else:
- hostname = 'hostname'
- username = 'username'
- password = 'password'
- vserver = 'vserver'
- ostype = 'linux'
- path = 'abcd/vol'
- size = 20
- return dict({
- 'hostname': hostname,
- 'username': username,
- 'password': password,
- 'ostype': ostype,
- 'vserver': vserver,
- 'path': path,
- 'size': size
- })
-
- def test_module_fail_when_required_args_missing(self):
- ''' required arguments are reported as errors '''
- with pytest.raises(AnsibleFailJson) as exc:
- set_module_args({})
- my_module()
- print('Info: %s' % exc.value.args[0]['msg'])
-
- def test_ensure_get_called(self):
- ''' test get_namespace() for non-existent namespace'''
- set_module_args(self.set_default_args())
- my_obj = my_module()
- my_obj.server = self.server
- assert my_obj.get_namespace() is None
-
- def test_ensure_get_called_existing(self):
- ''' test get_namespace() for existing namespace'''
- set_module_args(self.set_default_args())
- my_obj = my_module()
- my_obj.server = MockONTAPConnection(kind='namespace')
- assert my_obj.get_namespace()
-
- @patch('ansible.modules.storage.netapp.na_ontap_nvme_namespace.NetAppONTAPNVMENamespace.create_namespace')
- def test_successful_create(self, create_namespace):
- ''' creating namespace and testing idempotency '''
- set_module_args(self.set_default_args())
- my_obj = my_module()
- if not self.onbox:
- my_obj.server = self.server
- with pytest.raises(AnsibleExitJson) as exc:
- my_obj.apply()
- assert exc.value.args[0]['changed']
- create_namespace.assert_called_with()
- # to reset na_helper from remembering the previous 'changed' value
- my_obj = my_module()
- if not self.onbox:
- my_obj.server = MockONTAPConnection('namespace')
- with pytest.raises(AnsibleExitJson) as exc:
- my_obj.apply()
- assert not exc.value.args[0]['changed']
-
- @patch('ansible.modules.storage.netapp.na_ontap_nvme_namespace.NetAppONTAPNVMENamespace.delete_namespace')
- def test_successful_delete(self, delete_namespace):
- ''' deleting namespace and testing idempotency '''
- data = self.set_default_args()
- data['state'] = 'absent'
- set_module_args(data)
- my_obj = my_module()
- if not self.onbox:
- my_obj.server = MockONTAPConnection('namespace')
- with pytest.raises(AnsibleExitJson) as exc:
- my_obj.apply()
- assert exc.value.args[0]['changed']
- delete_namespace.assert_called_with()
- # to reset na_helper from remembering the previous 'changed' value
- my_obj = my_module()
- if not self.onbox:
- my_obj.server = self.server
- with pytest.raises(AnsibleExitJson) as exc:
- my_obj.apply()
- assert not exc.value.args[0]['changed']
-
- def test_if_all_methods_catch_exception(self):
- module_args = {}
- module_args.update(self.set_default_args())
- set_module_args(module_args)
- my_obj = my_module()
- if not self.onbox:
- my_obj.server = MockONTAPConnection('quota_fail')
- with pytest.raises(AnsibleFailJson) as exc:
- my_obj.get_namespace()
- assert 'Error fetching namespace info:' in exc.value.args[0]['msg']
- with pytest.raises(AnsibleFailJson) as exc:
- my_obj.create_namespace()
- assert 'Error creating namespace for path' in exc.value.args[0]['msg']
- with pytest.raises(AnsibleFailJson) as exc:
- my_obj.delete_namespace()
- assert 'Error deleting namespace for path' in exc.value.args[0]['msg']
diff --git a/test/units/modules/storage/netapp/test_na_ontap_nvme_subsystem.py b/test/units/modules/storage/netapp/test_na_ontap_nvme_subsystem.py
deleted file mode 100644
index b047817ff8..0000000000
--- a/test/units/modules/storage/netapp/test_na_ontap_nvme_subsystem.py
+++ /dev/null
@@ -1,241 +0,0 @@
-# (c) 2018, NetApp, Inc
-# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-
-''' unit tests ONTAP Ansible module: na_ontap_nvme_subsystem '''
-
-from __future__ import print_function
-import json
-import pytest
-
-from units.compat import unittest
-from units.compat.mock import patch
-from ansible.module_utils import basic
-from ansible.module_utils._text import to_bytes
-import ansible.module_utils.netapp as netapp_utils
-
-from ansible.modules.storage.netapp.na_ontap_nvme_subsystem \
- import NetAppONTAPNVMESubsystem as my_module
-
-if not netapp_utils.has_netapp_lib():
- pytestmark = pytest.mark.skip('skipping as missing required netapp_lib')
-
-
-def set_module_args(args):
- """prepare arguments so that they will be picked up during module creation"""
- args = json.dumps({'ANSIBLE_MODULE_ARGS': args})
- basic._ANSIBLE_ARGS = to_bytes(args) # pylint: disable=protected-access
-
-
-class AnsibleExitJson(Exception):
- """Exception class to be raised by module.exit_json and caught by the test case"""
- pass
-
-
-class AnsibleFailJson(Exception):
- """Exception class to be raised by module.fail_json and caught by the test case"""
- pass
-
-
-def exit_json(*args, **kwargs): # pylint: disable=unused-argument
- """function to patch over exit_json; package return data into an exception"""
- if 'changed' not in kwargs:
- kwargs['changed'] = False
- raise AnsibleExitJson(kwargs)
-
-
-def fail_json(*args, **kwargs): # pylint: disable=unused-argument
- """function to patch over fail_json; package return data into an exception"""
- kwargs['failed'] = True
- raise AnsibleFailJson(kwargs)
-
-
-class MockONTAPConnection(object):
- ''' mock server connection to ONTAP host '''
-
- def __init__(self, kind=None, parm1=None):
- ''' save arguments '''
- self.type = kind
- self.parm1 = parm1
- self.xml_in = None
- self.xml_out = None
-
- def invoke_successfully(self, xml, enable_tunneling): # pylint: disable=unused-argument
- ''' mock invoke_successfully returning xml data '''
- self.xml_in = xml
- if self.type == 'subsystem':
- xml = self.build_subsystem_info(self.parm1)
- self.xml_out = xml
- return xml
-
- @staticmethod
- def build_subsystem_info(vserver):
- ''' build xml data for vserser-info '''
- xml = netapp_utils.zapi.NaElement('xml')
- data = {'num-records': 2,
- 'attributes-list': [{'nvme-target-subsystem-map-info': {'path': 'abcd/vol'}},
- {'nvme-target-subsystem-map-info': {'path': 'xyz/vol'}}]}
- xml.translate_struct(data)
- return xml
-
-
-class TestMyModule(unittest.TestCase):
- ''' a group of related Unit Tests '''
-
- def setUp(self):
- self.mock_module_helper = patch.multiple(basic.AnsibleModule,
- exit_json=exit_json,
- fail_json=fail_json)
- self.mock_module_helper.start()
- self.addCleanup(self.mock_module_helper.stop)
- self.server = MockONTAPConnection()
- self.onbox = False
-
- def set_default_args(self):
- if self.onbox:
- hostname = '10.193.75.3'
- username = 'admin'
- password = 'netapp1!'
- subsystem = 'test'
- vserver = 'ansible'
- ostype = 'linux'
- paths = ['abcd/vol', 'xyz/vol']
- else:
- hostname = 'hostname'
- username = 'username'
- password = 'password'
- subsystem = 'test'
- vserver = 'vserver'
- ostype = 'linux'
- paths = ['abcd/vol', 'xyz/vol']
- return dict({
- 'hostname': hostname,
- 'username': username,
- 'password': password,
- 'subsystem': subsystem,
- 'ostype': ostype,
- 'vserver': vserver,
- 'paths': paths
- })
-
- def test_module_fail_when_required_args_missing(self):
- ''' required arguments are reported as errors '''
- with pytest.raises(AnsibleFailJson) as exc:
- set_module_args({})
- my_module()
- print('Info: %s' % exc.value.args[0]['msg'])
-
- def test_ensure_get_called(self):
- ''' test get_subsystem() for non-existent subsystem'''
- set_module_args(self.set_default_args())
- my_obj = my_module()
- my_obj.server = self.server
- assert my_obj.get_subsystem() is None
-
- def test_ensure_get_called_existing(self):
- ''' test get_subsystem() for existing subsystem'''
- set_module_args(self.set_default_args())
- my_obj = my_module()
- my_obj.server = MockONTAPConnection(kind='subsystem')
- assert my_obj.get_subsystem()
-
- @patch('ansible.modules.storage.netapp.na_ontap_nvme_subsystem.NetAppONTAPNVMESubsystem.create_subsystem')
- def test_successful_create(self, create_subsystem):
- ''' creating subsystem and testing idempotency '''
- set_module_args(self.set_default_args())
- my_obj = my_module()
- if not self.onbox:
- my_obj.server = self.server
- with pytest.raises(AnsibleExitJson) as exc:
- my_obj.apply()
- assert exc.value.args[0]['changed']
- create_subsystem.assert_called_with()
- # to reset na_helper from remembering the previous 'changed' value
- my_obj = my_module()
- if not self.onbox:
- my_obj.server = MockONTAPConnection('subsystem')
- with pytest.raises(AnsibleExitJson) as exc:
- my_obj.apply()
- assert not exc.value.args[0]['changed']
-
- @patch('ansible.modules.storage.netapp.na_ontap_nvme_subsystem.NetAppONTAPNVMESubsystem.delete_subsystem')
- def test_successful_delete(self, delete_subsystem):
- ''' deleting subsystem and testing idempotency '''
- data = self.set_default_args()
- data['state'] = 'absent'
- set_module_args(data)
- my_obj = my_module()
- if not self.onbox:
- my_obj.server = MockONTAPConnection('subsystem')
- with pytest.raises(AnsibleExitJson) as exc:
- my_obj.apply()
- assert exc.value.args[0]['changed']
- delete_subsystem.assert_called_with()
- # to reset na_helper from remembering the previous 'changed' value
- my_obj = my_module()
- if not self.onbox:
- my_obj.server = self.server
- with pytest.raises(AnsibleExitJson) as exc:
- my_obj.apply()
- assert not exc.value.args[0]['changed']
-
- def test_ensure_get_called(self):
- ''' test get_subsystem_host_map() for non-existent subsystem'''
- set_module_args(self.set_default_args())
- my_obj = my_module()
- my_obj.server = self.server
- assert my_obj.get_subsystem_host_map('paths') is None
-
- def test_ensure_get_called_existing(self):
- ''' test get_subsystem_host_map() for existing subsystem'''
- set_module_args(self.set_default_args())
- my_obj = my_module()
- my_obj.server = MockONTAPConnection(kind='subsystem')
- assert my_obj.get_subsystem_host_map('paths')
-
- @patch('ansible.modules.storage.netapp.na_ontap_nvme_subsystem.NetAppONTAPNVMESubsystem.add_subsystem_host_map')
- def test_successful_add_mock(self, add_subsystem_host_map):
- ''' adding subsystem host/map and testing idempotency '''
- set_module_args(self.set_default_args())
- my_obj = my_module()
- if not self.onbox:
- my_obj.server = self.server
- with pytest.raises(AnsibleExitJson) as exc:
- my_obj.apply()
- assert exc.value.args[0]['changed']
- add_subsystem_host_map.assert_called_with(['abcd/vol', 'xyz/vol'], 'paths')
-
- @patch('ansible.modules.storage.netapp.na_ontap_nvme_subsystem.NetAppONTAPNVMESubsystem.remove_subsystem_host_map')
- def test_successful_remove_mock(self, remove_subsystem_host_map):
- ''' removing subsystem host/map and testing idempotency '''
- data = self.set_default_args()
- data['paths'] = ['abcd/vol']
- set_module_args(data)
- my_obj = my_module()
- if not self.onbox:
- my_obj.server = MockONTAPConnection('subsystem')
- with pytest.raises(AnsibleExitJson) as exc:
- my_obj.apply()
- assert exc.value.args[0]['changed']
- remove_subsystem_host_map.assert_called_with(['xyz/vol'], 'paths')
-
- def test_successful_add(self):
- ''' adding subsystem host/map and testing idempotency '''
- set_module_args(self.set_default_args())
- my_obj = my_module()
- if not self.onbox:
- my_obj.server = self.server
- with pytest.raises(AnsibleExitJson) as exc:
- my_obj.apply()
- assert exc.value.args[0]['changed']
-
- def test_successful_remove(self):
- ''' removing subsystem host/map and testing idempotency '''
- data = self.set_default_args()
- data['paths'] = ['abcd/vol']
- set_module_args(data)
- my_obj = my_module()
- if not self.onbox:
- my_obj.server = MockONTAPConnection('subsystem')
- with pytest.raises(AnsibleExitJson) as exc:
- my_obj.apply()
- assert exc.value.args[0]['changed']
diff --git a/test/units/modules/storage/netapp/test_na_ontap_object_store.py b/test/units/modules/storage/netapp/test_na_ontap_object_store.py
deleted file mode 100644
index a15de05c79..0000000000
--- a/test/units/modules/storage/netapp/test_na_ontap_object_store.py
+++ /dev/null
@@ -1,200 +0,0 @@
-# (c) 2019, NetApp, Inc
-# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-
-""" unit tests for Ansible module: na_ontap_object_store """
-
-from __future__ import (absolute_import, division, print_function)
-__metaclass__ = type
-import json
-import pytest
-
-from units.compat import unittest
-from units.compat.mock import patch, Mock
-from ansible.module_utils import basic
-from ansible.module_utils._text import to_bytes
-import ansible.module_utils.netapp as netapp_utils
-
-from ansible.modules.storage.netapp.na_ontap_object_store \
- import NetAppOntapObjectStoreConfig as my_module # module under test
-
-if not netapp_utils.has_netapp_lib():
- pytestmark = pytest.mark.skip('skipping as missing required netapp_lib')
-
-
-def set_module_args(args):
- """prepare arguments so that they will be picked up during module creation"""
- args = json.dumps({'ANSIBLE_MODULE_ARGS': args})
- basic._ANSIBLE_ARGS = to_bytes(args) # pylint: disable=protected-access
-
-
-class AnsibleExitJson(Exception):
- """Exception class to be raised by module.exit_json and caught by the test case"""
- pass
-
-
-class AnsibleFailJson(Exception):
- """Exception class to be raised by module.fail_json and caught by the test case"""
- pass
-
-
-def exit_json(*args, **kwargs): # pylint: disable=unused-argument
- """function to patch over exit_json; package return data into an exception"""
- if 'changed' not in kwargs:
- kwargs['changed'] = False
- raise AnsibleExitJson(kwargs)
-
-
-def fail_json(*args, **kwargs): # pylint: disable=unused-argument
- """function to patch over fail_json; package return data into an exception"""
- kwargs['failed'] = True
- raise AnsibleFailJson(kwargs)
-
-
-class MockONTAPConnection(object):
- ''' mock server connection to ONTAP host '''
-
- def __init__(self, kind=None):
- ''' save arguments '''
- self.type = kind
- self.xml_in = None
- self.xml_out = None
-
- def invoke_successfully(self, xml, enable_tunneling): # pylint: disable=unused-argument
- ''' mock invoke_successfully returning xml data '''
- self.xml_in = xml
- if self.type == 'object_store':
- xml = self.build_object_store_info()
- elif self.type == 'object_store_fail':
- raise netapp_utils.zapi.NaApiError(code='TEST', message="This exception is from the unit test")
- self.xml_out = xml
- return xml
-
- @staticmethod
- def build_object_store_info():
- ''' build xml data for object store '''
- xml = netapp_utils.zapi.NaElement('xml')
- data = {'attributes':
- {'aggr-object-store-config-info':
- {'object-store-name': 'ansible'}
- }
- }
- xml.translate_struct(data)
- print(xml.to_string())
- return xml
-
-
-class TestMyModule(unittest.TestCase):
- ''' a group of related Unit Tests '''
-
- def setUp(self):
- self.mock_module_helper = patch.multiple(basic.AnsibleModule,
- exit_json=exit_json,
- fail_json=fail_json)
- self.mock_module_helper.start()
- self.addCleanup(self.mock_module_helper.stop)
- self.server = MockONTAPConnection()
- # whether to use a mock or a simulator
- self.onbox = False
-
- def set_default_args(self):
- if self.onbox:
- hostname = '10.10.10.10'
- username = 'admin'
- password = 'password'
- name = 'ansible'
- else:
- hostname = 'hostname'
- username = 'username'
- password = 'password'
- name = 'ansible'
- return dict({
- 'hostname': hostname,
- 'username': username,
- 'password': password,
- 'name': name
- })
-
- def call_command(self, module_args):
- ''' utility function to call apply '''
- module_args.update(self.set_default_args())
- set_module_args(module_args)
- my_obj = my_module()
- my_obj.asup_log_for_cserver = Mock(return_value=None)
- if not self.onbox:
- # mock the connection
- my_obj.server = MockONTAPConnection('object_store')
- with pytest.raises(AnsibleExitJson) as exc:
- my_obj.apply()
- return exc.value.args[0]['changed']
-
- def test_module_fail_when_required_args_missing(self):
- ''' required arguments are reported as errors '''
- with pytest.raises(AnsibleFailJson) as exc:
- set_module_args({})
- my_module()
- print('Info: %s' % exc.value.args[0]['msg'])
-
- def test_ensure_object_store_get_called(self):
- ''' fetching details of object store '''
- set_module_args(self.set_default_args())
- my_obj = my_module()
- my_obj.server = self.server
- assert my_obj.get_aggr_object_store() is not None
-
- def test_ensure_get_called_existing(self):
- ''' test for existing object store'''
- set_module_args(self.set_default_args())
- my_obj = my_module()
- my_obj.server = MockONTAPConnection(kind='object_store')
- assert my_obj.get_aggr_object_store()
-
- def test_object_store_create(self):
- ''' test for creating object store'''
- module_args = {
- 'provider_type': 'abc',
- 'server': 'abc',
- 'container': 'abc',
- 'access_key': 'abc',
- 'secret_password': 'abc'
- }
- module_args.update(self.set_default_args())
- set_module_args(module_args)
- my_obj = my_module()
- my_obj.asup_log_for_cserver = Mock(return_value=None)
- if not self.onbox:
- # mock the connection
- my_obj.server = MockONTAPConnection(kind='object_store')
- with pytest.raises(AnsibleExitJson) as exc:
- my_obj.apply()
- assert not exc.value.args[0]['changed']
-
- def test_object_store_delete(self):
- ''' test for deleting object store'''
- module_args = {
- 'state': 'absent',
- }
- changed = self.call_command(module_args)
- assert changed
-
- def test_if_all_methods_catch_exception(self):
- module_args = {
- 'provider_type': 'abc',
- 'server': 'abc',
- 'container': 'abc',
- 'access_key': 'abc',
- 'secret_password': 'abc'
- }
- module_args.update(self.set_default_args())
- set_module_args(module_args)
- my_obj = my_module()
- if not self.onbox:
- my_obj.server = MockONTAPConnection('object_store_fail')
- with pytest.raises(AnsibleFailJson) as exc:
- my_obj.get_aggr_object_store()
- assert '' in exc.value.args[0]['msg']
- with pytest.raises(AnsibleFailJson) as exc:
- my_obj.create_aggr_object_store()
- assert 'Error provisioning object store config ' in exc.value.args[0]['msg']
- with pytest.raises(AnsibleFailJson) as exc:
- my_obj.delete_aggr_object_store()
- assert 'Error removing object store config ' in exc.value.args[0]['msg']
diff --git a/test/units/modules/storage/netapp/test_na_ontap_ports.py b/test/units/modules/storage/netapp/test_na_ontap_ports.py
deleted file mode 100644
index 84ea4699cf..0000000000
--- a/test/units/modules/storage/netapp/test_na_ontap_ports.py
+++ /dev/null
@@ -1,173 +0,0 @@
-# (c) 2019, NetApp, Inc
-# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-
-''' unit tests for ONTAP Ansible module: na_ontap_port'''
-
-from __future__ import (absolute_import, division, print_function)
-__metaclass__ = type
-import json
-import pytest
-
-from units.compat import unittest
-from units.compat.mock import patch, Mock
-from ansible.module_utils import basic
-from ansible.module_utils._text import to_bytes
-import ansible.module_utils.netapp as netapp_utils
-
-from ansible.modules.storage.netapp.na_ontap_ports \
- import NetAppOntapPorts as port_module # module under test
-
-if not netapp_utils.has_netapp_lib():
- pytestmark = pytest.mark.skip('skipping as missing required netapp_lib')
-
-
-def set_module_args(args):
- """prepare arguments so that they will be picked up during module creation"""
- args = json.dumps({'ANSIBLE_MODULE_ARGS': args})
- basic._ANSIBLE_ARGS = to_bytes(args) # pylint: disable=protected-access
-
-
-class AnsibleExitJson(Exception):
- """Exception class to be raised by module.exit_json and caught by the test case"""
- pass
-
-
-class AnsibleFailJson(Exception):
- """Exception class to be raised by module.fail_json and caught by the test case"""
- pass
-
-
-def exit_json(*args, **kwargs): # pylint: disable=unused-argument
- """function to patch over exit_json; package return data into an exception"""
- if 'changed' not in kwargs:
- kwargs['changed'] = False
- raise AnsibleExitJson(kwargs)
-
-
-def fail_json(*args, **kwargs): # pylint: disable=unused-argument
- """function to patch over fail_json; package return data into an exception"""
- kwargs['failed'] = True
- raise AnsibleFailJson(kwargs)
-
-
-class TestMyModule(unittest.TestCase):
- ''' a group of related Unit Tests '''
-
- def setUp(self):
- self.mock_module_helper = patch.multiple(basic.AnsibleModule,
- exit_json=exit_json,
- fail_json=fail_json)
- self.mock_module_helper.start()
- self.addCleanup(self.mock_module_helper.stop)
-
- def mock_args(self, choice):
- if choice == 'broadcast_domain':
- return {
- 'names': ['test_port_1', 'test_port_2'],
- 'resource_name': 'test_domain',
- 'resource_type': 'broadcast_domain',
- 'hostname': 'test',
- 'username': 'test_user',
- 'password': 'test_pass!'
- }
- elif choice == 'portset':
- return {
- 'names': ['test_lif'],
- 'resource_name': 'test_portset',
- 'resource_type': 'portset',
- 'hostname': 'test',
- 'username': 'test_user',
- 'password': 'test_pass!',
- 'vserver': 'test_vserver'
- }
-
- def get_port_mock_object(self):
- """
- Helper method to return an na_ontap_port object
- """
- port_obj = port_module()
- port_obj.asup_log_for_cserver = Mock(return_value=None)
- port_obj.server = Mock()
- port_obj.server.invoke_successfully = Mock()
-
- return port_obj
-
- @patch('ansible.modules.storage.netapp.na_ontap_ports.NetAppOntapPorts.add_broadcast_domain_ports')
- @patch('ansible.modules.storage.netapp.na_ontap_ports.NetAppOntapPorts.get_broadcast_domain_ports')
- def test_successfully_add_broadcast_domain_ports(self, get_broadcast_domain_ports, add_broadcast_domain_ports):
- ''' Test successful add broadcast domain ports '''
- data = self.mock_args('broadcast_domain')
- set_module_args(data)
- get_broadcast_domain_ports.side_effect = [
- []
- ]
- with pytest.raises(AnsibleExitJson) as exc:
- self.get_port_mock_object().apply()
- assert exc.value.args[0]['changed']
-
- @patch('ansible.modules.storage.netapp.na_ontap_ports.NetAppOntapPorts.add_broadcast_domain_ports')
- @patch('ansible.modules.storage.netapp.na_ontap_ports.NetAppOntapPorts.get_broadcast_domain_ports')
- def test_add_broadcast_domain_ports_idempotency(self, get_broadcast_domain_ports, add_broadcast_domain_ports):
- ''' Test add broadcast domain ports idempotency '''
- data = self.mock_args('broadcast_domain')
- set_module_args(data)
- get_broadcast_domain_ports.side_effect = [
- ['test_port_1', 'test_port_2']
- ]
- with pytest.raises(AnsibleExitJson) as exc:
- self.get_port_mock_object().apply()
- assert not exc.value.args[0]['changed']
-
- @patch('ansible.modules.storage.netapp.na_ontap_ports.NetAppOntapPorts.add_portset_ports')
- @patch('ansible.modules.storage.netapp.na_ontap_ports.NetAppOntapPorts.portset_get')
- def test_successfully_add_portset_ports(self, portset_get, add_portset_ports):
- ''' Test successful add portset ports '''
- data = self.mock_args('portset')
- set_module_args(data)
- portset_get.side_effect = [
- []
- ]
- with pytest.raises(AnsibleExitJson) as exc:
- self.get_port_mock_object().apply()
- assert exc.value.args[0]['changed']
-
- @patch('ansible.modules.storage.netapp.na_ontap_ports.NetAppOntapPorts.add_portset_ports')
- @patch('ansible.modules.storage.netapp.na_ontap_ports.NetAppOntapPorts.portset_get')
- def test_add_portset_ports_idempotency(self, portset_get, add_portset_ports):
- ''' Test add portset ports idempotency '''
- data = self.mock_args('portset')
- set_module_args(data)
- portset_get.side_effect = [
- ['test_lif']
- ]
- with pytest.raises(AnsibleExitJson) as exc:
- self.get_port_mock_object().apply()
- assert not exc.value.args[0]['changed']
-
- @patch('ansible.modules.storage.netapp.na_ontap_ports.NetAppOntapPorts.add_broadcast_domain_ports')
- @patch('ansible.modules.storage.netapp.na_ontap_ports.NetAppOntapPorts.get_broadcast_domain_ports')
- def test_successfully_remove_broadcast_domain_ports(self, get_broadcast_domain_ports, add_broadcast_domain_ports):
- ''' Test successful remove broadcast domain ports '''
- data = self.mock_args('broadcast_domain')
- data['state'] = 'absent'
- set_module_args(data)
- get_broadcast_domain_ports.side_effect = [
- ['test_port_1', 'test_port_2']
- ]
- with pytest.raises(AnsibleExitJson) as exc:
- self.get_port_mock_object().apply()
- assert exc.value.args[0]['changed']
-
- @patch('ansible.modules.storage.netapp.na_ontap_ports.NetAppOntapPorts.add_portset_ports')
- @patch('ansible.modules.storage.netapp.na_ontap_ports.NetAppOntapPorts.portset_get')
- def test_remove_add_portset_ports(self, portset_get, add_portset_ports):
- ''' Test successful remove portset ports '''
- data = self.mock_args('portset')
- data['state'] = 'absent'
- set_module_args(data)
- portset_get.side_effect = [
- ['test_lif']
- ]
- with pytest.raises(AnsibleExitJson) as exc:
- self.get_port_mock_object().apply()
- assert exc.value.args[0]['changed']
diff --git a/test/units/modules/storage/netapp/test_na_ontap_portset.py b/test/units/modules/storage/netapp/test_na_ontap_portset.py
deleted file mode 100644
index 7b5f356ddd..0000000000
--- a/test/units/modules/storage/netapp/test_na_ontap_portset.py
+++ /dev/null
@@ -1,189 +0,0 @@
-# (c) 2018, NetApp, Inc
-# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-
-''' unit tests for ONTAP Ansible module: na_ontap_portset'''
-
-from __future__ import print_function
-import json
-import pytest
-
-from units.compat import unittest
-from units.compat.mock import patch
-from ansible.module_utils import basic
-from ansible.module_utils._text import to_bytes
-import ansible.module_utils.netapp as netapp_utils
-
-from ansible.modules.storage.netapp.na_ontap_portset \
- import NetAppONTAPPortset as my_module # module under test
-
-if not netapp_utils.has_netapp_lib():
- pytestmark = pytest.mark.skip('skipping as missing required netapp_lib')
-
-
-def set_module_args(args):
- """prepare arguments so that they will be picked up during module creation"""
- args = json.dumps({'ANSIBLE_MODULE_ARGS': args})
- basic._ANSIBLE_ARGS = to_bytes(args) # pylint: disable=protected-access
-
-
-class AnsibleExitJson(Exception):
- """Exception class to be raised by module.exit_json and caught by the test case"""
- pass
-
-
-class AnsibleFailJson(Exception):
- """Exception class to be raised by module.fail_json and caught by the test case"""
- pass
-
-
-def exit_json(*args, **kwargs): # pylint: disable=unused-argument
- """function to patch over exit_json; package return data into an exception"""
- if 'changed' not in kwargs:
- kwargs['changed'] = False
- raise AnsibleExitJson(kwargs)
-
-
-def fail_json(*args, **kwargs): # pylint: disable=unused-argument
- """function to patch over fail_json; package return data into an exception"""
- kwargs['failed'] = True
- raise AnsibleFailJson(kwargs)
-
-
-class MockONTAPConnection(object):
- ''' mock server connection to ONTAP host '''
-
- def __init__(self, kind=None, parm1=None, parm2=None, parm3=None):
- ''' save arguments '''
- self.type = kind
- self.parm1 = parm1
- self.parm2 = parm2
- self.parm3 = parm3
- self.xml_in = None
- self.xml_out = None
-
- def invoke_successfully(self, xml, enable_tunneling): # pylint: disable=unused-argument
- ''' mock invoke_successfully returning xml data '''
- self.xml_in = xml
- if self.type == 'portset':
- xml = self.build_portset_info(self.parm1, self.parm2, self.parm3)
- self.xml_out = xml
- return xml
-
- @staticmethod
- def build_portset_info(portset, vserver, type):
- ''' build xml data for vserser-info '''
- xml = netapp_utils.zapi.NaElement('xml')
- data = {'num-records': 1,
- 'attributes-list': {'portset-info': {'portset-name': portset,
- 'vserver': vserver, 'portset-type': type,
- 'portset-port-total': '0'}}}
- xml.translate_struct(data)
- print(xml.to_string())
- return xml
-
-
-class TestMyModule(unittest.TestCase):
- ''' a group of related Unit Tests '''
-
- def setUp(self):
- self.mock_module_helper = patch.multiple(basic.AnsibleModule,
- exit_json=exit_json,
- fail_json=fail_json)
- self.mock_module_helper.start()
- self.addCleanup(self.mock_module_helper.stop)
- self.server = MockONTAPConnection()
- self.use_vsim = False
-
- def set_default_args(self):
- if self.use_vsim:
- hostname = '10.193.77.154'
- username = 'admin'
- password = 'netapp1!'
- name = 'test'
- type = 'mixed'
- vserver = 'ansible_test'
- ports = ['a1', 'a2']
- else:
- hostname = 'hostname'
- username = 'username'
- password = 'password'
- name = 'name'
- type = 'mixed'
- vserver = 'vserver'
- ports = ['a1', 'a2']
- return dict({
- 'hostname': hostname,
- 'username': username,
- 'password': password,
- 'name': name,
- 'type': type,
- 'vserver': vserver,
- 'ports': ports
- })
-
- def test_module_fail_when_required_args_missing(self):
- ''' required arguments are reported as errors '''
- with pytest.raises(AnsibleFailJson) as exc:
- set_module_args({})
- my_module()
- print('Info: %s' % exc.value.args[0]['msg'])
-
- def test_ensure_portset_get_called(self):
- ''' a more interesting test '''
- set_module_args(self.set_default_args())
- my_obj = my_module()
- my_obj.server = self.server
- portset = my_obj.portset_get()
- print('Info: test_portset_get: %s' % repr(portset))
- assert portset is None
-
- def test_ensure_portset_apply_called(self):
- ''' Test successful create '''
- module_args = {'name': 'create'}
- module_args.update(self.set_default_args())
- set_module_args(module_args)
- my_obj = my_module()
- if not self.use_vsim:
- my_obj.server = self.server
- portset = my_obj.portset_get()
- print('Info: test_portset_get: %s' % repr(portset))
- assert portset is None
- with pytest.raises(AnsibleExitJson) as exc:
- my_obj.apply()
- print('Info: test_portset_apply: %s' % repr(exc.value))
- assert exc.value.args[0]['changed']
- if not self.use_vsim:
- my_obj.server = MockONTAPConnection('portset', 'create', 'vserver', 'mixed')
- portset = my_obj.portset_get()
- print('Info: test_portset_get: %s' % repr(portset))
- assert portset is not None
- with pytest.raises(AnsibleExitJson) as exc:
- my_obj.apply()
- print('Info: test_portset_apply: %s' % repr(exc.value))
- assert exc.value.args[0]['changed']
-
- def test_modify_ports(self):
- ''' Test modify_portset method '''
- module_args = {'ports': ['l1', 'l2']}
- module_args.update(self.set_default_args())
- set_module_args(module_args)
- my_obj = my_module()
- if not self.use_vsim:
- my_obj.server = MockONTAPConnection('portset')
- with pytest.raises(AnsibleExitJson) as exc:
- my_obj.apply()
- print('Info: test_portset_apply: %s' % repr(exc.value))
- assert exc.value.args[0]['changed']
-
- def test_delete_portset(self):
- ''' Test successful delete '''
- module_args = {'state': 'absent'}
- module_args.update(self.set_default_args())
- set_module_args(module_args)
- my_obj = my_module()
- if not self.use_vsim:
- my_obj.server = MockONTAPConnection('portset')
- with pytest.raises(AnsibleExitJson) as exc:
- my_obj.apply()
- print('Info: test_portset_apply: %s' % repr(exc.value))
- assert exc.value.args[0]['changed']
diff --git a/test/units/modules/storage/netapp/test_na_ontap_qos_adaptive_policy_group.py b/test/units/modules/storage/netapp/test_na_ontap_qos_adaptive_policy_group.py
deleted file mode 100644
index 06683dff14..0000000000
--- a/test/units/modules/storage/netapp/test_na_ontap_qos_adaptive_policy_group.py
+++ /dev/null
@@ -1,347 +0,0 @@
-# (c) 2018, NetApp, Inc
-# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-
-''' unit test template for ONTAP Ansible module '''
-
-from __future__ import (absolute_import, division, print_function)
-__metaclass__ = type
-import json
-import pytest
-
-from units.compat import unittest
-from units.compat.mock import patch, Mock
-from ansible.module_utils import basic
-from ansible.module_utils._text import to_bytes
-import ansible.module_utils.netapp as netapp_utils
-
-from ansible.modules.storage.netapp.na_ontap_qos_adaptive_policy_group \
- import NetAppOntapAdaptiveQosPolicyGroup as qos_policy_group_module # module under test
-
-if not netapp_utils.has_netapp_lib():
- pytestmark = pytest.mark.skip('skipping as missing required netapp_lib')
-
-
-def set_module_args(args):
- """prepare arguments so that they will be picked up during module creation"""
- args = json.dumps({'ANSIBLE_MODULE_ARGS': args})
- basic._ANSIBLE_ARGS = to_bytes(args) # pylint: disable=protected-access
-
-
-class AnsibleExitJson(Exception):
- """Exception class to be raised by module.exit_json and caught by the test case"""
- pass
-
-
-class AnsibleFailJson(Exception):
- """Exception class to be raised by module.fail_json and caught by the test case"""
- pass
-
-
-def exit_json(*args, **kwargs): # pylint: disable=unused-argument
- """function to patch over exit_json; package return data into an exception"""
- if 'changed' not in kwargs:
- kwargs['changed'] = False
- raise AnsibleExitJson(kwargs)
-
-
-def fail_json(*args, **kwargs): # pylint: disable=unused-argument
- """function to patch over fail_json; package return data into an exception"""
- kwargs['failed'] = True
- raise AnsibleFailJson(kwargs)
-
-
-class MockONTAPConnection(object):
- ''' mock server connection to ONTAP host '''
-
- def __init__(self, kind=None, data=None):
- ''' save arguments '''
- self.kind = kind
- self.params = data
- self.xml_in = None
- self.xml_out = None
-
- def invoke_successfully(self, xml, enable_tunneling): # pylint: disable=unused-argument
- ''' mock invoke_successfully returning xml data '''
- self.xml_in = xml
- if self.kind == 'policy':
- xml = self.build_policy_group_info(self.params)
- if self.kind == 'error':
- error = netapp_utils.zapi.NaApiError('test', 'error')
- raise error
- self.xml_out = xml
- return xml
-
- @staticmethod
- def build_policy_group_info(vol_details):
- ''' build xml data for volume-attributes '''
- xml = netapp_utils.zapi.NaElement('xml')
- attributes = {
- 'num-records': 1,
- 'attributes-list': {
- 'qos-adaptive-policy-group-info': {
- 'absolute-min-iops': '50IOPS',
- 'expected-iops': '150IOPS/TB',
- 'peak-iops': '220IOPS/TB',
- 'peak-iops-allocation': 'used_space',
- 'num-workloads': 0,
- 'pgid': 6941,
- 'policy-group': vol_details['name'],
- 'vserver': vol_details['vserver']
- }
- }
- }
- xml.translate_struct(attributes)
- return xml
-
-
-class TestMyModule(unittest.TestCase):
- ''' a group of related Unit Tests '''
-
- def setUp(self):
- self.mock_module_helper = patch.multiple(basic.AnsibleModule,
- exit_json=exit_json,
- fail_json=fail_json)
- self.mock_module_helper.start()
- self.addCleanup(self.mock_module_helper.stop)
- self.mock_policy_group = {
- 'name': 'policy_1',
- 'vserver': 'policy_vserver',
- 'absolute_min_iops': '50IOPS',
- 'expected_iops': '150IOPS/TB',
- 'peak_iops': '220IOPS/TB',
- 'peak_iops_allocation': 'used_space'
- }
-
- def mock_args(self):
- return {
- 'name': self.mock_policy_group['name'],
- 'vserver': self.mock_policy_group['vserver'],
- 'absolute_min_iops': '50IOPS',
- 'expected_iops': '150IOPS/TB',
- 'peak_iops': '220IOPS/TB',
- 'peak_iops_allocation': 'used_space',
- 'hostname': 'test',
- 'username': 'test_user',
- 'password': 'test_pass!',
- 'https': 'False'
- }
-
- def get_policy_group_mock_object(self, kind=None):
- """
- Helper method to return an na_ontap_volume object
- :param kind: passes this param to MockONTAPConnection()
- :return: na_ontap_volume object
- """
- policy_obj = qos_policy_group_module()
- policy_obj.autosupport_log = Mock(return_value=None)
- policy_obj.cluster = Mock()
- policy_obj.cluster.invoke_successfully = Mock()
- if kind is None:
- policy_obj.server = MockONTAPConnection()
- else:
- policy_obj.server = MockONTAPConnection(kind=kind, data=self.mock_policy_group)
- return policy_obj
-
- def test_module_fail_when_required_args_missing(self):
- ''' required arguments are reported as errors '''
- with pytest.raises(AnsibleFailJson) as exc:
- set_module_args({})
- qos_policy_group_module()
- print('Info: %s' % exc.value.args[0]['msg'])
-
- def test_get_nonexistent_policy(self):
- ''' Test if get_policy_group returns None for non-existent policy_group '''
- set_module_args(self.mock_args())
- result = self.get_policy_group_mock_object().get_policy_group()
- assert result is None
-
- def test_get_existing_policy_group(self):
- ''' Test if get_policy_group returns details for existing policy_group '''
- set_module_args(self.mock_args())
- result = self.get_policy_group_mock_object('policy').get_policy_group()
- assert result['name'] == self.mock_policy_group['name']
- assert result['vserver'] == self.mock_policy_group['vserver']
-
- def test_create_error_missing_param(self):
- ''' Test if create throws an error if name is not specified'''
- data = self.mock_args()
- del data['name']
- set_module_args(data)
- with pytest.raises(AnsibleFailJson) as exc:
- self.get_policy_group_mock_object('policy').create_policy_group()
- msg = 'missing required arguments: name'
- assert exc.value.args[0]['msg'] == msg
-
- def test_successful_create(self):
- ''' Test successful create '''
- data = self.mock_args()
- set_module_args(data)
- with pytest.raises(AnsibleExitJson) as exc:
- self.get_policy_group_mock_object().apply()
- assert exc.value.args[0]['changed']
-
- def test_create_idempotency(self):
- ''' Test create idempotency '''
- set_module_args(self.mock_args())
- obj = self.get_policy_group_mock_object('policy')
- with pytest.raises(AnsibleExitJson) as exc:
- obj.apply()
- assert not exc.value.args[0]['changed']
-
- @patch('ansible.modules.storage.netapp.na_ontap_qos_adaptive_policy_group.NetAppOntapAdaptiveQosPolicyGroup.get_policy_group')
- def test_create_error(self, get_policy_group):
- ''' Test create error '''
- set_module_args(self.mock_args())
- get_policy_group.side_effect = [
- None
- ]
- with pytest.raises(AnsibleFailJson) as exc:
- self.get_policy_group_mock_object('error').apply()
- assert exc.value.args[0]['msg'] == 'Error creating adaptive qos policy group policy_1: NetApp API failed. Reason - test:error'
-
- def test_successful_delete(self):
- ''' Test delete existing volume '''
- data = self.mock_args()
- data['state'] = 'absent'
- set_module_args(data)
- with pytest.raises(AnsibleExitJson) as exc:
- self.get_policy_group_mock_object('policy').apply()
- assert exc.value.args[0]['changed']
-
- def test_delete_idempotency(self):
- ''' Test delete idempotency '''
- data = self.mock_args()
- data['state'] = 'absent'
- set_module_args(data)
- with pytest.raises(AnsibleExitJson) as exc:
- self.get_policy_group_mock_object().apply()
- assert not exc.value.args[0]['changed']
-
- @patch('ansible.modules.storage.netapp.na_ontap_qos_adaptive_policy_group.NetAppOntapAdaptiveQosPolicyGroup.get_policy_group')
- def test_delete_error(self, get_policy_group):
- ''' Test create idempotency'''
- data = self.mock_args()
- data['state'] = 'absent'
- set_module_args(data)
- current = {
- 'absolute_min_iops': '50IOPS',
- 'expected_iops': '150IOPS/TB',
- 'peak_iops': '220IOPS/TB',
- 'peak_iops_allocation': 'used_space',
- 'name': 'policy_1',
- 'vserver': 'policy_vserver'
- }
- get_policy_group.side_effect = [
- current
- ]
- with pytest.raises(AnsibleFailJson) as exc:
- self.get_policy_group_mock_object('error').apply()
- assert exc.value.args[0]['msg'] == 'Error deleting adaptive qos policy group policy_1: NetApp API failed. Reason - test:error'
-
- def test_successful_modify_expected_iops(self):
- ''' Test successful modify expected iops '''
- data = self.mock_args()
- data['expected_iops'] = '175IOPS'
- set_module_args(data)
- with pytest.raises(AnsibleExitJson) as exc:
- self.get_policy_group_mock_object('policy').apply()
- assert exc.value.args[0]['changed']
-
- def test_modify_expected_iops_idempotency(self):
- ''' Test modify idempotency '''
- data = self.mock_args()
- set_module_args(data)
- with pytest.raises(AnsibleExitJson) as exc:
- self.get_policy_group_mock_object('policy').apply()
- assert not exc.value.args[0]['changed']
-
- @patch('ansible.modules.storage.netapp.na_ontap_qos_adaptive_policy_group.NetAppOntapAdaptiveQosPolicyGroup.get_policy_group')
- def test_modify_error(self, get_policy_group):
- ''' Test create idempotency '''
- data = self.mock_args()
- data['expected_iops'] = '175IOPS'
- set_module_args(data)
- current = {
- 'absolute_min_iops': '50IOPS',
- 'expected_iops': '150IOPS/TB',
- 'peak_iops': '220IOPS/TB',
- 'peak_iops_allocation': 'used_space',
- 'name': 'policy_1',
- 'vserver': 'policy_vserver'
- }
- get_policy_group.side_effect = [
- current
- ]
- with pytest.raises(AnsibleFailJson) as exc:
- self.get_policy_group_mock_object('error').apply()
- assert exc.value.args[0]['msg'] == 'Error modifying adaptive qos policy group policy_1: NetApp API failed. Reason - test:error'
-
- @patch('ansible.modules.storage.netapp.na_ontap_qos_adaptive_policy_group.NetAppOntapAdaptiveQosPolicyGroup.get_policy_group')
- def test_rename(self, get_policy_group):
- ''' Test rename idempotency '''
- data = self.mock_args()
- data['name'] = 'policy_2'
- data['from_name'] = 'policy_1'
- set_module_args(data)
- current = {
- 'absolute_min_iops': '50IOPS',
- 'expected_iops': '150IOPS/TB',
- 'peak_iops': '220IOPS/TB',
- 'peak_iops_allocation': 'used_space',
- 'name': 'policy_1',
- 'vserver': 'policy_vserver'
- }
- get_policy_group.side_effect = [
- None,
- current
- ]
- with pytest.raises(AnsibleExitJson) as exc:
- self.get_policy_group_mock_object('policy').apply()
- assert exc.value.args[0]['changed']
-
- @patch('ansible.modules.storage.netapp.na_ontap_qos_adaptive_policy_group.NetAppOntapAdaptiveQosPolicyGroup.get_policy_group')
- def test_rename_idempotency(self, get_policy_group):
- ''' Test rename idempotency '''
- data = self.mock_args()
- data['name'] = 'policy_1'
- data['from_name'] = 'policy_1'
- current = {
- 'absolute_min_iops': '50IOPS',
- 'expected_iops': '150IOPS/TB',
- 'peak_iops': '220IOPS/TB',
- 'peak_iops_allocation': 'used_space',
- 'name': 'policy_1',
- 'vserver': 'policy_vserver'
- }
- get_policy_group.side_effect = [
- current,
- current
- ]
- set_module_args(data)
- with pytest.raises(AnsibleExitJson) as exc:
- self.get_policy_group_mock_object('policy').apply()
- assert not exc.value.args[0]['changed']
-
- @patch('ansible.modules.storage.netapp.na_ontap_qos_adaptive_policy_group.NetAppOntapAdaptiveQosPolicyGroup.get_policy_group')
- def test_rename_error(self, get_policy_group):
- ''' Test create idempotency '''
- data = self.mock_args()
- data['from_name'] = 'policy_1'
- data['name'] = 'policy_2'
- set_module_args(data)
- current = {
- 'absolute_min_iops': '50IOPS',
- 'expected_iops': '150IOPS/TB',
- 'peak_iops': '220IOPS/TB',
- 'peak_iops_allocation': 'used_space',
- 'is_shared': 'true',
- 'name': 'policy_1',
- 'vserver': 'policy_vserver'
- }
- get_policy_group.side_effect = [
- None,
- current
- ]
- with pytest.raises(AnsibleFailJson) as exc:
- self.get_policy_group_mock_object('error').apply()
- assert exc.value.args[0]['msg'] == 'Error renaming adaptive qos policy group policy_1: NetApp API failed. Reason - test:error'
diff --git a/test/units/modules/storage/netapp/test_na_ontap_qos_policy_group.py b/test/units/modules/storage/netapp/test_na_ontap_qos_policy_group.py
deleted file mode 100644
index 6278d49e82..0000000000
--- a/test/units/modules/storage/netapp/test_na_ontap_qos_policy_group.py
+++ /dev/null
@@ -1,331 +0,0 @@
-# (c) 2018, NetApp, Inc
-# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-
-''' unit test template for ONTAP Ansible module '''
-
-from __future__ import print_function
-import json
-import pytest
-
-from units.compat import unittest
-from units.compat.mock import patch, Mock
-from ansible.module_utils import basic
-from ansible.module_utils._text import to_bytes
-import ansible.module_utils.netapp as netapp_utils
-
-from ansible.modules.storage.netapp.na_ontap_qos_policy_group \
- import NetAppOntapQosPolicyGroup as qos_policy_group_module # module under test
-
-if not netapp_utils.has_netapp_lib():
- pytestmark = pytest.mark.skip('skipping as missing required netapp_lib')
-
-
-def set_module_args(args):
- """prepare arguments so that they will be picked up during module creation"""
- args = json.dumps({'ANSIBLE_MODULE_ARGS': args})
- basic._ANSIBLE_ARGS = to_bytes(args) # pylint: disable=protected-access
-
-
-class AnsibleExitJson(Exception):
- """Exception class to be raised by module.exit_json and caught by the test case"""
- pass
-
-
-class AnsibleFailJson(Exception):
- """Exception class to be raised by module.fail_json and caught by the test case"""
- pass
-
-
-def exit_json(*args, **kwargs): # pylint: disable=unused-argument
- """function to patch over exit_json; package return data into an exception"""
- if 'changed' not in kwargs:
- kwargs['changed'] = False
- raise AnsibleExitJson(kwargs)
-
-
-def fail_json(*args, **kwargs): # pylint: disable=unused-argument
- """function to patch over fail_json; package return data into an exception"""
- kwargs['failed'] = True
- raise AnsibleFailJson(kwargs)
-
-
-class MockONTAPConnection(object):
- ''' mock server connection to ONTAP host '''
-
- def __init__(self, kind=None, data=None):
- ''' save arguments '''
- self.kind = kind
- self.params = data
- self.xml_in = None
- self.xml_out = None
-
- def invoke_successfully(self, xml, enable_tunneling): # pylint: disable=unused-argument
- ''' mock invoke_successfully returning xml data '''
- self.xml_in = xml
- if self.kind == 'policy':
- xml = self.build_policy_group_info(self.params)
- if self.kind == 'error':
- error = netapp_utils.zapi.NaApiError('test', 'error')
- raise error
- self.xml_out = xml
- return xml
-
- @staticmethod
- def build_policy_group_info(vol_details):
- ''' build xml data for volume-attributes '''
- xml = netapp_utils.zapi.NaElement('xml')
- attributes = {
- 'num-records': 1,
- 'attributes-list': {
- 'qos-policy-group-info': {
- 'is-shared': 'true',
- 'max-throughput': '800KB/s,800IOPS',
- 'min-throughput': '100IOPS',
- 'num-workloads': 0,
- 'pgid': 8690,
- 'policy-group': vol_details['name'],
- 'vserver': vol_details['vserver']
- }
- }
- }
- xml.translate_struct(attributes)
- return xml
-
-
-class TestMyModule(unittest.TestCase):
- ''' a group of related Unit Tests '''
-
- def setUp(self):
- self.mock_module_helper = patch.multiple(basic.AnsibleModule,
- exit_json=exit_json,
- fail_json=fail_json)
- self.mock_module_helper.start()
- self.addCleanup(self.mock_module_helper.stop)
- self.mock_policy_group = {
- 'name': 'policy_1',
- 'vserver': 'policy_vserver',
- 'max_throughput': '800KB/s,800IOPS',
- 'min_throughput': '100IOPS'
- }
-
- def mock_args(self):
- return {
- 'name': self.mock_policy_group['name'],
- 'vserver': self.mock_policy_group['vserver'],
- 'max_throughput': '800KB/s,800IOPS',
- 'min_throughput': '100IOPS',
- 'hostname': 'test',
- 'username': 'test_user',
- 'password': 'test_pass!',
- 'https': 'False'
- }
-
- def get_policy_group_mock_object(self, kind=None):
- """
- Helper method to return an na_ontap_volume object
- :param kind: passes this param to MockONTAPConnection()
- :return: na_ontap_volume object
- """
- policy_obj = qos_policy_group_module()
- policy_obj.asup_log_for_cserver = Mock(return_value=None)
- policy_obj.cluster = Mock()
- policy_obj.cluster.invoke_successfully = Mock()
- if kind is None:
- policy_obj.server = MockONTAPConnection()
- else:
- policy_obj.server = MockONTAPConnection(kind=kind, data=self.mock_policy_group)
- return policy_obj
-
- def test_module_fail_when_required_args_missing(self):
- ''' required arguments are reported as errors '''
- with pytest.raises(AnsibleFailJson) as exc:
- set_module_args({})
- qos_policy_group_module()
- print('Info: %s' % exc.value.args[0]['msg'])
-
- def test_get_nonexistent_policy(self):
- ''' Test if get_policy_group returns None for non-existent policy_group '''
- set_module_args(self.mock_args())
- result = self.get_policy_group_mock_object().get_policy_group()
- assert result is None
-
- def test_get_existing_policy_group(self):
- ''' Test if get_policy_group returns details for existing policy_group '''
- set_module_args(self.mock_args())
- result = self.get_policy_group_mock_object('policy').get_policy_group()
- assert result['name'] == self.mock_policy_group['name']
- assert result['vserver'] == self.mock_policy_group['vserver']
-
- def test_create_error_missing_param(self):
- ''' Test if create throws an error if name is not specified'''
- data = self.mock_args()
- del data['name']
- set_module_args(data)
- with pytest.raises(AnsibleFailJson) as exc:
- self.get_policy_group_mock_object('policy').create_policy_group()
- msg = 'missing required arguments: name'
- assert exc.value.args[0]['msg'] == msg
-
- def test_successful_create(self):
- ''' Test successful create '''
- data = self.mock_args()
- set_module_args(data)
- with pytest.raises(AnsibleExitJson) as exc:
- self.get_policy_group_mock_object().apply()
- assert exc.value.args[0]['changed']
-
- def test_create_idempotency(self):
- ''' Test create idempotency '''
- set_module_args(self.mock_args())
- obj = self.get_policy_group_mock_object('policy')
- with pytest.raises(AnsibleExitJson) as exc:
- obj.apply()
- assert not exc.value.args[0]['changed']
-
- @patch('ansible.modules.storage.netapp.na_ontap_qos_policy_group.NetAppOntapQosPolicyGroup.get_policy_group')
- def test_create_error(self, get_policy_group):
- ''' Test create error '''
- set_module_args(self.mock_args())
- get_policy_group.side_effect = [
- None
- ]
- with pytest.raises(AnsibleFailJson) as exc:
- self.get_policy_group_mock_object('error').apply()
- assert exc.value.args[0]['msg'] == 'Error creating qos policy group policy_1: NetApp API failed. Reason - test:error'
-
- def test_successful_delete(self):
- ''' Test delete existing volume '''
- data = self.mock_args()
- data['state'] = 'absent'
- set_module_args(data)
- with pytest.raises(AnsibleExitJson) as exc:
- self.get_policy_group_mock_object('policy').apply()
- assert exc.value.args[0]['changed']
-
- def test_delete_idempotency(self):
- ''' Test delete idempotency '''
- data = self.mock_args()
- data['state'] = 'absent'
- set_module_args(data)
- with pytest.raises(AnsibleExitJson) as exc:
- self.get_policy_group_mock_object().apply()
- assert not exc.value.args[0]['changed']
-
- @patch('ansible.modules.storage.netapp.na_ontap_qos_policy_group.NetAppOntapQosPolicyGroup.get_policy_group')
- def test_delete_error(self, get_policy_group):
- ''' Test create idempotency '''
- data = self.mock_args()
- data['state'] = 'absent'
- set_module_args(data)
- current = {
- 'max_throughput': '800KB/s,800IOPS',
- 'min_throughput': '100IOPS',
- 'name': 'policy_1',
- 'vserver': 'policy_vserver'
- }
- get_policy_group.side_effect = [
- current
- ]
- with pytest.raises(AnsibleFailJson) as exc:
- self.get_policy_group_mock_object('error').apply()
- assert exc.value.args[0]['msg'] == 'Error deleting qos policy group policy_1: NetApp API failed. Reason - test:error'
-
- def test_successful_modify_max_throughput(self):
- ''' Test successful modify max throughput '''
- data = self.mock_args()
- data['max_throughput'] = '900KB/s,800iops'
- set_module_args(data)
- with pytest.raises(AnsibleExitJson) as exc:
- self.get_policy_group_mock_object('policy').apply()
- assert exc.value.args[0]['changed']
-
- def test_modify_max_throughput_idempotency(self):
- ''' Test modify idempotency '''
- data = self.mock_args()
- set_module_args(data)
- with pytest.raises(AnsibleExitJson) as exc:
- self.get_policy_group_mock_object('policy').apply()
- assert not exc.value.args[0]['changed']
-
- @patch('ansible.modules.storage.netapp.na_ontap_qos_policy_group.NetAppOntapQosPolicyGroup.get_policy_group')
- def test_modify_error(self, get_policy_group):
- ''' Test create idempotency '''
- data = self.mock_args()
- data['max_throughput'] = '900KB/s,900IOPS'
- set_module_args(data)
- current = {
- 'max_throughput': '800KB/s,800IOPS',
- 'min_throughput': '100IOPS',
- 'name': 'policy_1',
- 'vserver': 'policy_vserver'
- }
- get_policy_group.side_effect = [
- current
- ]
- with pytest.raises(AnsibleFailJson) as exc:
- self.get_policy_group_mock_object('error').apply()
- assert exc.value.args[0]['msg'] == 'Error modifying qos policy group policy_1: NetApp API failed. Reason - test:error'
-
- @patch('ansible.modules.storage.netapp.na_ontap_qos_policy_group.NetAppOntapQosPolicyGroup.get_policy_group')
- def test_rename(self, get_policy_group):
- ''' Test rename idempotency '''
- data = self.mock_args()
- data['name'] = 'policy_2'
- data['from_name'] = 'policy_1'
- set_module_args(data)
- current = {
- 'max_throughput': '800KB/s,800IOPS',
- 'min_throughput': '100IOPS',
- 'name': 'policy_1',
- 'vserver': 'policy_vserver'
- }
- get_policy_group.side_effect = [
- None,
- current
- ]
- with pytest.raises(AnsibleExitJson) as exc:
- self.get_policy_group_mock_object('policy').apply()
- assert exc.value.args[0]['changed']
-
- @patch('ansible.modules.storage.netapp.na_ontap_qos_policy_group.NetAppOntapQosPolicyGroup.get_policy_group')
- def test_rename_idempotency(self, get_policy_group):
- ''' Test rename idempotency '''
- data = self.mock_args()
- data['name'] = 'policy_1'
- data['from_name'] = 'policy_1'
- current = {
- 'max_throughput': '800KB/s,800IOPS',
- 'min_throughput': '100IOPS',
- 'name': 'policy_1',
- 'vserver': 'policy_vserver'
- }
- get_policy_group.side_effect = [
- current,
- current
- ]
- set_module_args(data)
- with pytest.raises(AnsibleExitJson) as exc:
- self.get_policy_group_mock_object('policy').apply()
- assert not exc.value.args[0]['changed']
-
- @patch('ansible.modules.storage.netapp.na_ontap_qos_policy_group.NetAppOntapQosPolicyGroup.get_policy_group')
- def test_rename_error(self, get_policy_group):
- ''' Test create idempotency '''
- data = self.mock_args()
- data['from_name'] = 'policy_1'
- data['name'] = 'policy_2'
- set_module_args(data)
- current = {
- 'is_shared': 'true',
- 'max_throughput': '800KB/s,800IOPS',
- 'min_throughput': '100IOPS',
- 'name': 'policy_1',
- 'vserver': 'policy_vserver'
- }
- get_policy_group.side_effect = [
- None,
- current
- ]
- with pytest.raises(AnsibleFailJson) as exc:
- self.get_policy_group_mock_object('error').apply()
- assert exc.value.args[0]['msg'] == 'Error renaming qos policy group policy_1: NetApp API failed. Reason - test:error'
diff --git a/test/units/modules/storage/netapp/test_na_ontap_quotas.py b/test/units/modules/storage/netapp/test_na_ontap_quotas.py
deleted file mode 100644
index 3791a5f4d3..0000000000
--- a/test/units/modules/storage/netapp/test_na_ontap_quotas.py
+++ /dev/null
@@ -1,238 +0,0 @@
-''' unit tests ONTAP Ansible module: na_ontap_quotas '''
-
-from __future__ import print_function
-import json
-import pytest
-
-from units.compat import unittest
-from units.compat.mock import patch
-from ansible.module_utils import basic
-from ansible.module_utils._text import to_bytes
-import ansible.module_utils.netapp as netapp_utils
-
-from ansible.modules.storage.netapp.na_ontap_quotas \
- import NetAppONTAPQuotas as my_module
-
-if not netapp_utils.has_netapp_lib():
- pytestmark = pytest.mark.skip('skipping as missing required netapp_lib')
-
-
-def set_module_args(args):
- """prepare arguments so that they will be picked up during module creation"""
- args = json.dumps({'ANSIBLE_MODULE_ARGS': args})
- basic._ANSIBLE_ARGS = to_bytes(args) # pylint: disable=protected-access
-
-
-class AnsibleExitJson(Exception):
- """Exception class to be raised by module.exit_json and caught by the test case"""
- pass
-
-
-class AnsibleFailJson(Exception):
- """Exception class to be raised by module.fail_json and caught by the test case"""
- pass
-
-
-def exit_json(*args, **kwargs): # pylint: disable=unused-argument
- """function to patch over exit_json; package return data into an exception"""
- if 'changed' not in kwargs:
- kwargs['changed'] = False
- raise AnsibleExitJson(kwargs)
-
-
-def fail_json(*args, **kwargs): # pylint: disable=unused-argument
- """function to patch over fail_json; package return data into an exception"""
- kwargs['failed'] = True
- raise AnsibleFailJson(kwargs)
-
-
-class MockONTAPConnection(object):
- ''' mock server connection to ONTAP host '''
-
- def __init__(self, kind=None):
- ''' save arguments '''
- self.type = kind
- self.xml_in = None
- self.xml_out = None
-
- def invoke_successfully(self, xml, enable_tunneling): # pylint: disable=unused-argument
- ''' mock invoke_successfully returning xml data '''
- self.xml_in = xml
- if self.type == 'quotas':
- xml = self.build_quota_info()
- elif self.type == 'quota_fail':
- raise netapp_utils.zapi.NaApiError(code='TEST', message="This exception is from the unit test")
- self.xml_out = xml
- return xml
-
- @staticmethod
- def build_quota_info():
- ''' build xml data for quota-entry '''
- xml = netapp_utils.zapi.NaElement('xml')
- data = {'num-records': 1,
- 'attributes-list': {'quota-entry': {'volume': 'ansible',
- 'file-limit': '-', 'disk-limit': '-', 'threshold': '-'}},
- 'status': 'true'}
- xml.translate_struct(data)
- return xml
-
-
-class TestMyModule(unittest.TestCase):
- ''' a group of related Unit Tests '''
-
- def setUp(self):
- self.mock_module_helper = patch.multiple(basic.AnsibleModule,
- exit_json=exit_json,
- fail_json=fail_json)
- self.mock_module_helper.start()
- self.addCleanup(self.mock_module_helper.stop)
- self.server = MockONTAPConnection()
- self.onbox = False
-
- def set_default_args(self):
- if self.onbox:
- hostname = '10.193.75.3'
- username = 'admin'
- password = 'netapp1!'
- volume = 'ansible'
- vserver = 'ansible'
- policy = 'ansible'
- quota_target = '/vol/ansible'
- type = 'user'
- else:
- hostname = 'hostname'
- username = 'username'
- password = 'password'
- volume = 'ansible'
- vserver = 'ansible'
- policy = 'ansible'
- quota_target = '/vol/ansible'
- type = 'user'
- return dict({
- 'hostname': hostname,
- 'username': username,
- 'password': password,
- 'volume': volume,
- 'vserver': vserver,
- 'policy': policy,
- 'quota_target': quota_target,
- 'type': type
- })
-
- def test_module_fail_when_required_args_missing(self):
- ''' required arguments are reported as errors '''
- with pytest.raises(AnsibleFailJson) as exc:
- set_module_args({})
- my_module()
- print('Info: %s' % exc.value.args[0]['msg'])
-
- def test_ensure_get_called(self):
- ''' test get_quota for non-existent quota'''
- set_module_args(self.set_default_args())
- my_obj = my_module()
- my_obj.server = self.server
- assert my_obj.get_quotas is not None
-
- def test_ensure_get_called_existing(self):
- ''' test get_quota for existing quota'''
- set_module_args(self.set_default_args())
- my_obj = my_module()
- my_obj.server = MockONTAPConnection(kind='quotas')
- assert my_obj.get_quotas()
-
- @patch('ansible.modules.storage.netapp.na_ontap_quotas.NetAppONTAPQuotas.quota_entry_set')
- def test_successful_create(self, quota_entry_set):
- ''' creating quota and testing idempotency '''
- data = self.set_default_args()
- data.update({'file_limit': '3',
- 'disk_limit': '4'})
- # data['file_limit'] = '3'
- # data['disk_limit'] = '4'
- # data['threshold'] = '4'
- set_module_args(data)
- my_obj = my_module()
- if not self.onbox:
- my_obj.server = self.server
- with pytest.raises(AnsibleExitJson) as exc:
- my_obj.apply()
- assert exc.value.args[0]['changed']
- quota_entry_set.assert_called_with()
- # to reset na_helper from remembering the previous 'changed' value
- set_module_args(self.set_default_args())
- my_obj = my_module()
- if not self.onbox:
- my_obj.server = MockONTAPConnection('quotas')
- with pytest.raises(AnsibleExitJson) as exc:
- my_obj.apply()
- assert not exc.value.args[0]['changed']
-
- @patch('ansible.modules.storage.netapp.na_ontap_quotas.NetAppONTAPQuotas.quota_entry_delete')
- def test_successful_delete(self, quota_entry_delete):
- ''' deleting quota and testing idempotency '''
- data = self.set_default_args()
- data['state'] = 'absent'
- set_module_args(data)
- my_obj = my_module()
- if not self.onbox:
- my_obj.server = MockONTAPConnection('quotas')
- with pytest.raises(AnsibleExitJson) as exc:
- my_obj.apply()
- assert exc.value.args[0]['changed']
- quota_entry_delete.assert_called_with()
- # to reset na_helper from remembering the previous 'changed' value
- my_obj = my_module()
- if not self.onbox:
- my_obj.server = self.server
- with pytest.raises(AnsibleExitJson) as exc:
- my_obj.apply()
- assert not exc.value.args[0]['changed']
-
- def test_successful_modify(self):
- ''' modifying quota and testing idempotency '''
- data = self.set_default_args()
- data['file_limit'] = '3'
- set_module_args(data)
- my_obj = my_module()
- if not self.onbox:
- my_obj.server = MockONTAPConnection('quotas')
- with pytest.raises(AnsibleExitJson) as exc:
- my_obj.apply()
- assert exc.value.args[0]['changed']
-
- def test_quota_on_off(self):
- ''' quota set on or off '''
- data = self.set_default_args()
- data['set_quota_status'] = 'false'
- set_module_args(data)
- my_obj = my_module()
- if not self.onbox:
- my_obj.server = MockONTAPConnection('quotas')
- with pytest.raises(AnsibleExitJson) as exc:
- my_obj.apply()
- assert not exc.value.args[0]['changed']
-
- def test_if_all_methods_catch_exception(self):
- module_args = {}
- module_args.update(self.set_default_args())
- set_module_args(module_args)
- my_obj = my_module()
- if not self.onbox:
- my_obj.server = MockONTAPConnection('quota_fail')
- with pytest.raises(AnsibleFailJson) as exc:
- my_obj.get_quota_status()
- assert 'Error fetching quotas status info' in exc.value.args[0]['msg']
- with pytest.raises(AnsibleFailJson) as exc:
- my_obj.get_quotas()
- assert 'Error fetching quotas info' in exc.value.args[0]['msg']
- with pytest.raises(AnsibleFailJson) as exc:
- my_obj.quota_entry_set()
- assert 'Error adding/modifying quota entry' in exc.value.args[0]['msg']
- with pytest.raises(AnsibleFailJson) as exc:
- my_obj.quota_entry_delete()
- assert 'Error deleting quota entry' in exc.value.args[0]['msg']
- with pytest.raises(AnsibleFailJson) as exc:
- my_obj.quota_entry_modify(module_args)
- assert 'Error modifying quota entry' in exc.value.args[0]['msg']
- with pytest.raises(AnsibleFailJson) as exc:
- my_obj.on_or_off_quota('quota-on')
- assert 'Error setting quota-on for ansible' in exc.value.args[0]['msg']
diff --git a/test/units/modules/storage/netapp/test_na_ontap_security_key_manager.py b/test/units/modules/storage/netapp/test_na_ontap_security_key_manager.py
deleted file mode 100644
index d1e7a7a6a7..0000000000
--- a/test/units/modules/storage/netapp/test_na_ontap_security_key_manager.py
+++ /dev/null
@@ -1,173 +0,0 @@
-# (c) 2019, NetApp, Inc
-# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-
-''' unit test template for ONTAP Ansible module '''
-
-from __future__ import print_function
-import json
-import pytest
-
-from units.compat import unittest
-from units.compat.mock import patch, Mock
-from ansible.module_utils import basic
-from ansible.module_utils._text import to_bytes
-import ansible.module_utils.netapp as netapp_utils
-
-from ansible.modules.storage.netapp.na_ontap_security_key_manager \
- import NetAppOntapSecurityKeyManager as key_manager_module # module under test
-
-if not netapp_utils.has_netapp_lib():
- pytestmark = pytest.mark.skip('skipping as missing required netapp_lib')
-
-
-def set_module_args(args):
- """prepare arguments so that they will be picked up during module creation"""
- args = json.dumps({'ANSIBLE_MODULE_ARGS': args})
- basic._ANSIBLE_ARGS = to_bytes(args) # pylint: disable=protected-access
-
-
-class AnsibleExitJson(Exception):
- """Exception class to be raised by module.exit_json and caught by the test case"""
- pass
-
-
-class AnsibleFailJson(Exception):
- """Exception class to be raised by module.fail_json and caught by the test case"""
- pass
-
-
-def exit_json(*args, **kwargs): # pylint: disable=unused-argument
- """function to patch over exit_json; package return data into an exception"""
- if 'changed' not in kwargs:
- kwargs['changed'] = False
- raise AnsibleExitJson(kwargs)
-
-
-def fail_json(*args, **kwargs): # pylint: disable=unused-argument
- """function to patch over fail_json; package return data into an exception"""
- kwargs['failed'] = True
- raise AnsibleFailJson(kwargs)
-
-
-class MockONTAPConnection(object):
- ''' mock server connection to ONTAP host '''
-
- def __init__(self, kind=None, data=None):
- ''' save arguments '''
- self.type = kind
- self.data = data
- self.xml_in = None
- self.xml_out = None
-
- def invoke_successfully(self, xml, enable_tunneling): # pylint: disable=unused-argument
- ''' mock invoke_successfully returning xml data '''
- self.xml_in = xml
- if self.type == 'key_manager':
- xml = self.build_port_info(self.data)
- self.xml_out = xml
- return xml
-
- @staticmethod
- def build_port_info(key_manager_details):
- ''' build xml data for-key-manager-info '''
- xml = netapp_utils.zapi.NaElement('xml')
- attributes = {
- 'num-records': 1,
- 'attributes-list': {
- 'key-manager-info': {
- 'key-manager-ip-address': '0.0.0.0',
- 'key-manager-server-status': 'available',
- 'key-manager-tcp-port': '5696',
- 'node-name': 'test_node'
- }
- }
- }
- xml.translate_struct(attributes)
- return xml
-
-
-class TestMyModule(unittest.TestCase):
- ''' a group of related Unit Tests '''
-
- def setUp(self):
- self.mock_module_helper = patch.multiple(basic.AnsibleModule,
- exit_json=exit_json,
- fail_json=fail_json)
- self.mock_module_helper.start()
- self.addCleanup(self.mock_module_helper.stop)
- self.mock_key_manager = {
- 'node_name': 'test_node',
- 'tcp_port': 5696,
- 'ip_address': '0.0.0.0',
- 'server_status': 'available'
- }
-
- def mock_args(self):
- return {
- 'node': self.mock_key_manager['node_name'],
- 'tcp_port': self.mock_key_manager['tcp_port'],
- 'ip_address': self.mock_key_manager['ip_address'],
- 'hostname': 'test',
- 'username': 'test_user',
- 'password': 'test_pass!',
- 'https': 'False'
- }
-
- def get_key_manager_mock_object(self, kind=None):
- """
- Helper method to return an na_ontap_security_key_manager object
- :param kind: passes this param to MockONTAPConnection()
- :return: na_ontap_security_key_manager object
- """
- obj = key_manager_module()
- obj.asup_log_for_cserver = Mock(return_value=None)
- obj.cluster = Mock()
- obj.cluster.invoke_successfully = Mock()
- if kind is None:
- obj.cluster = MockONTAPConnection()
- else:
- obj.cluster = MockONTAPConnection(kind=kind, data=self.mock_key_manager)
- return obj
-
- def test_module_fail_when_required_args_missing(self):
- ''' required arguments are reported as errors '''
- with pytest.raises(AnsibleFailJson) as exc:
- set_module_args({})
- key_manager_module()
- print('Info: %s' % exc.value.args[0]['msg'])
-
- def test_get_nonexistent_key_manager(self):
- ''' Test if get_key_manager() returns None for non-existent key manager '''
- set_module_args(self.mock_args())
- result = self.get_key_manager_mock_object().get_key_manager()
- assert result is None
-
- def test_get_existing_key_manager(self):
- ''' Test if get_key_manager() returns details for existing key manager '''
- set_module_args(self.mock_args())
- result = self.get_key_manager_mock_object('key_manager').get_key_manager()
- assert result['ip_address'] == self.mock_key_manager['ip_address']
-
- @patch('ansible.modules.storage.netapp.na_ontap_security_key_manager.NetAppOntapSecurityKeyManager.get_key_manager')
- def test_successfully_add_key_manager(self, get_key_manager):
- ''' Test successfully add key manager'''
- data = self.mock_args()
- data['state'] = 'present'
- set_module_args(data)
- get_key_manager.side_effect = [
- None
- ]
- obj = self.get_key_manager_mock_object('key_manager')
- with pytest.raises(AnsibleExitJson) as exc:
- obj.apply()
- assert exc.value.args[0]['changed']
-
- def test_successfully_delete_key_manager(self):
- ''' Test successfully delete key manager'''
- data = self.mock_args()
- data['state'] = 'absent'
- set_module_args(data)
- obj = self.get_key_manager_mock_object('key_manager')
- with pytest.raises(AnsibleExitJson) as exc:
- obj.apply()
- assert exc.value.args[0]['changed']
diff --git a/test/units/modules/storage/netapp/test_na_ontap_service_processor_network.py b/test/units/modules/storage/netapp/test_na_ontap_service_processor_network.py
deleted file mode 100644
index b07509eb92..0000000000
--- a/test/units/modules/storage/netapp/test_na_ontap_service_processor_network.py
+++ /dev/null
@@ -1,232 +0,0 @@
-''' unit test template for ONTAP Ansible module '''
-
-from __future__ import print_function
-import json
-import pytest
-
-from units.compat import unittest
-from units.compat.mock import patch, Mock
-from ansible.module_utils import basic
-from ansible.module_utils._text import to_bytes
-import ansible.module_utils.netapp as netapp_utils
-
-from ansible.modules.storage.netapp.na_ontap_service_processor_network \
- import NetAppOntapServiceProcessorNetwork as sp_module # module under test
-
-if not netapp_utils.has_netapp_lib():
- pytestmark = pytest.mark.skip('skipping as missing required netapp_lib')
-
-
-def set_module_args(args):
- """prepare arguments so that they will be picked up during module creation"""
- args = json.dumps({'ANSIBLE_MODULE_ARGS': args})
- basic._ANSIBLE_ARGS = to_bytes(args) # pylint: disable=protected-access
-
-
-class AnsibleExitJson(Exception):
- """Exception class to be raised by module.exit_json and caught by the test case"""
- pass
-
-
-class AnsibleFailJson(Exception):
- """Exception class to be raised by module.fail_json and caught by the test case"""
- pass
-
-
-def exit_json(*args, **kwargs): # pylint: disable=unused-argument
- """function to patch over exit_json; package return data into an exception"""
- if 'changed' not in kwargs:
- kwargs['changed'] = False
- raise AnsibleExitJson(kwargs)
-
-
-def fail_json(*args, **kwargs): # pylint: disable=unused-argument
- """function to patch over fail_json; package return data into an exception"""
- kwargs['failed'] = True
- raise AnsibleFailJson(kwargs)
-
-
-class MockONTAPConnection(object):
- ''' mock server connection to ONTAP host '''
-
- def __init__(self, kind=None, data=None):
- ''' save arguments '''
- self.kind = kind
- self.data = data
- self.xml_in = None
- self.xml_out = None
-
- def invoke_successfully(self, xml, enable_tunneling): # pylint: disable=unused-argument
- ''' mock invoke_successfully returning xml data '''
- self.xml_in = xml
- if self.kind == 'sp-enabled':
- xml = self.build_sp_info(self.data)
- elif self.kind == 'sp-disabled':
- xml = self.build_sp_disabled_info(self.data)
- else:
- xml = self.build_info()
- self.xml_out = xml
- return xml
-
- @staticmethod
- def build_sp_info(sp):
- ''' build xml data for vserser-info '''
- xml = netapp_utils.zapi.NaElement('xml')
- attributes = {
- 'num-records': 1,
- 'attributes-list':
- {
- 'service-processor-network-info':
- {
- 'node-name': sp['node'],
- 'is-enabled': 'true',
- 'address-type': sp['address_type'],
- 'dhcp': 'v4',
- 'gateway-ip-address': sp['gateway_ip_address'],
- 'netmask': sp['netmask'],
- 'ip-address': sp['ip_address'],
- 'setup-status': 'succeeded',
- }
- }
- }
- xml.translate_struct(attributes)
- return xml
-
- @staticmethod
- def build_info():
- ''' build xml data for vserser-info '''
- xml = netapp_utils.zapi.NaElement('xml')
- attributes = {
- 'num-records': 0
- }
- xml.translate_struct(attributes)
- return xml
-
- @staticmethod
- def build_sp_disabled_info(sp):
- ''' build xml data for vserser-info '''
- xml = netapp_utils.zapi.NaElement('xml')
- attributes = {
- 'num-records': 1,
- 'attributes-list':
- {
- 'service-processor-network-info':
- {
- 'node-name': sp['node'],
- 'is-enabled': 'false',
- 'address-type': sp['address_type'],
- 'setup-status': 'not_setup',
- }
- }
- }
- xml.translate_struct(attributes)
- return xml
-
-
-class TestMyModule(unittest.TestCase):
- ''' a group of related Unit Tests '''
-
- def setUp(self):
- self.mock_module_helper = patch.multiple(basic.AnsibleModule,
- exit_json=exit_json,
- fail_json=fail_json)
- self.mock_module_helper.start()
- self.addCleanup(self.mock_module_helper.stop)
- self.server = MockONTAPConnection()
- self.mock_sp = {
- 'node': 'test-vsim1',
- 'gateway_ip_address': '2.2.2.2',
- 'address_type': 'ipv4',
- 'ip_address': '1.1.1.1',
- 'netmask': '255.255.248.0',
- 'dhcp': 'v4'
- }
-
- def mock_args(self, enable=True):
- data = {
- 'node': self.mock_sp['node'],
- 'is_enabled': enable,
- 'address_type': self.mock_sp['address_type'],
- 'hostname': 'host',
- 'username': 'admin',
- 'password': 'password',
- }
- if enable is True:
- data['ip_address'] = self.mock_sp['ip_address']
- data['gateway_ip_address'] = self.mock_sp['gateway_ip_address']
- data['netmask'] = self.mock_sp['netmask']
- data['dhcp'] = 'v4'
- return data
-
- def get_sp_mock_object(self, kind=None):
- """
- Helper method to return an na_ontap_volume object
- :param kind: passes this param to MockONTAPConnection()
- :return: na_ontap_volume object
- """
- sp_obj = sp_module()
- sp_obj.autosupport_log = Mock(return_value=None)
- if kind is None:
- sp_obj.server = MockONTAPConnection()
- else:
- sp_obj.server = MockONTAPConnection(kind=kind, data=self.mock_sp)
- return sp_obj
-
- def test_module_fail_when_required_args_missing(self):
- ''' required arguments are reported as errors '''
- with pytest.raises(AnsibleFailJson) as exc:
- set_module_args({})
- sp_module()
- print('Info: %s' % exc.value.args[0]['msg'])
-
- def test_modify_error_on_disabled_sp(self):
- ''' a more interesting test '''
- data = self.mock_args(enable=False)
- data['ip_address'] = self.mock_sp['ip_address']
- set_module_args(data)
- with pytest.raises(AnsibleFailJson) as exc:
- self.get_sp_mock_object('sp-disabled').apply()
- assert 'Error: Cannot modify a service processor network if it is disabled.' in \
- exc.value.args[0]['msg']
-
- def test_modify_sp(self):
- ''' a more interesting test '''
- data = self.mock_args()
- data['ip_address'] = '3.3.3.3'
- set_module_args(data)
- with pytest.raises(AnsibleExitJson) as exc:
- self.get_sp_mock_object('sp-enabled').apply()
- assert exc.value.args[0]['changed']
-
- def test_modify_sp_wait(self):
- ''' a more interesting test '''
- data = self.mock_args()
- data['ip_address'] = '3.3.3.3'
- data['wait_for_completion'] = True
- set_module_args(data)
- with pytest.raises(AnsibleExitJson) as exc:
- self.get_sp_mock_object('sp-enabled').apply()
- assert exc.value.args[0]['changed']
-
- @patch('ansible.modules.storage.netapp.na_ontap_service_processor_network.NetAppOntapServiceProcessorNetwork.'
- 'get_service_processor_network')
- def test_non_existing_sp(self, get_sp):
- set_module_args(self.mock_args())
- get_sp.return_value = None
- with pytest.raises(AnsibleFailJson) as exc:
- self.get_sp_mock_object().apply()
- assert 'Error No Service Processor for node: test-vsim1' in exc.value.args[0]['msg']
-
- @patch('ansible.modules.storage.netapp.na_ontap_service_processor_network.NetAppOntapServiceProcessorNetwork.'
- 'get_sp_network_status')
- @patch('time.sleep')
- def test_wait_on_sp_status(self, get_sp, sleep):
- data = self.mock_args()
- data['gateway_ip_address'] = '4.4.4.4'
- data['wait_for_completion'] = True
- set_module_args(data)
- get_sp.side_effect = ['in_progress', 'done']
- with pytest.raises(AnsibleExitJson) as exc:
- self.get_sp_mock_object('sp-enabled').apply()
- sleep.assert_called_once_with()
- assert exc.value.args[0]['changed']
diff --git a/test/units/modules/storage/netapp/test_na_ontap_snapmirror.py b/test/units/modules/storage/netapp/test_na_ontap_snapmirror.py
deleted file mode 100644
index 9ef67ccdcf..0000000000
--- a/test/units/modules/storage/netapp/test_na_ontap_snapmirror.py
+++ /dev/null
@@ -1,574 +0,0 @@
-''' unit tests ONTAP Ansible module: na_ontap_snapmirror '''
-
-from __future__ import print_function
-import json
-import pytest
-
-from units.compat import unittest
-from units.compat.mock import patch, Mock
-from ansible.module_utils import basic
-from ansible.module_utils._text import to_bytes
-import ansible.module_utils.netapp as netapp_utils
-
-from ansible.modules.storage.netapp.na_ontap_snapmirror \
- import NetAppONTAPSnapmirror as my_module
-
-if not netapp_utils.has_netapp_lib():
- pytestmark = pytest.mark.skip('skipping as missing required netapp_lib')
-
-
-def set_module_args(args):
- """prepare arguments so that they will be picked up during module creation"""
- args = json.dumps({'ANSIBLE_MODULE_ARGS': args})
- basic._ANSIBLE_ARGS = to_bytes(args) # pylint: disable=protected-access
-
-
-class AnsibleExitJson(Exception):
- """Exception class to be raised by module.exit_json and caught by the test case"""
- pass
-
-
-class AnsibleFailJson(Exception):
- """Exception class to be raised by module.fail_json and caught by the test case"""
- pass
-
-
-def exit_json(*args, **kwargs): # pylint: disable=unused-argument
- """function to patch over exit_json; package return data into an exception"""
- if 'changed' not in kwargs:
- kwargs['changed'] = False
- raise AnsibleExitJson(kwargs)
-
-
-def fail_json(*args, **kwargs): # pylint: disable=unused-argument
- """function to patch over fail_json; package return data into an exception"""
- kwargs['failed'] = True
- raise AnsibleFailJson(kwargs)
-
-
-class MockONTAPConnection(object):
- ''' mock server connection to ONTAP host '''
-
- def __init__(self, kind=None, parm=None, status=None):
- ''' save arguments '''
- self.type = kind
- self.xml_in = None
- self.xml_out = None
- self.parm = parm
- self.status = status
-
- def invoke_successfully(self, xml, enable_tunneling): # pylint: disable=unused-argument
- ''' mock invoke_successfully returning xml data '''
- self.xml_in = xml
- if self.type == 'snapmirror':
- xml = self.build_snapmirror_info(self.parm, self.status)
- elif self.type == 'snapmirror_fail':
- raise netapp_utils.zapi.NaApiError(code='TEST', message="This exception is from the unit test")
- self.xml_out = xml
- return xml
-
- @staticmethod
- def build_snapmirror_info(mirror_state, status):
- ''' build xml data for snapmirror-entry '''
- xml = netapp_utils.zapi.NaElement('xml')
- data = {'num-records': 1,
- 'attributes-list': {'snapmirror-info': {'mirror-state': mirror_state, 'schedule': None,
- 'source-location': 'ansible:ansible',
- 'relationship-status': status, 'policy': 'ansible',
- 'relationship-type': 'data_protection',
- 'max-transfer-rate': 1000,
- 'identity-preserve': 'true'},
- 'snapmirror-destination-info': {'destination-location': 'ansible'}}}
- xml.translate_struct(data)
- return xml
-
-
-class TestMyModule(unittest.TestCase):
- ''' a group of related Unit Tests '''
-
- def setUp(self):
- self.mock_module_helper = patch.multiple(basic.AnsibleModule,
- exit_json=exit_json,
- fail_json=fail_json)
- self.mock_module_helper.start()
- self.addCleanup(self.mock_module_helper.stop)
- self.server = MockONTAPConnection()
- self.source_server = MockONTAPConnection()
- self.onbox = False
-
- def set_default_args(self):
- if self.onbox:
- hostname = '10.10.10.10'
- username = 'admin'
- password = 'password'
- source_path = 'ansible:ansible'
- destination_path = 'ansible:ansible'
- policy = 'ansible'
- source_vserver = 'ansible'
- destination_vserver = 'ansible'
- relationship_type = 'data_protection'
- schedule = None
- source_username = 'admin'
- source_password = 'password'
- else:
- hostname = '10.10.10.10'
- username = 'admin'
- password = 'password'
- source_path = 'ansible:ansible'
- destination_path = 'ansible:ansible'
- policy = 'ansible'
- source_vserver = 'ansible'
- destination_vserver = 'ansible'
- relationship_type = 'data_protection'
- schedule = None
- source_username = 'admin'
- source_password = 'password'
- return dict({
- 'hostname': hostname,
- 'username': username,
- 'password': password,
- 'source_path': source_path,
- 'destination_path': destination_path,
- 'policy': policy,
- 'source_vserver': source_vserver,
- 'destination_vserver': destination_vserver,
- 'relationship_type': relationship_type,
- 'schedule': schedule,
- 'source_username': source_username,
- 'source_password': source_password
- })
-
- def test_module_fail_when_required_args_missing(self):
- ''' required arguments are reported as errors '''
- with pytest.raises(AnsibleFailJson) as exc:
- set_module_args({})
- my_module()
- print('Info: %s' % exc.value.args[0]['msg'])
-
- def test_ensure_get_called(self):
- ''' test snapmirror_get for non-existent snapmirror'''
- set_module_args(self.set_default_args())
- my_obj = my_module()
- my_obj.server = self.server
- assert my_obj.snapmirror_get is not None
-
- def test_ensure_get_called_existing(self):
- ''' test snapmirror_get for existing snapmirror'''
- set_module_args(self.set_default_args())
- my_obj = my_module()
- my_obj.server = MockONTAPConnection(kind='snapmirror', status='idle')
- assert my_obj.snapmirror_get()
-
- @patch('ansible.modules.storage.netapp.na_ontap_snapmirror.NetAppONTAPSnapmirror.snapmirror_create')
- def test_successful_create(self, snapmirror_create):
- ''' creating snapmirror and testing idempotency '''
- data = self.set_default_args()
- data['schedule'] = 'abc'
- data['identity_preserve'] = True
- set_module_args(data)
- my_obj = my_module()
- my_obj.asup_log_for_cserver = Mock(return_value=None)
- if not self.onbox:
- my_obj.server = self.server
- with pytest.raises(AnsibleExitJson) as exc:
- my_obj.apply()
- assert exc.value.args[0]['changed']
- snapmirror_create.assert_called_with()
- # to reset na_helper from remembering the previous 'changed' value
- set_module_args(self.set_default_args())
- my_obj = my_module()
- my_obj.asup_log_for_cserver = Mock(return_value=None)
- if not self.onbox:
- my_obj.server = MockONTAPConnection('snapmirror', 'snapmirrored', status='idle')
- with pytest.raises(AnsibleExitJson) as exc:
- my_obj.apply()
- assert not exc.value.args[0]['changed']
-
- @patch('ansible.modules.storage.netapp.na_ontap_snapmirror.NetAppONTAPSnapmirror.snapmirror_create')
- @patch('ansible.modules.storage.netapp.na_ontap_snapmirror.NetAppONTAPSnapmirror.check_elementsw_parameters')
- def test_successful_element_ontap_create(self, check_param, snapmirror_create):
- ''' creating ElementSW to ONTAP snapmirror '''
- data = self.set_default_args()
- data['schedule'] = 'abc'
- data['connection_type'] = 'elementsw_ontap'
- data['source_hostname'] = '10.10.10.10'
- set_module_args(data)
- my_obj = my_module()
- my_obj.asup_log_for_cserver = Mock(return_value=None)
- if not self.onbox:
- my_obj.server = self.server
- with pytest.raises(AnsibleExitJson) as exc:
- my_obj.apply()
- assert exc.value.args[0]['changed']
- snapmirror_create.assert_called_with()
- check_param.assert_called_with()
-
- @patch('ansible.modules.storage.netapp.na_ontap_snapmirror.NetAppONTAPSnapmirror.snapmirror_create')
- @patch('ansible.modules.storage.netapp.na_ontap_snapmirror.NetAppONTAPSnapmirror.check_elementsw_parameters')
- @patch('ansible.modules.storage.netapp.na_ontap_snapmirror.NetAppONTAPSnapmirror.snapmirror_get')
- def test_successful_ontap_element_create(self, snapmirror_get, check_param, snapmirror_create):
- ''' creating ONTAP to ElementSW snapmirror '''
- data = self.set_default_args()
- data['schedule'] = 'abc'
- data['connection_type'] = 'ontap_elementsw'
- data['source_hostname'] = '10.10.10.10'
- set_module_args(data)
- my_obj = my_module()
- my_obj.asup_log_for_cserver = Mock(return_value=None)
- snapmirror_get.side_effect = [
- Mock(),
- None
- ]
- if not self.onbox:
- my_obj.server = self.server
- with pytest.raises(AnsibleExitJson) as exc:
- my_obj.apply()
- assert exc.value.args[0]['changed']
- snapmirror_create.assert_called_with()
- check_param.assert_called_with('destination')
-
- @patch('ansible.modules.storage.netapp.na_ontap_snapmirror.NetAppONTAPSnapmirror.delete_snapmirror')
- def test_successful_delete(self, delete_snapmirror):
- ''' deleting snapmirror and testing idempotency '''
- data = self.set_default_args()
- data['state'] = 'absent'
- data['source_hostname'] = '10.10.10.10'
- set_module_args(data)
- my_obj = my_module()
- my_obj.asup_log_for_cserver = Mock(return_value=None)
- my_obj.get_destination = Mock(return_value=True)
- if not self.onbox:
- my_obj.server = MockONTAPConnection('snapmirror', status='idle')
- with pytest.raises(AnsibleExitJson) as exc:
- my_obj.apply()
- assert exc.value.args[0]['changed']
- delete_snapmirror.assert_called_with(False, 'data_protection')
- # to reset na_helper from remembering the previous 'changed' value
- my_obj = my_module()
- my_obj.asup_log_for_cserver = Mock(return_value=None)
- if not self.onbox:
- my_obj.server = self.server
- with pytest.raises(AnsibleExitJson) as exc:
- my_obj.apply()
- assert not exc.value.args[0]['changed']
-
- def test_successful_delete_error_check(self):
- ''' check required parameter source cluster hostname deleting snapmirror'''
- data = self.set_default_args()
- data['state'] = 'absent'
- set_module_args(data)
- my_obj = my_module()
- my_obj.asup_log_for_cserver = Mock(return_value=None)
- if not self.onbox:
- my_obj.server = MockONTAPConnection('snapmirror', status='idle')
- with pytest.raises(AnsibleFailJson) as exc:
- my_obj.apply()
- assert 'Missing parameters for delete:' in exc.value.args[0]['msg']
-
- def test_successful_delete_check_get_destination(self):
- ''' check required parameter source cluster hostname deleting snapmirror'''
- data = self.set_default_args()
- data['state'] = 'absent'
- data['source_hostname'] = '10.10.10.10'
- set_module_args(data)
- my_obj = my_module()
- my_obj.asup_log_for_cserver = Mock(return_value=None)
- if not self.onbox:
- my_obj.server = MockONTAPConnection('snapmirror', status='idle')
- my_obj.source_server = MockONTAPConnection('snapmirror', status='idle')
- res = my_obj.get_destination()
- assert res is True
-
- def test_snapmirror_release(self):
- data = self.set_default_args()
- set_module_args(data)
- my_obj = my_module()
- my_obj.asup_log_for_cserver = Mock(return_value=None)
- if not self.onbox:
- my_obj.source_server = MockONTAPConnection('snapmirror', status='idle', parm='snapmirrored')
- my_obj.snapmirror_release()
- assert my_obj.source_server.xml_in['destination-location'] == data['destination_path']
-
- @patch('ansible.modules.storage.netapp.na_ontap_snapmirror.NetAppONTAPSnapmirror.snapmirror_abort')
- def test_successful_abort(self, snapmirror_abort):
- ''' deleting snapmirror and testing idempotency '''
- data = self.set_default_args()
- data['state'] = 'absent'
- data['source_hostname'] = '10.10.10.10'
- set_module_args(data)
- my_obj = my_module()
- my_obj.asup_log_for_cserver = Mock(return_value=None)
- if not self.onbox:
- my_obj.server = MockONTAPConnection('snapmirror', status='transferring')
- with pytest.raises(AnsibleExitJson) as exc:
- my_obj.apply()
- assert exc.value.args[0]['changed']
- snapmirror_abort.assert_called_with()
- # to reset na_helper from remembering the previous 'changed' value
- my_obj = my_module()
- my_obj.asup_log_for_cserver = Mock(return_value=None)
- if not self.onbox:
- my_obj.server = self.server
- with pytest.raises(AnsibleExitJson) as exc:
- my_obj.apply()
- assert not exc.value.args[0]['changed']
-
- @patch('ansible.modules.storage.netapp.na_ontap_snapmirror.NetAppONTAPSnapmirror.snapmirror_modify')
- def test_successful_modify(self, snapmirror_modify):
- ''' modifying snapmirror and testing idempotency '''
- data = self.set_default_args()
- data['policy'] = 'ansible2'
- data['schedule'] = 'abc2'
- data['max_transfer_rate'] = 2000
- set_module_args(data)
- my_obj = my_module()
- my_obj.asup_log_for_cserver = Mock(return_value=None)
- if not self.onbox:
- my_obj.server = MockONTAPConnection('snapmirror', status='idle')
- with pytest.raises(AnsibleExitJson) as exc:
- my_obj.apply()
- assert exc.value.args[0]['changed']
- snapmirror_modify.assert_called_with({'policy': 'ansible2', 'schedule': 'abc2', 'max_transfer_rate': 2000})
- # to reset na_helper from remembering the previous 'changed' value
- data = self.set_default_args()
- set_module_args(data)
- my_obj = my_module()
- my_obj.asup_log_for_cserver = Mock(return_value=None)
- if not self.onbox:
- my_obj.server = MockONTAPConnection('snapmirror', status='idle', parm='snapmirrored')
- with pytest.raises(AnsibleExitJson) as exc:
- my_obj.apply()
- assert not exc.value.args[0]['changed']
-
- @patch('ansible.modules.storage.netapp.na_ontap_snapmirror.NetAppONTAPSnapmirror.snapmirror_initialize')
- def test_successful_initialize(self, snapmirror_initialize):
- ''' initialize snapmirror and testing idempotency '''
- data = self.set_default_args()
- set_module_args(data)
- my_obj = my_module()
- my_obj.asup_log_for_cserver = Mock(return_value=None)
- if not self.onbox:
- my_obj.server = MockONTAPConnection('snapmirror', status='transferring')
- with pytest.raises(AnsibleExitJson) as exc:
- my_obj.apply()
- assert exc.value.args[0]['changed']
- snapmirror_initialize.assert_called_with()
- # to reset na_helper from remembering the previous 'changed' value
- data = self.set_default_args()
- set_module_args(data)
- my_obj = my_module()
- my_obj.asup_log_for_cserver = Mock(return_value=None)
- if not self.onbox:
- my_obj.server = MockONTAPConnection('snapmirror', status='idle', parm='snapmirrored')
- with pytest.raises(AnsibleExitJson) as exc:
- my_obj.apply()
- assert not exc.value.args[0]['changed']
-
- @patch('ansible.modules.storage.netapp.na_ontap_snapmirror.NetAppONTAPSnapmirror.snapmirror_update')
- def test_successful_update(self, snapmirror_update):
- ''' update snapmirror and testing idempotency '''
- data = self.set_default_args()
- set_module_args(data)
- my_obj = my_module()
- my_obj.asup_log_for_cserver = Mock(return_value=None)
- if not self.onbox:
- my_obj.server = MockONTAPConnection('snapmirror', status='idle', parm='snapmirrored')
- with pytest.raises(AnsibleExitJson) as exc:
- my_obj.apply()
- assert not exc.value.args[0]['changed']
- snapmirror_update.assert_called_with()
-
- def test_elementsw_volume_exists(self):
- ''' elementsw_volume_exists '''
- data = self.set_default_args()
- set_module_args(data)
- my_obj = my_module()
- my_obj.asup_log_for_cserver = Mock(return_value=None)
- mock_helper = Mock()
- mock_helper.volume_id_exists.side_effect = [1000, None]
- if not self.onbox:
- my_obj.server = MockONTAPConnection('snapmirror', status='idle', parm='snapmirrored')
- res = my_obj.check_if_elementsw_volume_exists('10.10.10.10:/lun/1000', mock_helper)
- assert res is None
- with pytest.raises(AnsibleFailJson) as exc:
- my_obj.check_if_elementsw_volume_exists('10.10.10.10:/lun/1000', mock_helper)
- assert 'Error: Source volume does not exist in the ElementSW cluster' in exc.value.args[0]['msg']
-
- def test_elementsw_svip_exists(self):
- ''' svip_exists '''
- data = self.set_default_args()
- set_module_args(data)
- my_obj = my_module()
- my_obj.asup_log_for_cserver = Mock(return_value=None)
- mock_helper = Mock()
- mock_helper.get_cluster_info.return_value.cluster_info.svip = '10.10.10.10'
- if not self.onbox:
- my_obj.server = MockONTAPConnection('snapmirror', status='idle', parm='snapmirrored')
- res = my_obj.validate_elementsw_svip('10.10.10.10:/lun/1000', mock_helper)
- assert res is None
-
- def test_elementsw_svip_exists_negative(self):
- ''' svip_exists negative testing'''
- data = self.set_default_args()
- set_module_args(data)
- my_obj = my_module()
- my_obj.asup_log_for_cserver = Mock(return_value=None)
- mock_helper = Mock()
- mock_helper.get_cluster_info.return_value.cluster_info.svip = '10.10.10.10'
- if not self.onbox:
- my_obj.server = MockONTAPConnection('snapmirror', status='idle', parm='snapmirrored')
- with pytest.raises(AnsibleFailJson) as exc:
- my_obj.validate_elementsw_svip('10.10.10.11:/lun/1000', mock_helper)
- assert 'Error: Invalid SVIP' in exc.value.args[0]['msg']
-
- @patch('ansible.modules.storage.netapp.na_ontap_snapmirror.NetAppONTAPSnapmirror.set_element_connection')
- @patch('ansible.modules.storage.netapp.na_ontap_snapmirror.NetAppONTAPSnapmirror.validate_elementsw_svip')
- @patch('ansible.modules.storage.netapp.na_ontap_snapmirror.NetAppONTAPSnapmirror.check_if_elementsw_volume_exists')
- def test_check_elementsw_params_source(self, validate_volume, validate_svip, connection):
- ''' check elementsw parameters for source '''
- data = self.set_default_args()
- data['source_path'] = '10.10.10.10:/lun/1000'
- set_module_args(data)
- my_obj = my_module()
- my_obj.asup_log_for_cserver = Mock(return_value=None)
- mock_elem, mock_helper = Mock(), Mock()
- connection.return_value = mock_helper, mock_elem
- if not self.onbox:
- my_obj.server = MockONTAPConnection('snapmirror', status='idle', parm='snapmirrored')
- my_obj.check_elementsw_parameters('source')
- connection.called_once_with('source')
- validate_svip.called_once_with(data['source_path'], mock_elem)
- validate_volume.called_once_with(data['source_path'], mock_helper)
-
- def test_check_elementsw_params_negative(self):
- ''' check elementsw parameters for source negative testing '''
- data = self.set_default_args()
- del data['source_path']
- set_module_args(data)
- my_obj = my_module()
- my_obj.asup_log_for_cserver = Mock(return_value=None)
- if not self.onbox:
- my_obj.server = MockONTAPConnection('snapmirror', status='idle', parm='snapmirrored')
- with pytest.raises(AnsibleFailJson) as exc:
- my_obj.check_elementsw_parameters('source')
- assert 'Error: Missing required parameter source_path' in exc.value.args[0]['msg']
-
- def test_check_elementsw_params_invalid(self):
- ''' check elementsw parameters for source invalid testing '''
- data = self.set_default_args()
- set_module_args(data)
- my_obj = my_module()
- my_obj.asup_log_for_cserver = Mock(return_value=None)
- if not self.onbox:
- my_obj.server = MockONTAPConnection('snapmirror', status='idle', parm='snapmirrored')
- with pytest.raises(AnsibleFailJson) as exc:
- my_obj.check_elementsw_parameters('source')
- assert 'Error: invalid source_path' in exc.value.args[0]['msg']
-
- def test_elementsw_source_path_format(self):
- ''' test element_source_path_format_matches '''
- data = self.set_default_args()
- set_module_args(data)
- my_obj = my_module()
- my_obj.asup_log_for_cserver = Mock(return_value=None)
- if not self.onbox:
- my_obj.server = MockONTAPConnection('snapmirror', status='idle', parm='snapmirrored')
- match = my_obj.element_source_path_format_matches('1.1.1.1:dummy')
- assert match is None
- match = my_obj.element_source_path_format_matches('10.10.10.10:/lun/10')
- assert match is not None
-
- @patch('ansible.module_utils.netapp.create_sf_connection')
- def test_set_elem_connection(self, create_sf_connection):
- ''' test set_elem_connection '''
- data = self.set_default_args()
- data['source_hostname'] = 'test_source'
- set_module_args(data)
- my_obj = my_module()
- my_obj.asup_log_for_cserver = Mock(return_value=None)
- create_sf_connection.return_value = Mock()
- if not self.onbox:
- my_obj.server = MockONTAPConnection('snapmirror', status='idle', parm='snapmirrored')
- my_obj.set_element_connection('source')
- assert my_obj.module.params['hostname'] == data['source_hostname']
- assert my_obj.module.params['username'] == data['source_username']
- assert my_obj.module.params['password'] == data['source_password']
-
- def test_remote_volume_exists(self):
- ''' test check_if_remote_volume_exists '''
- data = self.set_default_args()
- data['source_volume'] = 'test_vol'
- data['destination_volume'] = 'test_vol2'
- set_module_args(data)
- my_obj = my_module()
- my_obj.set_source_cluster_connection = Mock(return_value=None)
- my_obj.asup_log_for_cserver = Mock(return_value=None)
- if not self.onbox:
- my_obj.server = MockONTAPConnection('snapmirror', status='idle', parm='snapmirrored')
- my_obj.source_server = MockONTAPConnection('snapmirror', status='idle', parm='snapmirrored')
- res = my_obj.check_if_remote_volume_exists()
- assert res
-
- @patch('ansible.module_utils.netapp.create_sf_connection')
- def test_set_elem_connection_destination(self, create_sf_connection):
- ''' test set_elem_connection for destination'''
- data = self.set_default_args()
- data['source_hostname'] = 'test_source'
- set_module_args(data)
- my_obj = my_module()
- my_obj.asup_log_for_cserver = Mock(return_value=None)
- create_sf_connection.return_value = Mock()
- if not self.onbox:
- my_obj.server = MockONTAPConnection('snapmirror', status='idle', parm='snapmirrored')
- my_obj.set_element_connection('destination')
- assert my_obj.module.params['hostname'] == data['hostname']
- assert my_obj.module.params['username'] == data['username']
- assert my_obj.module.params['password'] == data['password']
-
- def test_if_all_methods_catch_exception(self):
- data = self.set_default_args()
- data['source_hostname'] = '10.10.10.10'
- data['source_volume'] = 'ansible'
- data['destination_volume'] = 'ansible2'
- set_module_args(data)
- my_obj = my_module()
- if not self.onbox:
- my_obj.server = MockONTAPConnection('snapmirror_fail')
- with pytest.raises(AnsibleFailJson) as exc:
- my_obj.snapmirror_get()
- assert 'Error fetching snapmirror info: ' in exc.value.args[0]['msg']
- with pytest.raises(AnsibleFailJson) as exc:
- my_obj.snapmirror_abort()
- assert 'Error aborting SnapMirror relationship :' in exc.value.args[0]['msg']
- with pytest.raises(AnsibleFailJson) as exc:
- my_obj.snapmirror_break()
- assert 'Error breaking SnapMirror relationship :' in exc.value.args[0]['msg']
- with pytest.raises(AnsibleFailJson) as exc:
- my_obj.snapmirror_get = Mock(return_value={'mirror_state': 'transferring'})
- my_obj.snapmirror_initialize()
- assert 'Error initializing SnapMirror :' in exc.value.args[0]['msg']
- with pytest.raises(AnsibleFailJson) as exc:
- my_obj.snapmirror_update()
- assert 'Error updating SnapMirror :' in exc.value.args[0]['msg']
- with pytest.raises(AnsibleFailJson) as exc:
- my_obj.set_source_cluster_connection = Mock(return_value=True)
- my_obj.source_server = MockONTAPConnection('snapmirror_fail')
- my_obj.check_if_remote_volume_exists()
- assert 'Error fetching source volume details' in exc.value.args[0]['msg']
- with pytest.raises(AnsibleFailJson) as exc:
- my_obj.check_if_remote_volume_exists = Mock(return_value=True)
- my_obj.source_server = MockONTAPConnection()
- my_obj.snapmirror_create()
- assert 'Error creating SnapMirror ' in exc.value.args[0]['msg']
- with pytest.raises(AnsibleFailJson) as exc:
- my_obj.snapmirror_quiesce()
- assert 'Error Quiescing SnapMirror :' in exc.value.args[0]['msg']
- with pytest.raises(AnsibleFailJson) as exc:
- my_obj.snapmirror_quiesce = Mock(return_value=None)
- my_obj.get_destination = Mock(return_value=None)
- my_obj.snapmirror_break = Mock(return_value=None)
- my_obj.delete_snapmirror(False, 'data_protection')
- assert 'Error deleting SnapMirror :' in exc.value.args[0]['msg']
- with pytest.raises(AnsibleFailJson) as exc:
- my_obj.snapmirror_modify({'policy': 'ansible2', 'schedule': 'abc2'})
- assert 'Error modifying SnapMirror schedule or policy :' in exc.value.args[0]['msg']
diff --git a/test/units/modules/storage/netapp/test_na_ontap_snapshot.py b/test/units/modules/storage/netapp/test_na_ontap_snapshot.py
deleted file mode 100644
index d4e140b396..0000000000
--- a/test/units/modules/storage/netapp/test_na_ontap_snapshot.py
+++ /dev/null
@@ -1,226 +0,0 @@
-# (c) 2018, NetApp, Inc
-# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-
-''' unit tests ONTAP Ansible module: na_ontap_nvme_snapshot'''
-
-from __future__ import print_function
-import json
-import pytest
-
-from units.compat import unittest
-from units.compat.mock import patch
-from ansible.module_utils import basic
-from ansible.module_utils._text import to_bytes
-import ansible.module_utils.netapp as netapp_utils
-
-from ansible.modules.storage.netapp.na_ontap_snapshot \
- import NetAppOntapSnapshot as my_module
-
-if not netapp_utils.has_netapp_lib():
- pytestmark = pytest.mark.skip('skipping as missing required netapp_lib')
-
-
-def set_module_args(args):
- """prepare arguments so that they will be picked up during module creation"""
- args = json.dumps({'ANSIBLE_MODULE_ARGS': args})
- basic._ANSIBLE_ARGS = to_bytes(args) # pylint: disable=protected-access
-
-
-class AnsibleExitJson(Exception):
- """Exception class to be raised by module.exit_json and caught by the test case"""
- pass
-
-
-class AnsibleFailJson(Exception):
- """Exception class to be raised by module.fail_json and caught by the test case"""
- pass
-
-
-def exit_json(*args, **kwargs): # pylint: disable=unused-argument
- """function to patch over exit_json; package return data into an exception"""
- if 'changed' not in kwargs:
- kwargs['changed'] = False
- raise AnsibleExitJson(kwargs)
-
-
-def fail_json(*args, **kwargs): # pylint: disable=unused-argument
- """function to patch over fail_json; package return data into an exception"""
- kwargs['failed'] = True
- raise AnsibleFailJson(kwargs)
-
-
-class MockONTAPConnection(object):
- ''' mock server connection to ONTAP host '''
-
- def __init__(self, kind=None):
- ''' save arguments '''
- self.type = kind
- self.xml_in = None
- self.xml_out = None
-
- def invoke_successfully(self, xml, enable_tunneling): # pylint: disable=unused-argument
- ''' mock invoke_successfully returning xml data '''
- self.xml_in = xml
- if self.type == 'snapshot':
- xml = self.build_snapshot_info()
- elif self.type == 'snapshot_fail':
- raise netapp_utils.zapi.NaApiError(code='TEST', message="This exception is from the unit test")
- self.xml_out = xml
- return xml
-
- @staticmethod
- def build_snapshot_info():
- ''' build xml data for snapshot-info '''
- xml = netapp_utils.zapi.NaElement('xml')
- data = {'num-records': 1,
- 'attributes-list': {'snapshot-info': {'comment': 'new comment',
- 'name': 'ansible',
- 'snapmirror-label': 'label12'}}}
- xml.translate_struct(data)
- return xml
-
-
-class TestMyModule(unittest.TestCase):
- ''' a group of related Unit Tests '''
-
- def setUp(self):
- self.mock_module_helper = patch.multiple(basic.AnsibleModule,
- exit_json=exit_json,
- fail_json=fail_json)
- self.mock_module_helper.start()
- self.addCleanup(self.mock_module_helper.stop)
- self.server = MockONTAPConnection()
- self.onbox = False
-
- def set_default_args(self):
- if self.onbox:
- hostname = '10.193.75.3'
- username = 'admin'
- password = 'netapp1!'
- vserver = 'ansible'
- volume = 'ansible'
- snapshot = 'ansible'
- comment = 'new comment'
- snapmirror_label = 'label12'
- else:
- hostname = 'hostname'
- username = 'username'
- password = 'password'
- vserver = 'vserver'
- volume = 'ansible'
- snapshot = 'ansible'
- comment = 'new comment'
- snapmirror_label = 'label12'
- return dict({
- 'hostname': hostname,
- 'username': username,
- 'password': password,
- 'vserver': vserver,
- 'volume': volume,
- 'snapshot': snapshot,
- 'comment': comment,
- 'snapmirror_label': snapmirror_label
- })
-
- def test_module_fail_when_required_args_missing(self):
- ''' required arguments are reported as errors '''
- with pytest.raises(AnsibleFailJson) as exc:
- set_module_args({})
- my_module()
- print('Info: %s' % exc.value.args[0]['msg'])
-
- def test_ensure_get_called(self):
- ''' test get_snapshot() for non-existent snapshot'''
- set_module_args(self.set_default_args())
- my_obj = my_module()
- my_obj.server = self.server
- assert my_obj.get_snapshot() is None
-
- def test_ensure_get_called_existing(self):
- ''' test get_snapshot() for existing snapshot'''
- set_module_args(self.set_default_args())
- my_obj = my_module()
- my_obj.server = MockONTAPConnection(kind='snapshot')
- assert my_obj.get_snapshot()
-
- @patch('ansible.modules.storage.netapp.na_ontap_snapshot.NetAppOntapSnapshot.create_snapshot')
- def test_successful_create(self, create_snapshot):
- ''' creating snapshot and testing idempotency '''
- set_module_args(self.set_default_args())
- my_obj = my_module()
- if not self.onbox:
- my_obj.server = self.server
- with pytest.raises(AnsibleExitJson) as exc:
- my_obj.apply()
- assert exc.value.args[0]['changed']
- create_snapshot.assert_called_with()
- # to reset na_helper from remembering the previous 'changed' value
- my_obj = my_module()
- if not self.onbox:
- my_obj.server = MockONTAPConnection('snapshot')
- with pytest.raises(AnsibleExitJson) as exc:
- my_obj.apply()
- assert not exc.value.args[0]['changed']
-
- @patch('ansible.modules.storage.netapp.na_ontap_snapshot.NetAppOntapSnapshot.modify_snapshot')
- def test_successful_modify(self, modify_snapshot):
- ''' modifying snapshot and testing idempotency '''
- data = self.set_default_args()
- data['comment'] = 'adding comment'
- data['snapmirror_label'] = 'label22'
- set_module_args(data)
- my_obj = my_module()
- if not self.onbox:
- my_obj.server = MockONTAPConnection('snapshot')
- with pytest.raises(AnsibleExitJson) as exc:
- my_obj.apply()
- assert exc.value.args[0]['changed']
- modify_snapshot.assert_called_with()
- # to reset na_helper from remembering the previous 'changed' value
- data['comment'] = 'new comment'
- data['snapmirror_label'] = 'label12'
- set_module_args(data)
- my_obj = my_module()
- if not self.onbox:
- my_obj.server = MockONTAPConnection('snapshot')
- with pytest.raises(AnsibleExitJson) as exc:
- my_obj.apply()
- assert not exc.value.args[0]['changed']
-
- @patch('ansible.modules.storage.netapp.na_ontap_snapshot.NetAppOntapSnapshot.delete_snapshot')
- def test_successful_delete(self, delete_snapshot):
- ''' deleting snapshot and testing idempotency '''
- data = self.set_default_args()
- data['state'] = 'absent'
- set_module_args(data)
- my_obj = my_module()
- if not self.onbox:
- my_obj.server = MockONTAPConnection('snapshot')
- with pytest.raises(AnsibleExitJson) as exc:
- my_obj.apply()
- assert exc.value.args[0]['changed']
- delete_snapshot.assert_called_with()
- # to reset na_helper from remembering the previous 'changed' value
- my_obj = my_module()
- if not self.onbox:
- my_obj.server = self.server
- with pytest.raises(AnsibleExitJson) as exc:
- my_obj.apply()
- assert not exc.value.args[0]['changed']
-
- def test_if_all_methods_catch_exception(self):
- module_args = {}
- module_args.update(self.set_default_args())
- set_module_args(module_args)
- my_obj = my_module()
- if not self.onbox:
- my_obj.server = MockONTAPConnection('snapshot_fail')
- with pytest.raises(AnsibleFailJson) as exc:
- my_obj.create_snapshot()
- assert 'Error creating snapshot ansible:' in exc.value.args[0]['msg']
- with pytest.raises(AnsibleFailJson) as exc:
- my_obj.delete_snapshot()
- assert 'Error deleting snapshot ansible:' in exc.value.args[0]['msg']
- with pytest.raises(AnsibleFailJson) as exc:
- my_obj.modify_snapshot()
- assert 'Error modifying snapshot ansible:' in exc.value.args[0]['msg']
diff --git a/test/units/modules/storage/netapp/test_na_ontap_snapshot_policy.py b/test/units/modules/storage/netapp/test_na_ontap_snapshot_policy.py
deleted file mode 100644
index 19b0864b64..0000000000
--- a/test/units/modules/storage/netapp/test_na_ontap_snapshot_policy.py
+++ /dev/null
@@ -1,654 +0,0 @@
-# (c) 2018, NetApp, Inc
-# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-
-''' unit tests ONTAP Ansible module: na_ontap_snapshot_policy'''
-
-from __future__ import print_function
-import json
-import pytest
-
-from units.compat import unittest
-from units.compat.mock import patch, Mock
-from ansible.module_utils import basic
-from ansible.module_utils._text import to_bytes
-import ansible.module_utils.netapp as netapp_utils
-
-from ansible.modules.storage.netapp.na_ontap_snapshot_policy \
- import NetAppOntapSnapshotPolicy as my_module
-
-if not netapp_utils.has_netapp_lib():
- pytestmark = pytest.mark.skip('skipping as missing required netapp_lib')
-
-
-def set_module_args(args):
- """prepare arguments so that they will be picked up during module creation"""
- args = json.dumps({'ANSIBLE_MODULE_ARGS': args})
- basic._ANSIBLE_ARGS = to_bytes(args) # pylint: disable=protected-access
-
-
-class AnsibleExitJson(Exception):
- """Exception class to be raised by module.exit_json and caught by the test case"""
- pass
-
-
-class AnsibleFailJson(Exception):
- """Exception class to be raised by module.fail_json and caught by the test case"""
- pass
-
-
-def exit_json(*args, **kwargs): # pylint: disable=unused-argument
- """function to patch over exit_json; package return data into an exception"""
- if 'changed' not in kwargs:
- kwargs['changed'] = False
- raise AnsibleExitJson(kwargs)
-
-
-def fail_json(*args, **kwargs): # pylint: disable=unused-argument
- """function to patch over fail_json; package return data into an exception"""
- kwargs['failed'] = True
- raise AnsibleFailJson(kwargs)
-
-
-class MockONTAPConnection(object):
- ''' mock server connection to ONTAP host '''
-
- def __init__(self, kind=None):
- ''' save arguments '''
- self.type = kind
- self.xml_in = None
- self.xml_out = None
-
- def invoke_successfully(self, xml, enable_tunneling): # pylint: disable=unused-argument
- ''' mock invoke_successfully returning xml data '''
- self.xml_in = xml
- if self.type == 'policy':
- xml = self.build_snapshot_policy_info()
- elif self.type == 'snapshot_policy_info_policy_disabled':
- xml = self.build_snapshot_policy_info_policy_disabled()
- elif self.type == 'snapshot_policy_info_comment_modified':
- xml = self.build_snapshot_policy_info_comment_modified()
- elif self.type == 'snapshot_policy_info_schedules_added':
- xml = self.build_snapshot_policy_info_schedules_added()
- elif self.type == 'snapshot_policy_info_schedules_deleted':
- xml = self.build_snapshot_policy_info_schedules_deleted()
- elif self.type == 'snapshot_policy_info_modified_schedule_counts':
- xml = self.build_snapshot_policy_info_modified_schedule_counts()
- elif self.type == 'policy_fail':
- raise netapp_utils.zapi.NaApiError(code='TEST', message="This exception is from the unit test")
- self.xml_out = xml
- return xml
-
- def asup_log_for_cserver(self):
- ''' mock autosupport log'''
- return None
-
- @staticmethod
- def build_snapshot_policy_info():
- ''' build xml data for snapshot-policy-info '''
- xml = netapp_utils.zapi.NaElement('xml')
- data = {'num-records': 1,
- 'attributes-list': {
- 'snapshot-policy-info': {
- 'comment': 'new comment',
- 'enabled': 'true',
- 'policy': 'ansible',
- 'snapshot-policy-schedules': {
- 'snapshot-schedule-info': {
- 'count': 100,
- 'schedule': 'hourly',
- 'snapmirror-label': ''
- }
- },
- 'vserver-name': 'hostname'
- }
- }}
- xml.translate_struct(data)
- return xml
-
- @staticmethod
- def build_snapshot_policy_info_comment_modified():
- ''' build xml data for snapshot-policy-info '''
- xml = netapp_utils.zapi.NaElement('xml')
- data = {'num-records': 1,
- 'attributes-list': {
- 'snapshot-policy-info': {
- 'comment': 'modified comment',
- 'enabled': 'true',
- 'policy': 'ansible',
- 'snapshot-policy-schedules': {
- 'snapshot-schedule-info': {
- 'count': 100,
- 'schedule': 'hourly',
- 'snapmirror-label': ''
- }
- },
- 'vserver-name': 'hostname'
- }
- }}
- xml.translate_struct(data)
- return xml
-
- @staticmethod
- def build_snapshot_policy_info_policy_disabled():
- ''' build xml data for snapshot-policy-info '''
- xml = netapp_utils.zapi.NaElement('xml')
- data = {'num-records': 1,
- 'attributes-list': {
- 'snapshot-policy-info': {
- 'comment': 'new comment',
- 'enabled': 'false',
- 'policy': 'ansible',
- 'snapshot-policy-schedules': {
- 'snapshot-schedule-info': {
- 'count': 100,
- 'schedule': 'hourly',
- 'snapmirror-label': ''
- }
- },
- 'vserver-name': 'hostname'
- }
- }}
- xml.translate_struct(data)
- return xml
-
- @staticmethod
- def build_snapshot_policy_info_schedules_added():
- ''' build xml data for snapshot-policy-info '''
- xml = netapp_utils.zapi.NaElement('xml')
- data = {'num-records': 1,
- 'attributes-list': {
- 'snapshot-policy-info': {
- 'comment': 'new comment',
- 'enabled': 'true',
- 'policy': 'ansible',
- 'snapshot-policy-schedules': [
- {
- 'snapshot-schedule-info': {
- 'count': 100,
- 'schedule': 'hourly',
- 'snapmirror-label': ''
- }
- },
- {
- 'snapshot-schedule-info': {
- 'count': 5,
- 'schedule': 'daily',
- 'snapmirror-label': 'daily'
- }
- },
- {
- 'snapshot-schedule-info': {
- 'count': 10,
- 'schedule': 'weekly',
- 'snapmirror-label': ''
- }
- }
- ],
- 'vserver-name': 'hostname'
- }
- }}
- xml.translate_struct(data)
- return xml
-
- @staticmethod
- def build_snapshot_policy_info_schedules_deleted():
- ''' build xml data for snapshot-policy-info '''
- xml = netapp_utils.zapi.NaElement('xml')
- data = {'num-records': 1,
- 'attributes-list': {
- 'snapshot-policy-info': {
- 'comment': 'new comment',
- 'enabled': 'true',
- 'policy': 'ansible',
- 'snapshot-policy-schedules': [
- {
- 'snapshot-schedule-info': {
- 'schedule': 'daily',
- 'count': 5,
- 'snapmirror-label': 'daily'
- }
- }
- ],
- 'vserver-name': 'hostname'
- }
- }}
- xml.translate_struct(data)
- return xml
-
- @staticmethod
- def build_snapshot_policy_info_modified_schedule_counts():
- ''' build xml data for snapshot-policy-info '''
- xml = netapp_utils.zapi.NaElement('xml')
- data = {'num-records': 1,
- 'attributes-list': {
- 'snapshot-policy-info': {
- 'comment': 'new comment',
- 'enabled': 'true',
- 'policy': 'ansible',
- 'snapshot-policy-schedules': [
- {
- 'snapshot-schedule-info': {
- 'count': 10,
- 'schedule': 'hourly',
- 'snapmirror-label': ''
- }
- },
- {
- 'snapshot-schedule-info': {
- 'count': 50,
- 'schedule': 'daily',
- 'snapmirror-label': 'daily'
- }
- },
- {
- 'snapshot-schedule-info': {
- 'count': 100,
- 'schedule': 'weekly',
- 'snapmirror-label': ''
- }
- }
- ],
- 'vserver-name': 'hostname'
- }
- }}
- xml.translate_struct(data)
- return xml
-
-
-class TestMyModule(unittest.TestCase):
- ''' a group of related Unit Tests '''
-
- def setUp(self):
- self.mock_module_helper = patch.multiple(basic.AnsibleModule,
- exit_json=exit_json,
- fail_json=fail_json)
- self.mock_module_helper.start()
- self.addCleanup(self.mock_module_helper.stop)
- self.server = MockONTAPConnection()
- self.onbox = False
-
- def set_default_args(self):
- if self.onbox:
- hostname = '10.10.10.10'
- username = 'admin'
- password = '1234'
- name = 'ansible'
- enabled = True
- count = 100
- schedule = 'hourly'
- comment = 'new comment'
- else:
- hostname = 'hostname'
- username = 'username'
- password = 'password'
- name = 'ansible'
- enabled = True
- count = 100
- schedule = 'hourly'
- comment = 'new comment'
- return dict({
- 'hostname': hostname,
- 'username': username,
- 'password': password,
- 'name': name,
- 'enabled': enabled,
- 'count': count,
- 'schedule': schedule,
- 'comment': comment
- })
-
- def set_default_current(self):
- default_args = self.set_default_args()
- return dict({
- 'name': default_args['name'],
- 'enabled': default_args['enabled'],
- 'count': [default_args['count']],
- 'schedule': [default_args['schedule']],
- 'snapmirror_label': [''],
- 'comment': default_args['comment'],
- 'vserver': default_args['hostname']
- })
-
- def test_module_fail_when_required_args_missing(self):
- ''' required arguments are reported as errors '''
- with pytest.raises(AnsibleFailJson) as exc:
- set_module_args({})
- my_module()
- print('Info: %s' % exc.value.args[0]['msg'])
-
- def test_ensure_get_called(self):
- ''' test get_snapshot_policy() for non-existent snapshot policy'''
- set_module_args(self.set_default_args())
- my_obj = my_module()
- my_obj.server = self.server
- assert my_obj.get_snapshot_policy() is None
-
- def test_ensure_get_called_existing(self):
- ''' test get_snapshot_policy() for existing snapshot policy'''
- set_module_args(self.set_default_args())
- my_obj = my_module()
- my_obj.server = MockONTAPConnection(kind='policy')
- assert my_obj.get_snapshot_policy()
-
- @patch('ansible.modules.storage.netapp.na_ontap_snapshot_policy.NetAppOntapSnapshotPolicy.create_snapshot_policy')
- def test_successful_create(self, create_snapshot):
- ''' creating snapshot policy and testing idempotency '''
- set_module_args(self.set_default_args())
- my_obj = my_module()
- my_obj.asup_log_for_cserver = Mock(return_value=None)
- if not self.onbox:
- my_obj.server = self.server
- with pytest.raises(AnsibleExitJson) as exc:
- my_obj.apply()
- assert exc.value.args[0]['changed']
- create_snapshot.assert_called_with()
- # to reset na_helper from remembering the previous 'changed' value
- my_obj = my_module()
- my_obj.asup_log_for_cserver = Mock(return_value=None)
- if not self.onbox:
- my_obj.server = MockONTAPConnection('policy')
- with pytest.raises(AnsibleExitJson) as exc:
- my_obj.apply()
- assert not exc.value.args[0]['changed']
-
- @patch('ansible.modules.storage.netapp.na_ontap_snapshot_policy.NetAppOntapSnapshotPolicy.modify_snapshot_policy')
- def test_successful_modify_comment(self, modify_snapshot):
- ''' modifying snapshot policy comment and testing idempotency '''
- data = self.set_default_args()
- data['comment'] = 'modified comment'
- set_module_args(data)
- my_obj = my_module()
- my_obj.asup_log_for_cserver = Mock(return_value=None)
- if not self.onbox:
- my_obj.server = MockONTAPConnection('policy')
- with pytest.raises(AnsibleExitJson) as exc:
- my_obj.apply()
- assert exc.value.args[0]['changed']
- current = self.set_default_current()
- modify_snapshot.assert_called_with(current)
- # to reset na_helper from remembering the previous 'changed' value
- my_obj = my_module()
- my_obj.asup_log_for_cserver = Mock(return_value=None)
- if not self.onbox:
- my_obj.server = MockONTAPConnection('snapshot_policy_info_comment_modified')
- with pytest.raises(AnsibleExitJson) as exc:
- my_obj.apply()
- assert not exc.value.args[0]['changed']
-
- @patch('ansible.modules.storage.netapp.na_ontap_snapshot_policy.NetAppOntapSnapshotPolicy.modify_snapshot_policy')
- def test_successful_disable_policy(self, modify_snapshot):
- ''' disabling snapshot policy and testing idempotency '''
- data = self.set_default_args()
- data['enabled'] = False
- set_module_args(data)
- my_obj = my_module()
- my_obj.asup_log_for_cserver = Mock(return_value=None)
- if not self.onbox:
- my_obj.server = MockONTAPConnection('policy')
- with pytest.raises(AnsibleExitJson) as exc:
- my_obj.apply()
- assert exc.value.args[0]['changed']
- current = self.set_default_current()
- modify_snapshot.assert_called_with(current)
- # to reset na_helper from remembering the previous 'changed' value
- my_obj = my_module()
- my_obj.asup_log_for_cserver = Mock(return_value=None)
- if not self.onbox:
- my_obj.server = MockONTAPConnection('snapshot_policy_info_policy_disabled')
- with pytest.raises(AnsibleExitJson) as exc:
- my_obj.apply()
- assert not exc.value.args[0]['changed']
-
- @patch('ansible.modules.storage.netapp.na_ontap_snapshot_policy.NetAppOntapSnapshotPolicy.modify_snapshot_policy')
- def test_successful_enable_policy(self, modify_snapshot):
- ''' enabling snapshot policy and testing idempotency '''
- data = self.set_default_args()
- data['enabled'] = True
- set_module_args(data)
- my_obj = my_module()
- my_obj.asup_log_for_cserver = Mock(return_value=None)
- if not self.onbox:
- my_obj.server = MockONTAPConnection('snapshot_policy_info_policy_disabled')
- with pytest.raises(AnsibleExitJson) as exc:
- my_obj.apply()
- assert exc.value.args[0]['changed']
- current = self.set_default_current()
- current['enabled'] = False
- modify_snapshot.assert_called_with(current)
- # to reset na_helper from remembering the previous 'changed' value
- my_obj = my_module()
- my_obj.asup_log_for_cserver = Mock(return_value=None)
- if not self.onbox:
- my_obj.server = MockONTAPConnection('policy')
- with pytest.raises(AnsibleExitJson) as exc:
- my_obj.apply()
- assert not exc.value.args[0]['changed']
-
- @patch('ansible.modules.storage.netapp.na_ontap_snapshot_policy.NetAppOntapSnapshotPolicy.modify_snapshot_policy')
- def test_successful_modify_schedules_add(self, modify_snapshot):
- ''' adding snapshot policy schedules and testing idempotency '''
- data = self.set_default_args()
- data['schedule'] = ['hourly', 'daily', 'weekly']
- data['count'] = [100, 5, 10]
- data['snapmirror_label'] = ['', 'daily', '']
- set_module_args(data)
- my_obj = my_module()
- my_obj.asup_log_for_cserver = Mock(return_value=None)
- if not self.onbox:
- my_obj.server = MockONTAPConnection('policy')
- with pytest.raises(AnsibleExitJson) as exc:
- my_obj.apply()
- assert exc.value.args[0]['changed']
- current = self.set_default_current()
- modify_snapshot.assert_called_with(current)
- # to reset na_helper from remembering the previous 'changed' value
- my_obj = my_module()
- my_obj.asup_log_for_cserver = Mock(return_value=None)
- if not self.onbox:
- my_obj.server = MockONTAPConnection('snapshot_policy_info_schedules_added')
- with pytest.raises(AnsibleExitJson) as exc:
- my_obj.apply()
- assert not exc.value.args[0]['changed']
-
- @patch('ansible.modules.storage.netapp.na_ontap_snapshot_policy.NetAppOntapSnapshotPolicy.modify_snapshot_policy')
- def test_successful_modify_schedules_delete(self, modify_snapshot):
- ''' deleting snapshot policy schedules and testing idempotency '''
- data = self.set_default_args()
- data['schedule'] = ['daily']
- data['count'] = [5]
- set_module_args(data)
- my_obj = my_module()
- my_obj.asup_log_for_cserver = Mock(return_value=None)
- if not self.onbox:
- my_obj.server = MockONTAPConnection('policy')
- with pytest.raises(AnsibleExitJson) as exc:
- my_obj.apply()
- assert exc.value.args[0]['changed']
- current = self.set_default_current()
- modify_snapshot.assert_called_with(current)
- # to reset na_helper from remembering the previous 'changed' value
- my_obj = my_module()
- my_obj.asup_log_for_cserver = Mock(return_value=None)
- if not self.onbox:
- my_obj.server = MockONTAPConnection('snapshot_policy_info_schedules_deleted')
- with pytest.raises(AnsibleExitJson) as exc:
- my_obj.apply()
- assert not exc.value.args[0]['changed']
-
- @patch('ansible.modules.storage.netapp.na_ontap_snapshot_policy.NetAppOntapSnapshotPolicy.modify_snapshot_policy')
- def test_successful_modify_schedules(self, modify_snapshot):
- ''' modifying snapshot policy schedule counts and testing idempotency '''
- data = self.set_default_args()
- data['schedule'] = ['hourly', 'daily', 'weekly']
- data['count'] = [10, 50, 100]
- set_module_args(data)
- my_obj = my_module()
- my_obj.asup_log_for_cserver = Mock(return_value=None)
- if not self.onbox:
- my_obj.server = MockONTAPConnection('policy')
- with pytest.raises(AnsibleExitJson) as exc:
- my_obj.apply()
- assert exc.value.args[0]['changed']
- current = self.set_default_current()
- modify_snapshot.assert_called_with(current)
- # to reset na_helper from remembering the previous 'changed' value
- my_obj = my_module()
- my_obj.asup_log_for_cserver = Mock(return_value=None)
- if not self.onbox:
- my_obj.server = MockONTAPConnection('snapshot_policy_info_modified_schedule_counts')
- with pytest.raises(AnsibleExitJson) as exc:
- my_obj.apply()
- assert not exc.value.args[0]['changed']
-
- @patch('ansible.modules.storage.netapp.na_ontap_snapshot_policy.NetAppOntapSnapshotPolicy.delete_snapshot_policy')
- def test_successful_delete(self, delete_snapshot):
- ''' deleting snapshot policy and testing idempotency '''
- data = self.set_default_args()
- data['state'] = 'absent'
- set_module_args(data)
- my_obj = my_module()
- my_obj.asup_log_for_cserver = Mock(return_value=None)
- if not self.onbox:
- my_obj.server = MockONTAPConnection('policy')
- with pytest.raises(AnsibleExitJson) as exc:
- my_obj.apply()
- assert exc.value.args[0]['changed']
- delete_snapshot.assert_called_with()
- # to reset na_helper from remembering the previous 'changed' value
- my_obj = my_module()
- my_obj.asup_log_for_cserver = Mock(return_value=None)
- if not self.onbox:
- my_obj.server = self.server
- with pytest.raises(AnsibleExitJson) as exc:
- my_obj.apply()
- assert not exc.value.args[0]['changed']
-
- def test_valid_schedule_count(self):
- ''' validate when schedule has same number of elements '''
- data = self.set_default_args()
- data['schedule'] = ['hourly', 'daily', 'weekly', 'monthly', '5min']
- data['count'] = [1, 2, 3, 4, 5]
- set_module_args(data)
- my_obj = my_module()
- my_obj.asup_log_for_cserver = Mock(return_value=None)
- if not self.onbox:
- my_obj.server = self.server
- my_obj.create_snapshot_policy()
- create_xml = my_obj.server.xml_in
- assert data['count'][2] == int(create_xml['count3'])
- assert data['schedule'][4] == create_xml['schedule5']
-
- def test_valid_schedule_count_with_snapmirror_labels(self):
- ''' validate when schedule has same number of elements with snapmirror labels '''
- data = self.set_default_args()
- data['schedule'] = ['hourly', 'daily', 'weekly', 'monthly', '5min']
- data['count'] = [1, 2, 3, 4, 5]
- data['snapmirror_label'] = ['hourly', 'daily', 'weekly', 'monthly', '5min']
- set_module_args(data)
- my_obj = my_module()
- my_obj.asup_log_for_cserver = Mock(return_value=None)
- if not self.onbox:
- my_obj.server = self.server
- my_obj.create_snapshot_policy()
- create_xml = my_obj.server.xml_in
- assert data['count'][2] == int(create_xml['count3'])
- assert data['schedule'][4] == create_xml['schedule5']
- assert data['snapmirror_label'][3] == create_xml['snapmirror-label4']
-
- def test_invalid_params(self):
- ''' validate error when schedule does not have same number of elements '''
- data = self.set_default_args()
- data['schedule'] = ['s1', 's2']
- data['count'] = [1, 2, 3]
- set_module_args(data)
- my_obj = my_module()
- my_obj.asup_log_for_cserver = Mock(return_value=None)
- if not self.onbox:
- my_obj.server = self.server
- with pytest.raises(AnsibleFailJson) as exc:
- my_obj.create_snapshot_policy()
- msg = 'Error: A Snapshot policy must have at least 1 ' \
- 'schedule and can have up to a maximum of 5 schedules, with a count ' \
- 'representing the maximum number of Snapshot copies for each schedule'
- assert exc.value.args[0]['msg'] == msg
-
- def test_invalid_schedule_count(self):
- ''' validate error when schedule has more than 5 elements '''
- data = self.set_default_args()
- data['schedule'] = ['s1', 's2', 's3', 's4', 's5', 's6']
- data['count'] = [1, 2, 3, 4, 5, 6]
- set_module_args(data)
- my_obj = my_module()
- my_obj.asup_log_for_cserver = Mock(return_value=None)
- if not self.onbox:
- my_obj.server = self.server
- with pytest.raises(AnsibleFailJson) as exc:
- my_obj.create_snapshot_policy()
- msg = 'Error: A Snapshot policy must have at least 1 ' \
- 'schedule and can have up to a maximum of 5 schedules, with a count ' \
- 'representing the maximum number of Snapshot copies for each schedule'
- assert exc.value.args[0]['msg'] == msg
-
- def test_invalid_schedule_count_less_than_one(self):
- ''' validate error when schedule has less than 1 element '''
- data = self.set_default_args()
- data['schedule'] = []
- data['count'] = []
- set_module_args(data)
- my_obj = my_module()
- my_obj.asup_log_for_cserver = Mock(return_value=None)
- if not self.onbox:
- my_obj.server = self.server
- with pytest.raises(AnsibleFailJson) as exc:
- my_obj.create_snapshot_policy()
- msg = 'Error: A Snapshot policy must have at least 1 ' \
- 'schedule and can have up to a maximum of 5 schedules, with a count ' \
- 'representing the maximum number of Snapshot copies for each schedule'
- assert exc.value.args[0]['msg'] == msg
-
- def test_invalid_schedule_count_is_none(self):
- ''' validate error when schedule is None '''
- data = self.set_default_args()
- data['schedule'] = None
- data['count'] = None
- set_module_args(data)
- my_obj = my_module()
- my_obj.asup_log_for_cserver = Mock(return_value=None)
- if not self.onbox:
- my_obj.server = self.server
- with pytest.raises(AnsibleFailJson) as exc:
- my_obj.create_snapshot_policy()
- msg = 'Error: A Snapshot policy must have at least 1 ' \
- 'schedule and can have up to a maximum of 5 schedules, with a count ' \
- 'representing the maximum number of Snapshot copies for each schedule'
- assert exc.value.args[0]['msg'] == msg
-
- def test_invalid_schedule_count_with_snapmirror_labels(self):
- ''' validate error when schedule with snapmirror labels does not have same number of elements '''
- data = self.set_default_args()
- data['schedule'] = ['s1', 's2', 's3']
- data['count'] = [1, 2, 3]
- data['snapmirror_label'] = ['sm1', 'sm2']
- set_module_args(data)
- my_obj = my_module()
- my_obj.asup_log_for_cserver = Mock(return_value=None)
- if not self.onbox:
- my_obj.server = self.server
- with pytest.raises(AnsibleFailJson) as exc:
- my_obj.create_snapshot_policy()
- msg = 'Error: Each Snapshot Policy schedule must have an accompanying SnapMirror Label'
- assert exc.value.args[0]['msg'] == msg
-
- def test_if_all_methods_catch_exception(self):
- module_args = {}
- module_args.update(self.set_default_args())
- set_module_args(module_args)
- my_obj = my_module()
- if not self.onbox:
- my_obj.server = MockONTAPConnection('policy_fail')
- with pytest.raises(AnsibleFailJson) as exc:
- my_obj.create_snapshot_policy()
- assert 'Error creating snapshot policy ansible:' in exc.value.args[0]['msg']
- with pytest.raises(AnsibleFailJson) as exc:
- my_obj.delete_snapshot_policy()
- assert 'Error deleting snapshot policy ansible:' in exc.value.args[0]['msg']
diff --git a/test/units/modules/storage/netapp/test_na_ontap_software_update.py b/test/units/modules/storage/netapp/test_na_ontap_software_update.py
deleted file mode 100644
index cb9f39eb28..0000000000
--- a/test/units/modules/storage/netapp/test_na_ontap_software_update.py
+++ /dev/null
@@ -1,163 +0,0 @@
-# (c) 2018, NetApp, Inc
-# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-
-''' unit tests ONTAP Ansible module: na_ontap_software_update '''
-
-from __future__ import print_function
-import json
-import pytest
-
-from units.compat import unittest
-from units.compat.mock import patch, Mock
-from ansible.module_utils import basic
-from ansible.module_utils._text import to_bytes
-import ansible.module_utils.netapp as netapp_utils
-
-from ansible.modules.storage.netapp.na_ontap_software_update \
- import NetAppONTAPSoftwareUpdate as my_module # module under test
-
-if not netapp_utils.has_netapp_lib():
- pytestmark = pytest.mark.skip('skipping as missing required netapp_lib')
-
-
-def set_module_args(args):
- """prepare arguments so that they will be picked up during module creation"""
- args = json.dumps({'ANSIBLE_MODULE_ARGS': args})
- basic._ANSIBLE_ARGS = to_bytes(args) # pylint: disable=protected-access
-
-
-class AnsibleExitJson(Exception):
- """Exception class to be raised by module.exit_json and caught by the test case"""
- pass
-
-
-class AnsibleFailJson(Exception):
- """Exception class to be raised by module.fail_json and caught by the test case"""
- pass
-
-
-def exit_json(*args, **kwargs): # pylint: disable=unused-argument
- """function to patch over exit_json; package return data into an exception"""
- if 'changed' not in kwargs:
- kwargs['changed'] = False
- raise AnsibleExitJson(kwargs)
-
-
-def fail_json(*args, **kwargs): # pylint: disable=unused-argument
- """function to patch over fail_json; package return data into an exception"""
- kwargs['failed'] = True
- raise AnsibleFailJson(kwargs)
-
-
-class MockONTAPConnection(object):
- ''' mock server connection to ONTAP host '''
-
- def __init__(self, kind=None, parm1=None, parm2=None):
- ''' save arguments '''
- self.type = kind
- self.parm1 = parm1
- self.parm2 = parm2
- self.xml_in = None
- self.xml_out = None
-
- def invoke_successfully(self, xml, enable_tunneling): # pylint: disable=unused-argument
- ''' mock invoke_successfully returning xml data '''
- self.xml_in = xml
- if self.type == 'software_update':
- xml = self.build_software_update_info(self.parm1, self.parm2)
- self.xml_out = xml
- return xml
-
- def autosupport_log(self):
- ''' mock autosupport log'''
- return None
-
- @staticmethod
- def build_software_update_info(status, node):
- ''' build xml data for software-update-info '''
- xml = netapp_utils.zapi.NaElement('xml')
- data = {
- 'num-records': 1,
- 'attributes-list': {'cluster-image-info': {'node-id': node}},
- 'progress-status': status,
- 'attributes': {'ndu-progress-info': {'overall-status': 'completed',
- 'completed-node-count': '0'}},
- }
- xml.translate_struct(data)
- print(xml.to_string())
- return xml
-
-
-class TestMyModule(unittest.TestCase):
- ''' a group of related Unit Tests '''
-
- def setUp(self):
- self.mock_module_helper = patch.multiple(basic.AnsibleModule,
- exit_json=exit_json,
- fail_json=fail_json)
- self.mock_module_helper.start()
- self.addCleanup(self.mock_module_helper.stop)
- self.server = MockONTAPConnection()
- self.use_vsim = False
-
- def set_default_args(self):
- if self.use_vsim:
- hostname = '10.10.10.10'
- username = 'admin'
- password = 'admin'
- node = 'vsim1'
- package_version = 'Fattire__9.3.0'
- package_url = 'abc.com'
- else:
- hostname = 'hostname'
- username = 'username'
- password = 'password'
- node = 'abc'
- package_version = 'test'
- package_url = 'abc.com'
- return dict({
- 'hostname': hostname,
- 'username': username,
- 'password': password,
- 'nodes': node,
- 'package_version': package_version,
- 'package_url': package_url,
- 'https': 'true'
- })
-
- def test_module_fail_when_required_args_missing(self):
- ''' required arguments are reported as errors '''
- with pytest.raises(AnsibleFailJson) as exc:
- set_module_args({})
- my_module()
- print('Info: %s' % exc.value.args[0]['msg'])
-
- def test_ensure_image_get_called(self):
- ''' a more interesting test '''
- set_module_args(self.set_default_args())
- my_obj = my_module()
- my_obj.server = self.server
- cluster_image_get = my_obj.cluster_image_get()
- print('Info: test_software_update_get: %s' % repr(cluster_image_get))
- assert cluster_image_get is None
-
- def test_ensure_apply_for_update_called(self):
- ''' updating software and checking idempotency '''
- module_args = {}
- module_args.update(self.set_default_args())
- module_args.update({'package_url': 'abc.com'})
- set_module_args(module_args)
- my_obj = my_module()
- my_obj.autosupport_log = Mock(return_value=None)
- if not self.use_vsim:
- my_obj.server = self.server
- with pytest.raises(AnsibleExitJson) as exc:
- my_obj.apply()
- print('Info: test_software_update_apply: %s' % repr(exc.value))
- assert not exc.value.args[0]['changed']
- if not self.use_vsim:
- my_obj.server = MockONTAPConnection('software_update', 'async_pkg_get_phase_complete', 'abc')
- with pytest.raises(AnsibleExitJson) as exc:
- my_obj.apply()
- print('Info: test_software_update_apply: %s' % repr(exc.value))
- assert exc.value.args[0]['changed']
diff --git a/test/units/modules/storage/netapp/test_na_ontap_svm.py b/test/units/modules/storage/netapp/test_na_ontap_svm.py
deleted file mode 100644
index fe3c918eea..0000000000
--- a/test/units/modules/storage/netapp/test_na_ontap_svm.py
+++ /dev/null
@@ -1,267 +0,0 @@
-# (c) 2018, NetApp, Inc
-# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-
-''' unit test template for ONTAP Ansible module '''
-
-from __future__ import print_function
-import json
-import pytest
-
-from units.compat import unittest
-from units.compat.mock import patch, Mock
-from ansible.module_utils import basic
-from ansible.module_utils._text import to_bytes
-import ansible.module_utils.netapp as netapp_utils
-
-from ansible.modules.storage.netapp.na_ontap_svm \
- import NetAppOntapSVM as svm_module # module under test
-
-if not netapp_utils.has_netapp_lib():
- pytestmark = pytest.mark.skip('skipping as missing required netapp_lib')
-
-
-def set_module_args(args):
- """prepare arguments so that they will be picked up during module creation"""
- args = json.dumps({'ANSIBLE_MODULE_ARGS': args})
- basic._ANSIBLE_ARGS = to_bytes(args) # pylint: disable=protected-access
-
-
-class AnsibleExitJson(Exception):
- """Exception class to be raised by module.exit_json and caught by the test case"""
- pass
-
-
-class AnsibleFailJson(Exception):
- """Exception class to be raised by module.fail_json and caught by the test case"""
- pass
-
-
-def exit_json(*args, **kwargs): # pylint: disable=unused-argument
- """function to patch over exit_json; package return data into an exception"""
- if 'changed' not in kwargs:
- kwargs['changed'] = False
- raise AnsibleExitJson(kwargs)
-
-
-def fail_json(*args, **kwargs): # pylint: disable=unused-argument
- """function to patch over fail_json; package return data into an exception"""
- kwargs['failed'] = True
- raise AnsibleFailJson(kwargs)
-
-
-class MockONTAPConnection(object):
- ''' mock server connection to ONTAP host '''
-
- def __init__(self, kind=None, data=None):
- ''' save arguments '''
- self.type = kind
- self.params = data
- self.xml_in = None
- self.xml_out = None
-
- def invoke_successfully(self, xml, enable_tunneling): # pylint: disable=unused-argument
- ''' mock invoke_successfully returning xml data '''
- self.xml_in = xml
- if self.type == 'vserver':
- xml = self.build_vserver_info(self.params)
- self.xml_out = xml
- return xml
-
- @staticmethod
- def build_vserver_info(vserver):
- ''' build xml data for vserser-info '''
- xml = netapp_utils.zapi.NaElement('xml')
- data = {'num-records': 1, 'attributes-list': {'vserver-info': {
- 'vserver-name': vserver['name'],
- 'ipspace': vserver['ipspace'],
- 'root-volume': vserver['root_volume'],
- 'root-volume-aggregate': vserver['root_volume_aggregate'],
- 'language': vserver['language'],
- 'comment': vserver['comment'],
- 'snapshot-policy': vserver['snapshot_policy'],
- 'vserver-subtype': vserver['subtype'],
- 'allowed-protocols': [{'protocol': 'nfs'}, {'protocol': 'cifs'}],
- 'aggr-list': [{'aggr-name': 'aggr_1'}, {'aggr-name': 'aggr_2'}],
- }}}
- xml.translate_struct(data)
- return xml
-
-
-class TestMyModule(unittest.TestCase):
- ''' a group of related Unit Tests '''
-
- def setUp(self):
- self.mock_module_helper = patch.multiple(basic.AnsibleModule,
- exit_json=exit_json,
- fail_json=fail_json)
- self.mock_module_helper.start()
- self.addCleanup(self.mock_module_helper.stop)
- self.server = MockONTAPConnection()
- self.mock_vserver = {
- 'name': 'test_svm',
- 'root_volume': 'ansible_vol',
- 'root_volume_aggregate': 'ansible_aggr',
- 'aggr_list': 'aggr_1,aggr_2',
- 'ipspace': 'ansible_ipspace',
- 'subtype': 'default',
- 'language': 'c.utf_8',
- 'snapshot_policy': 'old_snapshot_policy',
- 'comment': 'this is a comment'
- }
-
- def mock_args(self):
- return {
- 'name': self.mock_vserver['name'],
- 'root_volume': self.mock_vserver['root_volume'],
- 'root_volume_aggregate': self.mock_vserver['root_volume_aggregate'],
- 'aggr_list': self.mock_vserver['aggr_list'],
- 'ipspace': self.mock_vserver['ipspace'],
- 'comment': self.mock_vserver['comment'],
- 'subtype': 'default',
- 'hostname': 'test',
- 'username': 'test_user',
- 'password': 'test_pass!'
- }
-
- def get_vserver_mock_object(self, kind=None, data=None):
- """
- Helper method to return an na_ontap_volume object
- :param kind: passes this param to MockONTAPConnection()
- :param data: passes this param to MockONTAPConnection()
- :return: na_ontap_volume object
- """
- vserver_obj = svm_module()
- vserver_obj.asup_log_for_cserver = Mock(return_value=None)
- vserver_obj.cluster = Mock()
- vserver_obj.cluster.invoke_successfully = Mock()
- if kind is None:
- vserver_obj.server = MockONTAPConnection()
- else:
- if data is None:
- vserver_obj.server = MockONTAPConnection(kind='vserver', data=self.mock_vserver)
- else:
- vserver_obj.server = MockONTAPConnection(kind='vserver', data=data)
- return vserver_obj
-
- def test_module_fail_when_required_args_missing(self):
- ''' required arguments are reported as errors '''
- with pytest.raises(AnsibleFailJson) as exc:
- set_module_args({})
- svm_module()
- print('Info: %s' % exc.value.args[0]['msg'])
-
- def test_get_nonexistent_vserver(self):
- ''' test if get_vserver() throws an error if vserver is not specified '''
- data = self.mock_args()
- set_module_args(data)
- result = self.get_vserver_mock_object().get_vserver()
- assert result is None
-
- def test_create_error_missing_name(self):
- ''' Test if create throws an error if name is not specified'''
- data = self.mock_args()
- del data['name']
- set_module_args(data)
- with pytest.raises(AnsibleFailJson) as exc:
- self.get_vserver_mock_object('vserver').create_vserver()
- msg = 'missing required arguments: name'
- assert exc.value.args[0]['msg'] == msg
-
- @patch('ansible.modules.storage.netapp.na_ontap_svm.NetAppOntapSVM.create_vserver')
- def test_successful_create(self, create_vserver):
- '''Test successful create'''
- data = self.mock_args()
- set_module_args(data)
- with pytest.raises(AnsibleExitJson) as exc:
- self.get_vserver_mock_object().apply()
- assert exc.value.args[0]['changed']
- create_vserver.assert_called_with()
-
- @patch('ansible.modules.storage.netapp.na_ontap_svm.NetAppOntapSVM.create_vserver')
- def test_create_idempotency(self, create_vserver):
- '''Test successful create'''
- data = self.mock_args()
- set_module_args(data)
- with pytest.raises(AnsibleExitJson) as exc:
- self.get_vserver_mock_object('vserver').apply()
- assert not exc.value.args[0]['changed']
- create_vserver.assert_not_called()
-
- def test_successful_delete(self):
- '''Test successful delete'''
- data = self.mock_args()
- data['state'] = 'absent'
- set_module_args(data)
- with pytest.raises(AnsibleExitJson) as exc:
- self.get_vserver_mock_object('vserver').apply()
- assert exc.value.args[0]['changed']
-
- @patch('ansible.modules.storage.netapp.na_ontap_svm.NetAppOntapSVM.delete_vserver')
- def test_delete_idempotency(self, delete_vserver):
- '''Test delete idempotency'''
- data = self.mock_args()
- data['state'] = 'absent'
- set_module_args(data)
- with pytest.raises(AnsibleExitJson) as exc:
- self.get_vserver_mock_object().apply()
- assert not exc.value.args[0]['changed']
- delete_vserver.assert_not_called()
-
- @patch('ansible.modules.storage.netapp.na_ontap_svm.NetAppOntapSVM.get_vserver')
- def test_successful_rename(self, get_vserver):
- '''Test successful rename'''
- data = self.mock_args()
- data['from_name'] = 'test_svm'
- data['name'] = 'test_new_svm'
- set_module_args(data)
- current = {
- 'name': 'test_svm',
- 'root_volume': 'ansible_vol',
- 'root_volume_aggregate': 'ansible_aggr',
- 'ipspace': 'ansible_ipspace',
- 'subtype': 'default',
- 'language': 'c.utf_8'
- }
- get_vserver.side_effect = [
- None,
- current
- ]
- with pytest.raises(AnsibleExitJson) as exc:
- self.get_vserver_mock_object().apply()
- assert exc.value.args[0]['changed']
-
- def test_successful_modify_language(self):
- '''Test successful modify language'''
- data = self.mock_args()
- data['language'] = 'c'
- set_module_args(data)
- with pytest.raises(AnsibleExitJson) as exc:
- self.get_vserver_mock_object('vserver').apply()
- assert exc.value.args[0]['changed']
-
- def test_successful_modify_snapshot_policy(self):
- '''Test successful modify language'''
- data = self.mock_args()
- data['snapshot_policy'] = 'new_snapshot_policy'
- set_module_args(data)
- with pytest.raises(AnsibleExitJson) as exc:
- self.get_vserver_mock_object('vserver').apply()
- assert exc.value.args[0]['changed']
-
- def test_successful_modify_allowed_protocols(self):
- '''Test successful modify allowed protocols'''
- data = self.mock_args()
- data['allowed_protocols'] = 'protocol_1,protocol_2'
- set_module_args(data)
- with pytest.raises(AnsibleExitJson) as exc:
- self.get_vserver_mock_object('vserver').apply()
- assert exc.value.args[0]['changed']
-
- def test_successful_modify_aggr_list(self):
- '''Test successful modify aggr-list'''
- data = self.mock_args()
- data['aggr_list'] = 'aggr_3,aggr_4'
- set_module_args(data)
- with pytest.raises(AnsibleExitJson) as exc:
- self.get_vserver_mock_object('vserver').apply()
- assert exc.value.args[0]['changed']
diff --git a/test/units/modules/storage/netapp/test_na_ontap_ucadapter.py b/test/units/modules/storage/netapp/test_na_ontap_ucadapter.py
deleted file mode 100644
index 3a0ca710ec..0000000000
--- a/test/units/modules/storage/netapp/test_na_ontap_ucadapter.py
+++ /dev/null
@@ -1,159 +0,0 @@
-# (c) 2018, NetApp, Inc
-# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-
-''' unit tests ONTAP Ansible module: na_ontap_ucadapter '''
-
-from __future__ import print_function
-import json
-import pytest
-
-from units.compat import unittest
-from units.compat.mock import patch, Mock
-from ansible.module_utils import basic
-from ansible.module_utils._text import to_bytes
-import ansible.module_utils.netapp as netapp_utils
-
-from ansible.modules.storage.netapp.na_ontap_ucadapter \
- import NetAppOntapadapter as my_module # module under test
-
-if not netapp_utils.has_netapp_lib():
- pytestmark = pytest.mark.skip('skipping as missing required netapp_lib')
-
-
-def set_module_args(args):
- """prepare arguments so that they will be picked up during module creation"""
- args = json.dumps({'ANSIBLE_MODULE_ARGS': args})
- basic._ANSIBLE_ARGS = to_bytes(args) # pylint: disable=protected-access
-
-
-class AnsibleExitJson(Exception):
- """Exception class to be raised by module.exit_json and caught by the test case"""
- pass
-
-
-class AnsibleFailJson(Exception):
- """Exception class to be raised by module.fail_json and caught by the test case"""
- pass
-
-
-def exit_json(*args, **kwargs): # pylint: disable=unused-argument
- """function to patch over exit_json; package return data into an exception"""
- if 'changed' not in kwargs:
- kwargs['changed'] = False
- raise AnsibleExitJson(kwargs)
-
-
-def fail_json(*args, **kwargs): # pylint: disable=unused-argument
- """function to patch over fail_json; package return data into an exception"""
- kwargs['failed'] = True
- raise AnsibleFailJson(kwargs)
-
-
-class MockONTAPConnection(object):
- ''' mock server connection to ONTAP host '''
-
- def __init__(self, kind=None, parm1=None):
- ''' save arguments '''
- self.type = kind
- self.parm1 = parm1
- self.xml_in = None
- self.xml_out = None
-
- def invoke_successfully(self, xml, enable_tunneling): # pylint: disable=unused-argument
- ''' mock invoke_successfully returning xml data '''
- self.xml_in = xml
- if self.type == 'ucadapter':
- xml = self.build_ucadapter_info(self.parm1)
- self.xml_out = xml
- return xml
-
- def autosupport_log(self):
- ''' mock autosupport log'''
- return None
-
- @staticmethod
- def build_ucadapter_info(status):
- ''' build xml data for ucadapter_info '''
- xml = netapp_utils.zapi.NaElement('xml')
- data = {'attributes': {'uc-adapter-info': {
- 'mode': 'fc',
- 'pending-mode': 'abc',
- 'type': 'target',
- 'pending-type': 'intitiator',
- 'status': status,
- }}}
- xml.translate_struct(data)
- print(xml.to_string())
- return xml
-
-
-class TestMyModule(unittest.TestCase):
- ''' a group of related Unit Tests '''
-
- def setUp(self):
- self.mock_module_helper = patch.multiple(basic.AnsibleModule,
- exit_json=exit_json,
- fail_json=fail_json)
- self.mock_module_helper.start()
- self.addCleanup(self.mock_module_helper.stop)
- self.server = MockONTAPConnection()
- self.use_vsim = False
-
- def set_default_args(self):
- if self.use_vsim:
- hostname = '10.192.39.6'
- username = 'admin'
- password = 'netapp123'
- node_name = 'bumblebee-reloaded-01'
- adapter_name = '0f'
- else:
- hostname = 'hostname'
- username = 'username'
- password = 'password'
- node_name = 'abc'
- adapter_name = '0f'
- return dict({
- 'hostname': hostname,
- 'username': username,
- 'password': password,
- 'node_name': node_name,
- 'adapter_name': adapter_name,
- })
-
- def test_module_fail_when_required_args_missing(self):
- ''' required arguments are reported as errors '''
- with pytest.raises(AnsibleFailJson) as exc:
- set_module_args({})
- my_module()
- print('Info: %s' % exc.value.args[0]['msg'])
-
- def test_ensure_ucadapter_get_called(self):
- ''' fetching ucadapter details '''
- set_module_args(self.set_default_args())
- my_obj = my_module()
- my_obj.server = self.server
- get_adapter = my_obj.get_adapter()
- print('Info: test_ucadapter_get: %s' % repr(get_adapter))
- assert get_adapter is None
-
- def test_ensure_apply_for_ucadapter_called(self):
- ''' configuring ucadaptor and checking idempotency '''
- module_args = {}
- module_args.update(self.set_default_args())
- module_args.update({'type': 'target'})
- module_args.update({'mode': 'initiator'})
- set_module_args(module_args)
- my_obj = my_module()
- my_obj.autosupport_log = Mock(return_value=None)
- if not self.use_vsim:
- my_obj.server = self.server
- with pytest.raises(AnsibleExitJson) as exc:
- my_obj.apply()
- print('Info: test_ucadapter_apply: %s' % repr(exc.value))
- assert not exc.value.args[0]['changed']
- if not self.use_vsim:
- my_obj.server = MockONTAPConnection('ucadapter', 'up')
- with pytest.raises(AnsibleExitJson) as exc:
- my_obj.apply()
- print('Info: test_ucadapter_apply: %s' % repr(exc.value))
- assert exc.value.args[0]['changed']
diff --git a/test/units/modules/storage/netapp/test_na_ontap_unix_group.py b/test/units/modules/storage/netapp/test_na_ontap_unix_group.py
deleted file mode 100644
index e34df9baa4..0000000000
--- a/test/units/modules/storage/netapp/test_na_ontap_unix_group.py
+++ /dev/null
@@ -1,288 +0,0 @@
-# (c) 2018, NetApp, Inc
-# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-
-''' unit test template for ONTAP Ansible module '''
-
-from __future__ import print_function
-import json
-import pytest
-
-from units.compat import unittest
-from units.compat.mock import patch, Mock
-from ansible.module_utils import basic
-from ansible.module_utils._text import to_bytes
-import ansible.module_utils.netapp as netapp_utils
-
-from ansible.modules.storage.netapp.na_ontap_unix_group \
- import NetAppOntapUnixGroup as group_module # module under test
-
-if not netapp_utils.has_netapp_lib():
- pytestmark = pytest.mark.skip('skipping as missing required netapp_lib')
-
-
-def set_module_args(args):
- """prepare arguments so that they will be picked up during module creation"""
- args = json.dumps({'ANSIBLE_MODULE_ARGS': args})
- basic._ANSIBLE_ARGS = to_bytes(args) # pylint: disable=protected-access
-
-
-class AnsibleExitJson(Exception):
- """Exception class to be raised by module.exit_json and caught by the test case"""
- pass
-
-
-class AnsibleFailJson(Exception):
- """Exception class to be raised by module.fail_json and caught by the test case"""
- pass
-
-
-def exit_json(*args, **kwargs): # pylint: disable=unused-argument
- """function to patch over exit_json; package return data into an exception"""
- if 'changed' not in kwargs:
- kwargs['changed'] = False
- raise AnsibleExitJson(kwargs)
-
-
-def fail_json(*args, **kwargs): # pylint: disable=unused-argument
- """function to patch over fail_json; package return data into an exception"""
- kwargs['failed'] = True
- raise AnsibleFailJson(kwargs)
-
-
-class MockONTAPConnection(object):
- ''' mock server connection to ONTAP host '''
-
- def __init__(self, kind=None, data=None):
- ''' save arguments '''
- self.kind = kind
- self.params = data
- self.xml_in = None
- self.xml_out = None
-
- def invoke_successfully(self, xml, enable_tunneling): # pylint: disable=unused-argument
- ''' mock invoke_successfully returning xml data '''
- self.xml_in = xml
- if self.kind == 'group':
- xml = self.build_group_info(self.params)
- elif self.kind == 'group-fail':
- raise netapp_utils.zapi.NaApiError(code='TEST', message="This exception is from the unit test")
- self.xml_out = xml
- return xml
-
- @staticmethod
- def build_group_info(data):
- ''' build xml data for vserser-info '''
- xml = netapp_utils.zapi.NaElement('xml')
- attributes = \
- {'attributes-list': {'unix-group-info': {'group-name': data['name'],
- 'group-id': data['id']}},
- 'num-records': 1}
- xml.translate_struct(attributes)
- return xml
-
-
-class TestMyModule(unittest.TestCase):
- ''' a group of related Unit Tests '''
-
- def setUp(self):
- self.mock_module_helper = patch.multiple(basic.AnsibleModule,
- exit_json=exit_json,
- fail_json=fail_json)
- self.mock_module_helper.start()
- self.addCleanup(self.mock_module_helper.stop)
- self.server = MockONTAPConnection()
- self.mock_group = {
- 'name': 'test',
- 'id': '11',
- 'vserver': 'something',
- }
-
- def mock_args(self):
- return {
- 'name': self.mock_group['name'],
- 'id': self.mock_group['id'],
- 'vserver': self.mock_group['vserver'],
- 'hostname': 'test',
- 'username': 'test_user',
- 'password': 'test_pass!'
- }
-
- def get_group_mock_object(self, kind=None, data=None):
- """
- Helper method to return an na_ontap_unix_group object
- :param kind: passes this param to MockONTAPConnection()
- :return: na_ontap_unix_group object
- """
- obj = group_module()
- obj.autosupport_log = Mock(return_value=None)
- if data is None:
- data = self.mock_group
- obj.server = MockONTAPConnection(kind=kind, data=data)
- return obj
-
- def test_module_fail_when_required_args_missing(self):
- ''' required arguments are reported as errors '''
- with pytest.raises(AnsibleFailJson) as exc:
- set_module_args({})
- group_module()
-
- def test_get_nonexistent_group(self):
- ''' Test if get_unix_group returns None for non-existent group '''
- set_module_args(self.mock_args())
- result = self.get_group_mock_object().get_unix_group()
- assert result is None
-
- def test_get_existing_group(self):
- ''' Test if get_unix_group returns details for existing group '''
- set_module_args(self.mock_args())
- result = self.get_group_mock_object('group').get_unix_group()
- assert result['name'] == self.mock_group['name']
-
- def test_get_xml(self):
- set_module_args(self.mock_args())
- obj = self.get_group_mock_object('group')
- result = obj.get_unix_group()
- assert obj.server.xml_in['query']
- assert obj.server.xml_in['query']['unix-group-info']
- group_info = obj.server.xml_in['query']['unix-group-info']
- assert group_info['group-name'] == self.mock_group['name']
- assert group_info['vserver'] == self.mock_group['vserver']
-
- def test_create_error_missing_params(self):
- data = self.mock_args()
- del data['id']
- set_module_args(data)
- with pytest.raises(AnsibleFailJson) as exc:
- self.get_group_mock_object('group').create_unix_group()
- assert 'Error: Missing a required parameter for create: (id)' == exc.value.args[0]['msg']
-
- @patch('ansible.modules.storage.netapp.na_ontap_unix_group.NetAppOntapUnixGroup.create_unix_group')
- def test_create_called(self, create_group):
- set_module_args(self.mock_args())
- with pytest.raises(AnsibleExitJson) as exc:
- self.get_group_mock_object().apply()
- assert exc.value.args[0]['changed']
- create_group.assert_called_with()
-
- def test_create_xml(self):
- '''Test create ZAPI element'''
- set_module_args(self.mock_args())
- create = self.get_group_mock_object()
- with pytest.raises(AnsibleExitJson) as exc:
- create.apply()
- mock_key = {
- 'group-name': 'name',
- 'group-id': 'id',
- }
- for key in ['group-name', 'group-id']:
- assert create.server.xml_in[key] == self.mock_group[mock_key[key]]
-
- @patch('ansible.modules.storage.netapp.na_ontap_unix_group.NetAppOntapUnixGroup.modify_unix_group')
- @patch('ansible.modules.storage.netapp.na_ontap_unix_group.NetAppOntapUnixGroup.delete_unix_group')
- def test_delete_called(self, delete_group, modify_group):
- ''' Test delete existing group '''
- data = self.mock_args()
- data['state'] = 'absent'
- set_module_args(data)
- with pytest.raises(AnsibleExitJson) as exc:
- self.get_group_mock_object('group').apply()
- assert exc.value.args[0]['changed']
- delete_group.assert_called_with()
- assert modify_group.call_count == 0
-
- @patch('ansible.modules.storage.netapp.na_ontap_unix_group.NetAppOntapUnixGroup.get_unix_group')
- @patch('ansible.modules.storage.netapp.na_ontap_unix_group.NetAppOntapUnixGroup.modify_unix_group')
- def test_modify_called(self, modify_group, get_group):
- ''' Test modify group group_id '''
- data = self.mock_args()
- data['id'] = 20
- set_module_args(data)
- get_group.return_value = {'id': 10}
- obj = self.get_group_mock_object('group')
- with pytest.raises(AnsibleExitJson) as exc:
- obj.apply()
- get_group.assert_called_with()
- modify_group.assert_called_with({'id': 20})
-
- def test_modify_only_id(self):
- ''' Test modify group id '''
- set_module_args(self.mock_args())
- modify = self.get_group_mock_object('group')
- modify.modify_unix_group({'id': 123})
- print(modify.server.xml_in.to_string())
- assert modify.server.xml_in['group-id'] == '123'
- with pytest.raises(KeyError):
- modify.server.xml_in['id']
-
- def test_modify_xml(self):
- ''' Test modify group full_name '''
- set_module_args(self.mock_args())
- modify = self.get_group_mock_object('group')
- modify.modify_unix_group({'id': 25})
- assert modify.server.xml_in['group-name'] == self.mock_group['name']
- assert modify.server.xml_in['group-id'] == '25'
-
- @patch('ansible.modules.storage.netapp.na_ontap_unix_group.NetAppOntapUnixGroup.create_unix_group')
- @patch('ansible.modules.storage.netapp.na_ontap_unix_group.NetAppOntapUnixGroup.delete_unix_group')
- @patch('ansible.modules.storage.netapp.na_ontap_unix_group.NetAppOntapUnixGroup.modify_unix_group')
- def test_do_nothing(self, modify, delete, create):
- ''' changed is False and none of the opetaion methods are called'''
- data = self.mock_args()
- data['state'] = 'absent'
- set_module_args(data)
- obj = self.get_group_mock_object()
- with pytest.raises(AnsibleExitJson) as exc:
- obj.apply()
- create.assert_not_called()
- delete.assert_not_called()
- modify.assert_not_called()
-
- def test_get_exception(self):
- set_module_args(self.mock_args())
- with pytest.raises(AnsibleFailJson) as exc:
- self.get_group_mock_object('group-fail').get_unix_group()
- assert 'Error getting UNIX group' in exc.value.args[0]['msg']
-
- def test_create_exception(self):
- set_module_args(self.mock_args())
- with pytest.raises(AnsibleFailJson) as exc:
- self.get_group_mock_object('group-fail').create_unix_group()
- assert 'Error creating UNIX group' in exc.value.args[0]['msg']
-
- def test_modify_exception(self):
- set_module_args(self.mock_args())
- with pytest.raises(AnsibleFailJson) as exc:
- self.get_group_mock_object('group-fail').modify_unix_group({'id': '123'})
- assert 'Error modifying UNIX group' in exc.value.args[0]['msg']
-
- def test_delete_exception(self):
- set_module_args(self.mock_args())
- with pytest.raises(AnsibleFailJson) as exc:
- self.get_group_mock_object('group-fail').delete_unix_group()
- assert 'Error removing UNIX group' in exc.value.args[0]['msg']
-
- @patch('ansible.modules.storage.netapp.na_ontap_unix_group.NetAppOntapUnixGroup.get_unix_group')
- def test_add_user_exception(self, get_unix_group):
- data = self.mock_args()
- data['users'] = 'test_user'
- set_module_args(data)
- get_unix_group.side_effect = [
- {'users': []}
- ]
- with pytest.raises(AnsibleFailJson) as exc:
- self.get_group_mock_object('group-fail').modify_users_in_group()
- print(exc.value.args[0]['msg'])
- assert 'Error adding user' in exc.value.args[0]['msg']
-
- @patch('ansible.modules.storage.netapp.na_ontap_unix_group.NetAppOntapUnixGroup.get_unix_group')
- def test_delete_user_exception(self, get_unix_group):
- data = self.mock_args()
- data['users'] = ''
- set_module_args(data)
- get_unix_group.side_effect = [
- {'users': ['test_user']}
- ]
- with pytest.raises(AnsibleFailJson) as exc:
- self.get_group_mock_object('group-fail').modify_users_in_group()
- print(exc.value.args[0]['msg'])
- assert 'Error deleting user' in exc.value.args[0]['msg']
diff --git a/test/units/modules/storage/netapp/test_na_ontap_unix_user.py b/test/units/modules/storage/netapp/test_na_ontap_unix_user.py
deleted file mode 100644
index 2051db2daf..0000000000
--- a/test/units/modules/storage/netapp/test_na_ontap_unix_user.py
+++ /dev/null
@@ -1,282 +0,0 @@
-# (c) 2018, NetApp, Inc
-# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-
-''' unit test template for ONTAP Ansible module '''
-
-from __future__ import print_function
-import json
-import pytest
-
-from units.compat import unittest
-from units.compat.mock import patch, Mock
-from ansible.module_utils import basic
-from ansible.module_utils._text import to_bytes
-import ansible.module_utils.netapp as netapp_utils
-
-from ansible.modules.storage.netapp.na_ontap_unix_user \
- import NetAppOntapUnixUser as user_module # module under test
-
-if not netapp_utils.has_netapp_lib():
- pytestmark = pytest.mark.skip('skipping as missing required netapp_lib')
-
-
-def set_module_args(args):
- """prepare arguments so that they will be picked up during module creation"""
- args = json.dumps({'ANSIBLE_MODULE_ARGS': args})
- basic._ANSIBLE_ARGS = to_bytes(args) # pylint: disable=protected-access
-
-
-class AnsibleExitJson(Exception):
- """Exception class to be raised by module.exit_json and caught by the test case"""
- pass
-
-
-class AnsibleFailJson(Exception):
- """Exception class to be raised by module.fail_json and caught by the test case"""
- pass
-
-
-def exit_json(*args, **kwargs): # pylint: disable=unused-argument
- """function to patch over exit_json; package return data into an exception"""
- if 'changed' not in kwargs:
- kwargs['changed'] = False
- raise AnsibleExitJson(kwargs)
-
-
-def fail_json(*args, **kwargs): # pylint: disable=unused-argument
- """function to patch over fail_json; package return data into an exception"""
- kwargs['failed'] = True
- raise AnsibleFailJson(kwargs)
-
-
-class MockONTAPConnection(object):
- ''' mock server connection to ONTAP host '''
-
- def __init__(self, kind=None, data=None):
- ''' save arguments '''
- self.kind = kind
- self.params = data
- self.xml_in = None
- self.xml_out = None
-
- def invoke_successfully(self, xml, enable_tunneling): # pylint: disable=unused-argument
- ''' mock invoke_successfully returning xml data '''
- self.xml_in = xml
- if self.kind == 'user':
- xml = self.build_user_info(self.params)
- elif self.kind == 'user-fail':
- raise netapp_utils.zapi.NaApiError(code='TEST', message="This exception is from the unit test")
- self.xml_out = xml
- return xml
-
- @staticmethod
- def build_user_info(data):
- ''' build xml data for vserser-info '''
- xml = netapp_utils.zapi.NaElement('xml')
- attributes = \
- {'attributes-list': {'unix-user-info': {'user-id': data['id'],
- 'group-id': data['group_id'], 'full-name': data['full_name']}},
- 'num-records': 1}
- xml.translate_struct(attributes)
- return xml
-
-
-class TestMyModule(unittest.TestCase):
- ''' a group of related Unit Tests '''
-
- def setUp(self):
- self.mock_module_helper = patch.multiple(basic.AnsibleModule,
- exit_json=exit_json,
- fail_json=fail_json)
- self.mock_module_helper.start()
- self.addCleanup(self.mock_module_helper.stop)
- self.server = MockONTAPConnection()
- self.mock_user = {
- 'name': 'test',
- 'id': '11',
- 'group_id': '12',
- 'vserver': 'something',
- 'full_name': 'Test User'
- }
-
- def mock_args(self):
- return {
- 'name': self.mock_user['name'],
- 'group_id': self.mock_user['group_id'],
- 'id': self.mock_user['id'],
- 'vserver': self.mock_user['vserver'],
- 'full_name': self.mock_user['full_name'],
- 'hostname': 'test',
- 'username': 'test_user',
- 'password': 'test_pass!'
- }
-
- def get_user_mock_object(self, kind=None, data=None):
- """
- Helper method to return an na_ontap_unix_user object
- :param kind: passes this param to MockONTAPConnection()
- :return: na_ontap_unix_user object
- """
- obj = user_module()
- obj.autosupport_log = Mock(return_value=None)
- if data is None:
- data = self.mock_user
- obj.server = MockONTAPConnection(kind=kind, data=data)
- return obj
-
- def test_module_fail_when_required_args_missing(self):
- ''' required arguments are reported as errors '''
- with pytest.raises(AnsibleFailJson) as exc:
- set_module_args({})
- user_module()
-
- def test_get_nonexistent_user(self):
- ''' Test if get_unix_user returns None for non-existent user '''
- set_module_args(self.mock_args())
- result = self.get_user_mock_object().get_unix_user()
- assert result is None
-
- def test_get_existing_user(self):
- ''' Test if get_unix_user returns details for existing user '''
- set_module_args(self.mock_args())
- result = self.get_user_mock_object('user').get_unix_user()
- assert result['full_name'] == self.mock_user['full_name']
-
- def test_get_xml(self):
- set_module_args(self.mock_args())
- obj = self.get_user_mock_object('user')
- result = obj.get_unix_user()
- assert obj.server.xml_in['query']
- assert obj.server.xml_in['query']['unix-user-info']
- user_info = obj.server.xml_in['query']['unix-user-info']
- assert user_info['user-name'] == self.mock_user['name']
- assert user_info['vserver'] == self.mock_user['vserver']
-
- def test_create_error_missing_params(self):
- data = self.mock_args()
- del data['group_id']
- set_module_args(data)
- with pytest.raises(AnsibleFailJson) as exc:
- self.get_user_mock_object('user').create_unix_user()
- assert 'Error: Missing one or more required parameters for create: (group_id, id)' == exc.value.args[0]['msg']
-
- @patch('ansible.modules.storage.netapp.na_ontap_unix_user.NetAppOntapUnixUser.create_unix_user')
- def test_create_called(self, create_user):
- set_module_args(self.mock_args())
- with pytest.raises(AnsibleExitJson) as exc:
- self.get_user_mock_object().apply()
- assert exc.value.args[0]['changed']
- create_user.assert_called_with()
-
- def test_create_xml(self):
- '''Test create ZAPI element'''
- set_module_args(self.mock_args())
- create = self.get_user_mock_object()
- with pytest.raises(AnsibleExitJson) as exc:
- create.apply()
- mock_key = {
- 'user-name': 'name',
- 'group-id': 'group_id',
- 'user-id': 'id',
- 'full-name': 'full_name'
- }
- for key in ['user-name', 'user-id', 'group-id', 'full-name']:
- assert create.server.xml_in[key] == self.mock_user[mock_key[key]]
-
- def test_create_wihtout_full_name(self):
- '''Test create ZAPI element'''
- data = self.mock_args()
- del data['full_name']
- set_module_args(data)
- create = self.get_user_mock_object()
- with pytest.raises(AnsibleExitJson) as exc:
- create.apply()
- with pytest.raises(KeyError):
- create.server.xml_in['full-name']
-
- @patch('ansible.modules.storage.netapp.na_ontap_unix_user.NetAppOntapUnixUser.modify_unix_user')
- @patch('ansible.modules.storage.netapp.na_ontap_unix_user.NetAppOntapUnixUser.delete_unix_user')
- def test_delete_called(self, delete_user, modify_user):
- ''' Test delete existing user '''
- data = self.mock_args()
- data['state'] = 'absent'
- set_module_args(data)
- with pytest.raises(AnsibleExitJson) as exc:
- self.get_user_mock_object('user').apply()
- assert exc.value.args[0]['changed']
- delete_user.assert_called_with()
- assert modify_user.call_count == 0
-
- @patch('ansible.modules.storage.netapp.na_ontap_unix_user.NetAppOntapUnixUser.get_unix_user')
- @patch('ansible.modules.storage.netapp.na_ontap_unix_user.NetAppOntapUnixUser.modify_unix_user')
- def test_modify_called(self, modify_user, get_user):
- ''' Test modify user group_id '''
- data = self.mock_args()
- data['group_id'] = 20
- set_module_args(data)
- get_user.return_value = {'group_id': 10}
- obj = self.get_user_mock_object('user')
- with pytest.raises(AnsibleExitJson) as exc:
- obj.apply()
- get_user.assert_called_with()
- modify_user.assert_called_with({'group_id': 20})
-
- def test_modify_only_id(self):
- ''' Test modify user id '''
- set_module_args(self.mock_args())
- modify = self.get_user_mock_object('user')
- modify.modify_unix_user({'id': 123})
- assert modify.server.xml_in['user-id'] == '123'
- with pytest.raises(KeyError):
- modify.server.xml_in['group-id']
- with pytest.raises(KeyError):
- modify.server.xml_in['full-name']
-
- def test_modify_xml(self):
- ''' Test modify user full_name '''
- set_module_args(self.mock_args())
- modify = self.get_user_mock_object('user')
- modify.modify_unix_user({'full_name': 'New Name',
- 'group_id': '25'})
- assert modify.server.xml_in['user-name'] == self.mock_user['name']
- assert modify.server.xml_in['full-name'] == 'New Name'
- assert modify.server.xml_in['group-id'] == '25'
-
- @patch('ansible.modules.storage.netapp.na_ontap_unix_user.NetAppOntapUnixUser.create_unix_user')
- @patch('ansible.modules.storage.netapp.na_ontap_unix_user.NetAppOntapUnixUser.delete_unix_user')
- @patch('ansible.modules.storage.netapp.na_ontap_unix_user.NetAppOntapUnixUser.modify_unix_user')
- def test_do_nothing(self, modify, delete, create):
- ''' changed is False and none of the opetaion methods are called'''
- data = self.mock_args()
- data['state'] = 'absent'
- set_module_args(data)
- obj = self.get_user_mock_object()
- with pytest.raises(AnsibleExitJson) as exc:
- obj.apply()
- create.assert_not_called()
- delete.assert_not_called()
- modify.assert_not_called()
-
- def test_get_exception(self):
- set_module_args(self.mock_args())
- with pytest.raises(AnsibleFailJson) as exc:
- self.get_user_mock_object('user-fail').get_unix_user()
- assert 'Error getting UNIX user' in exc.value.args[0]['msg']
-
- def test_create_exception(self):
- set_module_args(self.mock_args())
- with pytest.raises(AnsibleFailJson) as exc:
- self.get_user_mock_object('user-fail').create_unix_user()
- assert 'Error creating UNIX user' in exc.value.args[0]['msg']
-
- def test_modify_exception(self):
- set_module_args(self.mock_args())
- with pytest.raises(AnsibleFailJson) as exc:
- self.get_user_mock_object('user-fail').modify_unix_user({'id': '123'})
- assert 'Error modifying UNIX user' in exc.value.args[0]['msg']
-
- def test_delete_exception(self):
- set_module_args(self.mock_args())
- with pytest.raises(AnsibleFailJson) as exc:
- self.get_user_mock_object('user-fail').delete_unix_user()
- assert 'Error removing UNIX user' in exc.value.args[0]['msg']
diff --git a/test/units/modules/storage/netapp/test_na_ontap_user.py b/test/units/modules/storage/netapp/test_na_ontap_user.py
deleted file mode 100644
index f4b0475dd8..0000000000
--- a/test/units/modules/storage/netapp/test_na_ontap_user.py
+++ /dev/null
@@ -1,320 +0,0 @@
-# (c) 2018, NetApp, Inc
-# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-
-''' unit tests ONTAP Ansible module: na_ontap_user '''
-
-from __future__ import print_function
-import json
-import pytest
-
-from units.compat import unittest
-from units.compat.mock import patch, Mock
-from ansible.module_utils import basic
-from ansible.module_utils._text import to_bytes
-import ansible.module_utils.netapp as netapp_utils
-
-from ansible.modules.storage.netapp.na_ontap_user \
- import NetAppOntapUser as my_module # module under test
-
-if not netapp_utils.has_netapp_lib():
- pytestmark = pytest.mark.skip('skipping as missing required netapp_lib')
-
-
-def set_module_args(args):
- """prepare arguments so that they will be picked up during module creation"""
- args = json.dumps({'ANSIBLE_MODULE_ARGS': args})
- basic._ANSIBLE_ARGS = to_bytes(args) # pylint: disable=protected-access
-
-
-class AnsibleExitJson(Exception):
- """Exception class to be raised by module.exit_json and caught by the test case"""
- pass
-
-
-class AnsibleFailJson(Exception):
- """Exception class to be raised by module.fail_json and caught by the test case"""
- pass
-
-
-def exit_json(*args, **kwargs): # pylint: disable=unused-argument
- """function to patch over exit_json; package return data into an exception"""
- if 'changed' not in kwargs:
- kwargs['changed'] = False
- raise AnsibleExitJson(kwargs)
-
-
-def fail_json(*args, **kwargs): # pylint: disable=unused-argument
- """function to patch over fail_json; package return data into an exception"""
- kwargs['failed'] = True
- raise AnsibleFailJson(kwargs)
-
-
-class MockONTAPConnection(object):
- ''' mock server connection to ONTAP host '''
-
- def __init__(self, kind=None, parm1=None, parm2=None):
- ''' save arguments '''
- self.type = kind
- self.parm1 = parm1
- self.parm2 = parm2
- self.xml_in = None
- self.xml_out = None
-
- def invoke_successfully(self, xml, enable_tunneling): # pylint: disable=unused-argument
- ''' mock invoke_successfully returning xml data '''
- self.xml_in = xml
- if self.type == 'user':
- xml = self.build_user_info(self.parm1, self.parm2)
- elif self.type == 'user_fail':
- raise netapp_utils.zapi.NaApiError(code='TEST', message="This exception is from the unit test")
- self.xml_out = xml
- return xml
-
- @staticmethod
- def set_vserver(vserver):
- '''mock set vserver'''
- pass
-
- @staticmethod
- def build_user_info(locked, role_name):
- ''' build xml data for user-info '''
- xml = netapp_utils.zapi.NaElement('xml')
- data = {'num-records': 1,
- 'attributes-list': {'security-login-account-info': {'is-locked': locked, 'role-name': role_name}}}
-
- xml.translate_struct(data)
- print(xml.to_string())
- return xml
-
-
-class TestMyModule(unittest.TestCase):
- ''' a group of related Unit Tests '''
-
- def setUp(self):
- self.mock_module_helper = patch.multiple(basic.AnsibleModule,
- exit_json=exit_json,
- fail_json=fail_json)
- self.mock_module_helper.start()
- self.addCleanup(self.mock_module_helper.stop)
- self.server = MockONTAPConnection()
- self.onbox = False
-
- def set_default_args(self):
- if self.onbox:
- hostname = '10.10.10.10'
- username = 'username'
- password = 'password'
- user_name = 'test'
- vserver = 'ansible_test'
- application = 'console'
- authentication_method = 'password'
- else:
- hostname = 'hostname'
- username = 'username'
- password = 'password'
- user_name = 'name'
- vserver = 'vserver'
- application = 'console'
- authentication_method = 'password'
-
- return dict({
- 'hostname': hostname,
- 'username': username,
- 'password': password,
- 'name': user_name,
- 'vserver': vserver,
- 'applications': application,
- 'authentication_method': authentication_method
- })
-
- def test_module_fail_when_required_args_missing(self):
- ''' required arguments are reported as errors '''
- with pytest.raises(AnsibleFailJson) as exc:
- set_module_args({})
- my_module()
- print('Info: %s' % exc.value.args[0]['msg'])
-
- def test_ensure_user_get_called(self):
- ''' a more interesting test '''
- module_args = {}
- module_args.update(self.set_default_args())
- module_args.update({'role_name': 'test'})
- set_module_args(module_args)
- my_obj = my_module()
- my_obj.server = self.server
- user_info = my_obj.get_user()
- print('Info: test_user_get: %s' % repr(user_info))
- assert user_info is None
-
- def test_ensure_user_apply_called(self):
- ''' creating user and checking idempotency '''
- module_args = {}
- module_args.update(self.set_default_args())
- module_args.update({'name': 'create'})
- module_args.update({'role_name': 'test'})
- set_module_args(module_args)
- my_obj = my_module()
- if not self.onbox:
- my_obj.server = self.server
- with pytest.raises(AnsibleExitJson) as exc:
- my_obj.apply()
- print('Info: test_user_apply: %s' % repr(exc.value))
- assert exc.value.args[0]['changed']
- if not self.onbox:
- my_obj.server = MockONTAPConnection('user', 'false')
- with pytest.raises(AnsibleExitJson) as exc:
- my_obj.apply()
- print('Info: test_user_apply: %s' % repr(exc.value))
- assert exc.value.args[0]['changed']
-
- def test_ensure_user_apply_for_delete_called(self):
- ''' deleting user and checking idempotency '''
- module_args = {}
- module_args.update(self.set_default_args())
- module_args.update({'name': 'create'})
- module_args.update({'role_name': 'test'})
- set_module_args(module_args)
- my_obj = my_module()
- if not self.onbox:
- my_obj.server = MockONTAPConnection('user', 'false', 'test')
- with pytest.raises(AnsibleExitJson) as exc:
- my_obj.apply()
- print('Info: test_user_apply: %s' % repr(exc.value))
- assert not exc.value.args[0]['changed']
- module_args.update({'state': 'absent'})
- set_module_args(module_args)
- my_obj = my_module()
- if not self.onbox:
- my_obj.server = MockONTAPConnection('user', 'false', 'test')
- with pytest.raises(AnsibleExitJson) as exc:
- my_obj.apply()
- print('Info: test_user_delete: %s' % repr(exc.value))
- assert exc.value.args[0]['changed']
-
- def test_ensure_user_lock_called(self):
- ''' changing user_lock to True and checking idempotency'''
- module_args = {}
- module_args.update(self.set_default_args())
- module_args.update({'name': 'create'})
- module_args.update({'role_name': 'test'})
- module_args.update({'lock_user': 'false'})
- set_module_args(module_args)
- my_obj = my_module()
- if not self.onbox:
- my_obj.server = MockONTAPConnection('user', 'false', 'test')
- with pytest.raises(AnsibleExitJson) as exc:
- my_obj.apply()
- print('Info: test_user_apply: %s' % repr(exc.value))
- assert not exc.value.args[0]['changed']
- module_args.update({'lock_user': 'true'})
- set_module_args(module_args)
- my_obj = my_module()
- if not self.onbox:
- my_obj.server = MockONTAPConnection('user', 'false')
- with pytest.raises(AnsibleExitJson) as exc:
- my_obj.apply()
- print('Info: test_user_lock: %s' % repr(exc.value))
- assert exc.value.args[0]['changed']
-
- def test_ensure_user_unlock_called(self):
- ''' changing user_lock to False and checking idempotency'''
- module_args = {}
- module_args.update(self.set_default_args())
- module_args.update({'name': 'create'})
- module_args.update({'role_name': 'test'})
- module_args.update({'lock_user': 'false'})
- set_module_args(module_args)
- my_obj = my_module()
- if not self.onbox:
- my_obj.server = MockONTAPConnection('user', 'false', 'test')
- with pytest.raises(AnsibleExitJson) as exc:
- my_obj.apply()
- print('Info: test_user_apply: %s' % repr(exc.value))
- assert not exc.value.args[0]['changed']
- module_args.update({'lock_user': 'false'})
- set_module_args(module_args)
- my_obj = my_module()
- if not self.onbox:
- my_obj.server = MockONTAPConnection('user', 'true', 'test')
- with pytest.raises(AnsibleExitJson) as exc:
- my_obj.apply()
- print('Info: test_user_unlock: %s' % repr(exc.value))
- assert exc.value.args[0]['changed']
-
- def test_ensure_user_set_password_called(self):
- ''' set password '''
- module_args = {}
- module_args.update(self.set_default_args())
- module_args.update({'name': 'create'})
- module_args.update({'role_name': 'test'})
- module_args.update({'set_password': '123456'})
- set_module_args(module_args)
- my_obj = my_module()
- if not self.onbox:
- my_obj.server = MockONTAPConnection('user', 'true')
- with pytest.raises(AnsibleExitJson) as exc:
- my_obj.apply()
- print('Info: test_user_apply: %s' % repr(exc.value))
- assert exc.value.args[0]['changed']
-
- def test_ensure_user_role_update_called(self):
- ''' set password '''
- module_args = {}
- module_args.update(self.set_default_args())
- module_args.update({'name': 'create'})
- module_args.update({'role_name': 'test123'})
- module_args.update({'set_password': '123456'})
- set_module_args(module_args)
- my_obj = my_module()
- if not self.onbox:
- my_obj.server = MockONTAPConnection('user', 'true')
- with pytest.raises(AnsibleExitJson) as exc:
- my_obj.apply()
- print('Info: test_user_apply: %s' % repr(exc.value))
- assert exc.value.args[0]['changed']
-
- def test_ensure_user_role_update_additional_application_called(self):
- ''' set password '''
- module_args = {}
- module_args.update(self.set_default_args())
- module_args.update({'name': 'create'})
- module_args.update({'role_name': 'test123'})
- module_args.update({'application': 'http'})
- module_args.update({'set_password': '123456'})
- set_module_args(module_args)
- my_obj = my_module()
- if not self.onbox:
- my_obj.server = MockONTAPConnection('user', 'true')
- with pytest.raises(AnsibleExitJson) as exc:
- my_obj.apply()
- print('Info: test_user_apply: %s' % repr(exc.value))
- assert exc.value.args[0]['changed']
-
- def test_if_all_methods_catch_exception(self):
- data = self.set_default_args()
- data.update({'role_name': 'test'})
- set_module_args(data)
- my_obj = my_module()
- if not self.onbox:
- my_obj.server = MockONTAPConnection('user_fail')
- with pytest.raises(AnsibleFailJson) as exc:
- my_obj.get_user()
- assert 'Error getting user ' in exc.value.args[0]['msg']
- with pytest.raises(AnsibleFailJson) as exc:
- my_obj.create_user(data['applications'])
- assert 'Error creating user ' in exc.value.args[0]['msg']
- with pytest.raises(AnsibleFailJson) as exc:
- my_obj.lock_given_user()
- assert 'Error locking user ' in exc.value.args[0]['msg']
- with pytest.raises(AnsibleFailJson) as exc:
- my_obj.unlock_given_user()
- assert 'Error unlocking user ' in exc.value.args[0]['msg']
- with pytest.raises(AnsibleFailJson) as exc:
- my_obj.delete_user(data['applications'])
- assert 'Error removing user ' in exc.value.args[0]['msg']
- with pytest.raises(AnsibleFailJson) as exc:
- my_obj.change_password()
- assert 'Error setting password for user ' in exc.value.args[0]['msg']
- with pytest.raises(AnsibleFailJson) as exc:
- my_obj.modify_user(data['applications'])
- assert 'Error modifying user ' in exc.value.args[0]['msg']
diff --git a/test/units/modules/storage/netapp/test_na_ontap_user_role.py b/test/units/modules/storage/netapp/test_na_ontap_user_role.py
deleted file mode 100644
index bb3dfd615e..0000000000
--- a/test/units/modules/storage/netapp/test_na_ontap_user_role.py
+++ /dev/null
@@ -1,238 +0,0 @@
-# (c) 2018, NetApp, Inc
-# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-
-''' unit test template for ONTAP Ansible module '''
-
-from __future__ import print_function
-import json
-import pytest
-
-from units.compat import unittest
-from units.compat.mock import patch, Mock
-from ansible.module_utils import basic
-from ansible.module_utils._text import to_bytes
-import ansible.module_utils.netapp as netapp_utils
-
-from ansible.modules.storage.netapp.na_ontap_user_role \
- import NetAppOntapUserRole as role_module # module under test
-
-if not netapp_utils.has_netapp_lib():
- pytestmark = pytest.mark.skip('skipping as missing required netapp_lib')
-
-
-def set_module_args(args):
- """prepare arguments so that they will be picked up during module creation"""
- args = json.dumps({'ANSIBLE_MODULE_ARGS': args})
- basic._ANSIBLE_ARGS = to_bytes(args) # pylint: disable=protected-access
-
-
-class AnsibleExitJson(Exception):
- """Exception class to be raised by module.exit_json and caught by the test case"""
- pass
-
-
-class AnsibleFailJson(Exception):
- """Exception class to be raised by module.fail_json and caught by the test case"""
- pass
-
-
-def exit_json(*args, **kwargs): # pylint: disable=unused-argument
- """function to patch over exit_json; package return data into an exception"""
- if 'changed' not in kwargs:
- kwargs['changed'] = False
- raise AnsibleExitJson(kwargs)
-
-
-def fail_json(*args, **kwargs): # pylint: disable=unused-argument
- """function to patch over fail_json; package return data into an exception"""
- kwargs['failed'] = True
- raise AnsibleFailJson(kwargs)
-
-
-class MockONTAPConnection(object):
- ''' mock server connection to ONTAP host '''
-
- def __init__(self, kind=None, data=None):
- ''' save arguments '''
- self.kind = kind
- self.params = data
- self.xml_in = None
- self.xml_out = None
-
- def invoke_successfully(self, xml, enable_tunneling): # pylint: disable=unused-argument
- ''' mock invoke_successfully returning xml data '''
- self.xml_in = xml
- if self.kind == 'role':
- xml = self.build_role_info(self.params)
- if self.kind == 'error':
- error = netapp_utils.zapi.NaApiError('test', 'error')
- raise error
- self.xml_out = xml
- return xml
-
- @staticmethod
- def build_role_info(vol_details):
- ''' build xml data for role-attributes '''
- xml = netapp_utils.zapi.NaElement('xml')
- attributes = {
- 'num-records': 1,
- 'attributes-list': {
- 'security-login-role-info': {
- 'access-level': 'all',
- 'command-directory-name': 'volume',
- 'role-name': 'testrole',
- 'role-query': 'show',
- 'vserver': 'ansible'
- }
- }
- }
- xml.translate_struct(attributes)
- return xml
-
-
-class TestMyModule(unittest.TestCase):
- ''' a group of related Unit Tests '''
-
- def setUp(self):
- self.mock_module_helper = patch.multiple(basic.AnsibleModule,
- exit_json=exit_json,
- fail_json=fail_json)
- self.mock_module_helper.start()
- self.addCleanup(self.mock_module_helper.stop)
- self.mock_role = {
- 'name': 'testrole',
- 'access_level': 'all',
- 'command_directory_name': 'volume',
- 'vserver': 'ansible'
- }
-
- def mock_args(self):
- return {
- 'name': self.mock_role['name'],
- 'vserver': self.mock_role['vserver'],
- 'command_directory_name': self.mock_role['command_directory_name'],
- 'hostname': 'test',
- 'username': 'test_user',
- 'password': 'test_pass!',
- 'https': 'False'
- }
-
- def get_role_mock_object(self, kind=None):
- """
- Helper method to return an na_ontap_user_role object
- :param kind: passes this param to MockONTAPConnection()
- :return: na_ontap_user_role object
- """
- role_obj = role_module()
- role_obj.asup_log_for_cserver = Mock(return_value=None)
- role_obj.cluster = Mock()
- role_obj.cluster.invoke_successfully = Mock()
- if kind is None:
- role_obj.server = MockONTAPConnection()
- else:
- role_obj.server = MockONTAPConnection(kind=kind, data=self.mock_role)
- return role_obj
-
- def test_module_fail_when_required_args_missing(self):
- ''' required arguments are reported as errors '''
- with pytest.raises(AnsibleFailJson) as exc:
- set_module_args({})
- role_module()
- print('Info: %s' % exc.value.args[0]['msg'])
-
- def test_get_nonexistent_policy(self):
- ''' Test if get_role returns None for non-existent role '''
- set_module_args(self.mock_args())
- result = self.get_role_mock_object().get_role()
- assert result is None
-
- def test_get_existing_role(self):
- ''' Test if get_role returns details for existing role '''
- set_module_args(self.mock_args())
- result = self.get_role_mock_object('role').get_role()
- assert result['name'] == self.mock_role['name']
-
- def test_successful_create(self):
- ''' Test successful create '''
- data = self.mock_args()
- set_module_args(data)
- with pytest.raises(AnsibleExitJson) as exc:
- self.get_role_mock_object().apply()
- assert exc.value.args[0]['changed']
-
- def test_create_idempotency(self):
- ''' Test create idempotency '''
- data = self.mock_args()
- data['query'] = 'show'
- set_module_args(data)
- obj = self.get_role_mock_object('role')
- with pytest.raises(AnsibleExitJson) as exc:
- obj.apply()
- assert not exc.value.args[0]['changed']
-
- @patch('ansible.modules.storage.netapp.na_ontap_user_role.NetAppOntapUserRole.get_role')
- def test_create_error(self, get_role):
- ''' Test create error '''
- set_module_args(self.mock_args())
- get_role.side_effect = [
- None
- ]
- with pytest.raises(AnsibleFailJson) as exc:
- self.get_role_mock_object('error').apply()
- assert exc.value.args[0]['msg'] == 'Error creating role testrole: NetApp API failed. Reason - test:error'
-
- @patch('ansible.modules.storage.netapp.na_ontap_user_role.NetAppOntapUserRole.get_role')
- def test_successful_modify(self, get_role):
- ''' Test successful modify '''
- data = self.mock_args()
- data['query'] = 'show'
- set_module_args(data)
- current = self.mock_role
- current['query'] = 'show-space'
- get_role.side_effect = [
- current
- ]
- obj = self.get_role_mock_object()
- with pytest.raises(AnsibleExitJson) as exc:
- obj.apply()
- assert exc.value.args[0]['changed']
-
- @patch('ansible.modules.storage.netapp.na_ontap_user_role.NetAppOntapUserRole.get_role')
- def test_modify_idempotency(self, get_role):
- ''' Test modify idempotency '''
- data = self.mock_args()
- data['query'] = 'show'
- set_module_args(data)
- current = self.mock_role
- current['query'] = 'show'
- get_role.side_effect = [
- current
- ]
- obj = self.get_role_mock_object()
- with pytest.raises(AnsibleExitJson) as exc:
- obj.apply()
- assert not exc.value.args[0]['changed']
-
- @patch('ansible.modules.storage.netapp.na_ontap_user_role.NetAppOntapUserRole.get_role')
- def test_modify_error(self, get_role):
- ''' Test modify error '''
- data = self.mock_args()
- data['query'] = 'show'
- set_module_args(data)
- current = self.mock_role
- current['query'] = 'show-space'
- get_role.side_effect = [
- current
- ]
- with pytest.raises(AnsibleFailJson) as exc:
- self.get_role_mock_object('error').apply()
- assert exc.value.args[0]['msg'] == 'Error modifying role testrole: NetApp API failed. Reason - test:error'
-
- def test_successful_delete(self):
- ''' Test delete existing role '''
- data = self.mock_args()
- data['state'] = 'absent'
- set_module_args(data)
- with pytest.raises(AnsibleExitJson) as exc:
- self.get_role_mock_object('role').apply()
- assert exc.value.args[0]['changed']
diff --git a/test/units/modules/storage/netapp/test_na_ontap_volume.py b/test/units/modules/storage/netapp/test_na_ontap_volume.py
deleted file mode 100644
index 207a9a36ea..0000000000
--- a/test/units/modules/storage/netapp/test_na_ontap_volume.py
+++ /dev/null
@@ -1,970 +0,0 @@
-# (c) 2018, NetApp, Inc
-# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-
-''' unit test template for ONTAP Ansible module '''
-
-from __future__ import print_function
-import json
-import pytest
-
-from units.compat import unittest
-from units.compat.mock import patch, Mock
-from ansible.module_utils import basic
-from ansible.module_utils._text import to_bytes
-import ansible.module_utils.netapp as netapp_utils
-
-from ansible.modules.storage.netapp.na_ontap_volume \
- import NetAppOntapVolume as vol_module # module under test
-
-if not netapp_utils.has_netapp_lib():
- pytestmark = pytest.mark.skip('skipping as missing required netapp_lib')
-
-
-def set_module_args(args):
- """prepare arguments so that they will be picked up during module creation"""
- args = json.dumps({'ANSIBLE_MODULE_ARGS': args})
- basic._ANSIBLE_ARGS = to_bytes(args) # pylint: disable=protected-access
-
-
-class AnsibleExitJson(Exception):
- """Exception class to be raised by module.exit_json and caught by the test case"""
- pass
-
-
-class AnsibleFailJson(Exception):
- """Exception class to be raised by module.fail_json and caught by the test case"""
- pass
-
-
-def exit_json(*args, **kwargs): # pylint: disable=unused-argument
- """function to patch over exit_json; package return data into an exception"""
- if 'changed' not in kwargs:
- kwargs['changed'] = False
- raise AnsibleExitJson(kwargs)
-
-
-def fail_json(*args, **kwargs): # pylint: disable=unused-argument
- """function to patch over fail_json; package return data into an exception"""
- kwargs['failed'] = True
- raise AnsibleFailJson(kwargs)
-
-
-class MockONTAPConnection(object):
- ''' mock server connection to ONTAP host '''
-
- def __init__(self, kind=None, data=None, job_error=None):
- ''' save arguments '''
- self.kind = kind
- self.params = data
- self.xml_in = None
- self.xml_out = None
- self.job_error = job_error
-
- def invoke_successfully(self, xml, enable_tunneling): # pylint: disable=unused-argument
- ''' mock invoke_successfully returning xml data '''
- self.xml_in = xml
- if self.kind == 'volume':
- xml = self.build_volume_info(self.params)
- elif self.kind == 'job_info':
- xml = self.build_job_info(self.job_error)
- elif self.kind == 'error_modify':
- xml = self.build_modify_error()
- elif self.kind == 'failure_modify_async':
- xml = self.build_failure_modify_async()
- elif self.kind == 'success_modify_async':
- xml = self.build_success_modify_async()
- elif self.kind == 'zapi_error':
- error = netapp_utils.zapi.NaApiError('test', 'error')
- raise error
- self.xml_out = xml
- return xml
-
- @staticmethod
- def build_volume_info(vol_details):
- ''' build xml data for volume-attributes '''
- xml = netapp_utils.zapi.NaElement('xml')
- attributes = {
- 'num-records': 1,
- 'attributes-list': {
- 'volume-attributes': {
- 'volume-id-attributes': {
- 'containing-aggregate-name': vol_details['aggregate'],
- 'junction-path': vol_details['junction_path'],
- 'style-extended': 'flexvol',
- 'comment': vol_details['comment']
- },
- 'volume-language-attributes': {
- 'language-code': 'en'
- },
- 'volume-export-attributes': {
- 'policy': 'default'
- },
- 'volume-performance-attributes': {
- 'is-atime-update-enabled': 'true'
- },
- 'volume-state-attributes': {
- 'state': "online",
- 'is-nvfail-enabled': 'true'
- },
- 'volume-space-attributes': {
- 'space-guarantee': 'none',
- 'size': vol_details['size'],
- 'percentage-snapshot-reserve': vol_details['percent_snapshot_space'],
- 'space-slo': 'thick'
- },
- 'volume-snapshot-attributes': {
- 'snapshot-policy': vol_details['snapshot_policy']
- },
- 'volume-comp-aggr-attributes': {
- 'tiering-policy': 'snapshot-only'
- },
- 'volume-security-attributes': {
- 'volume-security-unix-attributes': {
- 'permissions': vol_details['unix_permissions']
- }
- },
- 'volume-vserver-dr-protection-attributes': {
- 'vserver-dr-protection': vol_details['vserver_dr_protection'],
- },
- 'volume-qos-attributes': {
- 'policy-group-name': vol_details['qos_policy_group'],
- 'adaptive-policy-group-name': vol_details['qos_adaptive_policy_group']
- }
- }
- }
- }
- xml.translate_struct(attributes)
- return xml
-
- @staticmethod
- def build_flex_group_info(vol_details):
- ''' build xml data for flexGroup volume-attributes '''
- xml = netapp_utils.zapi.NaElement('xml')
- attributes = {
- 'num-records': 1,
- 'attributes-list': {
- 'volume-attributes': {
- 'volume-id-attributes': {
- 'aggr-list': vol_details['aggregate'],
- 'junction-path': vol_details['junction_path'],
- 'style-extended': 'flexgroup',
- 'comment': vol_details['comment']
- },
- 'volume-language-attributes': {
- 'language-code': 'en'
- },
- 'volume-export-attributes': {
- 'policy': 'default'
- },
- 'volume-performance-attributes': {
- 'is-atime-update-enabled': 'true'
- },
- 'volume-state-attributes': {
- 'state': "online"
- },
- 'volume-space-attributes': {
- 'space-guarantee': 'none',
- 'size': vol_details['size']
- },
- 'volume-snapshot-attributes': {
- 'snapshot-policy': vol_details['snapshot_policy']
- },
- 'volume-security-attributes': {
- 'volume-security-unix-attributes': {
- 'permissions': vol_details['unix_permissions']
- }
- }
- }
- }
- }
- xml.translate_struct(attributes)
- return xml
-
- @staticmethod
- def build_job_info(error):
- ''' build xml data for a job '''
- xml = netapp_utils.zapi.NaElement('xml')
- attributes = netapp_utils.zapi.NaElement('attributes')
- if error is None:
- state = 'success'
- elif error == 'time_out':
- state = 'running'
- elif error == 'failure':
- state = 'failure'
- else:
- state = 'other'
- attributes.add_node_with_children('job-info', **{
- 'job-state': state,
- 'job-progress': 'dummy',
- 'job-completion': error,
- })
- xml.add_child_elem(attributes)
- xml.add_new_child('result-status', 'in_progress')
- xml.add_new_child('result-jobid', '1234')
- return xml
-
- @staticmethod
- def build_modify_error():
- ''' build xml data for modify error '''
- xml = netapp_utils.zapi.NaElement('xml')
- attributes = netapp_utils.zapi.NaElement('failure-list')
- info_list_obj = netapp_utils.zapi.NaElement('volume-modify-iter-info')
- info_list_obj.add_new_child('error-message', 'modify error message')
- attributes.add_child_elem(info_list_obj)
- xml.add_child_elem(attributes)
- return xml
-
- @staticmethod
- def build_success_modify_async():
- ''' build xml data for success modify async '''
- xml = netapp_utils.zapi.NaElement('xml')
- attributes = netapp_utils.zapi.NaElement('success-list')
- info_list_obj = netapp_utils.zapi.NaElement('volume-modify-iter-async-info')
- info_list_obj.add_new_child('status', 'in_progress')
- info_list_obj.add_new_child('jobid', '1234')
- attributes.add_child_elem(info_list_obj)
- xml.add_child_elem(attributes)
- return xml
-
- @staticmethod
- def build_failure_modify_async():
- ''' build xml data for failure modify async '''
- xml = netapp_utils.zapi.NaElement('xml')
- attributes = netapp_utils.zapi.NaElement('failure-list')
- info_list_obj = netapp_utils.zapi.NaElement('volume-modify-iter-async-info')
- info_list_obj.add_new_child('status', 'failed')
- info_list_obj.add_new_child('jobid', '1234')
- info_list_obj.add_new_child('error-message', 'modify error message')
- attributes.add_child_elem(info_list_obj)
- xml.add_child_elem(attributes)
- return xml
-
-
-class TestMyModule(unittest.TestCase):
- ''' a group of related Unit Tests '''
-
- def setUp(self):
- self.mock_module_helper = patch.multiple(basic.AnsibleModule,
- exit_json=exit_json,
- fail_json=fail_json)
- self.mock_module_helper.start()
- self.addCleanup(self.mock_module_helper.stop)
- self.mock_vol = {
- 'name': 'test_vol',
- 'aggregate': 'test_aggr',
- 'junction_path': '/test',
- 'vserver': 'test_vserver',
- 'size': 20971520,
- 'unix_permissions': '755',
- 'snapshot_policy': 'default',
- 'qos_policy_group': 'performance',
- 'qos_adaptive_policy_group': 'performance',
- 'percent_snapshot_space': 60,
- 'language': 'en',
- 'vserver_dr_protection': 'unprotected',
- 'comment': 'test comment'
- }
-
- def mock_args(self, tag=None):
- args = {
- 'hostname': 'test',
- 'username': 'test_user',
- 'password': 'test_pass!',
- 'name': self.mock_vol['name'],
- 'vserver': self.mock_vol['vserver'],
- 'space_guarantee': 'none',
- 'policy': 'default',
- 'language': self.mock_vol['language'],
- 'is_online': True,
- 'unix_permissions': '---rwxr-xr-x',
- 'snapshot_policy': 'default',
- 'qos_policy_group': 'performance',
- 'qos_adaptive_policy_group': 'performance',
- 'size': 20,
- 'size_unit': 'mb',
- 'junction_path': '/test',
- 'percent_snapshot_space': 60,
- 'type': 'type',
- 'nvfail_enabled': True,
- 'space_slo': 'thick',
- 'comment': self.mock_vol['comment']
- }
- if tag is None:
- args['aggregate_name'] = self.mock_vol['aggregate']
- return args
-
- elif tag == 'flexGroup_manual':
- args['aggr_list'] = 'aggr_0,aggr_1'
- args['aggr_list_multiplier'] = 2
- return args
-
- elif tag == 'flexGroup_auto':
- args['auto_provision_as'] = 'flexgroup'
- return args
-
- def get_volume_mock_object(self, kind=None, job_error=None):
- """
- Helper method to return an na_ontap_volume object
- :param kind: passes this param to MockONTAPConnection().
- :param job_error: error message when getting job status.
- :return: na_ontap_volume object
- """
- vol_obj = vol_module()
- vol_obj.ems_log_event = Mock(return_value=None)
- vol_obj.cluster = Mock()
- vol_obj.cluster.invoke_successfully = Mock()
- vol_obj.volume_style = None
- if kind is None:
- vol_obj.server = MockONTAPConnection()
- elif kind == 'volume':
- vol_obj.server = MockONTAPConnection(kind='volume', data=self.mock_vol)
- elif kind == 'job_info':
- vol_obj.server = MockONTAPConnection(kind='job_info', data=self.mock_vol, job_error=job_error)
- elif kind == 'error_modify':
- vol_obj.server = MockONTAPConnection(kind='error_modify', data=self.mock_vol)
- elif kind == 'failure_modify_async':
- vol_obj.server = MockONTAPConnection(kind='failure_modify_async', data=self.mock_vol)
- elif kind == 'success_modify_async':
- vol_obj.server = MockONTAPConnection(kind='success_modify_async', data=self.mock_vol)
- elif kind == 'zapi_error':
- vol_obj.server = MockONTAPConnection(kind='zapi_error', data=self.mock_vol)
- return vol_obj
-
- def test_module_fail_when_required_args_missing(self):
- ''' required arguments are reported as errors '''
- with pytest.raises(AnsibleFailJson) as exc:
- set_module_args({})
- vol_module()
- print('Info: %s' % exc.value.args[0]['msg'])
-
- def test_get_nonexistent_volume(self):
- ''' Test if get_volume returns None for non-existent volume '''
- set_module_args(self.mock_args())
- result = self.get_volume_mock_object().get_volume()
- assert result is None
-
- def test_get_existing_volume(self):
- ''' Test if get_volume returns details for existing volume '''
- set_module_args(self.mock_args())
- result = self.get_volume_mock_object('volume').get_volume()
- assert result['name'] == self.mock_vol['name']
- assert result['size'] == self.mock_vol['size']
-
- def test_create_error_missing_param(self):
- ''' Test if create throws an error if aggregate_name is not specified'''
- data = self.mock_args()
- del data['aggregate_name']
- set_module_args(data)
- with pytest.raises(AnsibleFailJson) as exc:
- self.get_volume_mock_object('volume').create_volume()
- msg = 'Error provisioning volume test_vol: aggregate_name is required'
- assert exc.value.args[0]['msg'] == msg
-
- def test_successful_create(self):
- ''' Test successful create '''
- data = self.mock_args()
- data['size'] = 20
- data['encrypt'] = True
- set_module_args(data)
- with pytest.raises(AnsibleExitJson) as exc:
- self.get_volume_mock_object().apply()
- assert exc.value.args[0]['changed']
-
- def test_create_idempotency(self):
- ''' Test create idempotency '''
- set_module_args(self.mock_args())
- obj = self.get_volume_mock_object('volume')
- with pytest.raises(AnsibleExitJson) as exc:
- obj.apply()
- assert not exc.value.args[0]['changed']
-
- def test_successful_delete(self):
- ''' Test delete existing volume '''
- data = self.mock_args()
- data['state'] = 'absent'
- set_module_args(data)
- with pytest.raises(AnsibleExitJson) as exc:
- self.get_volume_mock_object('volume').apply()
- assert exc.value.args[0]['changed']
-
- def test_delete_idempotency(self):
- ''' Test delete idempotency '''
- data = self.mock_args()
- data['state'] = 'absent'
- set_module_args(data)
- with pytest.raises(AnsibleExitJson) as exc:
- self.get_volume_mock_object().apply()
- assert not exc.value.args[0]['changed']
-
- def test_successful_modify_size(self):
- ''' Test successful modify size '''
- data = self.mock_args()
- data['size'] = 200
- set_module_args(data)
- with pytest.raises(AnsibleExitJson) as exc:
- self.get_volume_mock_object('volume').apply()
- assert exc.value.args[0]['changed']
-
- def test_modify_idempotency(self):
- ''' Test modify idempotency '''
- data = self.mock_args()
- set_module_args(data)
- with pytest.raises(AnsibleExitJson) as exc:
- self.get_volume_mock_object('volume').apply()
- assert not exc.value.args[0]['changed']
-
- def test_modify_error(self):
- ''' Test modify idempotency '''
- data = self.mock_args()
- set_module_args(data)
- with pytest.raises(AnsibleFailJson) as exc:
- self.get_volume_mock_object('error_modify').volume_modify_attributes(dict())
- assert exc.value.args[0]['msg'] == 'Error modifying volume test_vol: modify error message'
-
- def test_mount_volume(self):
- ''' Test mount volume '''
- data = self.mock_args()
- data['junction_path'] = "/test123"
- set_module_args(data)
- with pytest.raises(AnsibleExitJson) as exc:
- self.get_volume_mock_object('volume').apply()
- assert exc.value.args[0]['changed']
-
- def test_unmount_volume(self):
- ''' Test unmount volume '''
- data = self.mock_args()
- data['junction_path'] = ""
- set_module_args(data)
- with pytest.raises(AnsibleExitJson) as exc:
- self.get_volume_mock_object('volume').apply()
- assert exc.value.args[0]['changed']
-
- def test_successful_modify_space(self):
- ''' Test successful modify space '''
- data = self.mock_args()
- data['space_guarantee'] = 'volume'
- set_module_args(data)
- with pytest.raises(AnsibleExitJson) as exc:
- self.get_volume_mock_object('volume').apply()
- assert exc.value.args[0]['changed']
-
- def test_successful_modify_unix_permissions(self):
- ''' Test successful modify unix_permissions '''
- data = self.mock_args()
- data['unix_permissions'] = '---rw-r-xr-x'
- set_module_args(data)
- with pytest.raises(AnsibleExitJson) as exc:
- self.get_volume_mock_object('volume').apply()
- assert exc.value.args[0]['changed']
-
- def test_successful_modify_snapshot_policy(self):
- ''' Test successful modify snapshot_policy '''
- data = self.mock_args()
- data['snapshot_policy'] = 'default-1weekly'
- set_module_args(data)
- with pytest.raises(AnsibleExitJson) as exc:
- self.get_volume_mock_object('volume').apply()
- assert exc.value.args[0]['changed']
-
- def test_successful_modify_percent_snapshot_space(self):
- ''' Test successful modify percent_snapshot_space '''
- data = self.mock_args()
- data['percent_snapshot_space'] = '90'
- set_module_args(data)
- with pytest.raises(AnsibleExitJson) as exc:
- self.get_volume_mock_object('volume').apply()
- assert exc.value.args[0]['changed']
-
- def test_successful_modify_qos_policy_group(self):
- ''' Test successful modify qos_policy_group '''
- data = self.mock_args()
- data['qos_policy_group'] = 'extreme'
- set_module_args(data)
- with pytest.raises(AnsibleExitJson) as exc:
- self.get_volume_mock_object('volume').apply()
- assert exc.value.args[0]['changed']
-
- def test_successful_modify_qos_adaptive_policy_group(self):
- ''' Test successful modify qos_adaptive_policy_group '''
- data = self.mock_args()
- data['qos_adaptive_policy_group'] = 'extreme'
- set_module_args(data)
- with pytest.raises(AnsibleExitJson) as exc:
- self.get_volume_mock_object('volume').apply()
- assert exc.value.args[0]['changed']
-
- def test_successful_move(self):
- ''' Test successful modify aggregate '''
- data = self.mock_args()
- data['aggregate_name'] = 'different_aggr'
- set_module_args(data)
- with pytest.raises(AnsibleExitJson) as exc:
- self.get_volume_mock_object('volume').apply()
- assert exc.value.args[0]['changed']
-
- @patch('ansible.modules.storage.netapp.na_ontap_volume.NetAppOntapVolume.get_volume')
- def test_successful_rename(self, get_volume):
- ''' Test successful rename volume '''
- data = self.mock_args()
- data['from_name'] = self.mock_vol['name']
- data['name'] = 'new_name'
- set_module_args(data)
- current = {
- 'hostname': 'test',
- 'username': 'test_user',
- 'password': 'test_pass!',
- 'name': self.mock_vol['name'],
- 'vserver': self.mock_vol['vserver'],
- }
- get_volume.side_effect = [
- None,
- current
- ]
- with pytest.raises(AnsibleExitJson) as exc:
- self.get_volume_mock_object().apply()
- assert exc.value.args[0]['changed']
-
- @patch('ansible.modules.storage.netapp.na_ontap_volume.NetAppOntapVolume.get_volume')
- def test_successful_rename_async(self, get_volume):
- ''' Test successful rename volume '''
- data = self.mock_args()
- data['from_name'] = self.mock_vol['name']
- data['name'] = 'new_name'
- set_module_args(data)
- current = {
- 'hostname': 'test',
- 'username': 'test_user',
- 'password': 'test_pass!',
- 'name': self.mock_vol['name'],
- 'vserver': self.mock_vol['vserver'],
- 'is_infinite': True
- }
- get_volume.side_effect = [
- None,
- current
- ]
- obj = self.get_volume_mock_object('job_info')
- with pytest.raises(AnsibleExitJson) as exc:
- obj.apply()
- assert exc.value.args[0]['changed']
-
- @patch('ansible.modules.storage.netapp.na_ontap_volume.NetAppOntapVolume.change_volume_state')
- @patch('ansible.modules.storage.netapp.na_ontap_volume.NetAppOntapVolume.volume_mount')
- def test_modify_helper(self, mount_volume, change_state):
- data = self.mock_args()
- set_module_args(data)
- modify = {
- 'is_online': False,
- 'junction_path': 'something'
- }
- obj = self.get_volume_mock_object('volume')
- obj.modify_volume(modify)
- change_state.assert_called_with()
- mount_volume.assert_called_with()
-
- @patch('ansible.modules.storage.netapp.na_ontap_volume.NetAppOntapVolume.get_volume')
- def test_compare_chmod_value_true_1(self, get_volume):
- data = self.mock_args()
- data['unix_permissions'] = '------------'
- set_module_args(data)
- current = {
- 'unix_permissions': '0'
- }
- get_volume.side_effect = [
- current
- ]
- obj = self.get_volume_mock_object()
- assert obj.compare_chmod_value(current)
-
- @patch('ansible.modules.storage.netapp.na_ontap_volume.NetAppOntapVolume.get_volume')
- def test_compare_chmod_value_true_2(self, get_volume):
- data = self.mock_args()
- data['unix_permissions'] = '---rwxrwxrwx'
- set_module_args(data)
- current = {
- 'unix_permissions': '777'
- }
- get_volume.side_effect = [
- current
- ]
- obj = self.get_volume_mock_object()
- assert obj.compare_chmod_value(current)
-
- @patch('ansible.modules.storage.netapp.na_ontap_volume.NetAppOntapVolume.get_volume')
- def test_compare_chmod_value_true_3(self, get_volume):
- data = self.mock_args()
- data['unix_permissions'] = '---rwxr-xr-x'
- set_module_args(data)
- current = {
- 'unix_permissions': '755'
- }
- get_volume.side_effect = [
- current
- ]
- obj = self.get_volume_mock_object()
- assert obj.compare_chmod_value(current)
-
- @patch('ansible.modules.storage.netapp.na_ontap_volume.NetAppOntapVolume.get_volume')
- def test_compare_chmod_value_true_3(self, get_volume):
- data = self.mock_args()
- data['unix_permissions'] = '755'
- set_module_args(data)
- current = {
- 'unix_permissions': '755'
- }
- get_volume.side_effect = [
- current
- ]
- obj = self.get_volume_mock_object()
- assert obj.compare_chmod_value(current)
-
- @patch('ansible.modules.storage.netapp.na_ontap_volume.NetAppOntapVolume.get_volume')
- def test_compare_chmod_value_false_1(self, get_volume):
- data = self.mock_args()
- data['unix_permissions'] = '---rwxrwxrwx'
- set_module_args(data)
- current = {
- 'unix_permissions': '0'
- }
- get_volume.side_effect = [
- current
- ]
- obj = self.get_volume_mock_object()
- assert not obj.compare_chmod_value(current)
-
- @patch('ansible.modules.storage.netapp.na_ontap_volume.NetAppOntapVolume.get_volume')
- def test_compare_chmod_value_false_2(self, get_volume):
- data = self.mock_args()
- data['unix_permissions'] = '---rwxrwxrwx'
- set_module_args(data)
- current = None
- get_volume.side_effect = [
- current
- ]
- obj = self.get_volume_mock_object()
- assert not obj.compare_chmod_value(current)
-
- @patch('ansible.modules.storage.netapp.na_ontap_volume.NetAppOntapVolume.get_volume')
- def test_compare_chmod_value_invalid_input_1(self, get_volume):
- data = self.mock_args()
- data['unix_permissions'] = '---xwrxwrxwr'
- set_module_args(data)
- current = {
- 'unix_permissions': '777'
- }
- get_volume.side_effect = [
- current
- ]
- obj = self.get_volume_mock_object()
- assert not obj.compare_chmod_value(current)
-
- @patch('ansible.modules.storage.netapp.na_ontap_volume.NetAppOntapVolume.get_volume')
- def test_compare_chmod_value_invalid_input_2(self, get_volume):
- data = self.mock_args()
- data['unix_permissions'] = '---rwx-wx--a'
- set_module_args(data)
- current = {
- 'unix_permissions': '0'
- }
- get_volume.side_effect = [
- current
- ]
- obj = self.get_volume_mock_object()
- assert not obj.compare_chmod_value(current)
-
- @patch('ansible.modules.storage.netapp.na_ontap_volume.NetAppOntapVolume.get_volume')
- def test_compare_chmod_value_invalid_input_3(self, get_volume):
- data = self.mock_args()
- data['unix_permissions'] = '---'
- set_module_args(data)
- current = {
- 'unix_permissions': '0'
- }
- get_volume.side_effect = [
- current
- ]
- obj = self.get_volume_mock_object()
- assert not obj.compare_chmod_value(current)
-
- def test_successful_create_flex_group_manually(self):
- ''' Test successful create flexGroup manually '''
- data = self.mock_args('flexGroup_manual')
- data['time_out'] = 20
- set_module_args(data)
- obj = self.get_volume_mock_object('job_info')
- with pytest.raises(AnsibleExitJson) as exc:
- obj.apply()
- assert exc.value.args[0]['changed']
-
- def test_successful_create_flex_group_auto_provision(self):
- ''' Test successful create flexGroup auto provision '''
- data = self.mock_args('flexGroup_auto')
- data['time_out'] = 20
- set_module_args(data)
- obj = self.get_volume_mock_object('job_info')
- with pytest.raises(AnsibleExitJson) as exc:
- obj.apply()
- assert exc.value.args[0]['changed']
-
- @patch('ansible.modules.storage.netapp.na_ontap_volume.NetAppOntapVolume.get_volume')
- def test_successful_delete_flex_group(self, get_volume):
- ''' Test successful delete flexGroup '''
- data = self.mock_args('flexGroup_manual')
- data['state'] = 'absent'
- set_module_args(data)
- current = {
- 'hostname': 'test',
- 'username': 'test_user',
- 'password': 'test_pass!',
- 'name': self.mock_vol['name'],
- 'vserver': self.mock_vol['vserver'],
- 'style_extended': 'flexgroup',
- 'unix_permissions': '755'
- }
- get_volume.side_effect = [
- current
- ]
- obj = self.get_volume_mock_object('job_info')
- with pytest.raises(AnsibleExitJson) as exc:
- obj.apply()
- assert exc.value.args[0]['changed']
-
- @patch('ansible.modules.storage.netapp.na_ontap_volume.NetAppOntapVolume.get_volume')
- def test_successful_resize_flex_group(self, get_volume):
- ''' Test successful reszie flexGroup '''
- data = self.mock_args('flexGroup_manual')
- data['size'] = 400
- data['size_unit'] = 'mb'
- set_module_args(data)
- current = {
- 'hostname': 'test',
- 'username': 'test_user',
- 'password': 'test_pass!',
- 'name': self.mock_vol['name'],
- 'vserver': self.mock_vol['vserver'],
- 'style_extended': 'flexgroup',
- 'size': 20971520,
- 'unix_permissions': '755'
- }
- get_volume.side_effect = [
- current
- ]
- obj = self.get_volume_mock_object('job_info')
- with pytest.raises(AnsibleExitJson) as exc:
- obj.apply()
- assert exc.value.args[0]['changed']
-
- @patch('ansible.modules.storage.netapp.na_ontap_volume.NetAppOntapVolume.check_job_status')
- @patch('ansible.modules.storage.netapp.na_ontap_volume.NetAppOntapVolume.get_volume')
- def test_successful_modify_unix_permissions_flex_group(self, get_volume, check_job_status):
- ''' Test successful modify unix permissions flexGroup '''
- data = self.mock_args('flexGroup_manual')
- data['time_out'] = 20
- data['unix_permissions'] = '---rw-r-xr-x'
- set_module_args(data)
- current = {
- 'hostname': 'test',
- 'username': 'test_user',
- 'password': 'test_pass!',
- 'name': self.mock_vol['name'],
- 'vserver': self.mock_vol['vserver'],
- 'style_extended': 'flexgroup',
- 'unix_permissions': '777'
- }
- get_volume.side_effect = [
- current
- ]
- check_job_status.side_effect = [
- None
- ]
- obj = self.get_volume_mock_object('success_modify_async')
- with pytest.raises(AnsibleExitJson) as exc:
- obj.apply()
- assert exc.value.args[0]['changed']
-
- @patch('ansible.modules.storage.netapp.na_ontap_volume.NetAppOntapVolume.get_volume')
- def test_successful_modify_unix_permissions_flex_group_0_time_out(self, get_volume):
- ''' Test successful modify unix permissions flexGroup '''
- data = self.mock_args('flexGroup_manual')
- data['time_out'] = 0
- data['unix_permissions'] = '---rw-r-xr-x'
- set_module_args(data)
- current = {
- 'hostname': 'test',
- 'username': 'test_user',
- 'password': 'test_pass!',
- 'name': self.mock_vol['name'],
- 'vserver': self.mock_vol['vserver'],
- 'style_extended': 'flexgroup',
- 'unix_permissions': '777'
- }
- get_volume.side_effect = [
- current
- ]
- obj = self.get_volume_mock_object('success_modify_async')
- with pytest.raises(AnsibleExitJson) as exc:
- obj.apply()
- assert exc.value.args[0]['changed']
-
- @patch('ansible.modules.storage.netapp.na_ontap_volume.NetAppOntapVolume.check_job_status')
- @patch('ansible.modules.storage.netapp.na_ontap_volume.NetAppOntapVolume.get_volume')
- def test_error_modify_unix_permissions_flex_group(self, get_volume, check_job_status):
- ''' Test error modify unix permissions flexGroup '''
- data = self.mock_args('flexGroup_manual')
- data['time_out'] = 20
- data['unix_permissions'] = '---rw-r-xr-x'
- set_module_args(data)
- current = {
- 'hostname': 'test',
- 'username': 'test_user',
- 'password': 'test_pass!',
- 'name': self.mock_vol['name'],
- 'vserver': self.mock_vol['vserver'],
- 'style_extended': 'flexgroup',
- 'unix_permissions': '777'
- }
- get_volume.side_effect = [
- current
- ]
- check_job_status.side_effect = ['error']
- obj = self.get_volume_mock_object('success_modify_async')
- with pytest.raises(AnsibleFailJson) as exc:
- obj.apply()
- assert exc.value.args[0]['msg'] == 'Error when modify volume: error'
-
- @patch('ansible.modules.storage.netapp.na_ontap_volume.NetAppOntapVolume.get_volume')
- def test_failure_modify_unix_permissions_flex_group(self, get_volume):
- ''' Test failure modify unix permissions flexGroup '''
- data = self.mock_args('flexGroup_manual')
- data['unix_permissions'] = '---rw-r-xr-x'
- data['time_out'] = 20
- set_module_args(data)
- current = {
- 'hostname': 'test',
- 'username': 'test_user',
- 'password': 'test_pass!',
- 'name': self.mock_vol['name'],
- 'vserver': self.mock_vol['vserver'],
- 'style_extended': 'flexvol',
- 'unix_permissions': '777'
- }
- get_volume.side_effect = [
- current
- ]
- obj = self.get_volume_mock_object('failure_modify_async')
- with pytest.raises(AnsibleFailJson) as exc:
- obj.apply()
- assert exc.value.args[0]['msg'] == 'Error modifying volume test_vol: modify error message'
-
- @patch('ansible.modules.storage.netapp.na_ontap_volume.NetAppOntapVolume.get_volume')
- def test_successful_offline_state_flex_group(self, get_volume):
- ''' Test successful offline flexGroup state '''
- data = self.mock_args('flexGroup_manual')
- data['is_online'] = False
- set_module_args(data)
- current = {
- 'hostname': 'test',
- 'username': 'test_user',
- 'password': 'test_pass!',
- 'name': self.mock_vol['name'],
- 'vserver': self.mock_vol['vserver'],
- 'style_extended': 'flexgroup',
- 'is_online': True,
- 'junction_path': 'anything',
- 'unix_permissions': '755'
- }
- get_volume.side_effect = [
- current
- ]
- obj = self.get_volume_mock_object('job_info')
- with pytest.raises(AnsibleExitJson) as exc:
- obj.apply()
- assert exc.value.args[0]['changed']
-
- @patch('ansible.modules.storage.netapp.na_ontap_volume.NetAppOntapVolume.get_volume')
- def test_successful_online_state_flex_group(self, get_volume):
- ''' Test successful online flexGroup state '''
- data = self.mock_args('flexGroup_manual')
- set_module_args(data)
- current = {
- 'hostname': 'test',
- 'username': 'test_user',
- 'password': 'test_pass!',
- 'name': self.mock_vol['name'],
- 'vserver': self.mock_vol['vserver'],
- 'style_extended': 'flexgroup',
- 'is_online': False,
- 'junction_path': 'anything',
- 'unix_permissions': '755'
- }
- get_volume.side_effect = [
- current
- ]
- obj = self.get_volume_mock_object('job_info')
- with pytest.raises(AnsibleExitJson) as exc:
- obj.apply()
- assert exc.value.args[0]['changed']
-
- def test_check_job_status_error(self):
- ''' Test check job status error '''
- data = self.mock_args('flexGroup_manual')
- data['time_out'] = 0
- set_module_args(data)
- obj = self.get_volume_mock_object('job_info', job_error='failure')
- result = obj.check_job_status('123')
- assert result == 'failure'
-
- def test_check_job_status_time_out_is_0(self):
- ''' Test check job status time out is 0'''
- data = self.mock_args('flexGroup_manual')
- data['time_out'] = 0
- set_module_args(data)
- obj = self.get_volume_mock_object('job_info', job_error='time_out')
- result = obj.check_job_status('123')
- assert result == 'job completion exceeded expected timer of: 0 seconds'
-
- def test_check_job_status_unexpected(self):
- ''' Test check job status unexpected state '''
- data = self.mock_args('flexGroup_manual')
- data['time_out'] = 20
- set_module_args(data)
- obj = self.get_volume_mock_object('job_info', job_error='other')
- with pytest.raises(AnsibleFailJson) as exc:
- obj.check_job_status('123')
- assert exc.value.args[0]['failed']
-
- def test_error_assign_efficiency_policy(self):
- data = self.mock_args()
- data['efficiency_policy'] = 'test_policy'
- set_module_args(data)
- obj = self.get_volume_mock_object('zapi_error')
- with pytest.raises(AnsibleFailJson) as exc:
- obj.assign_efficiency_policy()
- assert exc.value.args[0]['msg'] == 'Error enable efficiency on volume test_vol: NetApp API failed. Reason - test:error'
-
- def test_error_assign_efficiency_policy_async(self):
- data = self.mock_args()
- data['efficiency_policy'] = 'test_policy'
- set_module_args(data)
- obj = self.get_volume_mock_object('zapi_error')
- with pytest.raises(AnsibleFailJson) as exc:
- obj.assign_efficiency_policy_async()
- assert exc.value.args[0]['msg'] == 'Error enable efficiency on volume test_vol: NetApp API failed. Reason - test:error'
-
- def test_successful_modify_tiering_policy(self):
- ''' Test successful modify tiering policy '''
- data = self.mock_args()
- data['tiering_policy'] = 'auto'
- set_module_args(data)
- with pytest.raises(AnsibleExitJson) as exc:
- self.get_volume_mock_object('volume').apply()
- assert exc.value.args[0]['changed']
-
- def test_successful_modify_vserver_dr_protection(self):
- ''' Test successful modify vserver_dr_protection '''
- data = self.mock_args()
- data['vserver_dr_protection'] = 'protected'
- set_module_args(data)
- with pytest.raises(AnsibleExitJson) as exc:
- self.get_volume_mock_object('volume').apply()
- assert exc.value.args[0]['changed']
diff --git a/test/units/modules/storage/netapp/test_na_ontap_volume_autosize.py b/test/units/modules/storage/netapp/test_na_ontap_volume_autosize.py
deleted file mode 100644
index 75dad9c3b0..0000000000
--- a/test/units/modules/storage/netapp/test_na_ontap_volume_autosize.py
+++ /dev/null
@@ -1,242 +0,0 @@
-# (c) 2019, NetApp, Inc
-# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-
-''' unit tests for Ansible module: na_ontap_volume_autosize '''
-
-from __future__ import (absolute_import, division, print_function)
-__metaclass__ = type
-import json
-import pytest
-
-from units.compat import unittest
-from units.compat.mock import patch, Mock
-from ansible.module_utils import basic
-from ansible.module_utils._text import to_bytes
-import ansible.module_utils.netapp as netapp_utils
-
-from ansible.modules.storage.netapp.na_ontap_volume_autosize \
- import NetAppOntapVolumeAutosize as autosize_module # module under test
-
-if not netapp_utils.has_netapp_lib():
- pytestmark = pytest.mark.skip('skipping as missing required netapp_lib')
-
-
-# REST API canned responses when mocking send_request
-SRR = {
- # common responses
- 'is_rest': (200, None),
- 'is_zapi': (400, "Unreachable"),
- 'empty_good': ({}, None),
- 'end_of_sequence': (None, "Unexpected call to send_request"),
- 'generic_error': (None, "Expected error"),
- # module specific responses
- 'get_uuid': ({'records': [{'uuid': 'testuuid'}]}, None),
- 'get_autosize': ({'uuid': 'testuuid',
- 'name': 'testname',
- 'autosize': {"maximum": 10737418240,
- "minimum": 22020096,
- "grow_threshold": 99,
- "shrink_threshold": 40,
- "mode": "grow"
- }
- }, None)
-}
-
-
-def set_module_args(args):
- """prepare arguments so that they will be picked up during module creation"""
- args = json.dumps({'ANSIBLE_MODULE_ARGS': args})
- basic._ANSIBLE_ARGS = to_bytes(args) # pylint: disable=protected-access
-
-
-class AnsibleExitJson(Exception):
- """Exception class to be raised by module.exit_json and caught by the test case"""
- pass
-
-
-class AnsibleFailJson(Exception):
- """Exception class to be raised by module.fail_json and caught by the test case"""
- pass
-
-
-def exit_json(*args, **kwargs): # pylint: disable=unused-argument
- """function to patch over exit_json; package return data into an exception"""
- if 'changed' not in kwargs:
- kwargs['changed'] = False
- raise AnsibleExitJson(kwargs)
-
-
-def fail_json(*args, **kwargs): # pylint: disable=unused-argument
- """function to patch over fail_json; package return data into an exception"""
- kwargs['failed'] = True
- raise AnsibleFailJson(kwargs)
-
-
-class MockONTAPConnection(object):
- ''' mock server connection to ONTAP host '''
-
- def __init__(self, kind=None, data=None):
- ''' save arguments '''
- self.kind = kind
- self.params = data
- self.xml_in = None
- self.xml_out = None
-
- def invoke_successfully(self, xml, enable_tunneling): # pylint: disable=unused-argument
- ''' mock invoke_successfully returning xml data '''
- self.xml_in = xml
- if self.kind == 'autosize':
- xml = self.build_autosize_info(self.params)
- self.xml_out = xml
- return xml
-
- @staticmethod
- def build_autosize_info(autosize_details):
- xml = netapp_utils.zapi.NaElement('xml')
- attributes = {
- 'grow-threshold-percent': autosize_details['grow_threshold_percent'],
- 'maximum-size': '10485760',
- 'minimum-size': '21504',
- 'increment_size': '10240',
- 'mode': autosize_details['mode'],
- 'shrink-threshold-percent': autosize_details['shrink_threshold_percent']
- }
- xml.translate_struct(attributes)
- return xml
-
-
-class TestMyModule(unittest.TestCase):
- ''' Unit tests for na_ontap_job_schedule '''
-
- def setUp(self):
- self.mock_module_helper = patch.multiple(basic.AnsibleModule,
- exit_json=exit_json,
- fail_json=fail_json)
- self.mock_module_helper.start()
- self.addCleanup(self.mock_module_helper.stop)
- self.mock_autosize = {
- 'grow_threshold_percent': 99,
- 'maximum_size': '10g',
- 'minimum_size': '21m',
- 'increment_size': '10m',
- 'mode': 'grow',
- 'shrink_threshold_percent': 40,
- 'vserver': 'test_vserver',
- 'volume': 'test_volume'
- }
-
- def mock_args(self, rest=False):
- if rest:
- return {
- 'vserver': self.mock_autosize['vserver'],
- 'volume': self.mock_autosize['volume'],
- 'grow_threshold_percent': self.mock_autosize['grow_threshold_percent'],
- 'maximum_size': self.mock_autosize['maximum_size'],
- 'minimum_size': self.mock_autosize['minimum_size'],
- 'mode': self.mock_autosize['mode'],
- 'shrink_threshold_percent': self.mock_autosize['shrink_threshold_percent'],
- 'hostname': 'test',
- 'username': 'test_user',
- 'password': 'test_pass!'
- }
- else:
- return {
- 'vserver': self.mock_autosize['vserver'],
- 'volume': self.mock_autosize['volume'],
- 'grow_threshold_percent': self.mock_autosize['grow_threshold_percent'],
- 'maximum_size': self.mock_autosize['maximum_size'],
- 'minimum_size': self.mock_autosize['minimum_size'],
- 'increment_size': self.mock_autosize['increment_size'],
- 'mode': self.mock_autosize['mode'],
- 'shrink_threshold_percent': self.mock_autosize['shrink_threshold_percent'],
- 'hostname': 'test',
- 'username': 'test_user',
- 'password': 'test_pass!'
- }
-
- def get_autosize_mock_object(self, type='zapi', kind=None):
- autosize_obj = autosize_module()
- if type == 'zapi':
- if kind is None:
- autosize_obj.server = MockONTAPConnection()
- elif kind == 'autosize':
- autosize_obj.server = MockONTAPConnection(kind='autosize', data=self.mock_autosize)
- return autosize_obj
-
- def test_module_fail_when_required_args_missing(self):
- ''' required arguments are reported as errors '''
- with pytest.raises(AnsibleFailJson) as exc:
- set_module_args({})
- autosize_module()
- print('Info: %s' % exc.value.args[0]['msg'])
-
- def test_idempotent_modify(self):
- set_module_args(self.mock_args())
- with pytest.raises(AnsibleExitJson) as exc:
- self.get_autosize_mock_object('zapi', 'autosize').apply()
- assert not exc.value.args[0]['changed']
-
- def test_successful_modify(self):
- data = self.mock_args()
- data['maximum_size'] = '11g'
- set_module_args(data)
- with pytest.raises(AnsibleExitJson) as exc:
- self.get_autosize_mock_object('zapi', 'autosize').apply()
- assert exc.value.args[0]['changed']
-
- def test_successful_reset(self):
- data = {}
- data['reset'] = True
- data['hostname'] = 'test'
- data['username'] = 'test_user'
- data['password'] = 'test_pass!'
- data['volume'] = 'test_vol'
- data['vserver'] = 'test_vserver'
- set_module_args(data)
- with pytest.raises(AnsibleExitJson) as exc:
- self.get_autosize_mock_object('zapi', 'autosize').apply()
- assert exc.value.args[0]['changed']
-
- @patch('ansible.module_utils.netapp.OntapRestAPI.send_request')
- def test_rest_error(self, mock_request):
- data = self.mock_args(rest=True)
- set_module_args(data)
- mock_request.side_effect = [
- SRR['is_rest'],
- SRR['generic_error'],
- SRR['end_of_sequence']
- ]
- with pytest.raises(AnsibleFailJson) as exc:
- self.get_autosize_mock_object(type='rest').apply()
- assert exc.value.args[0]['msg'] == SRR['generic_error'][1]
-
- @patch('ansible.module_utils.netapp.OntapRestAPI.send_request')
- def test_rest_successful_modify(self, mock_request):
- data = self.mock_args(rest=True)
- data['maximum_size'] = '11g'
- set_module_args(data)
- mock_request.side_effect = [
- SRR['is_rest'],
- SRR['get_uuid'],
- SRR['get_autosize'],
- SRR['empty_good'],
- SRR['end_of_sequence']
- ]
- with pytest.raises(AnsibleExitJson) as exc:
- self.get_autosize_mock_object(type='rest').apply()
- assert exc.value.args[0]['changed']
-
- @patch('ansible.module_utils.netapp.OntapRestAPI.send_request')
- def test_rest_idempotent_modify(self, mock_request):
- data = self.mock_args(rest=True)
- set_module_args(data)
- mock_request.side_effect = [
- SRR['is_rest'],
- SRR['get_uuid'],
- SRR['get_autosize'],
- SRR['empty_good'],
- SRR['end_of_sequence']
- ]
- with pytest.raises(AnsibleExitJson) as exc:
- self.get_autosize_mock_object(type='rest').apply()
- assert not exc.value.args[0]['changed']
diff --git a/test/units/modules/storage/netapp/test_na_ontap_volume_clone.py b/test/units/modules/storage/netapp/test_na_ontap_volume_clone.py
deleted file mode 100644
index 2aadb0660d..0000000000
--- a/test/units/modules/storage/netapp/test_na_ontap_volume_clone.py
+++ /dev/null
@@ -1,179 +0,0 @@
-# (c) 2018, NetApp, Inc
-# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-
-''' unit tests ONTAP Ansible module: na_ontap_volume_clone'''
-
-from __future__ import print_function
-import json
-import pytest
-
-from units.compat import unittest
-from units.compat.mock import patch
-from ansible.module_utils import basic
-from ansible.module_utils._text import to_bytes
-import ansible.module_utils.netapp as netapp_utils
-
-from ansible.modules.storage.netapp.na_ontap_volume_clone \
- import NetAppONTAPVolumeClone as my_module
-
-if not netapp_utils.has_netapp_lib():
- pytestmark = pytest.mark.skip('skipping as missing required netapp_lib')
-
-
-def set_module_args(args):
- """prepare arguments so that they will be picked up during module creation"""
- args = json.dumps({'ANSIBLE_MODULE_ARGS': args})
- basic._ANSIBLE_ARGS = to_bytes(args) # pylint: disable=protected-access
-
-
-class AnsibleExitJson(Exception):
- """Exception class to be raised by module.exit_json and caught by the test case"""
- pass
-
-
-class AnsibleFailJson(Exception):
- """Exception class to be raised by module.fail_json and caught by the test case"""
- pass
-
-
-def exit_json(*args, **kwargs): # pylint: disable=unused-argument
- """function to patch over exit_json; package return data into an exception"""
- if 'changed' not in kwargs:
- kwargs['changed'] = False
- raise AnsibleExitJson(kwargs)
-
-
-def fail_json(*args, **kwargs): # pylint: disable=unused-argument
- """function to patch over fail_json; package return data into an exception"""
- kwargs['failed'] = True
- raise AnsibleFailJson(kwargs)
-
-
-class MockONTAPConnection(object):
- ''' mock server connection to ONTAP host '''
-
- def __init__(self, kind=None):
- ''' save arguments '''
- self.type = kind
- self.xml_in = None
- self.xml_out = None
-
- def invoke_successfully(self, xml, enable_tunneling): # pylint: disable=unused-argument
- ''' mock invoke_successfully returning xml data '''
- self.xml_in = xml
- if self.type == 'volume_clone':
- xml = self.build_volume_clone_info()
- elif self.type == 'volume_clone_fail':
- raise netapp_utils.zapi.NaApiError(code='TEST', message="This exception is from the unit test")
- self.xml_out = xml
- return xml
-
- @staticmethod
- def build_volume_clone_info():
- ''' build xml data for volume-clone-info '''
- xml = netapp_utils.zapi.NaElement('xml')
- data = {'attributes': {'volume-clone-info': {'volume': 'ansible',
- 'parent-volume': 'ansible'}}}
- xml.translate_struct(data)
- return xml
-
-
-class TestMyModule(unittest.TestCase):
- ''' a group of related Unit Tests '''
-
- def setUp(self):
- self.mock_module_helper = patch.multiple(basic.AnsibleModule,
- exit_json=exit_json,
- fail_json=fail_json)
- self.mock_module_helper.start()
- self.addCleanup(self.mock_module_helper.stop)
- self.server = MockONTAPConnection()
- self.onbox = False
-
- def set_default_args(self):
- if self.onbox:
- hostname = '10.10.10.10'
- username = 'username'
- password = 'password'
- vserver = 'ansible'
- volume = 'ansible'
- parent_volume = 'ansible'
- else:
- hostname = '10.10.10.10'
- username = 'username'
- password = 'password'
- vserver = 'ansible'
- volume = 'ansible'
- parent_volume = 'ansible'
- return dict({
- 'hostname': hostname,
- 'username': username,
- 'password': password,
- 'vserver': vserver,
- 'volume': volume,
- 'parent_volume': parent_volume
- })
-
- def test_module_fail_when_required_args_missing(self):
- ''' required arguments are reported as errors '''
- with pytest.raises(AnsibleFailJson) as exc:
- set_module_args({})
- my_module()
- print('Info: %s' % exc.value.args[0]['msg'])
-
- def test_ensure_get_called(self):
- ''' test get_volume_clone() for non-existent volume clone'''
- set_module_args(self.set_default_args())
- my_obj = my_module()
- my_obj.server = self.server
- assert my_obj.get_volume_clone() is None
-
- def test_ensure_get_called_existing(self):
- ''' test get_volume_clone() for existing volume clone'''
- set_module_args(self.set_default_args())
- my_obj = my_module()
- my_obj.server = MockONTAPConnection(kind='volume_clone')
- assert my_obj.get_volume_clone()
-
- @patch('ansible.modules.storage.netapp.na_ontap_volume_clone.NetAppONTAPVolumeClone.create_volume_clone')
- def test_successful_create(self, create_volume_clone):
- ''' creating volume_clone and testing idempotency '''
- module_args = {
- 'parent_snapshot': 'abc',
- 'parent_vserver': 'abc',
- 'volume_type': 'dp',
- 'qos_policy_group_name': 'abc',
- 'junction_path': 'abc',
- 'uid': '1',
- 'gid': '1'
- }
- module_args.update(self.set_default_args())
- set_module_args(module_args)
- my_obj = my_module()
- if not self.onbox:
- my_obj.server = self.server
- with pytest.raises(AnsibleExitJson) as exc:
- my_obj.apply()
- assert exc.value.args[0]['changed']
- create_volume_clone.assert_called_with()
- # to reset na_helper from remembering the previous 'changed' value
- my_obj = my_module()
- if not self.onbox:
- my_obj.server = MockONTAPConnection('volume_clone')
- with pytest.raises(AnsibleExitJson) as exc:
- my_obj.apply()
- assert not exc.value.args[0]['changed']
-
- def test_if_all_methods_catch_exception(self):
- module_args = {}
- module_args.update(self.set_default_args())
- set_module_args(module_args)
- my_obj = my_module()
- if not self.onbox:
- my_obj.server = MockONTAPConnection('volume_clone_fail')
- with pytest.raises(AnsibleFailJson) as exc:
- my_obj.get_volume_clone()
- assert 'Error fetching volume clone information ' in exc.value.args[0]['msg']
- with pytest.raises(AnsibleFailJson) as exc:
- my_obj.create_volume_clone()
- assert 'Error creating volume clone: ' in exc.value.args[0]['msg']
diff --git a/test/units/modules/storage/netapp/test_na_ontap_vscan.py b/test/units/modules/storage/netapp/test_na_ontap_vscan.py
deleted file mode 100644
index bdc9eb1c59..0000000000
--- a/test/units/modules/storage/netapp/test_na_ontap_vscan.py
+++ /dev/null
@@ -1,231 +0,0 @@
-# (c) 2018, NetApp, Inc
-# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-
-''' unit tests for Ansible module: na_ontap_vscan'''
-
-from __future__ import print_function
-import json
-import pytest
-
-from units.compat import unittest
-from units.compat.mock import patch
-from ansible.module_utils import basic
-from ansible.module_utils._text import to_bytes
-import ansible.module_utils.netapp as netapp_utils
-
-from ansible.modules.storage.netapp.na_ontap_vscan \
- import NetAppOntapVscan as vscan_module # module under test
-
-if not netapp_utils.has_netapp_lib():
- pytestmark = pytest.mark.skip('skipping as missing required netapp_lib')
-HAS_NETAPP_ZAPI_MSG = "pip install netapp_lib is required"
-
-
-# REST API canned responses when mocking send_request
-SRR = {
- # common responses
- 'is_rest': (200, None),
- 'is_zapi': (400, "Unreachable"),
- 'empty_good': ({}, None),
- 'end_of_sequence': (None, "Ooops, the UT needs one more SRR response"),
- 'generic_error': (None, "Expected error"),
- # module specific responses
- 'enabled': ({'records': [{'enabled': True, 'svm': {'uuid': 'testuuid'}}]}, None),
- 'disabled': ({'records': [{'enabled': False, 'svm': {'uuid': 'testuuid'}}]}, None),
-}
-
-
-def set_module_args(args):
- """prepare arguments so that they will be picked up during module creation"""
- args = json.dumps({'ANSIBLE_MODULE_ARGS': args})
- basic._ANSIBLE_ARGS = to_bytes(args) # pylint: disable=protected-access
-
-
-class AnsibleExitJson(Exception):
- """Exception class to be raised by module.exit_json and caught by the test case"""
- pass
-
-
-class AnsibleFailJson(Exception):
- """Exception class to be raised by module.fail_json and caught by the test case"""
- pass
-
-
-def exit_json(*args, **kwargs): # pylint: disable=unused-argument
- """function to patch over exit_json; package return data into an exception"""
- if 'changed' not in kwargs:
- kwargs['changed'] = False
- raise AnsibleExitJson(kwargs)
-
-
-def fail_json(*args, **kwargs): # pylint: disable=unused-argument
- """function to patch over fail_json; package return data into an exception"""
- kwargs['failed'] = True
- raise AnsibleFailJson(kwargs)
-
-
-class MockONTAPConnection(object):
- ''' mock server connection to ONTAP host '''
-
- def __init__(self, kind=None, data=None):
- ''' save arguments '''
- self.kind = kind
- self.params = data
- self.xml_in = None
- self.xml_out = None
-
- def invoke_successfully(self, xml, enable_tunneling): # pylint: disable=unused-argument
- ''' mock invoke_successfully returning xml data '''
- self.xml_in = xml
- if self.kind == 'enable':
- xml = self.build_vscan_status_info(self.params)
- self.xml_out = xml
- return xml
-
- @staticmethod
- def build_vscan_status_info(status):
- xml = netapp_utils.zapi.NaElement('xml')
- attributes = {'num-records': 1,
- 'attributes-list': {'vscan-status-info': {'is-vscan-enabled': status}}}
- xml.translate_struct(attributes)
- return xml
-
-
-class TestMyModule(unittest.TestCase):
- ''' Unit tests for na_ontap_job_schedule '''
-
- def setUp(self):
- self.mock_module_helper = patch.multiple(basic.AnsibleModule,
- exit_json=exit_json,
- fail_json=fail_json)
- self.mock_module_helper.start()
- self.addCleanup(self.mock_module_helper.stop)
-
- def mock_args(self):
- return {
- 'enable': False,
- 'vserver': 'vserver',
- 'hostname': 'test',
- 'username': 'test_user',
- 'password': 'test_pass!'
- }
-
- def get_vscan_mock_object(self, type='zapi', kind=None, status=None):
- vscan_obj = vscan_module()
- if type == 'zapi':
- if kind is None:
- vscan_obj.server = MockONTAPConnection()
- else:
- vscan_obj.server = MockONTAPConnection(kind=kind, data=status)
- # For rest, mocking is achieved through side_effect
- return vscan_obj
-
- def test_module_fail_when_required_args_missing(self):
- ''' required arguments are reported as errors '''
- with pytest.raises(AnsibleFailJson) as exc:
- set_module_args({})
- vscan_module()
- print('Info: %s' % exc.value.args[0]['msg'])
-
- def test_successfully_enable(self):
- data = self.mock_args()
- data['enable'] = True
- set_module_args(data)
- with pytest.raises(AnsibleExitJson) as exc:
- self.get_vscan_mock_object('zapi', 'enable', 'false').apply()
- assert exc.value.args[0]['changed']
-
- def test_idempotently_enable(self):
- data = self.mock_args()
- data['enable'] = True
- set_module_args(data)
- with pytest.raises(AnsibleExitJson) as exc:
- self.get_vscan_mock_object('zapi', 'enable', 'true').apply()
- assert not exc.value.args[0]['changed']
-
- def test_successfully_disable(self):
- data = self.mock_args()
- data['enable'] = False
- set_module_args(data)
- with pytest.raises(AnsibleExitJson) as exc:
- self.get_vscan_mock_object('zapi', 'enable', 'true').apply()
- assert exc.value.args[0]['changed']
-
- def test_idempotently_disable(self):
- data = self.mock_args()
- data['enable'] = False
- set_module_args(data)
- with pytest.raises(AnsibleExitJson) as exc:
- self.get_vscan_mock_object('zapi', 'enable', 'false').apply()
- assert not exc.value.args[0]['changed']
-
- @patch('ansible.module_utils.netapp.OntapRestAPI.send_request')
- def test_rest_error(self, mock_request):
- data = self.mock_args()
- set_module_args(data)
- mock_request.side_effect = [
- SRR['is_rest'],
- SRR['generic_error'],
- SRR['end_of_sequence']
- ]
- with pytest.raises(AnsibleFailJson) as exc:
- self.get_vscan_mock_object(type='rest').apply()
- assert exc.value.args[0]['msg'] == SRR['generic_error'][1]
-
- @patch('ansible.module_utils.netapp.OntapRestAPI.send_request')
- def test_rest_successly_enable(self, mock_request):
- data = self.mock_args()
- data['enable'] = True
- set_module_args(data)
- mock_request.side_effect = [
- SRR['is_rest'],
- SRR['disabled'],
- SRR['empty_good'],
- SRR['end_of_sequence']
- ]
- with pytest.raises(AnsibleExitJson) as exc:
- self.get_vscan_mock_object(type='rest').apply()
- assert exc.value.args[0]['changed']
-
- @patch('ansible.module_utils.netapp.OntapRestAPI.send_request')
- def test_rest_idempotently_enable(self, mock_request):
- data = self.mock_args()
- data['enable'] = True
- set_module_args(data)
- mock_request.side_effect = [
- SRR['is_rest'],
- SRR['enabled'],
- SRR['end_of_sequence']
- ]
- with pytest.raises(AnsibleExitJson) as exc:
- self.get_vscan_mock_object(type='rest').apply()
- assert not exc.value.args[0]['changed']
-
- @patch('ansible.module_utils.netapp.OntapRestAPI.send_request')
- def test_rest_successly_disable(self, mock_request):
- data = self.mock_args()
- data['enable'] = False
- set_module_args(data)
- mock_request.side_effect = [
- SRR['is_rest'],
- SRR['enabled'],
- SRR['empty_good'],
- SRR['end_of_sequence']
- ]
- with pytest.raises(AnsibleExitJson) as exc:
- self.get_vscan_mock_object(type='rest').apply()
- assert exc.value.args[0]['changed']
-
- @patch('ansible.module_utils.netapp.OntapRestAPI.send_request')
- def test_rest_idempotently_disable(self, mock_request):
- data = self.mock_args()
- data['enable'] = False
- set_module_args(data)
- mock_request.side_effect = [
- SRR['is_rest'],
- SRR['disabled'],
- SRR['end_of_sequence']
- ]
- with pytest.raises(AnsibleExitJson) as exc:
- self.get_vscan_mock_object(type='rest').apply()
- assert not exc.value.args[0]['changed']
diff --git a/test/units/modules/storage/netapp/test_na_ontap_vscan_on_access_policy.py b/test/units/modules/storage/netapp/test_na_ontap_vscan_on_access_policy.py
deleted file mode 100644
index 50494f54ab..0000000000
--- a/test/units/modules/storage/netapp/test_na_ontap_vscan_on_access_policy.py
+++ /dev/null
@@ -1,156 +0,0 @@
-# (c) 2018, NetApp, Inc
-# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-
-''' unit tests for Ansible module: na_ontap_vscan_scanner_pool '''
-
-from __future__ import print_function
-import json
-import pytest
-
-from units.compat import unittest
-from units.compat.mock import patch
-from ansible.module_utils import basic
-from ansible.module_utils._text import to_bytes
-import ansible.module_utils.netapp as netapp_utils
-
-from ansible.modules.storage.netapp.na_ontap_vscan_on_access_policy \
- import NetAppOntapVscanOnAccessPolicy as policy_module # module under test
-
-if not netapp_utils.has_netapp_lib():
- pytestmark = pytest.mark.skip('skipping as missing required netapp_lib')
-HAS_NETAPP_ZAPI_MSG = "pip install netapp_lib is required"
-
-
-def set_module_args(args):
- """prepare arguments so that they will be picked up during module creation"""
- args = json.dumps({'ANSIBLE_MODULE_ARGS': args})
- basic._ANSIBLE_ARGS = to_bytes(args) # pylint: disable=protected-access
-
-
-class AnsibleExitJson(Exception):
- """Exception class to be raised by module.exit_json and caught by the test case"""
- pass
-
-
-class AnsibleFailJson(Exception):
- """Exception class to be raised by module.fail_json and caught by the test case"""
- pass
-
-
-def exit_json(*args, **kwargs): # pylint: disable=unused-argument
- """function to patch over exit_json; package return data into an exception"""
- if 'changed' not in kwargs:
- kwargs['changed'] = False
- raise AnsibleExitJson(kwargs)
-
-
-def fail_json(*args, **kwargs): # pylint: disable=unused-argument
- """function to patch over fail_json; package return data into an exception"""
- kwargs['failed'] = True
- raise AnsibleFailJson(kwargs)
-
-
-class MockONTAPConnection(object):
- ''' mock server connection to ONTAP host '''
-
- def __init__(self, kind=None, data=None):
- ''' save arguments '''
- self.kind = kind
- self.params = data
- self.xml_in = None
- self.xml_out = None
-
- def invoke_successfully(self, xml, enable_tunneling): # pylint: disable=unused-argument
- ''' mock invoke_successfully returning xml data '''
- self.xml_in = xml
- if self.kind == 'policy':
- xml = self.build_access_policy_info(self.params)
- self.xml_out = xml
- return xml
-
- @staticmethod
- def build_access_policy_info(policy_details):
- xml = netapp_utils.zapi.NaElement('xml')
- attributes = {'num-records': 1,
- 'attributes-list': {'vscan-on-access-policy-info': {'policy-name': policy_details['policy_name']}}}
- xml.translate_struct(attributes)
- return xml
-
-
-class TestMyModule(unittest.TestCase):
- ''' Unit tests for na_ontap_job_schedule '''
-
- def setUp(self):
- self.mock_module_helper = patch.multiple(basic.AnsibleModule,
- exit_json=exit_json,
- fail_json=fail_json)
- self.mock_module_helper.start()
- self.addCleanup(self.mock_module_helper.stop)
- self.mock_access_policy = {
- 'state': 'present',
- 'vserver': 'test_vserver',
- 'policy_name': 'test_carchi'
- }
-
- def mock_args(self):
- return {
- 'state': self.mock_access_policy['state'],
- 'vserver': self.mock_access_policy['vserver'],
- 'policy_name': self.mock_access_policy['policy_name'],
- 'hostname': 'test',
- 'username': 'test_user',
- 'password': 'test_pass!'
- }
-
- def get_policy_mock_object(self, kind=None):
- policy_obj = policy_module()
- if kind is None:
- policy_obj.server = MockONTAPConnection()
- else:
- policy_obj.server = MockONTAPConnection(kind='policy', data=self.mock_access_policy)
- return policy_obj
-
- def test_module_fail_when_required_args_missing(self):
- ''' required arguments are reported as errors '''
- with pytest.raises(AnsibleFailJson) as exc:
- set_module_args({})
- policy_module()
- print('Info: %s' % exc.value.args[0]['msg'])
-
- def test_get_nonexistent_policy(self):
- set_module_args(self.mock_args())
- result = self.get_policy_mock_object().exists_access_policy()
- assert not result
-
- def test_get_existing_scanner(self):
- set_module_args(self.mock_args())
- result = self.get_policy_mock_object('policy').exists_access_policy()
- assert result
-
- def test_successfully_create(self):
- set_module_args(self.mock_args())
- with pytest.raises(AnsibleExitJson) as exc:
- self.get_policy_mock_object().apply()
- assert exc.value.args[0]['changed']
-
- def test_create_idempotency(self):
- set_module_args(self.mock_args())
- with pytest.raises(AnsibleExitJson) as exc:
- self.get_policy_mock_object('policy').apply()
- assert exc.value.args[0]['changed']
-
- def test_successfully_delete(self):
- data = self.mock_args()
- data['state'] = 'absent'
- set_module_args(data)
- with pytest.raises(AnsibleExitJson) as exc:
- self.get_policy_mock_object('policy').apply()
- assert exc.value.args[0]['changed']
-
- def test_delete_idempotency(self):
- data = self.mock_args()
- data['state'] = 'absent'
- set_module_args(data)
- with pytest.raises(AnsibleExitJson) as exc:
- self.get_policy_mock_object().apply()
- assert not exc.value.args[0]['changed']
diff --git a/test/units/modules/storage/netapp/test_na_ontap_vscan_on_demand_task.py b/test/units/modules/storage/netapp/test_na_ontap_vscan_on_demand_task.py
deleted file mode 100644
index 544e0f6699..0000000000
--- a/test/units/modules/storage/netapp/test_na_ontap_vscan_on_demand_task.py
+++ /dev/null
@@ -1,167 +0,0 @@
-''' unit tests for Ansible module: na_ontap_vscan_on_demand_task '''
-
-from __future__ import print_function
-import json
-import pytest
-
-from units.compat import unittest
-from units.compat.mock import patch, Mock
-from ansible.module_utils import basic
-from ansible.module_utils._text import to_bytes
-import ansible.module_utils.netapp as netapp_utils
-
-from ansible.modules.storage.netapp.na_ontap_vscan_on_demand_task \
- import NetAppOntapVscanOnDemandTask as onDemand_module # module under test
-
-if not netapp_utils.has_netapp_lib():
- pytestmark = pytest.mark.skip('skipping as missing required netapp_lib')
-
-
-def set_module_args(args):
- """prepare arguments so that they will be picked up during module creation"""
- args = json.dumps({'ANSIBLE_MODULE_ARGS': args})
- basic._ANSIBLE_ARGS = to_bytes(args) # pylint: disable=protected-access
-
-
-class AnsibleExitJson(Exception):
- """Exception class to be raised by module.exit_json and caught by the test case"""
- pass
-
-
-class AnsibleFailJson(Exception):
- """Exception class to be raised by module.fail_json and caught by the test case"""
- pass
-
-
-def exit_json(*args, **kwargs): # pylint: disable=unused-argument
- """function to patch over exit_json; package return data into an exception"""
- if 'changed' not in kwargs:
- kwargs['changed'] = False
- raise AnsibleExitJson(kwargs)
-
-
-def fail_json(*args, **kwargs): # pylint: disable=unused-argument
- """function to patch over fail_json; package return data into an exception"""
- kwargs['failed'] = True
- raise AnsibleFailJson(kwargs)
-
-
-class MockONTAPConnection(object):
- ''' mock server connection to ONTAP host '''
-
- def __init__(self, kind=None, data=None):
- ''' save arguments '''
- self.kind = kind
- self.params = data
- self.xml_in = None
- self.xml_out = None
-
- def invoke_successfully(self, xml, enable_tunneling): # pylint: disable=unused-argument
- ''' mock invoke_successfully returning xml data '''
- self.xml_in = xml
- if self.kind == 'task':
- xml = self.build_onDemand_pool_info(self.params)
- self.xml_out = xml
- return xml
-
- @staticmethod
- def build_onDemand_pool_info(onDemand_details):
- xml = netapp_utils.zapi.NaElement('xml')
- attributes = {
- 'num-records': 1,
- 'attributes-list': {
- 'vscan-on-demand-task-info': {
- 'task-name': onDemand_details['task_name'],
- 'report-directory': onDemand_details['report_directory'],
- 'scan-paths': {
- 'string': onDemand_details['scan_paths']
- }
- }
- }
- }
- xml.translate_struct(attributes)
- return xml
-
-
-class TestMyModule(unittest.TestCase):
- ''' Unit tests for na_ontap_job_schedule '''
-
- def setUp(self):
- self.mock_module_helper = patch.multiple(basic.AnsibleModule,
- exit_json=exit_json,
- fail_json=fail_json)
- self.mock_module_helper.start()
- self.addCleanup(self.mock_module_helper.stop)
- self.mock_onDemand = {
- 'state': 'present',
- 'vserver': 'test_vserver',
- 'report_directory': '/',
- 'task_name': '/',
- 'scan_paths': '/'
- }
-
- def mock_args(self):
- return {
- 'state': self.mock_onDemand['state'],
- 'vserver': self.mock_onDemand['vserver'],
- 'report_directory': self.mock_onDemand['report_directory'],
- 'task_name': self.mock_onDemand['task_name'],
- 'scan_paths': self.mock_onDemand['scan_paths'],
- 'hostname': 'test',
- 'username': 'test_user',
- 'password': 'test_pass!'
- }
-
- def get_demand_mock_object(self, kind=None):
- scanner_obj = onDemand_module()
- scanner_obj.asup_log_for_cserver = Mock(return_value=None)
- if kind is None:
- scanner_obj.server = MockONTAPConnection()
- else:
- scanner_obj.server = MockONTAPConnection(kind='task', data=self.mock_onDemand)
- return scanner_obj
-
- def test_module_fail_when_required_args_missing(self):
- ''' required arguments are reported as errors '''
- with pytest.raises(AnsibleFailJson) as exc:
- set_module_args({})
- onDemand_module()
- print('Info: %s' % exc.value.args[0]['msg'])
-
- def test_get_nonexistent_demand_task(self):
- set_module_args(self.mock_args())
- result = self.get_demand_mock_object().get_demand_task()
- assert not result
-
- def test_get_existing_demand_task(self):
- set_module_args(self.mock_args())
- result = self.get_demand_mock_object('task').get_demand_task()
- assert result
-
- def test_successfully_create(self):
- set_module_args(self.mock_args())
- with pytest.raises(AnsibleExitJson) as exc:
- self.get_demand_mock_object().apply()
- assert exc.value.args[0]['changed']
-
- def test_create_idempotency(self):
- set_module_args(self.mock_args())
- with pytest.raises(AnsibleExitJson) as exc:
- self.get_demand_mock_object('task').apply()
- assert not exc.value.args[0]['changed']
-
- def test_successfully_delete(self):
- data = self.mock_args()
- data['state'] = 'absent'
- set_module_args(data)
- with pytest.raises(AnsibleExitJson) as exc:
- self.get_demand_mock_object('task').apply()
- assert exc.value.args[0]['changed']
-
- def test_delete_idempotency(self):
- data = self.mock_args()
- data['state'] = 'absent'
- set_module_args(data)
- with pytest.raises(AnsibleExitJson) as exc:
- self.get_demand_mock_object().apply()
- assert not exc.value.args[0]['changed']
diff --git a/test/units/modules/storage/netapp/test_na_ontap_vscan_scanner_pool.py b/test/units/modules/storage/netapp/test_na_ontap_vscan_scanner_pool.py
deleted file mode 100644
index 3609e91ef8..0000000000
--- a/test/units/modules/storage/netapp/test_na_ontap_vscan_scanner_pool.py
+++ /dev/null
@@ -1,179 +0,0 @@
-# (c) 2018, NetApp, Inc
-# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-
-''' unit tests for Ansible module: na_ontap_vscan_scanner_pool '''
-
-from __future__ import print_function
-import json
-import pytest
-
-from units.compat import unittest
-from units.compat.mock import patch, Mock
-from ansible.module_utils import basic
-from ansible.module_utils._text import to_bytes
-import ansible.module_utils.netapp as netapp_utils
-
-from ansible.modules.storage.netapp.na_ontap_vscan_scanner_pool \
- import NetAppOntapVscanScannerPool as scanner_module # module under test
-
-if not netapp_utils.has_netapp_lib():
- pytestmark = pytest.mark.skip('skipping as missing required netapp_lib')
-
-
-def set_module_args(args):
- """prepare arguments so that they will be picked up during module creation"""
- args = json.dumps({'ANSIBLE_MODULE_ARGS': args})
- basic._ANSIBLE_ARGS = to_bytes(args) # pylint: disable=protected-access
-
-
-class AnsibleExitJson(Exception):
- """Exception class to be raised by module.exit_json and caught by the test case"""
- pass
-
-
-class AnsibleFailJson(Exception):
- """Exception class to be raised by module.fail_json and caught by the test case"""
- pass
-
-
-def exit_json(*args, **kwargs): # pylint: disable=unused-argument
- """function to patch over exit_json; package return data into an exception"""
- if 'changed' not in kwargs:
- kwargs['changed'] = False
- raise AnsibleExitJson(kwargs)
-
-
-def fail_json(*args, **kwargs): # pylint: disable=unused-argument
- """function to patch over fail_json; package return data into an exception"""
- kwargs['failed'] = True
- raise AnsibleFailJson(kwargs)
-
-
-class MockONTAPConnection(object):
- ''' mock server connection to ONTAP host '''
-
- def __init__(self, kind=None, data=None):
- ''' save arguments '''
- self.kind = kind
- self.params = data
- self.xml_in = None
- self.xml_out = None
-
- def invoke_successfully(self, xml, enable_tunneling): # pylint: disable=unused-argument
- ''' mock invoke_successfully returning xml data '''
- self.xml_in = xml
- if self.kind == 'scanner':
- xml = self.build_scanner_pool_info(self.params)
- self.xml_out = xml
- return xml
-
- @staticmethod
- def build_scanner_pool_info(sanner_details):
- xml = netapp_utils.zapi.NaElement('xml')
- attributes = {
- 'num-records': 1,
- 'attributes-list': {
- 'vscan-scanner-pool-info': {
- 'scanner-pool': sanner_details['scanner_pool'],
- 'scanner-policy': sanner_details['scanner_policy']
- }
- }
- }
- xml.translate_struct(attributes)
- return xml
-
-
-class TestMyModule(unittest.TestCase):
- ''' Unit tests for na_ontap_job_schedule '''
-
- def setUp(self):
- self.mock_module_helper = patch.multiple(basic.AnsibleModule,
- exit_json=exit_json,
- fail_json=fail_json)
- self.mock_module_helper.start()
- self.addCleanup(self.mock_module_helper.stop)
- self.mock_scanner = {
- 'state': 'present',
- 'scanner_pool': 'test_pool',
- 'vserver': 'test_vserver',
- 'hostnames': ['host1', 'host2'],
- 'privileged_users': ['domain\\admin', 'domain\\carchi8py'],
- 'scanner_policy': 'primary'
- }
-
- def mock_args(self):
- return {
- 'state': self.mock_scanner['state'],
- 'scanner_pool': self.mock_scanner['scanner_pool'],
- 'vserver': self.mock_scanner['vserver'],
- 'hostnames': self.mock_scanner['hostnames'],
- 'privileged_users': self.mock_scanner['privileged_users'],
- 'hostname': 'test',
- 'username': 'test_user',
- 'password': 'test_pass!',
- 'scanner_policy': self.mock_scanner['scanner_policy']
- }
-
- def get_scanner_mock_object(self, kind=None):
- scanner_obj = scanner_module()
- scanner_obj.asup_log_for_cserver = Mock(return_value=None)
- if kind is None:
- scanner_obj.server = MockONTAPConnection()
- else:
- scanner_obj.server = MockONTAPConnection(kind='scanner', data=self.mock_scanner)
- return scanner_obj
-
- def test_module_fail_when_required_args_missing(self):
- ''' required arguments are reported as errors '''
- with pytest.raises(AnsibleFailJson) as exc:
- set_module_args({})
- scanner_module()
- print('Info: %s' % exc.value.args[0]['msg'])
-
- def test_get_nonexistent_scanner(self):
- ''' Test if get_scanner_pool returns None for non-existent job '''
- set_module_args(self.mock_args())
- result = self.get_scanner_mock_object().get_scanner_pool()
- assert not result
-
- def test_get_existing_scanner(self):
- ''' Test if get_scanner_pool returns None for non-existent job '''
- set_module_args(self.mock_args())
- result = self.get_scanner_mock_object('scanner').get_scanner_pool()
- assert result
-
- def test_successfully_create(self):
- set_module_args(self.mock_args())
- with pytest.raises(AnsibleExitJson) as exc:
- self.get_scanner_mock_object().apply()
- assert exc.value.args[0]['changed']
-
- def test_create_idempotency(self):
- set_module_args(self.mock_args())
- with pytest.raises(AnsibleExitJson) as exc:
- self.get_scanner_mock_object('scanner').apply()
- assert not exc.value.args[0]['changed']
-
- def test_apply_policy(self):
- data = self.mock_args()
- data['scanner_policy'] = 'secondary'
- set_module_args(data)
- with pytest.raises(AnsibleExitJson) as exc:
- self.get_scanner_mock_object('scanner').apply()
- assert exc.value.args[0]['changed']
-
- def test_successfully_delete(self):
- data = self.mock_args()
- data['state'] = 'absent'
- set_module_args(data)
- with pytest.raises(AnsibleExitJson) as exc:
- self.get_scanner_mock_object('scanner').apply()
- assert exc.value.args[0]['changed']
-
- def test_delete_idempotency(self):
- data = self.mock_args()
- data['state'] = 'absent'
- set_module_args(data)
- with pytest.raises(AnsibleExitJson) as exc:
- self.get_scanner_mock_object().apply()
- assert not exc.value.args[0]['changed']
diff --git a/test/units/modules/storage/netapp/test_na_ontap_vserver_cifs_security.py b/test/units/modules/storage/netapp/test_na_ontap_vserver_cifs_security.py
deleted file mode 100644
index d6dd0129fd..0000000000
--- a/test/units/modules/storage/netapp/test_na_ontap_vserver_cifs_security.py
+++ /dev/null
@@ -1,161 +0,0 @@
-# (c) 2019, NetApp, Inc
-# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-
-''' unit test template for ONTAP Ansible module '''
-
-from __future__ import (absolute_import, division, print_function)
-__metaclass__ = type
-import json
-import pytest
-
-from units.compat import unittest
-from units.compat.mock import patch, Mock
-from ansible.module_utils import basic
-from ansible.module_utils._text import to_bytes
-import ansible.module_utils.netapp as netapp_utils
-
-from ansible.modules.storage.netapp.na_ontap_vserver_cifs_security \
- import NetAppONTAPCifsSecurity as cifs_security_module # module under test
-
-if not netapp_utils.has_netapp_lib():
- pytestmark = pytest.mark.skip('skipping as missing required netapp_lib')
-
-
-def set_module_args(args):
- """prepare arguments so that they will be picked up during module creation"""
- args = json.dumps({'ANSIBLE_MODULE_ARGS': args})
- basic._ANSIBLE_ARGS = to_bytes(args) # pylint: disable=protected-access
-
-
-class AnsibleExitJson(Exception):
- """Exception class to be raised by module.exit_json and caught by the test case"""
- pass
-
-
-class AnsibleFailJson(Exception):
- """Exception class to be raised by module.fail_json and caught by the test case"""
- pass
-
-
-def exit_json(*args, **kwargs): # pylint: disable=unused-argument
- """function to patch over exit_json; package return data into an exception"""
- if 'changed' not in kwargs:
- kwargs['changed'] = False
- raise AnsibleExitJson(kwargs)
-
-
-def fail_json(*args, **kwargs): # pylint: disable=unused-argument
- """function to patch over fail_json; package return data into an exception"""
- kwargs['failed'] = True
- raise AnsibleFailJson(kwargs)
-
-
-class MockONTAPConnection(object):
- ''' mock server connection to ONTAP host '''
-
- def __init__(self, kind=None, data=None):
- ''' save arguments '''
- self.type = kind
- self.data = data
- self.xml_in = None
- self.xml_out = None
-
- def invoke_successfully(self, xml, enable_tunneling): # pylint: disable=unused-argument
- ''' mock invoke_successfully returning xml data '''
- self.xml_in = xml
- if self.type == 'cifs_security':
- xml = self.build_port_info(self.data)
- if self.type == 'error':
- error = netapp_utils.zapi.NaApiError('test', 'error')
- raise error
- self.xml_out = xml
- return xml
-
- @staticmethod
- def build_port_info(cifs_security_details):
- ''' build xml data for cifs-security '''
- xml = netapp_utils.zapi.NaElement('xml')
- attributes = {
- 'num-records': 1,
- 'attributes-list': {
- 'cifs-security': {
- 'is_aes_encryption_enabled': cifs_security_details['is_aes_encryption_enabled'],
- 'lm_compatibility_level': cifs_security_details['lm_compatibility_level']
- }
- }
- }
- xml.translate_struct(attributes)
- return xml
-
-
-class TestMyModule(unittest.TestCase):
- ''' a group of related Unit Tests '''
-
- def setUp(self):
- self.mock_module_helper = patch.multiple(basic.AnsibleModule,
- exit_json=exit_json,
- fail_json=fail_json)
- self.mock_module_helper.start()
- self.addCleanup(self.mock_module_helper.stop)
- self.mock_cifs_security = {
- 'is_aes_encryption_enabled': 'true',
- 'lm_compatibility_level': 'krb'
- }
-
- def mock_args(self):
- return {
- 'is_aes_encryption_enabled': self.mock_cifs_security['is_aes_encryption_enabled'],
- 'lm_compatibility_level': self.mock_cifs_security['lm_compatibility_level'],
- 'vserver': 'ansible',
- 'hostname': 'test',
- 'username': 'test_user',
- 'password': 'test_pass!',
- 'https': 'False'
- }
-
- def get_cifs_security_mock_object(self, kind=None):
- """
- Helper method to return an na_ontap_vserver_cifs_security object
- :param kind: passes this param to MockONTAPConnection()
- :return: na_ontap_vserver_cifs_security object
- """
- obj = cifs_security_module()
- obj.asup_log_for_cserver = Mock(return_value=None)
- obj.server = Mock()
- obj.server.invoke_successfully = Mock()
- if kind is None:
- obj.server = MockONTAPConnection()
- else:
- obj.server = MockONTAPConnection(kind=kind, data=self.mock_cifs_security)
- return obj
-
- @patch('ansible.modules.storage.netapp.na_ontap_vserver_cifs_security.NetAppONTAPCifsSecurity.cifs_security_get_iter')
- def test_successful_modify(self, get_cifs_security):
- ''' Test successful modify max throughput '''
- data = self.mock_args()
- set_module_args(data)
- current = {
- 'is_aes_encryption_enabled': False,
- 'lm_compatibility_level': 'lm_ntlm_ntlmv2_krb'
- }
- get_cifs_security.side_effect = [
- current
- ]
- with pytest.raises(AnsibleExitJson) as exc:
- self.get_cifs_security_mock_object('cifs_security').apply()
- assert exc.value.args[0]['changed']
-
- @patch('ansible.modules.storage.netapp.na_ontap_vserver_cifs_security.NetAppONTAPCifsSecurity.cifs_security_get_iter')
- def test_modify_error(self, get_cifs_security):
- ''' Test create idempotency '''
- data = self.mock_args()
- set_module_args(data)
- current = {
- 'is_aes_encryption_enabled': False
- }
- get_cifs_security.side_effect = [
- current
- ]
- with pytest.raises(AnsibleFailJson) as exc:
- self.get_cifs_security_mock_object('error').apply()
- assert exc.value.args[0]['msg'] == 'Error modifying cifs security on ansible: NetApp API failed. Reason - test:error'