NetApp ONTAP: Added support to Active/Active mode in NFS driver

This patch enables Active/Active support in NetApp NFS driver.

To support replication in A/A mode, the method failover_host was
splitted in two phases (failover and failover_completed) as
required by the spec [1].

[1] https://specs.openstack.org/openstack/cinder-specs/specs/ocata/ha-aa-replication.html

Change-Id: I401ca89440d44e04f460741981cf24a42e5264a0
This commit is contained in:
Nahim Alves de Souza 2023-04-19 12:22:30 +00:00 committed by Nahim Alves de Souza
parent 64b5543d92
commit 45263d54dc
6 changed files with 149 additions and 6 deletions

View File

@ -1573,6 +1573,72 @@ class NetAppCmodeNfsDriverTestCase(test.TestCase):
self.assertEqual('dev1', actual_active)
self.assertEqual([], vol_updates)
@ddt.data({'secondary_id': 'dev0', 'configured_targets': ['dev1']},
{'secondary_id': 'dev3', 'configured_targets': ['dev1', 'dev2']},
{'secondary_id': 'dev1', 'configured_targets': []},
{'secondary_id': None, 'configured_targets': []})
@ddt.unpack
def test_failover_invalid_replication_target(self, secondary_id,
configured_targets):
"""This tests executes a method in the DataMotionMixin."""
self.driver.backend_name = 'dev0'
self.mock_object(data_motion.DataMotionMixin,
'get_replication_backend_names',
return_value=configured_targets)
complete_failover_call = self.mock_object(
data_motion.DataMotionMixin, '_complete_failover')
self.assertRaises(exception.InvalidReplicationTarget,
self.driver.failover, 'fake_context', [],
secondary_id=secondary_id)
self.assertFalse(complete_failover_call.called)
def test_failover_unable_to_failover(self):
"""This tests executes a method in the DataMotionMixin."""
self.driver.backend_name = 'dev0'
self.mock_object(data_motion.DataMotionMixin, '_complete_failover',
side_effect=na_utils.NetAppDriverException)
self.mock_object(data_motion.DataMotionMixin,
'get_replication_backend_names',
return_value=['dev1', 'dev2'])
self.mock_object(self.driver.ssc_library, 'get_ssc_flexvol_names',
return_value=fake_ssc.SSC.keys())
self.mock_object(self.driver, '_update_zapi_client')
self.assertRaises(exception.UnableToFailOver,
self.driver.failover, 'fake_context', [],
secondary_id='dev1')
data_motion.DataMotionMixin._complete_failover.assert_called_once_with(
'dev0', ['dev1', 'dev2'], fake_ssc.SSC.keys(), [],
failover_target='dev1')
self.assertFalse(self.driver._update_zapi_client.called)
def test_failover(self):
"""This tests executes a method in the DataMotionMixin."""
self.driver.backend_name = 'dev0'
self.mock_object(data_motion.DataMotionMixin, '_complete_failover',
return_value=('dev1', []))
self.mock_object(data_motion.DataMotionMixin,
'get_replication_backend_names',
return_value=['dev1', 'dev2'])
self.mock_object(self.driver.ssc_library, 'get_ssc_flexvol_names',
return_value=fake_ssc.SSC.keys())
self.mock_object(self.driver, '_update_zapi_client')
actual_active, vol_updates, __ = self.driver.failover(
'fake_context', [], secondary_id='dev1', groups=[])
data_motion.DataMotionMixin._complete_failover.assert_called_once_with(
'dev0', ['dev1', 'dev2'], fake_ssc.SSC.keys(), [],
failover_target='dev1')
def test_failover_completed(self):
self.mock_object(self.driver, '_update_zapi_client')
self.driver.failover_completed('fake_context', secondary_id='dev1')
self.driver._update_zapi_client.assert_called_once_with('dev1')
self.assertTrue(self.driver.failed_over)
self.assertEqual('dev1', self.driver.failed_over_backend_name)
def test_delete_group_snapshot(self):
mock_delete_backing_file = self.mock_object(
self.driver, '_delete_backing_file_for_snapshot')

