Merge "Dell PowerStore driver: Add Cinder active-active support"

This commit is contained in:
Zuul 2024-09-04 20:31:06 +00:00 committed by Gerrit Code Review
commit fe0dc5d331
5 changed files with 100 additions and 2 deletions

View File

@ -62,6 +62,57 @@ class TestReplication(powerstore.TestPowerStoreDriver):
{}, [], "invalid_id")
self.assertIn("is not a valid choice", error.msg)
@mock.patch("cinder.volume.drivers.dell_emc.powerstore.adapter."
"NVMEoFAdapter.failover_host")
def test_failover_valid_secondary_id(self, mock_adapter_failover):
volumes = [self.volume]
mock_adapter_failover.return_value = (volumes, None)
result = self.driver.failover({}, volumes, self.replication_backend_id)
self.assertEqual(result, (self.replication_backend_id, volumes, None))
self.driver.adapter.failover_host.assert_called_once_with(
volumes, None, False)
def test_failover_invalid_secondary_id(self):
volumes = [self.volume]
secondary_id = "invalid_id"
self.driver.active_backend_id = None
self.assertRaises(exception.InvalidReplicationTarget,
self.driver.failover,
{}, volumes, secondary_id, None)
@mock.patch("cinder.volume.drivers.dell_emc.powerstore.adapter."
"NVMEoFAdapter.failover_host")
def test_failover_is_failback(self, mock_adapter_failover):
volumes = [self.volume]
mock_adapter_failover.return_value = (volumes, None)
secondary_id = "default"
self.driver.active_backend_id = self.replication_backend_id
result = self.driver.failover({}, volumes, secondary_id, None)
self.assertEqual(result, (secondary_id, volumes, None))
self.driver.adapter.failover_host.assert_called_once_with(
volumes, None, True)
def test_failover_completed_failback(self):
self.driver.failover_completed({}, None)
self.assertEqual(self.driver.active_backend_id, "default")
def test_failover_completed_failover(self):
self.driver.replication_devices = [{"backend_id":
self.replication_backend_id}]
self.driver.failover_completed({}, "failed over")
self.assertEqual(self.driver.active_backend_id,
self.replication_backend_id)
def test_failover_completed_invalid_target(self):
self.assertRaises(exception.InvalidReplicationTarget,
self.driver.failover_completed,
{}, "invalid_target")
@mock.patch("cinder.volume.drivers.dell_emc.powerstore.client."
"PowerStoreClient.wait_for_failover_completion")
@mock.patch("cinder.volume.drivers.dell_emc.powerstore.client."

View File

@ -53,10 +53,12 @@ class PowerStoreDriver(driver.VolumeDriver):
1.2.0 - Add NVMe-OF support
1.2.1 - Report trim/discard support
1.2.2 - QoS (Quality of Service) support
1.2.3 - Added Cinder active/active support
"""
VERSION = "1.2.2"
VERSION = "1.2.3"
VENDOR = "Dell EMC"
SUPPORTS_ACTIVE_ACTIVE = True
# ThirdPartySystems wiki page
CI_WIKI_NAME = "DellEMC_PowerStore_CI"
@ -180,6 +182,14 @@ class PowerStoreDriver(driver.VolumeDriver):
pass
def failover_host(self, context, volumes, secondary_id=None, groups=None):
active_backend_id, model_updates, group_update_list = (
self.failover(context, volumes, secondary_id, groups))
self.failover_completed(context, secondary_id)
return active_backend_id, model_updates, group_update_list
def failover(self, context, volumes, secondary_id=None, groups=None):
"""Like failover but for a host that is clustered."""
LOG.info("Invoking failover with target %s.", secondary_id)
if secondary_id not in self.failover_choices:
msg = (_("Target %(target)s is not a valid choice. "
"Valid choices: %(choices)s.") %
@ -194,8 +204,38 @@ class PowerStoreDriver(driver.VolumeDriver):
groups,
is_failback
)
LOG.info("Failover host completed.")
return secondary_id, volumes_updates, groups_updates
def failover_completed(self, context, active_backend_id=None):
"""This method is called after failover for clustered backends."""
LOG.info("Invoking failover_completed with target %s.",
active_backend_id)
target_device = self.replication_devices[0]["backend_id"]
if (not active_backend_id
or active_backend_id
== manager.VolumeManager.FAILBACK_SENTINEL):
# failback operation
self.active_backend_id = manager.VolumeManager.FAILBACK_SENTINEL
elif (active_backend_id == target_device
or active_backend_id == "failed over"):
# failover operation
self.active_backend_id = target_device
else:
choices = ['None', manager.VolumeManager.FAILBACK_SENTINEL,
'failed over', target_device]
msg = (_("Target %(target)s is not a valid choice. "
"Valid choices: %(choices)s.") %
{"target": active_backend_id,
"choices": ', '.join(choices)})
msg = f"Target {active_backend_id} is not valid."
LOG.error(msg)
raise exception.InvalidReplicationTarget(reason=msg)
LOG.info("Failover completion completed: "
"active_backend_id = %s.",
self.active_backend_id)
def create_group(self, context, group):
return self.adapter.create_group(group)

View File

@ -24,6 +24,7 @@ Supported operations
- Clone a Consistency Group.
- Create a Consistency Group from a Consistency Group snapshot.
- Quality of Service (QoS)
- Cinder volume active/active support.
Driver configuration
~~~~~~~~~~~~~~~~~~~~

View File

@ -990,7 +990,7 @@ notes=Vendor drivers that support running in an active/active
driver.datacore=missing
driver.datera=missing
driver.dell_emc_powermax=complete
driver.dell_emc_powerstore=missing
driver.dell_emc_powerstore=complete
driver.dell_emc_powerstore_nfs=missing
driver.dell_emc_powervault=missing
driver.dell_emc_sc=missing

View File

@ -0,0 +1,6 @@
---
features:
- |
Dell PowerStore driver: Enabled cinder volume active/active support.
This allows users to configure Dell PowerStore backends in cinder
clustered environments.