Merge "Add cinder active-active support for Dell PowerMax driver"
This commit is contained in:
commit
38cd59cd0a
@ -2937,7 +2937,7 @@ class PowerMaxCommonTest(test.TestCase):
|
|||||||
group = self.data.test_group_1
|
group = self.data.test_group_1
|
||||||
add_vols = []
|
add_vols = []
|
||||||
remove_vols = [self.data.test_volume_group_member]
|
remove_vols = [self.data.test_volume_group_member]
|
||||||
self.mock_object(self.common, 'failover', True)
|
self.mock_object(self.common, 'failedover', True)
|
||||||
self.assertRaises(
|
self.assertRaises(
|
||||||
exception.VolumeBackendAPIException, self.common.update_group,
|
exception.VolumeBackendAPIException, self.common.update_group,
|
||||||
group, add_vols, remove_vols)
|
group, add_vols, remove_vols)
|
||||||
|
@ -315,7 +315,7 @@ class PowerMaxFCTest(test.TestCase):
|
|||||||
|
|
||||||
def test_failover_host(self):
|
def test_failover_host(self):
|
||||||
with mock.patch.object(
|
with mock.patch.object(
|
||||||
self.common, 'failover_host',
|
self.common, 'failover',
|
||||||
return_value=(self.data.remote_array, [], [])) as mock_fo:
|
return_value=(self.data.remote_array, [], [])) as mock_fo:
|
||||||
self.driver.failover_host(self.data.ctx, [self.data.test_volume])
|
self.driver.failover_host(self.data.ctx, [self.data.test_volume])
|
||||||
mock_fo.assert_called_once_with([self.data.test_volume], None,
|
mock_fo.assert_called_once_with([self.data.test_volume], None,
|
||||||
|
@ -359,8 +359,8 @@ class PowerMaxISCSITest(test.TestCase):
|
|||||||
self.data.test_volume, new_type, host)
|
self.data.test_volume, new_type, host)
|
||||||
|
|
||||||
def test_failover_host(self):
|
def test_failover_host(self):
|
||||||
with mock.patch.object(self.common, 'failover_host',
|
with mock.patch.object(self.common, 'failover',
|
||||||
return_value={}) as mock_fo:
|
return_value=(None, [], [])) as mock_fo:
|
||||||
self.driver.failover_host({}, [self.data.test_volume])
|
self.driver.failover_host({}, [self.data.test_volume])
|
||||||
mock_fo.assert_called_once_with([self.data.test_volume], None,
|
mock_fo.assert_called_once_with([self.data.test_volume], None,
|
||||||
None)
|
None)
|
||||||
|
@ -297,7 +297,8 @@ class PowerMaxReplicationTest(test.TestCase):
|
|||||||
backend_id = self.data.rep_backend_id_sync
|
backend_id = self.data.rep_backend_id_sync
|
||||||
rep_configs = self.common.rep_configs
|
rep_configs = self.common.rep_configs
|
||||||
secondary_id, volume_update_list, group_update_list = (
|
secondary_id, volume_update_list, group_update_list = (
|
||||||
self.common.failover_host(volumes, backend_id, groups))
|
self.common.failover(volumes, backend_id, groups))
|
||||||
|
self.common.failover_completed(backend_id)
|
||||||
mck_validate.assert_called_once_with(
|
mck_validate.assert_called_once_with(
|
||||||
False, backend_id, rep_configs, self.data.array, ['123'], False)
|
False, backend_id, rep_configs, self.data.array, ['123'], False)
|
||||||
mck_populate.assert_called_once_with(volumes, groups, None)
|
mck_populate.assert_called_once_with(volumes, groups, None)
|
||||||
@ -314,7 +315,7 @@ class PowerMaxReplicationTest(test.TestCase):
|
|||||||
backend_id = self.data.rep_backend_id_sync
|
backend_id = self.data.rep_backend_id_sync
|
||||||
rep_configs = self.common.rep_configs
|
rep_configs = self.common.rep_configs
|
||||||
self.assertRaises(exception.InvalidReplicationTarget,
|
self.assertRaises(exception.InvalidReplicationTarget,
|
||||||
self.common.failover_host, volumes, backend_id)
|
self.common.failover, volumes, backend_id)
|
||||||
mck_validate.assert_called_once_with(
|
mck_validate.assert_called_once_with(
|
||||||
False, backend_id, rep_configs, self.data.array, ['123'], False)
|
False, backend_id, rep_configs, self.data.array, ['123'], False)
|
||||||
|
|
||||||
@ -331,7 +332,8 @@ class PowerMaxReplicationTest(test.TestCase):
|
|||||||
backend_id = utils.PMAX_FAILOVER_START_ARRAY_PROMOTION
|
backend_id = utils.PMAX_FAILOVER_START_ARRAY_PROMOTION
|
||||||
rep_configs = self.common.rep_configs
|
rep_configs = self.common.rep_configs
|
||||||
secondary_id, volume_update_list, group_update_list = (
|
secondary_id, volume_update_list, group_update_list = (
|
||||||
self.common.failover_host(volumes, backend_id, groups))
|
self.common.failover(volumes, backend_id, groups))
|
||||||
|
self.common.failover_completed(backend_id)
|
||||||
self.assertEqual(0, mck_populate.call_count)
|
self.assertEqual(0, mck_populate.call_count)
|
||||||
self.assertEqual(backend_id, secondary_id)
|
self.assertEqual(backend_id, secondary_id)
|
||||||
self.assertEqual(list(), volume_update_list)
|
self.assertEqual(list(), volume_update_list)
|
||||||
@ -358,7 +360,8 @@ class PowerMaxReplicationTest(test.TestCase):
|
|||||||
rep_configs = self.common.rep_configs
|
rep_configs = self.common.rep_configs
|
||||||
self.common.promotion = True
|
self.common.promotion = True
|
||||||
secondary_id, volume_update_list, group_update_list = (
|
secondary_id, volume_update_list, group_update_list = (
|
||||||
self.common.failover_host(volumes, backend_id, groups))
|
self.common.failover(volumes, backend_id, groups))
|
||||||
|
self.common.failover_completed(backend_id)
|
||||||
mck_populate.assert_called_once_with(volumes, groups, None)
|
mck_populate.assert_called_once_with(volumes, groups, None)
|
||||||
mck_validate.assert_called_once_with(
|
mck_validate.assert_called_once_with(
|
||||||
False, backend_id, rep_configs, self.data.array, ['123'], True)
|
False, backend_id, rep_configs, self.data.array, ['123'], True)
|
||||||
@ -497,7 +500,8 @@ class PowerMaxReplicationTest(test.TestCase):
|
|||||||
extra_specs['rep_mode'] = utils.REP_ASYNC
|
extra_specs['rep_mode'] = utils.REP_ASYNC
|
||||||
with mock.patch.object(common.PowerMaxCommon, '_initial_setup',
|
with mock.patch.object(common.PowerMaxCommon, '_initial_setup',
|
||||||
return_value=extra_specs):
|
return_value=extra_specs):
|
||||||
self.async_driver.common.failover_host(volumes, None, [])
|
self.async_driver.common.failover(volumes, None, [])
|
||||||
|
self.common.failover_completed(None)
|
||||||
mock_fg.assert_called_once()
|
mock_fg.assert_called_once()
|
||||||
|
|
||||||
@mock.patch.object(rest.PowerMaxRest,
|
@mock.patch.object(rest.PowerMaxRest,
|
||||||
|
@ -204,7 +204,7 @@ class PowerMaxCommon(object):
|
|||||||
self.next_gen = False
|
self.next_gen = False
|
||||||
self.replication_enabled = False
|
self.replication_enabled = False
|
||||||
self.rep_devices = []
|
self.rep_devices = []
|
||||||
self.failover = True if active_backend_id else False
|
self.failedover = True if active_backend_id else False
|
||||||
self.promotion = False
|
self.promotion = False
|
||||||
self.powermax_array_tag_list = None
|
self.powermax_array_tag_list = None
|
||||||
self.powermax_short_host_name_template = None
|
self.powermax_short_host_name_template = None
|
||||||
@ -1350,7 +1350,7 @@ class PowerMaxCommon(object):
|
|||||||
array_info_list = self.pool_info['arrays_info']
|
array_info_list = self.pool_info['arrays_info']
|
||||||
already_queried = False
|
already_queried = False
|
||||||
for array_info in array_info_list:
|
for array_info in array_info_list:
|
||||||
if self.failover:
|
if self.failedover:
|
||||||
rep_config = self.rep_configs[0]
|
rep_config = self.rep_configs[0]
|
||||||
array_info = self.get_secondary_stats_info(
|
array_info = self.get_secondary_stats_info(
|
||||||
rep_config, array_info)
|
rep_config, array_info)
|
||||||
@ -5540,7 +5540,7 @@ class PowerMaxCommon(object):
|
|||||||
|
|
||||||
return rdf_group_no, remote_array
|
return rdf_group_no, remote_array
|
||||||
|
|
||||||
def failover_host(self, volumes, secondary_id=None, groups=None):
|
def failover(self, volumes, secondary_id=None, groups=None):
|
||||||
"""Fails over the volumes on a host back and forth.
|
"""Fails over the volumes on a host back and forth.
|
||||||
|
|
||||||
Driver needs to update following info for failed-over volume:
|
Driver needs to update following info for failed-over volume:
|
||||||
@ -5557,36 +5557,53 @@ class PowerMaxCommon(object):
|
|||||||
primary_array = self.configuration.safe_get(utils.POWERMAX_ARRAY)
|
primary_array = self.configuration.safe_get(utils.POWERMAX_ARRAY)
|
||||||
array_list = self.rest.get_arrays_list()
|
array_list = self.rest.get_arrays_list()
|
||||||
is_valid, msg = self.utils.validate_failover_request(
|
is_valid, msg = self.utils.validate_failover_request(
|
||||||
self.failover, secondary_id, self.rep_configs, primary_array,
|
self.failedover, secondary_id, self.rep_configs, primary_array,
|
||||||
array_list, self.promotion)
|
array_list, self.promotion)
|
||||||
if not is_valid:
|
if not is_valid:
|
||||||
LOG.error(msg)
|
LOG.error(msg)
|
||||||
raise exception.InvalidReplicationTarget(msg)
|
raise exception.InvalidReplicationTarget(msg)
|
||||||
|
|
||||||
group_fo = None
|
group_fo = None
|
||||||
if not self.failover:
|
if not self.failedover:
|
||||||
self.failover = True
|
self.failedover = True
|
||||||
if not secondary_id:
|
if not secondary_id:
|
||||||
secondary_id = utils.RDF_FAILEDOVER_STATE
|
secondary_id = utils.RDF_FAILEDOVER_STATE
|
||||||
elif secondary_id == 'default':
|
elif secondary_id == 'default':
|
||||||
self.failover = False
|
self.failedover = False
|
||||||
group_fo = 'default'
|
group_fo = 'default'
|
||||||
|
|
||||||
if secondary_id == utils.PMAX_FAILOVER_START_ARRAY_PROMOTION:
|
if secondary_id != utils.PMAX_FAILOVER_START_ARRAY_PROMOTION:
|
||||||
self.promotion = True
|
|
||||||
LOG.info("Enabled array promotion.")
|
|
||||||
else:
|
|
||||||
volume_update_list, group_update_list = (
|
volume_update_list, group_update_list = (
|
||||||
self._populate_volume_and_group_update_lists(
|
self._populate_volume_and_group_update_lists(
|
||||||
volumes, groups, group_fo))
|
volumes, groups, group_fo))
|
||||||
|
|
||||||
if secondary_id == 'default' and self.promotion:
|
LOG.info("Failover host completed.")
|
||||||
self.promotion = False
|
|
||||||
LOG.info("Disabled array promotion.")
|
|
||||||
|
|
||||||
LOG.info("Failover host complete.")
|
|
||||||
return secondary_id, volume_update_list, group_update_list
|
return secondary_id, volume_update_list, group_update_list
|
||||||
|
|
||||||
|
def failover_completed(self, secondary_id=None, isAA=False):
|
||||||
|
"""This method is called after failover for clustered backends."""
|
||||||
|
if secondary_id == utils.PMAX_FAILOVER_START_ARRAY_PROMOTION:
|
||||||
|
self.promotion = True
|
||||||
|
LOG.info("Enabled array promotion.")
|
||||||
|
else:
|
||||||
|
if isAA:
|
||||||
|
if secondary_id == 'failed over':
|
||||||
|
self.failedover = True
|
||||||
|
elif not secondary_id:
|
||||||
|
self.failedover = False
|
||||||
|
if self.promotion:
|
||||||
|
self.promotion = False
|
||||||
|
LOG.info("Disabled array promotion.")
|
||||||
|
else:
|
||||||
|
if not secondary_id:
|
||||||
|
self.failedover = True
|
||||||
|
elif secondary_id == 'default':
|
||||||
|
self.failedover = False
|
||||||
|
if self.promotion:
|
||||||
|
self.promotion = False
|
||||||
|
LOG.info("Disabled array promotion.")
|
||||||
|
LOG.info('Failover completion completed.')
|
||||||
|
|
||||||
def _populate_volume_and_group_update_lists(
|
def _populate_volume_and_group_update_lists(
|
||||||
self, volumes, groups, group_fo):
|
self, volumes, groups, group_fo):
|
||||||
"""Populate volume and group update lists
|
"""Populate volume and group update lists
|
||||||
@ -5675,7 +5692,7 @@ class PowerMaxCommon(object):
|
|||||||
'volume_id': vol.id,
|
'volume_id': vol.id,
|
||||||
'updates': {
|
'updates': {
|
||||||
'replication_status': REPLICATION_DISABLED}})
|
'replication_status': REPLICATION_DISABLED}})
|
||||||
elif self.failover:
|
elif self.failedover:
|
||||||
# Since the array has been failed-over,
|
# Since the array has been failed-over,
|
||||||
# volumes without replication should be in error.
|
# volumes without replication should be in error.
|
||||||
for vol in non_rep_vol_list:
|
for vol in non_rep_vol_list:
|
||||||
@ -6347,7 +6364,7 @@ class PowerMaxCommon(object):
|
|||||||
if self.promotion:
|
if self.promotion:
|
||||||
self._update_group_promotion(
|
self._update_group_promotion(
|
||||||
group, add_volumes, remove_volumes)
|
group, add_volumes, remove_volumes)
|
||||||
elif self.failover:
|
elif self.failedover:
|
||||||
msg = _('Cannot perform group updates during failover, please '
|
msg = _('Cannot perform group updates during failover, please '
|
||||||
'either failback or perform a promotion operation.')
|
'either failback or perform a promotion operation.')
|
||||||
raise exception.VolumeBackendAPIException(msg)
|
raise exception.VolumeBackendAPIException(msg)
|
||||||
|
@ -132,9 +132,12 @@ class PowerMaxFCDriver(san.SanDriver, driver.FibreChannelDriver):
|
|||||||
- Support for Failover Abilities (bp/powermax-failover-abilities)
|
- Support for Failover Abilities (bp/powermax-failover-abilities)
|
||||||
4.4.0 - Early check for status of port
|
4.4.0 - Early check for status of port
|
||||||
4.4.1 - Report trim/discard support
|
4.4.1 - Report trim/discard support
|
||||||
|
4.5.0 - Add PowerMax v4 support
|
||||||
|
4.5.1 - Add active/active compliance
|
||||||
"""
|
"""
|
||||||
|
|
||||||
VERSION = "4.4.1"
|
VERSION = "4.5.1"
|
||||||
|
SUPPORTS_ACTIVE_ACTIVE = True
|
||||||
|
|
||||||
# ThirdPartySystems wiki
|
# ThirdPartySystems wiki
|
||||||
CI_WIKI_NAME = "DellEMC_PowerMAX_CI"
|
CI_WIKI_NAME = "DellEMC_PowerMAX_CI"
|
||||||
@ -661,7 +664,18 @@ class PowerMaxFCDriver(san.SanDriver, driver.FibreChannelDriver):
|
|||||||
:param groups: replication groups
|
:param groups: replication groups
|
||||||
:returns: secondary_id, volume_update_list, group_update_list
|
:returns: secondary_id, volume_update_list, group_update_list
|
||||||
"""
|
"""
|
||||||
return self.common.failover_host(volumes, secondary_id, groups)
|
active_backend_id, volume_update_list, group_update_list = (
|
||||||
|
self.common.failover(volumes, secondary_id, groups))
|
||||||
|
self.common.failover_completed(secondary_id, False)
|
||||||
|
return active_backend_id, volume_update_list, group_update_list
|
||||||
|
|
||||||
|
def failover(self, context, volumes, secondary_id=None, groups=None):
|
||||||
|
"""Like failover but for a host that is clustered."""
|
||||||
|
return self.common.failover(volumes, secondary_id, groups)
|
||||||
|
|
||||||
|
def failover_completed(self, context, active_backend_id=None):
|
||||||
|
"""This method is called after failover for clustered backends."""
|
||||||
|
return self.common.failover_completed(active_backend_id, True)
|
||||||
|
|
||||||
def create_group(self, context, group):
|
def create_group(self, context, group):
|
||||||
"""Creates a generic volume group.
|
"""Creates a generic volume group.
|
||||||
|
@ -137,9 +137,12 @@ class PowerMaxISCSIDriver(san.SanISCSIDriver):
|
|||||||
- Support for Failover Abilities (bp/powermax-failover-abilities)
|
- Support for Failover Abilities (bp/powermax-failover-abilities)
|
||||||
4.4.0 - Early check for status of port
|
4.4.0 - Early check for status of port
|
||||||
4.4.1 - Report trim/discard support
|
4.4.1 - Report trim/discard support
|
||||||
|
4.5.0 - Add PowerMax v4 support
|
||||||
|
4.5.1 - Add active/active compliance
|
||||||
"""
|
"""
|
||||||
|
|
||||||
VERSION = "4.4.1"
|
VERSION = "4.5.1"
|
||||||
|
SUPPORTS_ACTIVE_ACTIVE = True
|
||||||
|
|
||||||
# ThirdPartySystems wiki
|
# ThirdPartySystems wiki
|
||||||
CI_WIKI_NAME = "DellEMC_PowerMAX_CI"
|
CI_WIKI_NAME = "DellEMC_PowerMAX_CI"
|
||||||
@ -571,7 +574,18 @@ class PowerMaxISCSIDriver(san.SanISCSIDriver):
|
|||||||
:param groups: replication groups
|
:param groups: replication groups
|
||||||
:returns: secondary_id, volume_update_list, group_update_list
|
:returns: secondary_id, volume_update_list, group_update_list
|
||||||
"""
|
"""
|
||||||
return self.common.failover_host(volumes, secondary_id, groups)
|
active_backend_id, volume_update_list, group_update_list = (
|
||||||
|
self.common.failover(volumes, secondary_id, groups))
|
||||||
|
self.common.failover_completed(secondary_id, False)
|
||||||
|
return active_backend_id, volume_update_list, group_update_list
|
||||||
|
|
||||||
|
def failover(self, context, volumes, secondary_id=None, groups=None):
|
||||||
|
"""Like failover but for a host that is clustered."""
|
||||||
|
return self.common.failover(volumes, secondary_id, groups)
|
||||||
|
|
||||||
|
def failover_completed(self, context, active_backend_id=None):
|
||||||
|
"""This method is called after failover for clustered backends."""
|
||||||
|
return self.common.failover_completed(active_backend_id, True)
|
||||||
|
|
||||||
def create_group(self, context, group):
|
def create_group(self, context, group):
|
||||||
"""Creates a generic volume group.
|
"""Creates a generic volume group.
|
||||||
|
@ -251,6 +251,7 @@ PowerMax drivers also support the following features:
|
|||||||
- Snap id support
|
- Snap id support
|
||||||
- Seamless Live Migration from SMI-S support
|
- Seamless Live Migration from SMI-S support
|
||||||
- Port group & port performance load balancing
|
- Port group & port performance load balancing
|
||||||
|
- Cinder volume active/active support
|
||||||
|
|
||||||
.. note::
|
.. note::
|
||||||
|
|
||||||
@ -2418,6 +2419,16 @@ failover promotion.
|
|||||||
# cinder service-list
|
# cinder service-list
|
||||||
# cinder service-enable <host> <binary>
|
# cinder service-enable <host> <binary>
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
|
||||||
|
With Cinder volume active/active deployment, use the following commands to
|
||||||
|
view and enable the cluster as well.
|
||||||
|
|
||||||
|
.. code-block:: console
|
||||||
|
|
||||||
|
# cinder --os-volume-api-version 3.17 cluster-list
|
||||||
|
# cinder --os-volume-api-version 3.17 cluster-enable [<binary>] <cluster-name>
|
||||||
|
|
||||||
4. Remove all volumes from volume groups
|
4. Remove all volumes from volume groups
|
||||||
|
|
||||||
.. code-block:: console
|
.. code-block:: console
|
||||||
|
@ -977,7 +977,7 @@ notes=Vendor drivers that support running in an active/active
|
|||||||
a configuration.
|
a configuration.
|
||||||
driver.datacore=missing
|
driver.datacore=missing
|
||||||
driver.datera=missing
|
driver.datera=missing
|
||||||
driver.dell_emc_powermax=missing
|
driver.dell_emc_powermax=complete
|
||||||
driver.dell_emc_powerstore=missing
|
driver.dell_emc_powerstore=missing
|
||||||
driver.dell_emc_powerstore_nfs=missing
|
driver.dell_emc_powerstore_nfs=missing
|
||||||
driver.dell_emc_powervault=missing
|
driver.dell_emc_powervault=missing
|
||||||
|
@ -0,0 +1,7 @@
|
|||||||
|
---
|
||||||
|
features:
|
||||||
|
- |
|
||||||
|
Dell PowerMax driver: Enabled support for Active/Active
|
||||||
|
to both FC and iSCSI driver. This allows users to configure
|
||||||
|
Dell PowerMax backends in clustered environments.
|
||||||
|
|
Loading…
Reference in New Issue
Block a user