Merge "[NetApp] Fixed scoped account replica delete" into stable/train
This commit is contained in:
commit
9e277158ac
|
@ -3412,26 +3412,125 @@ class NetAppCmodeClient(client_base.NetAppBaseClient):
|
||||||
|
|
||||||
return result_info
|
return result_info
|
||||||
|
|
||||||
|
@na_utils.trace
|
||||||
|
def _get_snapmirror_destinations(self, source_path=None, dest_path=None,
|
||||||
|
source_vserver=None, source_volume=None,
|
||||||
|
dest_vserver=None, dest_volume=None,
|
||||||
|
desired_attributes=None):
|
||||||
|
"""Gets one or more SnapMirror at source endpoint."""
|
||||||
|
|
||||||
|
snapmirror_info = self._build_snapmirror_request(
|
||||||
|
source_path, dest_path, source_vserver,
|
||||||
|
dest_vserver, source_volume, dest_volume)
|
||||||
|
api_args = {}
|
||||||
|
if snapmirror_info:
|
||||||
|
api_args['query'] = {
|
||||||
|
'snapmirror-destination-info': snapmirror_info
|
||||||
|
}
|
||||||
|
if desired_attributes:
|
||||||
|
api_args['desired-attributes'] = desired_attributes
|
||||||
|
|
||||||
|
result = self.send_iter_request('snapmirror-get-destination-iter',
|
||||||
|
api_args)
|
||||||
|
if not self._has_records(result):
|
||||||
|
return []
|
||||||
|
else:
|
||||||
|
return result.get_child_by_name('attributes-list').get_children()
|
||||||
|
|
||||||
|
@na_utils.trace
|
||||||
|
def get_snapmirror_destinations(self, source_path=None, dest_path=None,
|
||||||
|
source_vserver=None, dest_vserver=None,
|
||||||
|
source_volume=None, dest_volume=None,
|
||||||
|
desired_attributes=None):
|
||||||
|
"""Gets one or more SnapMirror relationships in the source endpoint.
|
||||||
|
|
||||||
|
Either the source or destination info may be omitted.
|
||||||
|
Desired attributes should be a flat list of attribute names.
|
||||||
|
"""
|
||||||
|
self._ensure_snapmirror_v2()
|
||||||
|
|
||||||
|
if desired_attributes is not None:
|
||||||
|
desired_attributes = {
|
||||||
|
'snapmirror-destination-info': {
|
||||||
|
attr: None for attr in desired_attributes},
|
||||||
|
}
|
||||||
|
|
||||||
|
result = self._get_snapmirror_destinations(
|
||||||
|
source_path=source_path,
|
||||||
|
dest_path=dest_path,
|
||||||
|
source_vserver=source_vserver,
|
||||||
|
source_volume=source_volume,
|
||||||
|
dest_vserver=dest_vserver,
|
||||||
|
dest_volume=dest_volume,
|
||||||
|
desired_attributes=desired_attributes)
|
||||||
|
|
||||||
|
snapmirrors = []
|
||||||
|
|
||||||
|
for snapmirror_info in result:
|
||||||
|
snapmirror = {}
|
||||||
|
for child in snapmirror_info.get_children():
|
||||||
|
name = self._strip_xml_namespace(child.get_name())
|
||||||
|
snapmirror[name] = child.get_content()
|
||||||
|
snapmirrors.append(snapmirror)
|
||||||
|
|
||||||
|
return snapmirrors
|
||||||
|
|
||||||
|
def _build_snapmirror_request(self, source_path=None, dest_path=None,
|
||||||
|
source_vserver=None, dest_vserver=None,
|
||||||
|
source_volume=None, dest_volume=None):
|
||||||
|
"""Build a default SnapMirror request."""
|
||||||
|
|
||||||
|
req_args = {}
|
||||||
|
if source_path:
|
||||||
|
req_args['source-location'] = source_path
|
||||||
|
if dest_path:
|
||||||
|
req_args['destination-location'] = dest_path
|
||||||
|
if source_vserver:
|
||||||
|
req_args['source-vserver'] = source_vserver
|
||||||
|
if source_volume:
|
||||||
|
req_args['source-volume'] = source_volume
|
||||||
|
if dest_vserver:
|
||||||
|
req_args['destination-vserver'] = dest_vserver
|
||||||
|
if dest_volume:
|
||||||
|
req_args['destination-volume'] = dest_volume
|
||||||
|
|
||||||
|
return req_args
|
||||||
|
|
||||||
@na_utils.trace
|
@na_utils.trace
|
||||||
def release_snapmirror(self, source_vserver, source_volume,
|
def release_snapmirror(self, source_vserver, source_volume,
|
||||||
destination_vserver, destination_volume,
|
destination_vserver, destination_volume,
|
||||||
relationship_info_only=False):
|
relationship_info_only=False):
|
||||||
"""Removes a SnapMirror relationship on the source endpoint."""
|
"""Removes a SnapMirror relationship on the source endpoint."""
|
||||||
self._ensure_snapmirror_v2()
|
|
||||||
|
|
||||||
api_args = {
|
self._ensure_snapmirror_v2()
|
||||||
'query': {
|
snapmirror_destinations_list = self.get_snapmirror_destinations(
|
||||||
'snapmirror-destination-info': {
|
source_vserver=source_vserver,
|
||||||
'source-volume': source_volume,
|
dest_vserver=destination_vserver,
|
||||||
'source-vserver': source_vserver,
|
source_volume=source_volume,
|
||||||
'destination-volume': destination_volume,
|
dest_volume=destination_volume,
|
||||||
'destination-vserver': destination_vserver,
|
desired_attributes=['relationship-id'])
|
||||||
'relationship-info-only': ('true' if relationship_info_only
|
|
||||||
else 'false'),
|
if len(snapmirror_destinations_list) > 1:
|
||||||
}
|
msg = ("Expected snapmirror relationship to be unique. "
|
||||||
}
|
"List returned: %s." % snapmirror_destinations_list)
|
||||||
}
|
raise exception.NetAppException(msg)
|
||||||
self.send_request('snapmirror-release-iter', api_args)
|
|
||||||
|
api_args = self._build_snapmirror_request(
|
||||||
|
source_vserver=source_vserver, dest_vserver=destination_vserver,
|
||||||
|
source_volume=source_volume, dest_volume=destination_volume)
|
||||||
|
api_args['relationship-info-only'] = (
|
||||||
|
'true' if relationship_info_only else 'false')
|
||||||
|
|
||||||
|
# NOTE(nahimsouza): This verification is needed because an empty list
|
||||||
|
# is returned in snapmirror_destinations_list when a single share is
|
||||||
|
# created with only one replica and this replica is deleted, thus there
|
||||||
|
# will be no relationship-id in that case.
|
||||||
|
if len(snapmirror_destinations_list) == 1:
|
||||||
|
api_args['relationship-id'] = (
|
||||||
|
snapmirror_destinations_list[0]['relationship-id'])
|
||||||
|
|
||||||
|
self.send_request('snapmirror-release', api_args,
|
||||||
|
enable_tunneling=True)
|
||||||
|
|
||||||
@na_utils.trace
|
@na_utils.trace
|
||||||
def quiesce_snapmirror(self, source_vserver, source_volume,
|
def quiesce_snapmirror(self, source_vserver, source_volume,
|
||||||
|
|
|
@ -5877,10 +5877,24 @@ class NetAppClientCmodeTestCase(test.TestCase):
|
||||||
}
|
}
|
||||||
self.assertEqual(expected, result)
|
self.assertEqual(expected, result)
|
||||||
|
|
||||||
@ddt.data(True, False)
|
@ddt.data({'snapmirror_destinations_list': [],
|
||||||
def test_release_snapmirror(self, relationship_info_only):
|
'relationship_info_only': True},
|
||||||
|
{'snapmirror_destinations_list': [],
|
||||||
|
'relationship_info_only': False},
|
||||||
|
{'snapmirror_destinations_list':
|
||||||
|
[{'relationship-id': 'fake_relationship_id'}],
|
||||||
|
'relationship_info_only': True},
|
||||||
|
{'snapmirror_destinations_list':
|
||||||
|
[{'relationship-id': 'fake_relationship_id'}],
|
||||||
|
'relationship_info_only': False})
|
||||||
|
@ddt.unpack
|
||||||
|
def test_release_snapmirror(self, relationship_info_only,
|
||||||
|
snapmirror_destinations_list):
|
||||||
self.mock_object(self.client, 'send_request')
|
self.mock_object(self.client, 'send_request')
|
||||||
|
self.mock_object(
|
||||||
|
self.client,
|
||||||
|
'get_snapmirror_destinations',
|
||||||
|
mock.Mock(return_value=snapmirror_destinations_list))
|
||||||
|
|
||||||
self.client.release_snapmirror(
|
self.client.release_snapmirror(
|
||||||
fake.SM_SOURCE_VSERVER, fake.SM_SOURCE_VOLUME,
|
fake.SM_SOURCE_VSERVER, fake.SM_SOURCE_VOLUME,
|
||||||
|
@ -5888,19 +5902,31 @@ class NetAppClientCmodeTestCase(test.TestCase):
|
||||||
relationship_info_only=relationship_info_only)
|
relationship_info_only=relationship_info_only)
|
||||||
|
|
||||||
snapmirror_release_args = {
|
snapmirror_release_args = {
|
||||||
'query': {
|
'source-vserver': fake.SM_SOURCE_VSERVER,
|
||||||
'snapmirror-destination-info': {
|
'source-volume': fake.SM_SOURCE_VOLUME,
|
||||||
'source-vserver': fake.SM_SOURCE_VSERVER,
|
'destination-vserver': fake.SM_DEST_VSERVER,
|
||||||
'source-volume': fake.SM_SOURCE_VOLUME,
|
'destination-volume': fake.SM_DEST_VOLUME,
|
||||||
'destination-vserver': fake.SM_DEST_VSERVER,
|
'relationship-info-only': ('true' if relationship_info_only
|
||||||
'destination-volume': fake.SM_DEST_VOLUME,
|
else 'false'),
|
||||||
'relationship-info-only': ('true' if relationship_info_only
|
|
||||||
else 'false'),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
self.client.send_request.assert_has_calls([
|
|
||||||
mock.call('snapmirror-release-iter', snapmirror_release_args)])
|
if len(snapmirror_destinations_list) == 1:
|
||||||
|
snapmirror_release_args['relationship-id'] = 'fake_relationship_id'
|
||||||
|
|
||||||
|
self.client.send_request.assert_called_once_with(
|
||||||
|
'snapmirror-release', snapmirror_release_args,
|
||||||
|
enable_tunneling=True)
|
||||||
|
|
||||||
|
def test_release_snapmirror_error_not_unique_relationship(self):
|
||||||
|
self.mock_object(self.client, 'send_request')
|
||||||
|
self.mock_object(self.client, 'get_snapmirror_destinations',
|
||||||
|
mock.Mock(return_value=[{'relationship-id': 'fake'},
|
||||||
|
{'relationship-id': 'fake'}]))
|
||||||
|
|
||||||
|
self.assertRaises(exception.NetAppException,
|
||||||
|
self.client.release_snapmirror,
|
||||||
|
fake.SM_SOURCE_VSERVER, fake.SM_SOURCE_VOLUME,
|
||||||
|
fake.SM_DEST_VSERVER, fake.SM_DEST_VOLUME)
|
||||||
|
|
||||||
def test_quiesce_snapmirror(self):
|
def test_quiesce_snapmirror(self):
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
---
|
||||||
|
fixes:
|
||||||
|
- |
|
||||||
|
NetApp driver: fixed an issue with the ONTAP 9.8 and older, for scoped
|
||||||
|
account users, where the operation of deleting a replica was not working,
|
||||||
|
but returned a message of success. For more details, please refer to
|
||||||
|
`launchpad bug #1934889 <https://bugs.launchpad.net/manila/+bug/1934889>`_
|
Loading…
Reference in New Issue