NetApp cDOT: Handle replicated snapshots

This patch implements the following driver methods
required for replicated snapshots to be handled correctly.
create_replicated_snapshot
delete_replicated_snapshot
update_replicated_snapshot

Closes-Bug: #1557071
Related-Bug: #1546303

Change-Id: Ia4cd2a36e31418e7a3d1c218080caa632755fe16
This commit is contained in:
Alex Meade 2016-03-07 16:18:12 -05:00
parent 94e04c8de2
commit 430a18b50c
7 changed files with 911 additions and 26 deletions

View File

@ -1769,6 +1769,56 @@ class NetAppCmodeClient(client_base.NetAppBaseClient):
api_args = {'volume': volume_name, 'snapshot': snapshot_name}
self.send_request('snapshot-create', api_args)
@na_utils.trace
def snapshot_exists(self, snapshot_name, volume_name):
"""Checks if Snapshot exists for a specified volume."""
LOG.debug('Checking if snapshot %(snapshot)s exists for '
'volume %(volume)s',
{'snapshot': snapshot_name, 'volume': volume_name})
"""Gets a single snapshot."""
api_args = {
'query': {
'snapshot-info': {
'name': snapshot_name,
'volume': volume_name,
},
},
'desired-attributes': {
'snapshot-info': {
'name': None,
'volume': None,
'busy': None,
'snapshot-owners-list': {
'snapshot-owner': None,
}
},
},
}
result = self.send_request('snapshot-get-iter', api_args)
error_record_list = result.get_child_by_name(
'volume-errors') or netapp_api.NaElement('none')
errors = error_record_list.get_children()
if errors:
error = errors[0]
error_code = error.get_child_content('errno')
error_reason = error.get_child_content('reason')
msg = _('Could not read information for snapshot %(name)s. '
'Code: %(code)s. Reason: %(reason)s')
msg_args = {
'name': snapshot_name,
'code': error_code,
'reason': error_reason
}
if error_code == netapp_api.ESNAPSHOTNOTALLOWED:
raise exception.SnapshotUnavailable(msg % msg_args)
else:
raise exception.NetAppException(msg % msg_args)
return self._has_records(result)
@na_utils.trace
def get_snapshot(self, volume_name, snapshot_name):
"""Gets a single snapshot."""

View File

@ -137,3 +137,16 @@ class NetAppCmodeMultiSvmShareDriver(driver.ShareDriver):
access_rules, replica_snapshots,
share_server=None):
raise NotImplementedError()
def create_replicated_snapshot(self, context, replica_list,
replica_snapshots, share_server=None):
raise NotImplementedError()
def delete_replicated_snapshot(self, context, replica_list,
replica_snapshots, share_server=None):
raise NotImplementedError()
def update_replicated_snapshot(self, context, replica_list,
share_replica, replica_snapshots,
replica_snapshot, share_server=None):
raise NotImplementedError()

View File

@ -53,7 +53,7 @@ class NetAppCmodeSingleSvmShareDriver(driver.ShareDriver):
snapshot, **kwargs)
def create_snapshot(self, context, snapshot, **kwargs):
self.library.create_snapshot(context, snapshot, **kwargs)
return self.library.create_snapshot(context, snapshot, **kwargs)
def delete_share(self, context, share, **kwargs):
self.library.delete_share(context, share, **kwargs)
@ -123,11 +123,13 @@ class NetAppCmodeSingleSvmShareDriver(driver.ShareDriver):
def create_replica(self, context, replica_list, replica, access_rules,
replica_snapshots, **kwargs):
return self.library.create_replica(context, replica_list, replica,
access_rules, **kwargs)
access_rules, replica_snapshots,
**kwargs)
def delete_replica(self, context, replica_list, replica_snapshots, replica,
**kwargs):
self.library.delete_replica(context, replica_list, replica, **kwargs)
self.library.delete_replica(context, replica_list, replica,
replica_snapshots, **kwargs)
def promote_replica(self, context, replica_list, replica, access_rules,
share_server=None):
@ -142,4 +144,24 @@ class NetAppCmodeSingleSvmShareDriver(driver.ShareDriver):
replica_list,
replica,
access_rules,
replica_snapshots,
share_server=share_server)
def create_replicated_snapshot(self, context, replica_list,
replica_snapshots, share_server=None):
return self.library.create_replicated_snapshot(
context, replica_list, replica_snapshots,
share_server=share_server)
def delete_replicated_snapshot(self, context, replica_list,
replica_snapshots, share_server=None):
return self.library.delete_replicated_snapshot(
context, replica_list, replica_snapshots,
share_server=share_server)
def update_replicated_snapshot(self, context, replica_list,
share_replica, replica_snapshots,
replica_snapshot, share_server=None):
return self.library.update_replicated_snapshot(
replica_list, share_replica, replica_snapshots, replica_snapshot,
share_server=share_server)

View File

