Merge "NetApp SolidFire: Add active/active replication"

This commit is contained in:
Zuul 2020-04-13 22:08:12 +00:00 committed by Gerrit Code Review
commit a20d01550b
3 changed files with 129 additions and 27 deletions

View File

@ -150,6 +150,26 @@ class SolidFireVolumeTestCase(test.TestCase):
'qos': None,
'iqn': 'super_fake_iqn'}
self.fake_primary_cluster = (
{'endpoint': {
'passwd': 'admin',
'port': 443,
'url': 'https://192.168.139.11:443',
'svip': '10.10.8.11',
'mvip': '10.10.8.12',
'login': 'admin'},
'name': 'volume-f0632d53-d836-474c-a5bc-478ef18daa32',
'clusterPairID': 33,
'uuid': 'f0632d53-d836-474c-a5bc-478ef18daa32',
'svip': '10.10.8.11',
'mvipNodeID': 1,
'repCount': 1,
'encryptionAtRestState': 'disabled',
'attributes': {},
'mvip': '10.10.8.12',
'ensemble': ['10.10.5.130'],
'svipNodeID': 1})
self.cluster_pairs = (
[{'uniqueID': 'lu9f',
'endpoint': {'passwd': 'admin', 'port': 443,
@ -3121,6 +3141,61 @@ class SolidFireVolumeTestCase(test.TestCase):
mock_create_cluster_reference.assert_called()
mock_get_sfvol_by_cinder_vref.assert_called()
@mock.patch.object(solidfire.SolidFireDriver, '_set_cluster_pairs')
@mock.patch.object(solidfire.SolidFireDriver, 'failover')
@mock.patch.object(solidfire.SolidFireDriver, 'failover_completed')
def test_failover_host(self, mock_failover_completed,
mock_failover,
mock_set_cluster_pairs):
fake_context = None
fake_cinder_vols = [{'id': 'testvol1'}, {'id': 'testvol2'}]
fake_failover_updates = [{'volume_id': 'testvol1',
'updates': {
'replication_status': 'failed-over'}},
{'volume_id': 'testvol2',
'updates': {
'replication_status': 'failed-over'}}]
mock_failover.return_value = "secondary", fake_failover_updates, []
drv_args = {'active_backend_id': None}
sfv = solidfire.SolidFireDriver(configuration=self.configuration,
**drv_args)
cluster_id, updates, _ = sfv.failover_host(
fake_context, fake_cinder_vols, secondary_id='secondary',
groups=None)
mock_failover.called_with(fake_context, fake_cinder_vols, "secondary",
None)
mock_failover_completed.called_with(fake_context, "secondary")
self.assertEqual(cluster_id, "secondary")
self.assertEqual(fake_failover_updates, updates)
@mock.patch.object(solidfire.SolidFireDriver, '_set_cluster_pairs')
@mock.patch.object(solidfire.SolidFireDriver, '_create_cluster_reference')
def test_failover_completed(self, mock_create_cluster_reference,
mock_set_cluster_pairs):
ctx = context.get_admin_context()
drv_args = {'active_backend_id': None}
sfv = solidfire.SolidFireDriver(configuration=self.configuration,
**drv_args)
sfv.cluster_pairs = self.cluster_pairs
sfv.failover_completed(ctx, "secondary")
self.assertTrue(sfv.failed_over)
self.assertDictEqual(sfv.active_cluster, sfv.cluster_pairs[0])
mock_create_cluster_reference.return_value = self.fake_primary_cluster
sfv.failover_completed(ctx, '')
self.assertFalse(sfv.failed_over)
mock_create_cluster_reference.assert_called()
self.assertDictEqual(sfv.active_cluster, self.fake_primary_cluster)
@mock.patch.object(solidfire.SolidFireDriver, '_issue_api_request')
@mock.patch.object(solidfire.SolidFireDriver, '_create_cluster_reference')
@mock.patch.object(solidfire.SolidFireDriver, '_set_cluster_pairs')
@ -3130,15 +3205,15 @@ class SolidFireVolumeTestCase(test.TestCase):
@mock.patch.object(solidfire.SolidFireDriver, '_failover_volume')
@mock.patch.object(solidfire.SolidFireDriver, '_get_create_account')
@mock.patch.object(solidfire.SolidFireDriver, '_get_remote_info_by_id')
def test_failover_host(self, mock_get_remote_info_by_id,
mock_get_create_account,
mock_failover_volume,
mock_map_sf_volumes,
mock_get_cluster_info,
mock_update_cluster_status,
mock_set_cluster_pairs,
mock_create_cluster_reference,
mock_issue_api_request):
def test_failover(self, mock_get_remote_info_by_id,
mock_get_create_account,
mock_failover_volume,
mock_map_sf_volumes,
mock_get_cluster_info,
mock_update_cluster_status,
mock_set_cluster_pairs,
mock_create_cluster_reference,
mock_issue_api_request):
all_mocks = locals()
@ -3176,7 +3251,7 @@ class SolidFireVolumeTestCase(test.TestCase):
**drv_args)
self.assertRaises(exception.UnableToFailOver,
sfv.failover_host, ctx, cinder_vols, 'fake', None)
sfv.failover, ctx, cinder_vols, 'fake', None)
mock_map_sf_volumes.assert_not_called()
fake_replication_device = {'backend_id': 'fake',
@ -3187,26 +3262,28 @@ class SolidFireVolumeTestCase(test.TestCase):
self.configuration.replication_device = [fake_replication_device]
reset_mocks()
drv_args = {'active_backend_id': None}
drv_args = {'active_backend_id': ''}
sfv = solidfire.SolidFireDriver(configuration=self.configuration,
**drv_args)
sfv.replication_enabled = True
self.assertRaises(exception.InvalidReplicationTarget,
sfv.failover_host, ctx, cinder_vols, 'default', None)
sfv.failover, ctx, cinder_vols, 'default', None)
mock_map_sf_volumes.assert_not_called()
reset_mocks()
drv_args = {'active_backend_id': None}
sfv = solidfire.SolidFireDriver(configuration=self.configuration,
**drv_args)
sfv.replication_enabled = True
self.assertRaises(exception.InvalidReplicationTarget,
sfv.failover_host, ctx, cinder_vols,
sfv.failover, ctx, cinder_vols,
secondary_id='not_fake_id', groups=None)
mock_map_sf_volumes.assert_not_called()
mock_create_cluster_reference.return_value = self.cluster_pairs[0]
reset_mocks()
drv_args = {'active_backend_id': 'secondary'}
drv_args = {'active_backend_id': 'fake'}
sfv = solidfire.SolidFireDriver(configuration=self.configuration,
**drv_args)
sfv.cluster_pairs = self.cluster_pairs
@ -3223,7 +3300,6 @@ class SolidFireVolumeTestCase(test.TestCase):
mock_failover_volume.assert_called()
mock_map_sf_volumes.assert_called()
mock_update_cluster_status.assert_called()
mock_set_cluster_pairs.assert_called()
mock_create_cluster_reference.assert_called()
reset_mocks()
@ -3233,7 +3309,7 @@ class SolidFireVolumeTestCase(test.TestCase):
sfv.cluster_pairs = self.cluster_pairs
sfv.cluster_pairs[0]['backend_id'] = 'fake'
sfv.replication_enabled = True
cluster_id, updates, _ = sfv.failover_host(
cluster_id, updates, _ = sfv.failover(
ctx, cinder_vols, secondary_id='fake', groups=None)
self.assertEqual(5, len(updates))
for update in updates:
@ -3245,7 +3321,6 @@ class SolidFireVolumeTestCase(test.TestCase):
mock_failover_volume.assert_called()
mock_map_sf_volumes.assert_called()
mock_update_cluster_status.assert_called()
mock_set_cluster_pairs.assert_called()
mock_create_cluster_reference.assert_called()
@mock.patch.object(solidfire.SolidFireDriver, '_issue_api_request')

View File

@ -225,9 +225,14 @@ class SolidFireDriver(san.SanISCSIDriver):
SnapshotsOnly)
2.0.17 - Fix bug #1859653 SolidFire fails to failback when volume
service is restarted
2.1.0 - Add Cinder Active/Active support
- Enable Active/Active support flag
- Implement Active/Active replication support
"""
VERSION = '2.0.17'
VERSION = '2.1.0'
SUPPORTS_ACTIVE_ACTIVE = True
# ThirdPartySystems wiki page
CI_WIKI_NAME = "NetApp_SolidFire_CI"
@ -2347,7 +2352,7 @@ class SolidFireDriver(san.SanISCSIDriver):
self._issue_api_request('ModifyVolume', params,
endpoint=tgt_cluster['endpoint'])
def failover_host(self, context, volumes, secondary_id=None, groups=None):
def failover(self, context, volumes, secondary_id=None, groups=None):
"""Failover to replication target.
In order to do failback, you MUST specify the original/default cluster
@ -2484,10 +2489,7 @@ class SolidFireDriver(san.SanISCSIDriver):
}
}
vol_updates['updates'].update(conn_info)
volume_updates.append(vol_updates)
LOG.debug("Updates for volume: %(id)s %(updates)s",
{'id': v.id, 'updates': vol_updates})
except Exception as e:
volume_updates.append({'volume_id': v['id'],
@ -2500,18 +2502,37 @@ class SolidFireDriver(san.SanISCSIDriver):
volume_updates.append({'volume_id': v['id'],
'updates': {'status': 'error', }})
self.active_cluster = remote
return '' if failback else remote['backend_id'], volume_updates, []
if failback:
active_cluster_id = ''
def failover_completed(self, context, active_backend_id=None):
"""Update volume node when `failover` is completed.
Expects the following scenarios:
1) active_backend_id='' when failing back
2) active_backend_id=<secondary_backend_id> when failing over
3) When `failover` raises an Exception, this will be called
with the previous active_backend_id (Will be empty string
in case backend wasn't in failed-over state).
"""
if not active_backend_id:
LOG.info("Failback completed. "
"Switching active cluster back to default.")
self.active_cluster = self._create_cluster_reference()
self.failed_over = False
# Recreating cluster pairs after a successful failback
self._set_cluster_pairs()
else:
active_cluster_id = remote['backend_id']
LOG.info("Failover completed. "
"Switching active cluster to %s.", active_backend_id)
self.active_cluster = self.cluster_pairs[0]
self.failed_over = True
return active_cluster_id, volume_updates, []
def failover_host(self, context, volumes, secondary_id=None, groups=None):
"""Failover to replication target in non-clustered deployment."""
active_cluster_id, volume_updates, group_updates = (
self.failover(context, volumes, secondary_id, groups))
self.failover_completed(context, active_cluster_id)
return active_cluster_id, volume_updates, group_updates
def freeze_backend(self, context):
"""Freeze backend notification."""

View File

@ -0,0 +1,6 @@
---
features:
- |
NetApp SolidFire driver: Enabled support for Active/Active
(including replication) to the SolidFire driver. This allows
users to configure SolidFire backends in clustered environments.