diff options
Diffstat (limited to 'lib/ansible/modules')
77 files changed, 0 insertions, 24480 deletions
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() |