@ -562,8 +562,10 @@ class NetAppCmodeFileStorageLibrary(object):
"""Clones existing share."""
share_name = self._get_backend_share_name(share['id'])
parent_share_name = self._get_backend_share_name(snapshot['share_id'])
parent_snapshot_name = snapshot_name_func(self, snapshot['id'])
if snapshot.get('provider_location') is None:
parent_snapshot_name = snapshot_name_func(self, snapshot['id'])
else:
parent_snapshot_name = snapshot['provider_location']
LOG.debug('Creating share from snapshot %s', snapshot['id'])
vserver_client.create_volume_clone(share_name, parent_share_name,
parent_snapshot_name)
@ -713,9 +715,11 @@ class NetAppCmodeFileStorageLibrary(object):
snapshot_name = self._get_backend_snapshot_name(snapshot['id'])
LOG.debug('Creating snapshot %s', snapshot_name)
vserver_client.create_snapshot(share_name, snapshot_name)
return {'provider_location': snapshot_name}
@na_utils.trace
def delete_snapshot(self, context, snapshot, share_server=None):
def delete_snapshot(self, context, snapshot, share_server=None,
snapshot_name=None):
"""Deletes a snapshot of a share."""
try:
vserver, vserver_client = self._get_vserver(
@ -730,7 +734,8 @@ class NetAppCmodeFileStorageLibrary(object):
return
share_name = self._get_backend_share_name(snapshot['share_id'])
snapshot_name = self._get_backend_snapshot_name(snapshot['id'])
snapshot_name = (snapshot.get('provider_location') or snapshot_name or
self._get_backend_snapshot_name(snapshot['id']))
try:
self._delete_snapshot(vserver_client, share_name, snapshot_name)
@ -1111,7 +1116,7 @@ class NetAppCmodeFileStorageLibrary(object):
return r
def create_replica(self, context, replica_list, new_replica,
access_rules=None, share_server=None):
access_rules, share_snapshots, share_server=None):
"""Creates the new replica on this backend and sets up SnapMirror."""
active_replica = self._find_active_replica(replica_list)
dm_session = data_motion.DataMotionSession()
@ -1139,7 +1144,7 @@ class NetAppCmodeFileStorageLibrary(object):
return model_update
def delete_replica(self, context, replica_list, replica,
def delete_replica(self, context, replica_list, replica, share_snapshots,
share_server=None):
"""Removes the replica on this backend and destroys SnapMirror."""
dm_session = data_motion.DataMotionSession()
@ -1163,7 +1168,7 @@ class NetAppCmodeFileStorageLibrary(object):
self._deallocate_container(share_name, vserver_client)
def update_replica_state(self, context, replica_list, replica,
access_rules, share_server=None):
access_rules, share_snapshots, share_server=None):
"""Returns the status of the given replica on this backend."""
active_replica = self._find_active_replica(replica_list)
@ -1225,6 +1230,14 @@ class NetAppCmodeFileStorageLibrary(object):
3600))):
return constants.REPLICA_STATE_OUT_OF_SYNC
# Check all snapshots exist
snapshots = [snap['share_replica_snapshot']
for snap in share_snapshots]
for snap in snapshots:
snapshot_name = snap.get('provider_location')
if not vserver_client.snapshot_exists(snapshot_name, share_name):
return constants.REPLICA_STATE_OUT_OF_SYNC
return constants.REPLICA_STATE_IN_SYNC
def promote_replica(self, context, replica_list, replica, access_rules,
@ -1364,3 +1377,105 @@ class NetAppCmodeFileStorageLibrary(object):
replica['export_locations'] = []
return replica
def create_replicated_snapshot(self, context, replica_list,
snapshot_instances, share_server=None):
active_replica = self._find_active_replica(replica_list)
active_snapshot = [x for x in snapshot_instances
if x['share_id'] == active_replica['id']][0]
snapshot_name = self._get_backend_snapshot_name(active_snapshot['id'])
self.create_snapshot(context, active_snapshot,
share_server=share_server)
active_snapshot['status'] = constants.STATUS_AVAILABLE
active_snapshot['provider_location'] = snapshot_name
snapshots = [active_snapshot]
instances = zip(sorted(replica_list,
key=lambda x: x['id']),
sorted(snapshot_instances,
key=lambda x: x['share_id']))
for replica, snapshot in instances:
if snapshot['id'] != active_snapshot['id']:
snapshot['provider_location'] = snapshot_name
snapshots.append(snapshot)
dm_session = data_motion.DataMotionSession()
if replica.get('host'):
try:
dm_session.update_snapmirror(active_replica,
replica)
except netapp_api.NaApiError as e:
if e.code != netapp_api.EOBJECTNOTFOUND:
raise
return snapshots
def delete_replicated_snapshot(self, context, replica_list,
snapshot_instances, share_server=None):
active_replica = self._find_active_replica(replica_list)
active_snapshot = [x for x in snapshot_instances
if x['share_id'] == active_replica['id']][0]
self.delete_snapshot(context, active_snapshot,
share_server=share_server,
snapshot_name=active_snapshot['provider_location']
)
active_snapshot['status'] = constants.STATUS_DELETED
instances = zip(sorted(replica_list,
key=lambda x: x['id']),
sorted(snapshot_instances,
key=lambda x: x['share_id']))
for replica, snapshot in instances:
if snapshot['id'] != active_snapshot['id']:
dm_session = data_motion.DataMotionSession()
if replica.get('host'):
try:
dm_session.update_snapmirror(active_replica, replica)
except netapp_api.NaApiError as e:
if e.code != netapp_api.EOBJECTNOTFOUND:
raise
return [active_snapshot]
def update_replicated_snapshot(self, replica_list, share_replica,
snapshot_instances, snapshot_instance,
share_server=None):
active_replica = self._find_active_replica(replica_list)
vserver, vserver_client = self._get_vserver(share_server=share_server)
share_name = self._get_backend_share_name(
snapshot_instance['share_id'])
snapshot_name = snapshot_instance.get('provider_location')
# NOTE(ameade): If there is no provider location,
# then grab from active snapshot instance
if snapshot_name is None:
active_snapshot = [x for x in snapshot_instances
if x['share_id'] == active_replica['id']][0]
snapshot_name = active_snapshot.get('provider_location')
if not snapshot_name:
return
try:
snapshot_exists = vserver_client.snapshot_exists(snapshot_name,
share_name)
except exception.SnapshotUnavailable:
# The volume must still be offline
return
if (snapshot_exists and
snapshot_instance['status'] == constants.STATUS_CREATING):
return {
'status': constants.STATUS_AVAILABLE,
'provider_location': snapshot_name,
}
elif (not snapshot_exists and
snapshot_instance['status'] == constants.STATUS_DELETING):
raise exception.SnapshotResourceNotFound(
name=snapshot_instance.get('provider_location'))
dm_session = data_motion.DataMotionSession()
try:
dm_session.update_snapmirror(active_replica, share_replica)
except netapp_api.NaApiError as e:
if e.code != netapp_api.EOBJECTNOTFOUND:
raise

View File

@ -2545,6 +2545,68 @@ class NetAppClientCmodeTestCase(test.TestCase):
self.assertFalse(self.client.volume_exists(fake.SHARE_NAME))
def test_snapshot_exists(self):
api_response = netapp_api.NaElement(fake.VOLUME_GET_NAME_RESPONSE)
self.mock_object(self.client,
'send_request',
mock.Mock(return_value=api_response))
result = self.client.snapshot_exists(fake.SNAPSHOT_NAME,
fake.SHARE_NAME)
snapshot_get_iter_args = {
'query': {
'snapshot-info': {
'name': fake.SNAPSHOT_NAME,
'volume': fake.SHARE_NAME,
}
},
'desired-attributes': {
'snapshot-info': {
'name': None,
'volume': None,
'busy': None,
'snapshot-owners-list': {
'snapshot-owner': None,
}
}
}
}
self.client.send_request.assert_has_calls([
mock.call('snapshot-get-iter', snapshot_get_iter_args)])
self.assertTrue(result)
def test_snapshot_exists_not_found(self):
api_response = netapp_api.NaElement(fake.NO_RECORDS_RESPONSE)
self.mock_object(self.client,
'send_request',
mock.Mock(return_value=api_response))
self.assertFalse(self.client.snapshot_exists(fake.SNAPSHOT_NAME,
fake.SHARE_NAME))
@ddt.data({
'api_response_xml': fake.SNAPSHOT_GET_ITER_UNAVAILABLE_RESPONSE,
'raised_exception': exception.SnapshotUnavailable,
}, {
'api_response_xml': fake.SNAPSHOT_GET_ITER_OTHER_ERROR_RESPONSE,
'raised_exception': exception.NetAppException,
})
@ddt.unpack
def test_snapshot_exists_error(self, api_response_xml, raised_exception):
api_response = netapp_api.NaElement(api_response_xml)
self.mock_object(self.client,
'send_request',
mock.Mock(return_value=api_response))
self.assertRaises(raised_exception,
self.client.snapshot_exists,
fake.SNAPSHOT_NAME,
fake.SHARE_NAME)
def test_get_aggregate_for_volume(self):
api_response = netapp_api.NaElement(

View File

@ -20,6 +20,7 @@ import copy
import math
import socket
import time
import uuid
import ddt
import mock
@ -1112,9 +1113,8 @@ class NetAppFileStorageLibraryTestCase(test.TestCase):
mock.Mock(return_value=(fake.VSERVER1,
vserver_client)))
self.library.create_snapshot(self.context,
fake.SNAPSHOT,
share_server=fake.SHARE_SERVER)
model_update = self.library.create_snapshot(
self.context, fake.SNAPSHOT, share_server=fake.SHARE_SERVER)
share_name = self.library._get_backend_share_name(
fake.SNAPSHOT['share_id'])
@ -1122,6 +1122,7 @@ class NetAppFileStorageLibraryTestCase(test.TestCase):
fake.SNAPSHOT['id'])
vserver_client.create_snapshot.assert_called_once_with(share_name,
snapshot_name)
self.assertEqual(snapshot_name, model_update['provider_location'])
def test_delete_snapshot(self):
@ -1144,6 +1145,25 @@ class NetAppFileStorageLibraryTestCase(test.TestCase):
mock_delete_snapshot.assert_called_once_with(
vserver_client, share_name, snapshot_name)
def test_delete_snapshot_with_provider_location(self):
vserver_client = mock.Mock()
vserver_client.get_snapshot.return_value = fake.CDOT_SNAPSHOT
self.mock_object(self.library,
'_get_vserver',
mock.Mock(return_value=(fake.VSERVER1,
vserver_client)))
fake_snapshot = copy.deepcopy(fake.SNAPSHOT)
fake_snapshot['provider_location'] = 'fake_provider_location'
self.library.delete_snapshot(self.context,
fake_snapshot,
share_server=fake.SHARE_SERVER)
share_name = self.library._get_backend_share_name(
fake_snapshot['share_id'])
vserver_client.delete_snapshot.assert_called_once_with(
share_name, fake_snapshot['provider_location'])
@ddt.data(exception.InvalidInput(reason='fake_reason'),
exception.VserverNotSpecified(),
exception.VserverNotFound(vserver='fake_vserver'))
@ -2066,7 +2086,8 @@ class NetAppFileStorageLibraryTestCase(test.TestCase):
}
model_update = self.library.create_replica(
None, [fake.SHARE], fake.SHARE, share_server=None)
None, [fake.SHARE], fake.SHARE, [], [],
share_server=None)
self.assertDictMatch(expected_model_update, model_update)
mock_dm_session.create_snapmirror.assert_called_once_with(fake.SHARE,
@ -2092,7 +2113,8 @@ class NetAppFileStorageLibraryTestCase(test.TestCase):
}
model_update = self.library.create_replica(
None, [fake.SHARE], fake.SHARE, share_server=fake.SHARE_SERVER)
None, [fake.SHARE], fake.SHARE, [], [],
share_server=fake.SHARE_SERVER)
self.assertDictMatch(expected_model_update, model_update)
mock_dm_session.create_snapmirror.assert_called_once_with(fake.SHARE,
@ -2117,6 +2139,7 @@ class NetAppFileStorageLibraryTestCase(test.TestCase):
result = self.library.delete_replica(None,
[fake.SHARE],
fake.SHARE,
[],
share_server=None)
self.assertEqual(None, result)
mock_dm_session.delete_snapmirror.assert_called_with(fake.SHARE,
@ -2143,6 +2166,7 @@ class NetAppFileStorageLibraryTestCase(test.TestCase):
result = self.library.delete_replica(None,
[fake.SHARE],
fake.SHARE,
[],
share_server=fake.SHARE_SERVER)
self.assertEqual(None, result)
mock_dm_session.delete_snapmirror.assert_called_with(fake.SHARE,
@ -2170,6 +2194,7 @@ class NetAppFileStorageLibraryTestCase(test.TestCase):
result = self.library.delete_replica(None,
[fake.SHARE],
fake.SHARE,
[],
share_server=None)
self.assertEqual(None, result)
@ -2195,7 +2220,7 @@ class NetAppFileStorageLibraryTestCase(test.TestCase):
replica['status'] = constants.STATUS_CREATING
result = self.library.update_replica_state(
None, [replica], replica, None, share_server=None)
None, [replica], replica, None, [], share_server=None)
self.assertFalse(self.mock_dm_session.create_snapmirror.called)
self.assertEqual(constants.STATUS_OUT_OF_SYNC, result)
@ -2216,7 +2241,7 @@ class NetAppFileStorageLibraryTestCase(test.TestCase):
replica['status'] = constants.REPLICA_STATE_OUT_OF_SYNC
result = self.library.update_replica_state(
None, [replica], replica, None, share_server=None)
None, [replica], replica, None, [], share_server=None)
self.assertTrue(self.mock_dm_session.create_snapmirror.called)
self.assertEqual(constants.STATUS_ERROR, result)
@ -2236,7 +2261,7 @@ class NetAppFileStorageLibraryTestCase(test.TestCase):
replica['status'] = status
result = self.library.update_replica_state(
None, [replica], replica, None, share_server=None)
None, [replica], replica, None, [], share_server=None)
self.assertEqual(1, self.mock_dm_session.create_snapmirror.call_count)
self.assertEqual(constants.STATUS_OUT_OF_SYNC, result)
@ -2260,7 +2285,7 @@ class NetAppFileStorageLibraryTestCase(test.TestCase):
return_value=[fake_snapmirror])
result = self.library.update_replica_state(None, [fake.SHARE],
fake.SHARE, None,
fake.SHARE, None, [],
share_server=None)
vserver_client.resync_snapmirror.assert_called_once_with(
@ -2288,7 +2313,7 @@ class NetAppFileStorageLibraryTestCase(test.TestCase):
return_value=[fake_snapmirror])
result = self.library.update_replica_state(None, [fake.SHARE],
fake.SHARE, None,
fake.SHARE, None, [],
share_server=None)
self.assertEqual(constants.REPLICA_STATE_OUT_OF_SYNC, result)
@ -2305,7 +2330,7 @@ class NetAppFileStorageLibraryTestCase(test.TestCase):
netapp_api.NaApiError(code=0))
result = self.library.update_replica_state(None, [fake.SHARE],
fake.SHARE, None,
fake.SHARE, None, [],
share_server=None)
self.assertTrue(self.mock_dm_session.get_snapmirrors.called)
self.assertEqual(constants.STATUS_ERROR, result)
@ -2330,7 +2355,7 @@ class NetAppFileStorageLibraryTestCase(test.TestCase):
vserver_client.resync_snapmirror.side_effect = netapp_api.NaApiError
result = self.library.update_replica_state(None, [fake.SHARE],
fake.SHARE, None,
fake.SHARE, None, [],
share_server=None)
vserver_client.resync_snapmirror.assert_called_once_with(
@ -2356,7 +2381,7 @@ class NetAppFileStorageLibraryTestCase(test.TestCase):
return_value=[fake_snapmirror])
result = self.library.update_replica_state(None, [fake.SHARE],
fake.SHARE, None,
fake.SHARE, None, [],
share_server=None)
self.assertEqual(constants.REPLICA_STATE_OUT_OF_SYNC, result)
@ -2378,7 +2403,7 @@ class NetAppFileStorageLibraryTestCase(test.TestCase):
return_value=[fake_snapmirror])
result = self.library.update_replica_state(None, [fake.SHARE],
fake.SHARE, None,
fake.SHARE, None, [],
share_server=None)
self.assertEqual(constants.REPLICA_STATE_IN_SYNC, result)
@ -2394,9 +2419,59 @@ class NetAppFileStorageLibraryTestCase(test.TestCase):
self.assertRaises(exception.ShareResourceNotFound,
self.library.update_replica_state,
None, [fake.SHARE], fake.SHARE, None,
None, [fake.SHARE], fake.SHARE, None, [],
share_server=None)
def test_update_replica_state_in_sync_with_snapshots(self):
fake_snapmirror = {
'mirror-state': 'snapmirrored',
'relationship-status': 'idle',
'last-transfer-end-timestamp': '%s' % float(time.time())
}
fake_snapshot = copy.deepcopy(fake.SNAPSHOT)
fake_snapshot['share_id'] = fake.SHARE['id']
snapshots = [{'share_replica_snapshot': fake_snapshot}]
vserver_client = mock.Mock()
self.mock_object(vserver_client, 'snapshot_exists', mock.Mock(
return_value=True))
self.mock_object(self.library,
'_get_vserver',
mock.Mock(return_value=(fake.VSERVER1,
vserver_client)))
self.mock_dm_session.get_snapmirrors = mock.Mock(
return_value=[fake_snapmirror])
result = self.library.update_replica_state(None, [fake.SHARE],
fake.SHARE, None, snapshots,
share_server=None)
self.assertEqual(constants.REPLICA_STATE_IN_SYNC, result)
def test_update_replica_state_missing_snapshot(self):
fake_snapmirror = {
'mirror-state': 'snapmirrored',
'relationship-status': 'idle',
'last-transfer-end-timestamp': '%s' % float(time.time())
}
fake_snapshot = copy.deepcopy(fake.SNAPSHOT)
fake_snapshot['share_id'] = fake.SHARE['id']
snapshots = [{'share_replica_snapshot': fake_snapshot}]
vserver_client = mock.Mock()
self.mock_object(vserver_client, 'snapshot_exists', mock.Mock(
return_value=False))
self.mock_object(self.library,
'_get_vserver',
mock.Mock(return_value=(fake.VSERVER1,
vserver_client)))
self.mock_dm_session.get_snapmirrors = mock.Mock(
return_value=[fake_snapmirror])
result = self.library.update_replica_state(None, [fake.SHARE],
fake.SHARE, None, snapshots,
share_server=None)
self.assertEqual(constants.REPLICA_STATE_OUT_OF_SYNC, result)
def test_promote_replica(self):
self.mock_object(self.library,
'_get_vserver',
@ -2744,3 +2819,549 @@ class NetAppFileStorageLibraryTestCase(test.TestCase):
self.assertEqual([], replica['export_locations'])
self.assertEqual(constants.STATUS_ERROR,
replica['replica_state'])
def test_create_replicated_snapshot(self):
fake_replica_3 = copy.deepcopy(self.fake_replica_2)
fake_replica_3['id'] = fake.SHARE_ID3
replica_list = [self.fake_replica, self.fake_replica_2, fake_replica_3]
fake_snapshot = copy.deepcopy(fake.SNAPSHOT)
fake_snapshot['share_id'] = self.fake_replica['id']
fake_snapshot_2 = copy.deepcopy(fake.SNAPSHOT)
fake_snapshot_2['id'] = str(uuid.uuid4())
fake_snapshot_2['share_id'] = self.fake_replica_2['id']
fake_snapshot_3 = copy.deepcopy(fake.SNAPSHOT)
fake_snapshot_3['id'] = str(uuid.uuid4())
fake_snapshot_3['share_id'] = fake_replica_3['id']
snapshot_list = [fake_snapshot, fake_snapshot_2, fake_snapshot_3]
vserver_client = mock.Mock()
self.mock_object(self.library,
'_get_vserver',
mock.Mock(return_value=(fake.VSERVER1,
vserver_client)))
model_list = self.library.create_replicated_snapshot(
self.context, replica_list, snapshot_list,
share_server=fake.SHARE_SERVER)
share_name = self.library._get_backend_share_name(
fake_snapshot['share_id'])
snapshot_name = self.library._get_backend_snapshot_name(
fake_snapshot['id'])
vserver_client.create_snapshot.assert_called_once_with(share_name,
snapshot_name)
self.assertEqual(3, len(model_list))
for snapshot in model_list:
self.assertEqual(snapshot['provider_location'], snapshot_name)
actual_active_snapshot = list(filter(
lambda x: x['id'] == fake_snapshot['id'], model_list))[0]
self.assertEqual(constants.STATUS_AVAILABLE,
actual_active_snapshot['status'])
actual_non_active_snapshot_list = list(filter(
lambda x: x['id'] != fake_snapshot['id'], model_list))
for snapshot in actual_non_active_snapshot_list:
self.assertEqual(constants.STATUS_CREATING, snapshot['status'])
self.mock_dm_session.update_snapmirror.assert_has_calls(
[mock.call(self.fake_replica, self.fake_replica_2),
mock.call(self.fake_replica, fake_replica_3)],
any_order=True
)
def test_create_replicated_snapshot_with_creating_replica(self):
fake_replica_3 = copy.deepcopy(self.fake_replica_2)
fake_replica_3['id'] = fake.SHARE_ID3
fake_replica_3['host'] = None
replica_list = [self.fake_replica, self.fake_replica_2, fake_replica_3]
fake_snapshot = copy.deepcopy(fake.SNAPSHOT)
fake_snapshot['share_id'] = self.fake_replica['id']
fake_snapshot_2 = copy.deepcopy(fake.SNAPSHOT)
fake_snapshot_2['id'] = str(uuid.uuid4())
fake_snapshot_2['share_id'] = self.fake_replica_2['id']
fake_snapshot_3 = copy.deepcopy(fake.SNAPSHOT)
fake_snapshot_3['id'] = str(uuid.uuid4())
fake_snapshot_3['share_id'] = fake_replica_3['id']
snapshot_list = [fake_snapshot, fake_snapshot_2, fake_snapshot_3]
vserver_client = mock.Mock()
self.mock_object(self.library,
'_get_vserver',
mock.Mock(return_value=(fake.VSERVER1,
vserver_client)))
model_list = self.library.create_replicated_snapshot(
self.context, replica_list, snapshot_list,
share_server=fake.SHARE_SERVER)
share_name = self.library._get_backend_share_name(
fake_snapshot['share_id'])
snapshot_name = self.library._get_backend_snapshot_name(
fake_snapshot['id'])
vserver_client.create_snapshot.assert_called_once_with(share_name,
snapshot_name)
self.assertEqual(3, len(model_list))
for snapshot in model_list:
self.assertEqual(snapshot['provider_location'], snapshot_name)
actual_active_snapshot = list(filter(
lambda x: x['id'] == fake_snapshot['id'], model_list))[0]
self.assertEqual(constants.STATUS_AVAILABLE,
actual_active_snapshot['status'])
actual_non_active_snapshot_list = list(filter(
lambda x: x['id'] != fake_snapshot['id'], model_list))
for snapshot in actual_non_active_snapshot_list:
self.assertEqual(constants.STATUS_CREATING, snapshot['status'])
self.mock_dm_session.update_snapmirror.assert_has_calls(
[mock.call(self.fake_replica, self.fake_replica_2)],
any_order=True
)
def test_create_replicated_snapshot_no_snapmirror(self):
self.mock_dm_session.update_snapmirror.side_effect = [
None,
netapp_api.NaApiError(code=netapp_api.EOBJECTNOTFOUND)
]
fake_replica_3 = copy.deepcopy(self.fake_replica_2)
fake_replica_3['id'] = fake.SHARE_ID3
replica_list = [self.fake_replica, self.fake_replica_2, fake_replica_3]
fake_snapshot = copy.deepcopy(fake.SNAPSHOT)
fake_snapshot['share_id'] = self.fake_replica['id']
fake_snapshot_2 = copy.deepcopy(fake.SNAPSHOT)
fake_snapshot_2['id'] = str(uuid.uuid4())
fake_snapshot_2['share_id'] = self.fake_replica_2['id']
fake_snapshot_3 = copy.deepcopy(fake.SNAPSHOT)
fake_snapshot_3['id'] = str(uuid.uuid4())
fake_snapshot_3['share_id'] = fake_replica_3['id']
snapshot_list = [fake_snapshot, fake_snapshot_2, fake_snapshot_3]
vserver_client = mock.Mock()
self.mock_object(self.library,
'_get_vserver',
mock.Mock(return_value=(fake.VSERVER1,
vserver_client)))
model_list = self.library.create_replicated_snapshot(
self.context, replica_list, snapshot_list,
share_server=fake.SHARE_SERVER)
share_name = self.library._get_backend_share_name(
fake_snapshot['share_id'])
snapshot_name = self.library._get_backend_snapshot_name(
fake_snapshot['id'])
vserver_client.create_snapshot.assert_called_once_with(share_name,
snapshot_name)
self.assertEqual(3, len(model_list))
for snapshot in model_list:
self.assertEqual(snapshot['provider_location'], snapshot_name)
actual_active_snapshot = list(filter(
lambda x: x['id'] == fake_snapshot['id'], model_list))[0]
self.assertEqual(constants.STATUS_AVAILABLE,
actual_active_snapshot['status'])
actual_non_active_snapshot_list = list(filter(
lambda x: x['id'] != fake_snapshot['id'], model_list))
for snapshot in actual_non_active_snapshot_list:
self.assertEqual(constants.STATUS_CREATING, snapshot['status'])
self.mock_dm_session.update_snapmirror.assert_has_calls(
[mock.call(self.fake_replica, self.fake_replica_2),
mock.call(self.fake_replica, fake_replica_3)],
any_order=True
)
def test_create_replicated_snapshot_update_error(self):
self.mock_dm_session.update_snapmirror.side_effect = [
None,
netapp_api.NaApiError()
]
fake_replica_3 = copy.deepcopy(self.fake_replica_2)
fake_replica_3['id'] = fake.SHARE_ID3
replica_list = [self.fake_replica, self.fake_replica_2, fake_replica_3]
fake_snapshot = copy.deepcopy(fake.SNAPSHOT)
fake_snapshot['share_id'] = self.fake_replica['id']
fake_snapshot_2 = copy.deepcopy(fake.SNAPSHOT)
fake_snapshot_2['id'] = str(uuid.uuid4())
fake_snapshot_2['share_id'] = self.fake_replica_2['id']
fake_snapshot_3 = copy.deepcopy(fake.SNAPSHOT)
fake_snapshot_3['id'] = str(uuid.uuid4())
fake_snapshot_3['share_id'] = fake_replica_3['id']
snapshot_list = [fake_snapshot, fake_snapshot_2, fake_snapshot_3]
vserver_client = mock.Mock()
self.mock_object(self.library,
'_get_vserver',
mock.Mock(return_value=(fake.VSERVER1,
vserver_client)))
self.assertRaises(netapp_api.NaApiError,
self.library.create_replicated_snapshot,
self.context, replica_list, snapshot_list,
share_server=fake.SHARE_SERVER)
def test_delete_replicated_snapshot(self):
fake_replica_3 = copy.deepcopy(self.fake_replica_2)
fake_replica_3['id'] = fake.SHARE_ID3
replica_list = [self.fake_replica, self.fake_replica_2, fake_replica_3]
fake_snapshot = copy.deepcopy(fake.SNAPSHOT)
fake_snapshot['share_id'] = self.fake_replica['id']
share_name = self.library._get_backend_share_name(
fake_snapshot['share_id'])
snapshot_name = self.library._get_backend_snapshot_name(
fake_snapshot['id'])
fake_snapshot['provider_location'] = snapshot_name
fake_snapshot_2 = copy.deepcopy(fake.SNAPSHOT)
fake_snapshot_2['id'] = str(uuid.uuid4())
fake_snapshot_2['share_id'] = self.fake_replica_2['id']
fake_snapshot_2['provider_location'] = snapshot_name
fake_snapshot_3 = copy.deepcopy(fake.SNAPSHOT)
fake_snapshot_3['id'] = str(uuid.uuid4())
fake_snapshot_3['share_id'] = fake_replica_3['id']
fake_snapshot_3['provider_location'] = snapshot_name
snapshot_list = [fake_snapshot, fake_snapshot_2, fake_snapshot_3]
vserver_client = mock.Mock()
vserver_client.get_snapshot.return_value = fake.CDOT_SNAPSHOT
self.mock_object(self.library,
'_get_vserver',
mock.Mock(return_value=(fake.VSERVER1,
vserver_client)))
self.library.delete_replicated_snapshot(
self.context, replica_list, snapshot_list,
share_server=fake.SHARE_SERVER)
vserver_client.delete_snapshot.assert_called_once_with(share_name,
snapshot_name)
self.mock_dm_session.update_snapmirror.assert_has_calls(
[mock.call(self.fake_replica, self.fake_replica_2),
mock.call(self.fake_replica, fake_replica_3)],
any_order=True
)
def test_delete_replicated_snapshot_replica_still_creating(self):
fake_replica_3 = copy.deepcopy(self.fake_replica_2)
fake_replica_3['id'] = fake.SHARE_ID3
fake_replica_3['host'] = None
replica_list = [self.fake_replica, self.fake_replica_2, fake_replica_3]
fake_snapshot = copy.deepcopy(fake.SNAPSHOT)
fake_snapshot['share_id'] = self.fake_replica['id']
share_name = self.library._get_backend_share_name(
fake_snapshot['share_id'])
snapshot_name = self.library._get_backend_snapshot_name(
fake_snapshot['id'])
fake_snapshot['provider_location'] = snapshot_name
fake_snapshot_2 = copy.deepcopy(fake.SNAPSHOT)
fake_snapshot_2['id'] = str(uuid.uuid4())
fake_snapshot_2['share_id'] = self.fake_replica_2['id']
fake_snapshot_2['provider_location'] = snapshot_name
fake_snapshot_3 = copy.deepcopy(fake.SNAPSHOT)
fake_snapshot_3['id'] = str(uuid.uuid4())
fake_snapshot_3['share_id'] = fake_replica_3['id']
fake_snapshot_3['provider_location'] = snapshot_name
snapshot_list = [fake_snapshot, fake_snapshot_2, fake_snapshot_3]
vserver_client = mock.Mock()
vserver_client.get_snapshot.return_value = fake.CDOT_SNAPSHOT
self.mock_object(self.library,
'_get_vserver',
mock.Mock(return_value=(fake.VSERVER1,
vserver_client)))
self.library.delete_replicated_snapshot(
self.context, replica_list, snapshot_list,
share_server=fake.SHARE_SERVER)
vserver_client.delete_snapshot.assert_called_once_with(share_name,
snapshot_name)
self.mock_dm_session.update_snapmirror.assert_has_calls(
[mock.call(self.fake_replica, self.fake_replica_2)],
any_order=True
)
def test_delete_replicated_snapshot_missing_snapmirror(self):
self.mock_dm_session.update_snapmirror.side_effect = [
None,
netapp_api.NaApiError(code=netapp_api.EOBJECTNOTFOUND)
]
fake_replica_3 = copy.deepcopy(self.fake_replica_2)
fake_replica_3['id'] = fake.SHARE_ID3
replica_list = [self.fake_replica, self.fake_replica_2, fake_replica_3]
fake_snapshot = copy.deepcopy(fake.SNAPSHOT)
fake_snapshot['share_id'] = self.fake_replica['id']
share_name = self.library._get_backend_share_name(
fake_snapshot['share_id'])
snapshot_name = self.library._get_backend_snapshot_name(
fake_snapshot['id'])
fake_snapshot['provider_location'] = snapshot_name
fake_snapshot['busy'] = False
fake_snapshot_2 = copy.deepcopy(fake.SNAPSHOT)
fake_snapshot_2['id'] = str(uuid.uuid4())
fake_snapshot_2['share_id'] = self.fake_replica_2['id']
fake_snapshot_2['provider_location'] = snapshot_name
fake_snapshot_3 = copy.deepcopy(fake.SNAPSHOT)
fake_snapshot_3['id'] = str(uuid.uuid4())
fake_snapshot_3['share_id'] = fake_replica_3['id']
fake_snapshot_3['provider_location'] = snapshot_name
snapshot_list = [fake_snapshot, fake_snapshot_2, fake_snapshot_3]
vserver_client = mock.Mock()
vserver_client.get_snapshot.return_value = fake_snapshot
self.mock_object(self.library,
'_get_vserver',
mock.Mock(return_value=(fake.VSERVER1,
vserver_client)))
self.library.delete_replicated_snapshot(
self.context, replica_list, snapshot_list,
share_server=fake.SHARE_SERVER)
vserver_client.delete_snapshot.assert_called_once_with(share_name,
snapshot_name)
self.mock_dm_session.update_snapmirror.assert_has_calls(
[mock.call(self.fake_replica, self.fake_replica_2),
mock.call(self.fake_replica, fake_replica_3)],
any_order=True
)
def test_delete_replicated_snapshot_update_error(self):
self.mock_dm_session.update_snapmirror.side_effect = [
None,
netapp_api.NaApiError()
]
fake_replica_3 = copy.deepcopy(self.fake_replica_2)
fake_replica_3['id'] = fake.SHARE_ID3
replica_list = [self.fake_replica, self.fake_replica_2, fake_replica_3]
fake_snapshot = copy.deepcopy(fake.SNAPSHOT)
fake_snapshot['share_id'] = self.fake_replica['id']
snapshot_name = self.library._get_backend_snapshot_name(
fake_snapshot['id'])
fake_snapshot['provider_location'] = snapshot_name
fake_snapshot['busy'] = False
fake_snapshot_2 = copy.deepcopy(fake.SNAPSHOT)
fake_snapshot_2['id'] = str(uuid.uuid4())
fake_snapshot_2['share_id'] = self.fake_replica_2['id']
fake_snapshot_2['provider_location'] = snapshot_name
fake_snapshot_3 = copy.deepcopy(fake.SNAPSHOT)
fake_snapshot_3['id'] = str(uuid.uuid4())
fake_snapshot_3['share_id'] = fake_replica_3['id']
fake_snapshot_3['provider_location'] = snapshot_name
snapshot_list = [fake_snapshot, fake_snapshot_2, fake_snapshot_3]
vserver_client = mock.Mock()
vserver_client.get_snapshot.return_value = fake_snapshot
self.mock_object(self.library,
'_get_vserver',
mock.Mock(return_value=(fake.VSERVER1,
vserver_client)))
self.assertRaises(netapp_api.NaApiError,
self.library.delete_replicated_snapshot,
self.context, replica_list, snapshot_list,
share_server=fake.SHARE_SERVER)
def test_update_replicated_snapshot_still_creating(self):
vserver_client = mock.Mock()
vserver_client.snapshot_exists.return_value = False
self.mock_object(self.library,
'_get_vserver',
mock.Mock(return_value=(fake.VSERVER1,
vserver_client)))
replica_list = [self.fake_replica, self.fake_replica_2]
fake_snapshot = copy.deepcopy(fake.SNAPSHOT)
fake_snapshot['status'] = constants.STATUS_CREATING
fake_snapshot['share_id'] = self.fake_replica_2['id']
snapshot_name = self.library._get_backend_snapshot_name(
fake_snapshot['id'])
fake_snapshot['provider_location'] = snapshot_name
model_update = self.library.update_replicated_snapshot(
replica_list, self.fake_replica_2, [fake_snapshot], fake_snapshot)
self.assertEqual(None, model_update)
self.mock_dm_session.update_snapmirror.assert_called_once_with(
self.fake_replica, self.fake_replica_2
)
def test_update_replicated_snapshot_still_creating_no_host(self):
self.fake_replica_2['host'] = None
vserver_client = mock.Mock()
vserver_client.snapshot_exists.return_value = False
self.mock_object(self.library,
'_get_vserver',
mock.Mock(return_value=(fake.VSERVER1,
vserver_client)))
replica_list = [self.fake_replica, self.fake_replica_2]
fake_snapshot = copy.deepcopy(fake.SNAPSHOT)
fake_snapshot['status'] = constants.STATUS_CREATING
fake_snapshot['share_id'] = self.fake_replica_2['id']
snapshot_name = self.library._get_backend_snapshot_name(
fake_snapshot['id'])
fake_snapshot['provider_location'] = snapshot_name
model_update = self.library.update_replicated_snapshot(
replica_list, self.fake_replica_2, [fake_snapshot], fake_snapshot)
self.assertEqual(None, model_update)
self.mock_dm_session.update_snapmirror.assert_called_once_with(
self.fake_replica, self.fake_replica_2
)
def test_update_replicated_snapshot_no_snapmirror(self):
vserver_client = mock.Mock()
vserver_client.snapshot_exists.return_value = False
self.mock_dm_session.update_snapmirror.side_effect = (
netapp_api.NaApiError(code=netapp_api.EOBJECTNOTFOUND)
)
self.mock_object(self.library,
'_get_vserver',
mock.Mock(return_value=(fake.VSERVER1,
vserver_client)))
replica_list = [self.fake_replica, self.fake_replica_2]
fake_snapshot = copy.deepcopy(fake.SNAPSHOT)
fake_snapshot['status'] = constants.STATUS_CREATING
fake_snapshot['share_id'] = self.fake_replica_2['id']
snapshot_name = self.library._get_backend_snapshot_name(
fake_snapshot['id'])
fake_snapshot['provider_location'] = snapshot_name
model_update = self.library.update_replicated_snapshot(
replica_list, self.fake_replica_2, [fake_snapshot], fake_snapshot)
self.assertEqual(None, model_update)
self.mock_dm_session.update_snapmirror.assert_called_once_with(
self.fake_replica, self.fake_replica_2
)
def test_update_replicated_snapshot_update_error(self):
vserver_client = mock.Mock()
vserver_client.snapshot_exists.return_value = False
self.mock_dm_session.update_snapmirror.side_effect = (
netapp_api.NaApiError()
)
self.mock_object(self.library,
'_get_vserver',
mock.Mock(return_value=(fake.VSERVER1,
vserver_client)))
replica_list = [self.fake_replica, self.fake_replica_2]
fake_snapshot = copy.deepcopy(fake.SNAPSHOT)
fake_snapshot['status'] = constants.STATUS_CREATING
fake_snapshot['share_id'] = self.fake_replica_2['id']
snapshot_name = self.library._get_backend_snapshot_name(
fake_snapshot['id'])
fake_snapshot['provider_location'] = snapshot_name
self.assertRaises(netapp_api.NaApiError,
self.library.update_replicated_snapshot,
replica_list, self.fake_replica_2,
[fake_snapshot], fake_snapshot)
def test_update_replicated_snapshot_still_deleting(self):
vserver_client = mock.Mock()
vserver_client.snapshot_exists.return_value = True
vserver_client.get_snapshot.return_value = fake.CDOT_SNAPSHOT
self.mock_object(self.library,
'_get_vserver',
mock.Mock(return_value=(fake.VSERVER1,
vserver_client)))
replica_list = [self.fake_replica]
fake_snapshot = copy.deepcopy(fake.SNAPSHOT)
fake_snapshot['status'] = constants.STATUS_DELETING
fake_snapshot['share_id'] = self.fake_replica['id']
snapshot_name = self.library._get_backend_snapshot_name(
fake_snapshot['id'])
fake_snapshot['provider_location'] = snapshot_name
model_update = self.library.update_replicated_snapshot(
replica_list, self.fake_replica, [fake_snapshot], fake_snapshot)
self.assertEqual(None, model_update)
def test_update_replicated_snapshot_created(self):
vserver_client = mock.Mock()
vserver_client.snapshot_exists.return_value = True
self.mock_object(self.library,
'_get_vserver',
mock.Mock(return_value=(fake.VSERVER1,
vserver_client)))
replica_list = [self.fake_replica]
fake_snapshot = copy.deepcopy(fake.SNAPSHOT)
fake_snapshot['status'] = constants.STATUS_CREATING
fake_snapshot['share_id'] = self.fake_replica['id']
snapshot_name = self.library._get_backend_snapshot_name(
fake_snapshot['id'])
fake_snapshot['provider_location'] = snapshot_name
model_update = self.library.update_replicated_snapshot(
replica_list, self.fake_replica, [fake_snapshot], fake_snapshot)
self.assertEqual(constants.STATUS_AVAILABLE, model_update['status'])
self.assertEqual(snapshot_name, model_update['provider_location'])
def test_update_replicated_snapshot_created_no_provider_location(self):
vserver_client = mock.Mock()
vserver_client.snapshot_exists.return_value = True
self.mock_object(self.library,
'_get_vserver',
mock.Mock(return_value=(fake.VSERVER1,
vserver_client)))
replica_list = [self.fake_replica, self.fake_replica_2]
fake_snapshot = copy.deepcopy(fake.SNAPSHOT)
fake_snapshot['status'] = constants.STATUS_ACTIVE
fake_snapshot['share_id'] = self.fake_replica['id']
snapshot_name = self.library._get_backend_snapshot_name(
fake_snapshot['id'])
fake_snapshot['provider_location'] = snapshot_name
fake_snapshot_2 = copy.deepcopy(fake.SNAPSHOT)
fake_snapshot_2['status'] = constants.STATUS_CREATING
fake_snapshot_2['share_id'] = self.fake_replica_2['id']
model_update = self.library.update_replicated_snapshot(
replica_list, self.fake_replica_2,
[fake_snapshot, fake_snapshot_2], fake_snapshot_2)
self.assertEqual(constants.STATUS_AVAILABLE, model_update['status'])
self.assertEqual(snapshot_name, model_update['provider_location'])
def test_update_replicated_snapshot_deleted(self):
vserver_client = mock.Mock()
vserver_client.snapshot_exists.return_value = False
self.mock_object(self.library,
'_get_vserver',
mock.Mock(return_value=(fake.VSERVER1,
vserver_client)))
replica_list = [self.fake_replica]
fake_snapshot = copy.deepcopy(fake.SNAPSHOT)
fake_snapshot['status'] = constants.STATUS_DELETING
fake_snapshot['share_id'] = self.fake_replica['id']
snapshot_name = self.library._get_backend_snapshot_name(
fake_snapshot['id'])
fake_snapshot['provider_location'] = snapshot_name
self.assertRaises(exception.SnapshotResourceNotFound,
self.library.update_replicated_snapshot,
replica_list, self.fake_replica, [fake_snapshot],
fake_snapshot)
def test_update_replicated_snapshot_no_provider_locations(self):
vserver_client = mock.Mock()
vserver_client.snapshot_exists.return_value = True
self.mock_object(self.library,
'_get_vserver',
mock.Mock(return_value=(fake.VSERVER1,
vserver_client)))
replica_list = [self.fake_replica]
fake_snapshot = copy.deepcopy(fake.SNAPSHOT)
fake_snapshot['status'] = constants.STATUS_CREATING
fake_snapshot['share_id'] = self.fake_replica['id']
fake_snapshot['provider_location'] = None
model_update = self.library.update_replicated_snapshot(
replica_list, self.fake_replica, [fake_snapshot], fake_snapshot)
self.assertEqual(None, model_update)

View File

@ -262,7 +262,9 @@ SHARE_SERVER = {
SNAPSHOT = {
'id': SNAPSHOT_ID,
'project_id': TENANT_ID,
'share_id': PARENT_SHARE_ID
'share_id': PARENT_SHARE_ID,
'status': constants.STATUS_CREATING,
'provider_location': None,
}
CDOT_SNAPSHOT = {