View File

@ -1027,7 +1027,7 @@ class NetAppCDOTDataMotionMixinTestCase(test.TestCase):
self.assertEqual('fallback2', target)
self.assertFalse(mock_debug_log.called)
def test__failover_host_no_suitable_target(self):
def test__complete_failover_no_suitable_target(self):
flexvols = ['nvol1', 'nvol2']
replication_backends = ['fallback1', 'fallback2']
self.mock_object(self.dm_mixin, '_choose_failover_target',
@ -1045,7 +1045,7 @@ class NetAppCDOTDataMotionMixinTestCase(test.TestCase):
self.assertFalse(self.dm_mixin.break_snapmirrors.called)
@ddt.data('fallback1', None)
def test__failover_host(self, failover_target):
def test__complete_failover(self, failover_target):
flexvols = ['nvol1', 'nvol2', 'nvol3']
replication_backends = ['fallback1', 'fallback2']
volumes = [

View File

@ -66,12 +66,15 @@ class NetAppCmodeNfsDriver(nfs_base.NetAppNfsDriver,
Implement FlexGroup pool
3.0.0 - Add support for Intra-cluster Storage assisted volume migration
Add support for revert to snapshot
4.0.0 - Add Cinder Active/Active support (High Availability)
Implement Active/Active replication support
"""
VERSION = "3.0.0"
VERSION = "4.0.0"
REQUIRED_CMODE_FLAGS = ['netapp_vserver']
SUPPORTS_ACTIVE_ACTIVE = True
def __init__(self, *args, **kwargs):
super(NetAppCmodeNfsDriver, self).__init__(*args, **kwargs)
@ -867,9 +870,23 @@ class NetAppCmodeNfsDriver(nfs_base.NetAppNfsDriver,
super(NetAppCmodeNfsDriver, self).unmanage(volume)
def failover_host(self, context, volumes, secondary_id=None, groups=None):
"""Failover a backend to a secondary replication target."""
"""Failover a backend to a secondary replication target.
return self._failover_host(volumes, secondary_id=secondary_id)
This function combines failover() and failover_completed()
to perform failover when Active/Active is not enabled.
"""
active_backend_name, volume_updates, group_updates = (
self._failover(context, volumes, secondary_id, groups))
self._failover_completed(context, active_backend_name)
return active_backend_name, volume_updates, group_updates
def failover(self, context, volumes, secondary_id=None, groups=None):
"""Failover to replication target."""
return self._failover(context, volumes, secondary_id, groups)
def failover_completed(self, context, secondary_id=None):
"""Update volume node when `failover` is completed."""
return self._failover_completed(context, secondary_id)
def _get_backing_flexvol_names(self):
"""Returns a list of backing flexvol names."""

View File

@ -787,6 +787,48 @@ class DataMotionMixin(object):
return active_backend_name, volume_updates, []
def _failover(self, context, volumes, secondary_id=None, groups=None):
"""Failover to replication target."""
if secondary_id == self.backend_name:
msg = _("Cannot failover to the same host as the primary.")
raise exception.InvalidReplicationTarget(reason=msg)
replication_targets = self.get_replication_backend_names(
self.configuration)
if not replication_targets:
msg = _("No replication targets configured for backend "
"%s. Cannot failover.")
raise exception.InvalidReplicationTarget(reason=msg % self.host)
elif secondary_id and secondary_id not in replication_targets:
msg = _("%(target)s is not among replication targets configured "
"for back end %(host)s. Cannot failover.")
payload = {
'target': secondary_id,
'host': self.host,
}
raise exception.InvalidReplicationTarget(reason=msg % payload)
flexvols = self.ssc_library.get_ssc_flexvol_names()
try:
active_backend_name, volume_updates = self._complete_failover(
self.backend_name, replication_targets, flexvols, volumes,
failover_target=secondary_id)
except na_utils.NetAppDriverException as e:
msg = _("Could not complete failover: %s") % e
raise exception.UnableToFailOver(reason=msg)
return active_backend_name, volume_updates, []
def _failover_completed(self, context, secondary_id=None):
"""Update volume node when `failover` is completed."""
# Update the ZAPI client to the backend we failed over to
self._update_zapi_client(secondary_id)
self.failed_over = True
self.failed_over_backend_name = secondary_id
def _get_replication_volume_online_timeout(self):
return self.configuration.netapp_replication_volume_online_timeout

View File

@ -157,7 +157,10 @@ title=NEC Storage M Series Driver (iSCSI, FC)
title=NEC Storage V Series Driver (iSCSI, FC)
[driver.netapp_ontap]
title=NetApp Data ONTAP Driver (iSCSI, NFS, FC, NVMe/TCP)
title=NetApp Data ONTAP Driver (iSCSI, FC, NVMe/TCP)
[driver.netapp_ontap_nfs]
title=NetApp Data ONTAP Driver (NFS)
[driver.netapp_solidfire]
title=NetApp Solidfire Driver (iSCSI)
@ -286,6 +289,7 @@ driver.macrosan=complete
driver.nec=complete
driver.nec_v=complete
driver.netapp_ontap=complete
driver.netapp_ontap_nfs=complete
driver.netapp_solidfire=complete
driver.nexenta=complete
driver.nfs=complete
@ -363,6 +367,7 @@ driver.macrosan=complete
driver.nec=complete
driver.nec_v=complete
driver.netapp_ontap=complete
driver.netapp_ontap_nfs=complete
driver.netapp_solidfire=complete
driver.nexenta=complete
driver.nfs=missing
@ -443,6 +448,7 @@ driver.macrosan=complete
driver.nec=complete
driver.nec_v=missing
driver.netapp_ontap=complete
driver.netapp_ontap_nfs=complete
driver.netapp_solidfire=complete
driver.nexenta=missing
driver.nfs=missing
@ -522,6 +528,7 @@ driver.macrosan=complete
driver.nec=missing
driver.nec_v=missing
driver.netapp_ontap=complete
driver.netapp_ontap_nfs=complete
driver.netapp_solidfire=complete
driver.nexenta=missing
driver.nfs=missing
@ -602,6 +609,7 @@ driver.macrosan=missing
driver.nec=missing
driver.nec_v=complete
driver.netapp_ontap=complete
driver.netapp_ontap_nfs=complete
driver.netapp_solidfire=complete
driver.nexenta=missing
driver.nfs=missing
@ -681,6 +689,7 @@ driver.macrosan=complete
driver.nec=complete
driver.nec_v=complete
driver.netapp_ontap=complete
driver.netapp_ontap_nfs=complete
driver.netapp_solidfire=complete
driver.nexenta=missing
driver.nfs=complete
@ -761,6 +770,7 @@ driver.macrosan=complete
driver.nec=complete
driver.nec_v=missing
driver.netapp_ontap=complete
driver.netapp_ontap_nfs=complete
driver.netapp_solidfire=complete
driver.nexenta=missing
driver.nfs=missing
@ -841,6 +851,7 @@ driver.macrosan=missing
driver.nec=complete
driver.nec_v=complete
driver.netapp_ontap=complete
driver.netapp_ontap_nfs=complete
driver.netapp_solidfire=complete
driver.nexenta=missing
driver.nfs=missing
@ -918,6 +929,7 @@ driver.macrosan=missing
driver.nec=complete
driver.nec_v=complete
driver.netapp_ontap=complete
driver.netapp_ontap_nfs=complete
driver.netapp_solidfire=complete
driver.nexenta=missing
driver.nfs=missing
@ -999,6 +1011,7 @@ driver.macrosan=complete
driver.nec=missing
driver.nec_v=missing
driver.netapp_ontap=missing
driver.netapp_ontap_nfs=complete
driver.netapp_solidfire=complete
driver.nexenta=missing
driver.nfs=missing

View File

@ -0,0 +1,5 @@
---
features:
- |
NetApp ONTAP NFS driver: Enabled support for Active/Active environments
in the NetApp NFS driver (including replication).