From 9ed4d9ea5d885edc7be1d5d16a1b383dc5eb3f7e Mon Sep 17 00:00:00 2001 From: Chris Archibald Date: Wed, 4 Nov 2020 18:45:31 -0500 Subject: [PATCH] Sync bitbucket and GitHub --- .../netapp/azure/netapp-azure-20.8.0.tar.gz | Bin 19444 -> 19310 bytes ansible_collections/netapp/ontap/README.md | 5 + .../ontap/plugins/module_utils/netapp.py | 5 +- .../modules/na_ontap_metrocluster_dr_group.py | 222 ++++++++++++++++++ .../test_na_ontap_metrocluster_dr_group.py | 196 ++++++++++++++++ 5 files changed, 427 insertions(+), 1 deletion(-) create mode 100644 ansible_collections/netapp/ontap/plugins/modules/na_ontap_metrocluster_dr_group.py create mode 100644 ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_metrocluster_dr_group.py diff --git a/ansible_collections/netapp/azure/netapp-azure-20.8.0.tar.gz b/ansible_collections/netapp/azure/netapp-azure-20.8.0.tar.gz index 18983528bd3f34cd88ce1ceaa7405ce5b374c3f1..a8da82d0d77b97ead6ab8d339a1abf083822ebd8 100644 GIT binary patch literal 19310 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 Lc6ZKF0H^{0oyu{1 diff --git a/ansible_collections/netapp/ontap/README.md b/ansible_collections/netapp/ontap/README.md index af263cce..a3b67bd5 100644 --- a/ansible_collections/netapp/ontap/README.md +++ b/ansible_collections/netapp/ontap/README.md @@ -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. @@ -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 diff --git a/ansible_collections/netapp/ontap/plugins/module_utils/netapp.py b/ansible_collections/netapp/ontap/plugins/module_utils/netapp.py index e310b0bb..f58a6efd 100644 --- a/ansible_collections/netapp/ontap/plugins/module_utils/netapp.py +++ b/ansible_collections/netapp/ontap/plugins/module_utils/netapp.py @@ -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: diff --git a/ansible_collections/netapp/ontap/plugins/modules/na_ontap_metrocluster_dr_group.py b/ansible_collections/netapp/ontap/plugins/modules/na_ontap_metrocluster_dr_group.py new file mode 100644 index 00000000..d8345c3d --- /dev/null +++ b/ansible_collections/netapp/ontap/plugins/modules/na_ontap_metrocluster_dr_group.py @@ -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) +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() diff --git a/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_metrocluster_dr_group.py b/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_metrocluster_dr_group.py new file mode 100644 index 00000000..df349da4 --- /dev/null +++ b/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_metrocluster_dr_group.py @@ -0,0 +1,196 @@ +# (c) 2020, NetApp, Inc +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +''' unit tests for Ansible module: na_ontap_metrocluster ''' + +from __future__ import (absolute_import, division, print_function) + +__metaclass__ = type + +import json +import pytest + +from ansible_collections.netapp.ontap.tests.unit.compat import unittest +from ansible_collections.netapp.ontap.tests.unit.compat.mock import patch, Mock +from ansible.module_utils import basic +from ansible.module_utils._text import to_bytes + +from ansible_collections.netapp.ontap.plugins.modules.na_ontap_metrocluster_dr_group \ + import NetAppONTAPMetroClusterDRGroup as mcc_dr_pairs_module # module under test + +# REST API canned responses when mocking send_request +SRR = { + # common responses + 'is_rest': (200, {}, None), + 'is_zapi': (400, {}, "Unreachable"), + 'empty_good': (200, {}, None), + 'end_of_sequence': (500, None, "Unexpected call to send_request"), + 'generic_error': (400, None, "Expected error"), + # module specific responses + 'get_mcc_dr_pair_with_no_results': (200, {'records': [], 'num_records': 0}, None), + 'get_mcc_dr_pair_with_results': (200, {'records': [{'partner_cluster': {'name': 'rha2-b2b1_siteB'}, + 'dr_pairs': [{'node': {'name': 'rha17-a2'}, + 'partner': {'name': 'rha17-b2'}}, + {'node': {'name': 'rha17-b2'}, + 'partner': {'name': 'rha17-b1'}}], + 'id': '2'}], + 'num_records': 1}, None), + 'mcc_dr_pair_post': (200, {'job': { + 'uuid': 'fde79888-692a-11ea-80c2-005056b39fe7', + '_links': { + 'self': { + 'href': '/api/cluster/jobs/fde79888-692a-11ea-80c2-005056b39fe7'}}} + }, None), + 'get_mcc_dr_node': (200, {'records': [{'dr_group_id': '1'}], 'num_records': 1}, None), + 'get_mcc_dr_node_none': (200, {'records': [], 'num_records': 0}, None), + 'job': (200, { + "uuid": "cca3d070-58c6-11ea-8c0c-005056826c14", + "description": "POST /api/cluster/metrocluster", + "state": "success", + "message": "There are not enough disks in Pool1.", + "code": 2432836, + "start_time": "2020-02-26T10:35:44-08:00", + "end_time": "2020-02-26T10:47:38-08:00", + "_links": { + "self": { + "href": "/api/cluster/jobs/cca3d070-58c6-11ea-8c0c-005056826c14" + } + } + }, None) +} + + +def set_module_args(args): + """prepare arguments so that they will be picked up during module creation""" + args = json.dumps({'ANSIBLE_MODULE_ARGS': args}) + basic._ANSIBLE_ARGS = to_bytes(args) # pylint: disable=protected-access + + +class AnsibleExitJson(Exception): + """Exception class to be raised by module.exit_json and caught by the test case""" + + +class AnsibleFailJson(Exception): + """Exception class to be raised by module.fail_json and caught by the test case""" + + +def exit_json(*args, **kwargs): # pylint: disable=unused-argument + """function to patch over exit_json; package return data into an exception""" + if 'changed' not in kwargs: + kwargs['changed'] = False + raise AnsibleExitJson(kwargs) + + +def fail_json(*args, **kwargs): # pylint: disable=unused-argument + """function to patch over fail_json; package return data into an exception""" + kwargs['failed'] = True + raise AnsibleFailJson(kwargs) + + +class TestMyModule(unittest.TestCase): + """ Unit tests for na_ontap_metrocluster """ + + def setUp(self): + self.mock_module_helper = patch.multiple(basic.AnsibleModule, + exit_json=exit_json, + fail_json=fail_json) + self.mock_module_helper.start() + self.addCleanup(self.mock_module_helper.stop) + self.mock_mcc_dr_pair = { + 'partner_cluster_name': 'rha2-b2b1_siteB', + 'node_name': 'rha17-a2', + 'partner_node_name': 'rha17-b2', + 'node_name2': 'rha17-b2', + 'partner_node_name2': 'rha17-b1' + + } + + def mock_args(self): + return { + 'dr_pairs': [{ + 'node_name': self.mock_mcc_dr_pair['node_name'], + 'partner_node_name': self.mock_mcc_dr_pair['partner_node_name'], + }, { + 'node_name': self.mock_mcc_dr_pair['node_name2'], + 'partner_node_name': self.mock_mcc_dr_pair['partner_node_name2'], + }], + 'partner_cluster_name': self.mock_mcc_dr_pair['partner_cluster_name'], + 'hostname': 'test_host', + 'username': 'test_user', + 'password': 'test_pass!' + } + + def get_alias_mock_object(self): + alias_obj = mcc_dr_pairs_module() + return alias_obj + + @patch('ansible_collections.netapp.ontap.plugins.module_utils.netapp.OntapRestAPI.send_request') + def test_rest_successful_create(self, mock_request): + """Test successful rest create""" + data = self.mock_args() + set_module_args(data) + mock_request.side_effect = [ + SRR['is_rest'], + SRR['get_mcc_dr_pair_with_no_results'], + SRR['get_mcc_dr_pair_with_no_results'], + SRR['mcc_dr_pair_post'], + SRR['job'], + SRR['empty_good'], + SRR['end_of_sequence'] + ] + with pytest.raises(AnsibleExitJson) as exc: + self.get_alias_mock_object().apply() + assert exc.value.args[0]['changed'] + + @patch('ansible_collections.netapp.ontap.plugins.module_utils.netapp.OntapRestAPI.send_request') + def test_rest_create_idempotency(self, mock_request): + """Test rest create idempotency""" + data = self.mock_args() + set_module_args(data) + mock_request.side_effect = [ + SRR['is_rest'], + SRR['get_mcc_dr_pair_with_results'], + SRR['empty_good'], + SRR['end_of_sequence'] + ] + with pytest.raises(AnsibleExitJson) as exc: + self.get_alias_mock_object().apply() + assert not exc.value.args[0]['changed'] + + @patch('ansible_collections.netapp.ontap.plugins.module_utils.netapp.OntapRestAPI.send_request') + def test_rest_successful_delete(self, mock_request): + """Test successful rest delete""" + data = self.mock_args() + data['state'] = 'absent' + set_module_args(data) + mock_request.side_effect = [ + SRR['is_rest'], + SRR['get_mcc_dr_pair_with_results'], + SRR['mcc_dr_pair_post'], + SRR['job'], + SRR['empty_good'], + SRR['end_of_sequence'] + ] + with pytest.raises(AnsibleExitJson) as exc: + self.get_alias_mock_object().apply() + assert exc.value.args[0]['changed'] + + @patch('ansible_collections.netapp.ontap.plugins.module_utils.netapp.OntapRestAPI.send_request') + def test_rest_delete_idempotency(self, mock_request): + """Test rest delete idempotency""" + data = self.mock_args() + data['state'] = 'absent' + set_module_args(data) + mock_request.side_effect = [ + SRR['is_rest'], + SRR['get_mcc_dr_pair_with_no_results'], + SRR['get_mcc_dr_pair_with_no_results'], + SRR['get_mcc_dr_node_none'], + SRR['get_mcc_dr_node_none'], + SRR['job'], + SRR['empty_good'], + SRR['end_of_sequence'] + ] + with pytest.raises(AnsibleExitJson) as exc: + self.get_alias_mock_object().apply() + assert not exc.value.args[0]['changed']