diff --git a/ansible_collections/netapp/ontap/README.md b/ansible_collections/netapp/ontap/README.md index 9c3aa262..5f8cd44f 100644 --- a/ansible_collections/netapp/ontap/README.md +++ b/ansible_collections/netapp/ontap/README.md @@ -29,6 +29,9 @@ Join our Slack Channel at [Netapp.io](http://netapp.io/slack) ## 21.6.0 +### New Options + - na_ontap_vserver_peer - new options `local_name_for_source` and `local_name_for_peer` added. + ### Minor changes - na_ontap_rest_info - Added "autosupport_check_info"/"support/autosupport/check" to the attributes that will be collected when gathering info using the module. diff --git a/ansible_collections/netapp/ontap/changelogs/fragments/DEVOPS-3807.yaml b/ansible_collections/netapp/ontap/changelogs/fragments/DEVOPS-3807.yaml new file mode 100644 index 00000000..5378d29a --- /dev/null +++ b/ansible_collections/netapp/ontap/changelogs/fragments/DEVOPS-3807.yaml @@ -0,0 +1,2 @@ +minor_changes: + - na_ontap_vserver_peer - new options ``local_name_for_source`` and ``local_name_for_peer`` added. diff --git a/ansible_collections/netapp/ontap/plugins/modules/na_ontap_vserver_peer.py b/ansible_collections/netapp/ontap/plugins/modules/na_ontap_vserver_peer.py index 90a4c077..bc321db1 100644 --- a/ansible_collections/netapp/ontap/plugins/modules/na_ontap_vserver_peer.py +++ b/ansible_collections/netapp/ontap/plugins/modules/na_ontap_vserver_peer.py @@ -45,6 +45,16 @@ - Specifies name of the peer Cluster. - Required for creating the vserver peer relationship with a remote cluster type: str + local_name_for_peer: + description: + - Specifies local name of the peer Vserver in the relationship. + - Use this if you see "Error creating vserver peer ... Vserver name conflicts with one of the following". + type: str + local_name_for_source: + description: + - Specifies local name of the source Vserver in the relationship. + - Use this if you see "Error accepting vserver peer ... System generated a name for the peer Vserver because of a naming conflict". + type: str dest_hostname: description: - Destination hostname or IP address. @@ -71,6 +81,8 @@ state: present peer_vserver: ansible2 peer_cluster: ansibleCluster + local_name_for_peer: peername + local_name_for_source: sourcename vserver: ansible applications: ['snapmirror'] hostname: "{{ netapp_hostname }}" @@ -113,6 +125,8 @@ def __init__(self): vserver=dict(required=True, type='str'), peer_vserver=dict(required=True, type='str'), peer_cluster=dict(required=False, type='str'), + local_name_for_peer=dict(required=False, type='str'), + local_name_for_source=dict(required=False, type='str'), applications=dict(required=False, type='list', elements='str'), dest_hostname=dict(required=False, type='str'), dest_username=dict(required=False, type='str'), @@ -143,29 +157,36 @@ def __init__(self): self.module.params['username'] = self.parameters['username'] self.module.params['password'] = self.parameters['password'] - def vserver_peer_get_iter(self): + def vserver_peer_get_iter(self, target): """ - Compose NaElement object to query current vserver using peer-vserver and vserver parameters + Compose NaElement object to query current vserver using remote-vserver-name 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']) + if target == 'source': + vserver_peer_info.add_new_child('remote-vserver-name', self.parameters['peer_vserver']) + vserver_peer_info.add_new_child('vserver', self.parameters['vserver']) + elif target == 'peer': + vserver_peer_info.add_new_child('remote-vserver-name', self.parameters['vserver']) + vserver_peer_info.add_new_child('vserver', self.parameters['peer_vserver']) query.add_child_elem(vserver_peer_info) vserver_peer_get.add_child_elem(query) return vserver_peer_get - def vserver_peer_get(self): + def vserver_peer_get(self, target='source'): """ 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_peer_get_iter = self.vserver_peer_get_iter(target) vserver_info = dict() try: - result = self.server.invoke_successfully(vserver_peer_get_iter, enable_tunneling=True) + if target == 'source': + result = self.server.invoke_successfully(vserver_peer_get_iter, enable_tunneling=True) + elif target == 'peer': + result = self.dest_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)), @@ -174,18 +195,19 @@ def vserver_peer_get(self): 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['peer_vserver'] = vserver_peer_info.get_child_content('remote-vserver-name') vserver_info['vserver'] = vserver_peer_info.get_child_content('vserver') + vserver_info['local_peer_vserver'] = vserver_peer_info.get_child_content('peer-vserver') # required for delete and accept vserver_info['peer_state'] = vserver_peer_info.get_child_content('peer-state') return vserver_info return None - def vserver_peer_delete(self): + def vserver_peer_delete(self, current): """ 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-peer-delete', **{'peer-vserver': current['local_peer_vserver'], 'vserver': self.parameters['vserver']}) try: self.server.invoke_successfully(vserver_peer_delete, @@ -224,6 +246,8 @@ def vserver_peer_create(self): 'vserver-peer-create', **{'peer-vserver': self.parameters['peer_vserver'], 'vserver': self.parameters['vserver'], 'peer-cluster': self.parameters['peer_cluster']}) + if 'local_name_for_peer' in self.parameters: + vserver_peer_create.add_new_child('local-name', self.parameters['local_name_for_peer']) applications = netapp_utils.zapi.NaElement('applications') for application in self.parameters['applications']: applications.add_new_child('vserver-peer-application', application) @@ -248,9 +272,14 @@ def vserver_peer_accept(self): """ # peer-vserver -> remote (source vserver is provided) # vserver -> local (destination vserver is provided) + vserver_peer_info = self.vserver_peer_get('peer') + if vserver_peer_info is None: + self.module.fail_json(msg='Error retrieving vserver peer information while accepting') vserver_peer_accept = netapp_utils.zapi.NaElement.create_node_with_children( - 'vserver-peer-accept', **{'peer-vserver': self.parameters['vserver'], + 'vserver-peer-accept', **{'peer-vserver': vserver_peer_info['local_peer_vserver'], 'vserver': self.parameters['peer_vserver']}) + if 'local_name_for_source' in self.parameters: + vserver_peer_accept.add_new_child('local-name', self.parameters['local_name_for_source']) try: self.dest_server.invoke_successfully(vserver_peer_accept, enable_tunneling=True) except netapp_utils.zapi.NaApiError as error: @@ -284,7 +313,7 @@ def apply(self): if self.is_remote_peer(): self.vserver_peer_accept() elif cd_action == 'delete': - self.vserver_peer_delete() + self.vserver_peer_delete(current) self.module.exit_json(changed=self.na_helper.changed) diff --git a/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_vserver_peer.py b/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_vserver_peer.py index 01523aac..a8636d22 100644 --- a/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_vserver_peer.py +++ b/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_vserver_peer.py @@ -79,8 +79,9 @@ def build_vserver_peer_info(vserver): 'num-records': 1, 'attributes-list': { 'vserver-peer-info': { - 'peer-vserver': vserver['peer_vserver'], + 'remote-vserver-name': vserver['peer_vserver'], 'vserver': vserver['vserver'], + 'peer-vserver': vserver['peer_vserver'], 'peer-state': 'peered' } } @@ -118,6 +119,8 @@ def setUp(self): 'vserver': 'test', 'peer_vserver': 'test_peer', 'peer_cluster': 'test_cluster_peer', + 'local_name_for_peer': 'peer_name', + 'local_name_for_source': 'source_name', 'applications': ['snapmirror'], 'hostname': 'hostname', 'dest_hostname': 'hostname', @@ -156,9 +159,16 @@ def test_successful_create(self, vserver_peer_get): data['dest_hostname'] = 'test_destination' set_module_args(self.mock_vserver_peer) vserver_peer_get.return_value = None - with pytest.raises(AnsibleExitJson) as exc: - self.get_vserver_peer_mock_object().apply() - assert exc.value.args[0]['changed'] + + self.get_vserver_peer_mock_object().vserver_peer_create() + current = { + 'vserver': 'test', + 'peer_vserver': self.mock_vserver_peer['peer_vserver'], + 'local_peer_vserver': self.mock_vserver_peer['peer_vserver'], + 'peer_cluster': self.mock_vserver_peer['peer_cluster'] + } + vserver_peer_get.return_value = current + self.get_vserver_peer_mock_object().vserver_peer_accept() @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_vserver_peer.NetAppONTAPVserverPeer.vserver_peer_get') def test_create_idempotency(self, vserver_peer_get): @@ -182,10 +192,14 @@ def test_successful_delete(self, vserver_peer_get): data = self.mock_vserver_peer data['state'] = 'absent' set_module_args(data) - vserver_peer_get.return_value = Mock() - with pytest.raises(AnsibleExitJson) as exc: - self.get_vserver_peer_mock_object('vserver_peer').apply() - assert exc.value.args[0]['changed'] + current = { + 'vserver': 'test', + 'peer_vserver': self.mock_vserver_peer['peer_vserver'], + 'peer_cluster': self.mock_vserver_peer['peer_cluster'], + 'local_peer_vserver': self.mock_vserver_peer['local_name_for_peer'] + } + vserver_peer_get.return_value = current + self.get_vserver_peer_mock_object('vserver_peer').vserver_peer_delete(current) @patch('ansible_collections.netapp.ontap.plugins.modules.na_ontap_vserver_peer.NetAppONTAPVserverPeer.vserver_peer_get') def test_delete_idempotency(self, vserver_peer_get): @@ -202,13 +216,13 @@ def test_helper_vserver_peer_get_iter(self): ''' Test vserver_peer_get_iter method ''' set_module_args(self.mock_vserver_peer) obj = self.get_vserver_peer_mock_object('vserver_peer') - result = obj.vserver_peer_get_iter() + result = obj.vserver_peer_get_iter('source') print(result.to_string(pretty=True)) assert result['query'] is not None assert result['query']['vserver-peer-info'] is not None info = result['query']['vserver-peer-info'] assert info['vserver'] == self.mock_vserver_peer['vserver'] - assert info['peer-vserver'] == self.mock_vserver_peer['peer_vserver'] + assert info['remote-vserver-name'] == self.mock_vserver_peer['peer_vserver'] def test_get_packet(self): ''' Test vserver_peer_get method '''