Modify Pure driver to configure PG/Pod names

Add two config options to the Pure Storage driver to allow setting the
names for replication PGs and Pods. This is beneficial in multi-backend
scenarios so that each backend can use a different PG/Pod. It also
allows for the use of existing PGs/Pods.

These options should not be modified after volumes exist on a backend.

Change-Id: Ie0dbf00fb2ecea3344e58d2b84fd4dfd6ae1daee
This commit is contained in:
Avishay Traeger 2018-08-27 09:09:55 +03:00
parent 45df029a4d
commit 030d893697
4 changed files with 62 additions and 14 deletions

View File

@ -538,6 +538,8 @@ class PureBaseSharedDriverTestCase(PureDriverTestCase):
super(PureBaseSharedDriverTestCase, self).setUp() super(PureBaseSharedDriverTestCase, self).setUp()
self.driver = pure.PureBaseVolumeDriver(configuration=self.mock_config) self.driver = pure.PureBaseVolumeDriver(configuration=self.mock_config)
self.driver._array = self.array self.driver._array = self.array
self.driver._replication_pod_name = 'cinder-pod'
self.driver._replication_pg_name = 'cinder-group'
self.array.get_rest_version.return_value = '1.4' self.array.get_rest_version.return_value = '1.4'
self.purestorage_module.FlashArray.side_effect = None self.purestorage_module.FlashArray.side_effect = None
self.async_array2.get_rest_version.return_value = '1.4' self.async_array2.get_rest_version.return_value = '1.4'
@ -603,6 +605,9 @@ class PureBaseVolumeDriverTestCase(PureBaseSharedDriverTestCase):
REPLICATION_RETENTION_LONG_TERM) REPLICATION_RETENTION_LONG_TERM)
self.mock_config.pure_replica_retention_long_term_default = ( self.mock_config.pure_replica_retention_long_term_default = (
REPLICATION_RETENTION_LONG_TERM_PER_DAY) REPLICATION_RETENTION_LONG_TERM_PER_DAY)
self.mock_config.pure_replication_pg_name = 'cinder-group'
self.mock_config.pure_replication_pod_name = 'cinder-pod'
self.mock_config.safe_get.return_value = [ self.mock_config.safe_get.return_value = [
{"backend_id": self.driver._array.array_id, {"backend_id": self.driver._array.array_id,
"managed_backend_name": None, "managed_backend_name": None,
@ -2455,12 +2460,12 @@ class PureBaseVolumeDriverTestCase(PureBaseSharedDriverTestCase):
self.assertEqual(expected_model_update, model_update) self.assertEqual(expected_model_update, model_update)
if expected_add_to_group: if expected_add_to_group:
self.array.set_pgroup.assert_called_once_with( self.array.set_pgroup.assert_called_once_with(
pure.REPLICATION_CG_NAME, self.driver._replication_pg_name,
addvollist=[vol_name] addvollist=[vol_name]
) )
if expected_remove_from_pgroup: if expected_remove_from_pgroup:
self.array.set_pgroup.assert_called_once_with( self.array.set_pgroup.assert_called_once_with(
pure.REPLICATION_CG_NAME, self.driver._replication_pg_name,
remvollist=[vol_name] remvollist=[vol_name]
) )

View File

@ -72,6 +72,12 @@ PURE_OPTS = [
cfg.IntOpt("pure_replica_retention_long_term_default", default=7, cfg.IntOpt("pure_replica_retention_long_term_default", default=7,
help="Retain snapshots per day on target for this time " help="Retain snapshots per day on target for this time "
"(in days.)"), "(in days.)"),
cfg.StrOpt("pure_replication_pg_name", default="cinder-group",
help="Pure Protection Group name to use for async replication "
"(will be created if it does not exist)."),
cfg.StrOpt("pure_replication_pod_name", default="cinder-pod",
help="Pure Pod name to use for sync replication "
"(will be created if it does not exist)."),
cfg.BoolOpt("pure_eradicate_on_delete", cfg.BoolOpt("pure_eradicate_on_delete",
default=False, default=False,
help="When enabled, all Pure volumes, snapshots, and " help="When enabled, all Pure volumes, snapshots, and "
@ -89,8 +95,6 @@ CONF.register_opts(PURE_OPTS, group=configuration.SHARED_CONF_GROUP)
INVALID_CHARACTERS = re.compile(r"[^-a-zA-Z0-9]") INVALID_CHARACTERS = re.compile(r"[^-a-zA-Z0-9]")
GENERATED_NAME = re.compile(r".*-[a-f0-9]{32}-cinder$") GENERATED_NAME = re.compile(r".*-[a-f0-9]{32}-cinder$")
REPLICATION_CG_NAME = "cinder-group"
REPLICATION_POD_NAME = "cinder-pod"
REPLICATION_TYPE_SYNC = "sync" REPLICATION_TYPE_SYNC = "sync"
REPLICATION_TYPE_ASYNC = "async" REPLICATION_TYPE_ASYNC = "async"
REPLICATION_TYPES = [REPLICATION_TYPE_SYNC, REPLICATION_TYPE_ASYNC] REPLICATION_TYPES = [REPLICATION_TYPE_SYNC, REPLICATION_TYPE_ASYNC]
@ -181,8 +185,8 @@ class PureBaseVolumeDriver(san.SanDriver):
self._replication_target_arrays = [] self._replication_target_arrays = []
self._active_cluster_target_arrays = [] self._active_cluster_target_arrays = []
self._uniform_active_cluster_target_arrays = [] self._uniform_active_cluster_target_arrays = []
self._replication_pg_name = REPLICATION_CG_NAME self._replication_pg_name = None
self._replication_pod_name = REPLICATION_POD_NAME self._replication_pod_name = None
self._replication_interval = None self._replication_interval = None
self._replication_retention_short_term = None self._replication_retention_short_term = None
self._replication_retention_long_term = None self._replication_retention_long_term = None
@ -200,6 +204,10 @@ class PureBaseVolumeDriver(san.SanDriver):
} }
def parse_replication_configs(self): def parse_replication_configs(self):
self._replication_pg_name = (
self.configuration.pure_replication_pg_name)
self._replication_pod_name = (
self.configuration.pure_replication_pod_name)
self._replication_interval = ( self._replication_interval = (
self.configuration.pure_replica_interval_default) self.configuration.pure_replica_interval_default)
self._replication_retention_short_term = ( self._replication_retention_short_term = (
@ -1519,7 +1527,7 @@ class PureBaseVolumeDriver(san.SanDriver):
repl_type = self._get_replication_type_from_vol_type( repl_type = self._get_replication_type_from_vol_type(
volume.volume_type) volume.volume_type)
if repl_type == REPLICATION_TYPE_SYNC: if repl_type == REPLICATION_TYPE_SYNC:
base_name = REPLICATION_POD_NAME + "::" + base_name base_name = self._replication_pod_name + "::" + base_name
return base_name + "-cinder" return base_name + "-cinder"
@ -1547,7 +1555,7 @@ class PureBaseVolumeDriver(san.SanDriver):
# pod. # pod.
base_name = "" base_name = ""
if REPLICATION_TYPE_SYNC in self._group_potential_repl_types(pgroup): if REPLICATION_TYPE_SYNC in self._group_potential_repl_types(pgroup):
base_name = REPLICATION_POD_NAME + "::" base_name = self._replication_pod_name + "::"
return "%(base)sconsisgroup-%(id)s-cinder" % { return "%(base)sconsisgroup-%(id)s-cinder" % {
'base': base_name, 'id': pgroup.id} 'base': base_name, 'id': pgroup.id}
@ -2068,6 +2076,9 @@ class PureBaseVolumeDriver(san.SanDriver):
@pure_driver_debug_trace @pure_driver_debug_trace
def _create_pod_if_not_exist(self, source_array, name): def _create_pod_if_not_exist(self, source_array, name):
if not name:
raise exception.PureDriverException(
reason=_("Empty string passed for Pod name."))
try: try:
source_array.create_pod(name) source_array.create_pod(name)
except purestorage.PureHTTPError as err: except purestorage.PureHTTPError as err:
@ -2088,6 +2099,9 @@ class PureBaseVolumeDriver(san.SanDriver):
@pure_driver_debug_trace @pure_driver_debug_trace
def _create_protection_group_if_not_exist(self, source_array, pgname): def _create_protection_group_if_not_exist(self, source_array, pgname):
if not pgname:
raise exception.PureDriverException(
reason=_("Empty string passed for PG name."))
try: try:
source_array.create_pgroup(pgname) source_array.create_pgroup(pgname)
except purestorage.PureHTTPError as err: except purestorage.PureHTTPError as err:
@ -2154,7 +2168,7 @@ class PureBaseVolumeDriver(san.SanDriver):
try: try:
secondary_array = array secondary_array = array
# Ensure the pod is in a good state on the array # Ensure the pod is in a good state on the array
pod_info = secondary_array.get_pod(REPLICATION_POD_NAME) pod_info = secondary_array.get_pod(self._replication_pod_name)
for pod_array in pod_info["arrays"]: for pod_array in pod_info["arrays"]:
# Compare against Purity ID's # Compare against Purity ID's
if pod_array["array_id"] == secondary_array.array_id: if pod_array["array_id"] == secondary_array.array_id:
@ -2251,7 +2265,7 @@ class PureBaseVolumeDriver(san.SanDriver):
replicated_vol_names = set() replicated_vol_names = set()
for vol in array_volumes: for vol in array_volumes:
name = vol['name'] name = vol['name']
if name.startswith(REPLICATION_POD_NAME): if name.startswith(self._replication_pod_name):
replicated_vol_names.add(name) replicated_vol_names.add(name)
model_updates = [] model_updates = []

View File

@ -244,9 +244,9 @@ To create a volume type that specifies replication to remote back ends with asyn
The following table contains the optional configuration parameters available The following table contains the optional configuration parameters available
for async replication configuration with the Pure Storage array. for async replication configuration with the Pure Storage array.
==================================================== ============= ====== ==================================================== ============= ================
Option Description Default Option Description Default
==================================================== ============= ====== ==================================================== ============= ================
``pure_replica_interval_default`` Snapshot ``pure_replica_interval_default`` Snapshot
replication replication
interval in interval in
@ -267,8 +267,25 @@ Option Description Default
on target on target
for this for this
time (in time (in
days). ``7`` days). ``7``
==================================================== ============= ====== ``pure_replication_pg_name`` Pure
Protection
Group name to
use for async
replication
(will be
created if
it does not
exist). ``cinder-group``
``pure_replication_pod_name`` Pure Pod name
to use for
sync
replication
(will be
created if
it does not
exist). ``cinder-pod``
==================================================== ============= ================
.. note:: .. note::
@ -277,6 +294,12 @@ Option Description Default
multiple secondary arrays, but subsequent ``failover-host`` is only multiple secondary arrays, but subsequent ``failover-host`` is only
supported back to the original primary array. supported back to the original primary array.
.. note::
``pure_replication_pg_name`` and ``pure_replication_pod_name`` should not
be changed after volumes have been created in the Cinder backend, as this
could have unexpected results in both replication and failover.
Automatic thin-provisioning/oversubscription ratio Automatic thin-provisioning/oversubscription ratio
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

View File

@ -0,0 +1,6 @@
---
features:
- |
Pure Storage FlashArray driver has added configuration options
``pure_replication_pg_name`` and ``pure_replication_pod_name`` for setting
the names for replication PGs and Pods.