NetApp ONTAP: Fixed errors on failover-host operation with REST API

Fixed the functions _set_snapmirror_state and break_snapmirror that
were causing a failure in the failover-host operation.

Change-Id: Ib4ebe3cafb67a16aef4b961ccf30dade99fdec7f
Closes-Bug: #2028857
This commit is contained in:
Nahim Alves de Souza 2023-07-27 15:57:22 +00:00
parent 0dc8d75229
commit 64b5543d92
4 changed files with 88 additions and 43 deletions

View File

@ -2865,6 +2865,7 @@ SNAPMIRROR_GET_ITER_RESPONSE_REST = {
"type": "async"
},
"state": "snapmirrored",
"transfer": {"state": "success"},
"healthy": True
}
],
@ -2957,10 +2958,11 @@ REST_GET_SNAPMIRRORS_RESPONSE = [{
'lag-time': None,
'last-transfer-end-timestamp': None,
'mirror-state': 'snapmirrored',
'relationship-status': 'snapmirrored',
'relationship-status': 'idle',
'source-volume': SM_SOURCE_VOLUME,
'source-vserver': SM_SOURCE_VSERVER,
'uuid': FAKE_UUID,
'transferring-state': 'success',
}]
TRANSFERS_GET_ITER_REST = {

View File

@ -2737,8 +2737,8 @@ class NetAppRestCmodeClientTestCase(test.TestCase):
'destination.path': (fake_client.SM_DEST_VSERVER +
':' + fake_client.SM_DEST_VOLUME),
'fields': 'state,source.svm.name,source.path,destination.svm.name,'
'destination.path,transfer.end_time,lag_time,healthy,'
'uuid'
'destination.path,transfer.state,transfer.end_time,'
'lag_time,healthy,uuid'
}
mock_send_request.assert_called_once_with('/snapmirror/relationships',
@ -2762,8 +2762,8 @@ class NetAppRestCmodeClientTestCase(test.TestCase):
'destination.path': (fake_client.SM_DEST_VSERVER +
':' + fake_client.SM_DEST_VOLUME),
'fields': 'state,source.svm.name,source.path,destination.svm.name,'
'destination.path,transfer.end_time,lag_time,healthy,'
'uuid'
'destination.path,transfer.state,transfer.end_time,'
'lag_time,healthy,uuid'
}
mock_send_request.assert_called_once_with('/snapmirror/relationships',
@ -2789,8 +2789,8 @@ class NetAppRestCmodeClientTestCase(test.TestCase):
'destination.path': (fake_client.SM_DEST_VSERVER +
':' + fake_client.SM_DEST_VOLUME),
'fields': 'state,source.svm.name,source.path,destination.svm.name,'
'destination.path,transfer.end_time,lag_time,healthy,'
'uuid'
'destination.path,transfer.state,transfer.end_time,'
'lag_time,healthy,uuid'
}
mock_send_request.assert_called_once_with('/snapmirror/relationships',
@ -3233,37 +3233,23 @@ class NetAppRestCmodeClientTestCase(test.TestCase):
self.assertEqual(expected_job, result)
def test_break_snapmirror(self):
snapmirror_response = copy.deepcopy(
fake_client.SNAPMIRROR_GET_ITER_RESPONSE_REST)
fake_snapmirror = fake_client.REST_GET_SNAPMIRRORS_RESPONSE
fake_uuid = fake_snapmirror[0]['uuid']
fake_body = {'state': 'broken_off'}
snapmirror_response['state'] = 'broken_off'
response_list = [snapmirror_response]
self.mock_object(self.client, 'send_request')
self.mock_object(self.client, 'send_request',
side_effect=copy.deepcopy(response_list))
expected_job = {
'operation-id': None,
'status': None,
'jobid': fake_client.FAKE_UUID,
'error-code': None,
'error-message': None,
'relationship-uuid': fake_client.FAKE_UUID,
}
mock_set_snapmirror_state = self.mock_object(
self.client,
'_set_snapmirror_state',
return_value=expected_job)
mock_get_snap = self.mock_object(
self.client, '_get_snapmirrors',
mock.Mock(return_value=fake_snapmirror))
self.client.break_snapmirror(
fake_client.SM_SOURCE_VSERVER, fake_client.SM_SOURCE_VOLUME,
fake_client.SM_DEST_VSERVER, fake_client.SM_DEST_VOLUME)
mock_set_snapmirror_state.assert_called_once_with(
'broken-off',
fake_client.SM_SOURCE_VSERVER, fake_client.SM_SOURCE_VOLUME,
fake_client.SM_DEST_VSERVER, fake_client.SM_DEST_VOLUME)
mock_get_snap.assert_called_once()
self.client.send_request.assert_called_once_with(
f'/snapmirror/relationships/{fake_uuid}', 'patch', body=fake_body)
def test_break_snapmirror_not_found(self):
self.mock_object(
@ -3278,13 +3264,29 @@ class NetAppRestCmodeClientTestCase(test.TestCase):
fake_client.SM_DEST_VSERVER,
fake_client.SM_DEST_VOLUME)
def test_break_snapmirror_timeout(self):
# when a timeout happens, an exception is thrown by send_request
api_error = netapp_api.NaRetryableError()
def test__break_snapmirror_error(self):
fake_snapmirror = fake_client.REST_GET_SNAPMIRRORS_RESPONSE
self.mock_object(self.client, '_get_snapmirrors',
return_value=fake_snapmirror)
self.mock_object(self.client, 'send_request',
side_effect=api_error)
side_effect=self._mock_api_error())
self.assertRaises(netapp_api.NaApiError,
self.client.break_snapmirror,
fake_client.SM_SOURCE_VSERVER,
fake_client.SM_SOURCE_VOLUME,
fake_client.SM_DEST_VSERVER,
fake_client.SM_DEST_VOLUME)
self.assertRaises(netapp_api.NaRetryableError,
def test__break_snapmirror_exception(self):
fake_snapmirror = copy.deepcopy(
fake_client.REST_GET_SNAPMIRRORS_RESPONSE)
fake_snapmirror[0]['transferring-state'] = 'error'
self.mock_object(
self.client, '_get_snapmirrors',
mock.Mock(return_value=fake_snapmirror))
self.assertRaises(netapp_utils.NetAppDriverException,
self.client.break_snapmirror,
fake_client.SM_SOURCE_VSERVER,
fake_client.SM_SOURCE_VOLUME,

View File

@ -1956,7 +1956,7 @@ class RestClient(object):
destination_vserver=None, destination_volume=None):
fields = ['state', 'source.svm.name', 'source.path',
'destination.svm.name', 'destination.path',
'destination.svm.name', 'destination.path', 'transfer.state',
'transfer.end_time', 'lag_time', 'healthy', 'uuid']
query = {}
@ -1976,7 +1976,11 @@ class RestClient(object):
snapmirrors = []
for record in response.get('records', []):
snapmirrors.append({
'relationship-status': record.get('state'),
'relationship-status': (
'idle'
if record.get('state') == 'snapmirrored'
else record.get('state')),
'transferring-state': record.get('transfer', {}).get('state'),
'mirror-state': record['state'],
'source-vserver': record['source']['svm']['name'],
'source-volume': (record['source']['path'].split(':')[1] if
@ -2070,11 +2074,11 @@ class RestClient(object):
result = self.send_request('/snapmirror/relationships/' + uuid,
'patch', body=body,
wait_on_accepted=wait_result)
job = result['job']
job_info = {
'operation-id': None,
'status': None,
'jobid': job.get('uuid'),
'jobid': result.get('job', {}).get('uuid'),
'error-code': None,
'error-message': None,
'relationship-uuid': uuid,
@ -2247,9 +2251,39 @@ class RestClient(object):
destination_vserver, destination_volume):
"""Breaks a data protection SnapMirror relationship."""
self._set_snapmirror_state(
'broken-off', source_vserver, source_volume,
destination_vserver, destination_volume)
interval = 2
retries = (10 / interval)
@utils.retry(netapp_api.NaRetryableError, interval=interval,
retries=retries, backoff_rate=1)
def _waiter():
snapmirror = self.get_snapmirrors(
source_vserver=source_vserver,
source_volume=source_volume,
destination_vserver=destination_vserver,
destination_volume=destination_volume)
snapmirror_state = None
if snapmirror:
snapmirror_state = snapmirror[0].get('transferring-state')
if snapmirror_state == 'success':
uuid = snapmirror[0]['uuid']
body = {'state': 'broken_off'}
self.send_request(f'/snapmirror/relationships/{uuid}', 'patch',
body=body)
return
else:
message = 'Waiting for transfer state to be SUCCESS.'
code = ''
raise netapp_api.NaRetryableError(message=message, code=code)
try:
return _waiter()
except netapp_api.NaRetryableError:
msg = _("Transfer state did not reach the expected state. Retries "
"exhausted. Aborting.")
raise na_utils.NetAppDriverException(msg)
def update_snapmirror(self, source_vserver, source_volume,
destination_vserver, destination_volume):

View File

@ -0,0 +1,7 @@
---
fixes:
- |
NetApp ONTAP driver `bug #2028857
<https://bugs.launchpad.net/cinder/+bug/2028857>`_: Fixed
errors that were occuring in the replica failover operation when using
ONTAP REST API.