Skip to content
This repository has been archived by the owner on Feb 24, 2022. It is now read-only.

Commit

Permalink
Sync bitbucket and GitHub
Browse files Browse the repository at this point in the history
  • Loading branch information
carchi8py committed Nov 4, 2020
1 parent c807539 commit 9ed4d9e
Show file tree
Hide file tree
Showing 5 changed files with 427 additions and 1 deletion.
Binary file modified ansible_collections/netapp/azure/netapp-azure-20.8.0.tar.gz
Binary file not shown.
5 changes: 5 additions & 0 deletions ansible_collections/netapp/ontap/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@ Join our Slack Channel at [Netapp.io](http://netapp.io/slack)

## 20.11.0


### New Modules
- na_ontap_metrocluster_dr_group: Configure a Metrocluster DR group (Supports ONTAP 9.8+)

### Minor changes
- na_ontap_cifs - output `modified` if a modify action is taken.
- na_ontap_cluster_peer: optional parameter 'ipspace' added for cluster peer.
Expand All @@ -42,6 +46,7 @@ Join our Slack Channel at [Netapp.io](http://netapp.io/slack)
- na_ontap_info - Use `node-id` as key rather than `current-version`.
- na_ontap_ipspace - invalid call in error reporting (double error).
- na_ontap_lun - `use_exact_size` to create a lun with the exact given size so that the lun is not rounded up.
- na_ontap_metrocluster: Fix issue where module would fail on waiting for rest api job
- na_ontap_software_update - module is not idempotent.

## 20.10.0
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -462,8 +462,11 @@ def __init__(self, module, timeout=60):
self.check_required_library()

def requires_ontap_9_6(self, module_name):
self.requires_ontap_version(module_name)

def requires_ontap_version(self, module_name, version='9.6'):
suffix = " - %s" % self.is_rest_error if self.is_rest_error is not None else ""
return "%s only support REST, and requires ONTAP 9.6 or later.%s" % (module_name, suffix)
return "%s only support REST, and requires ONTAP %s or later.%s" % (module_name, version, suffix)

def check_required_library(self):
if not HAS_REQUESTS:
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,222 @@
#!/usr/bin/python
"""
(c) 2020, 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_metrocluster_dr_group
short_description: NetApp ONTAP manage MetroCluster DR Group
extends_documentation_fragment:
- netapp.ontap.netapp.na_ontap
version_added: 20.11.0
author: NetApp Ansible Team (@carchi8py) <ng-ansibleteam@netapp.com>
requirements:
- ONTAP >= 9.8
description:
- Create/Delete MetroCluster DR Group
- Create only supports MCC IP
- Delete supports both MCC IP and MCC FC
options:
state:
choices: ['present', 'absent']
description:
add or remove DR groups
default: present
type: str
dr_pairs:
description: disaster recovery pairs
type: list
required: true
elements: dict
suboptions:
node_name:
description:
- the name of the main node
required: true
type: str
partner_node_name:
description:
- the name of the main partner node
required: true
type: str
partner_cluster_name:
description:
- The name of the partner cluster
required: true
type: str
'''

EXAMPLES = '''
-
name: Manage MetroCluster DR group
hosts: localhost
collections:
- netapp.ontap
vars:
login: &login
hostname: "{{ hostname }}"
username: "{{ username }}"
password: "{{ password }}"
https: True
validate_certs: False
tasks:
- name: Create MetroCluster DR group
na_ontap_metrocluster_dr_group:
<<: *login
dr_pairs:
- partner_name: carchi_cluster3_01
node_name: carchi_cluster1_01
partner_cluster_name: carchi_cluster3
- name: Delete MetroCluster DR group
na_ontap_metrocluster_dr_group:
<<: *login
dr_pairs:
- partner_name: carchi_cluster3_01
node_name: carchi_cluster1_01
state: absent
partner_cluster_name: carchi_cluster3
'''

RETURN = '''
'''

from ansible.module_utils.basic import AnsibleModule
import ansible_collections.netapp.ontap.plugins.module_utils.netapp as netapp_utils
from ansible_collections.netapp.ontap.plugins.module_utils.netapp_module import NetAppModule
from ansible_collections.netapp.ontap.plugins.module_utils.netapp import OntapRestAPI


class NetAppONTAPMetroClusterDRGroup(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'),
dr_pairs=dict(required=True, type='list', elements='dict', options=dict(
node_name=dict(required=True, type='str'),
partner_node_name=dict(required=True, type='str')
)),
partner_cluster_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)
self.rest_api = OntapRestAPI(self.module)
self.use_rest = self.rest_api.is_rest()

if not self.use_rest:
self.module.fail_json(msg=self.rest_api.requires_ontap_version('na_ontap_metrocluster_dr_group',
version='9.8'))

def get_dr_group(self):
return_attrs = None
for pair in self.parameters['dr_pairs']:
api = 'cluster/metrocluster/dr-groups'
options = {'fields': '*',
'dr_pairs.node.name': pair['node_name'],
'dr_pairs.partner.name': pair['partner_node_name'],
'partner_cluster.name': self.parameters['partner_cluster_name']}
message, error = self.rest_api.get(api, options)
if error:
self.module.fail_json(msg=error)
if 'records' in message and message['num_records'] == 0:
continue
elif 'records' not in message or message['num_records'] != 1:
error = "Unexpected response from %s: %s" % (api, repr(message))
self.module.fail_json(msg=error)
record = message['records'][0]
return_attrs = {
'partner_cluster_name': record['partner_cluster']['name'],
'dr_pairs': [],
'id': record['id']
}
for dr_pair in record['dr_pairs']:
return_attrs['dr_pairs'].append({'node_name': dr_pair['node']['name'], 'partner_node_name': dr_pair['partner']['name']})
# if we have an return_dr_id we don't need to loop anymore
break
return return_attrs

def get_dr_group_ids_from_nodes(self):
delete_ids = []
for pair in self.parameters['dr_pairs']:
api = 'cluster/metrocluster/nodes'
options = {'fields': '*',
'node.name': pair['node_name']}
message, error = self.rest_api.get(api, options)
if error:
self.module.fail_json(msg=error)
if 'records' in message and message['num_records'] == 0:
continue
elif 'records' not in message or message['num_records'] != 1:
error = "Unexpected response from %s: %s" % (api, repr(message))
self.module.fail_json(msg=error)
record = message['records'][0]
if int(record['dr_group_id']) not in delete_ids:
delete_ids.append(int(record['dr_group_id']))
return delete_ids

def create_dr_group(self):
api = 'cluster/metrocluster/dr-groups'
dr_pairs = []
for pair in self.parameters['dr_pairs']:
dr_pairs.append({'node': {'name': pair['node_name']},
'partner': {'name': pair['partner_node_name']}})
partner_cluster = {'name': self.parameters['partner_cluster_name']}
data = {'dr_pairs': dr_pairs, 'partner_cluster': partner_cluster}
message, error = self.rest_api.post(api, data)
if error is not None:
self.module.fail_json(msg="%s" % error)
message, error = self.rest_api.wait_on_job(message['job'])
if error:
self.module.fail_json(msg="%s" % error)

def delete_dr_groups(self, dr_ids):
for dr_id in dr_ids:
api = 'cluster/metrocluster/dr-groups/' + str(dr_id)
message, error = self.rest_api.delete(api)
if error:
self.module.fail_json(msg=error)
message, error = self.rest_api.wait_on_job(message['job'])
if error:
self.module.fail_json(msg="%s" % error)

def apply(self):
current = self.get_dr_group()
delete_ids = None
cd_action = self.na_helper.get_cd_action(current, self.parameters)
if cd_action is None and current is None and self.parameters['state'] == 'absent':
# check if there is some FC group to delete
delete_ids = self.get_dr_group_ids_from_nodes()
if delete_ids:
cd_action = 'delete'
self.na_helper.changed = True
elif cd_action == 'delete':
delete_ids = [current['id']]
if cd_action and not self.module.check_mode:
if cd_action == 'create':
self.create_dr_group()
if cd_action == 'delete':
self.delete_dr_groups(delete_ids)
self.module.exit_json(changed=self.na_helper.changed)


def main():
obj = NetAppONTAPMetroClusterDRGroup()
obj.apply()


if __name__ == '__main__':
main()
Loading

0 comments on commit 9ed4d9e

Please sign in to comment.