Fujitsu Driver: Add support for revert to snapshot
Adding support to revert a volume to a snapshot in Fujitsu driver. This patch implements: - revert_to_snapshot() Implements: blueprint fujitsu-revert-to-snapshot-support Change-Id: I6d74c718784c6ca46c51ce0a122521081236ac0b
This commit is contained in:
parent
a50b897459
commit
68590efa06
@ -181,7 +181,7 @@ FAKE_POOLS = [{
|
|||||||
}]
|
}]
|
||||||
|
|
||||||
FAKE_STATS = {
|
FAKE_STATS = {
|
||||||
'driver_version': '1.4.6',
|
'driver_version': '1.4.7',
|
||||||
'storage_protocol': 'iSCSI',
|
'storage_protocol': 'iSCSI',
|
||||||
'vendor_name': 'FUJITSU',
|
'vendor_name': 'FUJITSU',
|
||||||
'QoS_support': True,
|
'QoS_support': True,
|
||||||
@ -191,7 +191,7 @@ FAKE_STATS = {
|
|||||||
'pools': FAKE_POOLS,
|
'pools': FAKE_POOLS,
|
||||||
}
|
}
|
||||||
FAKE_STATS2 = {
|
FAKE_STATS2 = {
|
||||||
'driver_version': '1.4.6',
|
'driver_version': '1.4.7',
|
||||||
'storage_protocol': 'FC',
|
'storage_protocol': 'FC',
|
||||||
'vendor_name': 'FUJITSU',
|
'vendor_name': 'FUJITSU',
|
||||||
'QoS_support': True,
|
'QoS_support': True,
|
||||||
@ -425,7 +425,7 @@ class FakeEternusConnection(object):
|
|||||||
SourceElement=None, TargetElement=None,
|
SourceElement=None, TargetElement=None,
|
||||||
Operation=None, CopyType=None,
|
Operation=None, CopyType=None,
|
||||||
Synchronization=None, ProtocolControllers=None,
|
Synchronization=None, ProtocolControllers=None,
|
||||||
TargetPool=None):
|
TargetPool=None, WaitForCopyState=None):
|
||||||
global MAP_STAT, VOL_STAT
|
global MAP_STAT, VOL_STAT
|
||||||
if MethodName == 'CreateOrModifyElementFromStoragePool':
|
if MethodName == 'CreateOrModifyElementFromStoragePool':
|
||||||
VOL_STAT = '1'
|
VOL_STAT = '1'
|
||||||
@ -630,6 +630,35 @@ class FakeEternusConnection(object):
|
|||||||
|
|
||||||
def _ref_storage_sync(self):
|
def _ref_storage_sync(self):
|
||||||
syncnames = []
|
syncnames = []
|
||||||
|
|
||||||
|
cpsessions = {}
|
||||||
|
|
||||||
|
synced = FakeCIMInstanceName()
|
||||||
|
synced_keybindings = {}
|
||||||
|
synced_keybindings['CreationClassName'] = STOR_VOL
|
||||||
|
synced_keybindings['DeviceID'] = FAKE_LUN_ID2
|
||||||
|
synced_keybindings['SystemCreationClassName'] = \
|
||||||
|
'FUJITSU_StorageComputerSystem'
|
||||||
|
synced_keybindings['SystemName'] = STORAGE_SYSTEM
|
||||||
|
synced['ClassName'] = STOR_VOL
|
||||||
|
synced.keybindings = synced_keybindings
|
||||||
|
cpsessions['SyncedElement'] = synced
|
||||||
|
|
||||||
|
system = FakeCIMInstanceName()
|
||||||
|
system_keybindings = {}
|
||||||
|
system_keybindings['CreationClassName'] = STOR_VOL
|
||||||
|
system_keybindings['DeviceID'] = FAKE_LUN_ID1
|
||||||
|
system_keybindings['SystemCreationClassName'] = \
|
||||||
|
'FUJITSU_StorageComputerSystem'
|
||||||
|
system_keybindings['SystemName'] = STORAGE_SYSTEM
|
||||||
|
system['ClassName'] = STOR_VOL
|
||||||
|
system.keybindings = system_keybindings
|
||||||
|
cpsessions['SystemElement'] = system
|
||||||
|
|
||||||
|
cpsessions['classname'] = STOR_SYNC
|
||||||
|
|
||||||
|
syncnames.append(cpsessions)
|
||||||
|
|
||||||
return syncnames
|
return syncnames
|
||||||
|
|
||||||
def _default_ref(self, objectpath):
|
def _default_ref(self, objectpath):
|
||||||
@ -842,6 +871,7 @@ class FakeEternusConnection(object):
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
volume['provider_location'] = str(name)
|
volume['provider_location'] = str(name)
|
||||||
|
volume.path.keybindings = name['keybindings']
|
||||||
volumes.append(volume)
|
volumes.append(volume)
|
||||||
|
|
||||||
volume3 = FJ_StorageVolume()
|
volume3 = FJ_StorageVolume()
|
||||||
@ -867,6 +897,7 @@ class FakeEternusConnection(object):
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
volume3['provider_location'] = str(name3)
|
volume3['provider_location'] = str(name3)
|
||||||
|
volume3.path.keybindings = name3['keybindings']
|
||||||
volumes.append(volume3)
|
volumes.append(volume3)
|
||||||
|
|
||||||
snap_vol = FJ_StorageVolume()
|
snap_vol = FJ_StorageVolume()
|
||||||
@ -891,6 +922,7 @@ class FakeEternusConnection(object):
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
snap_vol['provider_location'] = str(name2)
|
snap_vol['provider_location'] = str(name2)
|
||||||
|
snap_vol.path.keybindings = name2['keybindings']
|
||||||
volumes.append(snap_vol)
|
volumes.append(snap_vol)
|
||||||
|
|
||||||
snap_vol2 = FJ_StorageVolume()
|
snap_vol2 = FJ_StorageVolume()
|
||||||
@ -927,6 +959,18 @@ class FakeEternusConnection(object):
|
|||||||
clone_vol['SystemCreationClassName'] = 'FUJITSU_StorageComputerSystem'
|
clone_vol['SystemCreationClassName'] = 'FUJITSU_StorageComputerSystem'
|
||||||
clone_vol.path = clone_vol
|
clone_vol.path = clone_vol
|
||||||
clone_vol.path.classname = clone_vol['CreationClassName']
|
clone_vol.path.classname = clone_vol['CreationClassName']
|
||||||
|
|
||||||
|
name_clone = {
|
||||||
|
'classname': 'FUJITSU_StorageVolume',
|
||||||
|
'keybindings': {
|
||||||
|
'CreationClassName': 'FUJITSU_StorageVolume',
|
||||||
|
'SystemName': STORAGE_SYSTEM,
|
||||||
|
'DeviceID': clone_vol['DeviceID'],
|
||||||
|
'SystemCreationClassName': 'FUJITSU_StorageComputerSystem',
|
||||||
|
},
|
||||||
|
}
|
||||||
|
clone_vol['provider_location'] = str(name_clone)
|
||||||
|
clone_vol.path.keybindings = name_clone['keybindings']
|
||||||
volumes.append(clone_vol)
|
volumes.append(clone_vol)
|
||||||
|
|
||||||
return volumes
|
return volumes
|
||||||
@ -1318,6 +1362,23 @@ class FJFCDriverTestCase(test.TestCase):
|
|||||||
}
|
}
|
||||||
self.assertEqual(FAKE_MIGRATED_MODEL_UPDATE, model_update)
|
self.assertEqual(FAKE_MIGRATED_MODEL_UPDATE, model_update)
|
||||||
|
|
||||||
|
def test_revert_to_snapshot(self):
|
||||||
|
self.driver.common.revert_to_snapshot = mock.Mock()
|
||||||
|
model_info = self.driver.create_volume(TEST_VOLUME)
|
||||||
|
self.volume_update(TEST_VOLUME, model_info)
|
||||||
|
self.assertEqual(FAKE_MODEL_INFO1, model_info)
|
||||||
|
|
||||||
|
snap_info = self.driver.create_snapshot(TEST_SNAP)
|
||||||
|
self.volume_update(TEST_SNAP, snap_info)
|
||||||
|
self.assertEqual(FAKE_SNAP_INFO, snap_info)
|
||||||
|
|
||||||
|
self.driver.revert_to_snapshot(self.context,
|
||||||
|
TEST_VOLUME,
|
||||||
|
TEST_SNAP)
|
||||||
|
|
||||||
|
self.driver.common.revert_to_snapshot.assert_called_with(TEST_VOLUME,
|
||||||
|
TEST_SNAP)
|
||||||
|
|
||||||
|
|
||||||
class FJISCSIDriverTestCase(test.TestCase):
|
class FJISCSIDriverTestCase(test.TestCase):
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
@ -1618,6 +1679,23 @@ class FJISCSIDriverTestCase(test.TestCase):
|
|||||||
}
|
}
|
||||||
self.assertEqual(FAKE_MIGRATED_MODEL_UPDATE, model_update)
|
self.assertEqual(FAKE_MIGRATED_MODEL_UPDATE, model_update)
|
||||||
|
|
||||||
|
def test_revert_to_snapshot(self):
|
||||||
|
self.driver.common.revert_to_snapshot = mock.Mock()
|
||||||
|
model_info = self.driver.create_volume(TEST_VOLUME)
|
||||||
|
self.volume_update(TEST_VOLUME, model_info)
|
||||||
|
self.assertEqual(FAKE_MODEL_INFO1, model_info)
|
||||||
|
|
||||||
|
snap_info = self.driver.create_snapshot(TEST_SNAP)
|
||||||
|
self.volume_update(TEST_SNAP, snap_info)
|
||||||
|
self.assertEqual(FAKE_SNAP_INFO, snap_info)
|
||||||
|
|
||||||
|
self.driver.revert_to_snapshot(self.context,
|
||||||
|
TEST_VOLUME,
|
||||||
|
TEST_SNAP)
|
||||||
|
|
||||||
|
self.driver.common.revert_to_snapshot.assert_called_with(TEST_VOLUME,
|
||||||
|
TEST_SNAP)
|
||||||
|
|
||||||
|
|
||||||
class FJCLITestCase(test.TestCase):
|
class FJCLITestCase(test.TestCase):
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
@ -1701,6 +1779,8 @@ class FJCLITestCase(test.TestCase):
|
|||||||
ret = '%s\r\n00\r\nCLI> ' % exec_cmdline
|
ret = '%s\r\n00\r\nCLI> ' % exec_cmdline
|
||||||
elif exec_cmdline.startswith('start copy-snap-opc'):
|
elif exec_cmdline.startswith('start copy-snap-opc'):
|
||||||
ret = '%s\r\n00\r\n0019\r\nCLI> ' % exec_cmdline
|
ret = '%s\r\n00\r\n0019\r\nCLI> ' % exec_cmdline
|
||||||
|
elif exec_cmdline.startswith('start copy-opc'):
|
||||||
|
ret = '%s\r\n00\r\n0019\r\nCLI> ' % exec_cmdline
|
||||||
else:
|
else:
|
||||||
ret = None
|
ret = None
|
||||||
return ret
|
return ret
|
||||||
@ -1884,6 +1964,19 @@ class FJCLITestCase(test.TestCase):
|
|||||||
**FAKE_STOP_COPY_SESSION_OPTION)
|
**FAKE_STOP_COPY_SESSION_OPTION)
|
||||||
self.assertEqual(FAKE_STOP_OUTPUT, stop_output)
|
self.assertEqual(FAKE_STOP_OUTPUT, stop_output)
|
||||||
|
|
||||||
|
def test_start_copy_opc(self):
|
||||||
|
FAKE_SNAP_OPC_OPTION = self.create_fake_options(
|
||||||
|
source_volume_number=31,
|
||||||
|
destination_volume_number=39,
|
||||||
|
)
|
||||||
|
|
||||||
|
FAKE_OPC_ID = '0019'
|
||||||
|
FAKE_OPC_INFO = {**FAKE_CLI_OUTPUT,
|
||||||
|
'message': [FAKE_OPC_ID]}
|
||||||
|
|
||||||
|
opc_id = self.cli._start_copy_opc(**FAKE_SNAP_OPC_OPTION)
|
||||||
|
self.assertEqual(FAKE_OPC_INFO, opc_id)
|
||||||
|
|
||||||
def test_delete_volume(self):
|
def test_delete_volume(self):
|
||||||
FAKE_VOLUME_NAME = 'FJosv_0qJ4rpOHgFE8ipcJOMfBmg=='
|
FAKE_VOLUME_NAME = 'FJosv_0qJ4rpOHgFE8ipcJOMfBmg=='
|
||||||
FAKE_DELETE_OUTPUT = {**FAKE_CLI_OUTPUT, 'message': []}
|
FAKE_DELETE_OUTPUT = {**FAKE_CLI_OUTPUT, 'message': []}
|
||||||
|
@ -55,6 +55,7 @@ class FJDXCLI(object):
|
|||||||
'show_enclosure_status': self._show_enclosure_status,
|
'show_enclosure_status': self._show_enclosure_status,
|
||||||
'start_copy_snap_opc': self._start_copy_snap_opc,
|
'start_copy_snap_opc': self._start_copy_snap_opc,
|
||||||
'stop_copy_session': self._stop_copy_session,
|
'stop_copy_session': self._stop_copy_session,
|
||||||
|
'start_copy_opc': self._start_copy_opc,
|
||||||
'delete_volume': self._delete_volume
|
'delete_volume': self._delete_volume
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -491,6 +492,10 @@ class FJDXCLI(object):
|
|||||||
"""Exec stop copy-session."""
|
"""Exec stop copy-session."""
|
||||||
return self._exec_cli("stop copy-session", **option)
|
return self._exec_cli("stop copy-session", **option)
|
||||||
|
|
||||||
|
def _start_copy_opc(self, **option):
|
||||||
|
"""Exec start copy-opc."""
|
||||||
|
return self._exec_cli("start copy-opc", **option)
|
||||||
|
|
||||||
def _delete_volume(self, **option):
|
def _delete_volume(self, **option):
|
||||||
"""Exec delete volume."""
|
"""Exec delete volume."""
|
||||||
return self._exec_cli('delete volume', **option)
|
return self._exec_cli('delete volume', **option)
|
||||||
|
@ -75,10 +75,11 @@ class FJDXCommon(object):
|
|||||||
1.4.4 - Add support for update migrated volume.
|
1.4.4 - Add support for update migrated volume.
|
||||||
1.4.5 - Add metadata for snapshot.
|
1.4.5 - Add metadata for snapshot.
|
||||||
1.4.6 - Add parameter fujitsu_use_cli_copy.
|
1.4.6 - Add parameter fujitsu_use_cli_copy.
|
||||||
|
1.4.7 - Add support for revert-to-snapshot.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
VERSION = "1.4.6"
|
VERSION = "1.4.7"
|
||||||
stats = {
|
stats = {
|
||||||
'driver_version': VERSION,
|
'driver_version': VERSION,
|
||||||
'storage_protocol': None,
|
'storage_protocol': None,
|
||||||
@ -3438,6 +3439,129 @@ class FJDXCommon(object):
|
|||||||
LOG.warning(msg)
|
LOG.warning(msg)
|
||||||
raise exception.VolumeBackendAPIException(data=msg)
|
raise exception.VolumeBackendAPIException(data=msg)
|
||||||
|
|
||||||
|
def revert_to_snapshot(self, volume, snapshot):
|
||||||
|
"""Revert volume to snapshot."""
|
||||||
|
LOG.debug('revert_to_snapshot, Enter method, '
|
||||||
|
'volume id: %(vid)s, '
|
||||||
|
'snapshot id: %(sid)s. ',
|
||||||
|
{'vid': volume['id'], 'sid': snapshot['id']})
|
||||||
|
|
||||||
|
vol_instance = self._find_lun(volume)
|
||||||
|
sdv_instance = self._find_lun(snapshot)
|
||||||
|
volume_no = self._get_volume_number(vol_instance)
|
||||||
|
snapshot_no = self._get_volume_number(sdv_instance)
|
||||||
|
|
||||||
|
# Check the existence of volume.
|
||||||
|
if not vol_instance:
|
||||||
|
msg = (_('revert_to_snapshot, '
|
||||||
|
'source volume not found on ETERNUS, '
|
||||||
|
'volume: %(volume)s. ')
|
||||||
|
% {'volume': volume})
|
||||||
|
LOG.error(msg)
|
||||||
|
raise exception.VolumeBackendAPIException(data=msg)
|
||||||
|
|
||||||
|
# Check the existence of sdv.
|
||||||
|
if not sdv_instance:
|
||||||
|
msg = (_('revert_to_snapshot, '
|
||||||
|
'snapshot volume not found on ETERNUS. '
|
||||||
|
'snapshot: %(snapshot)s. ')
|
||||||
|
% {'snapshot': snapshot})
|
||||||
|
LOG.error(msg)
|
||||||
|
raise exception.VolumeBackendAPIException(data=msg)
|
||||||
|
|
||||||
|
sdvsession = None
|
||||||
|
cpsessionlist = self._find_copysession(vol_instance)
|
||||||
|
|
||||||
|
LOG.debug('revert_to_snapshot, '
|
||||||
|
'cpsessionlist: %(cpsessionlist)s. ',
|
||||||
|
{'cpsessionlist': cpsessionlist})
|
||||||
|
|
||||||
|
for cpsession in cpsessionlist:
|
||||||
|
if (cpsession['SystemElement'].keybindings.get('DeviceID') ==
|
||||||
|
vol_instance.path.keybindings.get('DeviceID')):
|
||||||
|
if (cpsession['SyncedElement'].keybindings.get('DeviceID') ==
|
||||||
|
sdv_instance.path.keybindings.get('DeviceID')):
|
||||||
|
sdvsession = cpsession
|
||||||
|
break
|
||||||
|
|
||||||
|
if sdvsession:
|
||||||
|
LOG.debug('revert_to_snapshot, '
|
||||||
|
'sdvsession: %(sdvsession)s. ',
|
||||||
|
{'sdvsession': sdvsession})
|
||||||
|
|
||||||
|
repservice = self._find_eternus_service(
|
||||||
|
"FUJITSU_ReplicationService")
|
||||||
|
|
||||||
|
if repservice is None:
|
||||||
|
msg = _('revert_to_snapshot, '
|
||||||
|
'Replication Service not found. ')
|
||||||
|
LOG.error(msg)
|
||||||
|
raise exception.VolumeBackendAPIException(data=msg)
|
||||||
|
|
||||||
|
# Invoke method for revert to snapshot
|
||||||
|
rc, errordesc, job = self._exec_eternus_service(
|
||||||
|
'ModifyReplicaSynchronization',
|
||||||
|
repservice,
|
||||||
|
Operation=self._pywbem_uint(15, '16'),
|
||||||
|
WaitForCopyState=self._pywbem_uint(8, '16'),
|
||||||
|
Synchronization=sdvsession)
|
||||||
|
|
||||||
|
if rc != 0:
|
||||||
|
msg = (_('revert_to_snapshot, '
|
||||||
|
'_exec_eternus_service error, '
|
||||||
|
'volume: %(volume)s, '
|
||||||
|
'Return code: %(rc)lu, '
|
||||||
|
'Error: %(errordesc)s, '
|
||||||
|
'Message: %(job)s.')
|
||||||
|
% {'volume': volume['id'],
|
||||||
|
'rc': rc,
|
||||||
|
'errordesc': errordesc,
|
||||||
|
'job': job})
|
||||||
|
LOG.error(msg)
|
||||||
|
raise exception.VolumeBackendAPIException(data=msg)
|
||||||
|
else:
|
||||||
|
LOG.debug('revert_to_snapshot, '
|
||||||
|
'successfully. ')
|
||||||
|
else:
|
||||||
|
is_find = False
|
||||||
|
cp_session_list = self._get_copy_sessions_list()
|
||||||
|
for cp in cp_session_list:
|
||||||
|
if (cp['Source Num'] == int(volume_no, 16) and
|
||||||
|
cp['Dest Num'] == int(snapshot_no, 16) and
|
||||||
|
cp['Type'] == 'Snap'):
|
||||||
|
is_find = True
|
||||||
|
break
|
||||||
|
|
||||||
|
if is_find is True:
|
||||||
|
param_dict = (
|
||||||
|
{'source-volume-number': int(snapshot_no, 16),
|
||||||
|
'destination-volume-number': int(volume_no, 16)})
|
||||||
|
|
||||||
|
rc, emsg, clidata = self._exec_eternus_cli(
|
||||||
|
'start_copy_opc',
|
||||||
|
**param_dict)
|
||||||
|
|
||||||
|
if rc != 0:
|
||||||
|
msg = (_('revert_to_snapshot, '
|
||||||
|
'start_copy_opc failed. '
|
||||||
|
'Return code: %(rc)lu, '
|
||||||
|
'Error: %(errormsg)s, '
|
||||||
|
'Message: %(clidata)s.')
|
||||||
|
% {'rc': rc,
|
||||||
|
'errormsg': emsg,
|
||||||
|
'clidata': clidata})
|
||||||
|
LOG.error(msg)
|
||||||
|
raise exception.VolumeBackendAPIException(data=msg)
|
||||||
|
else:
|
||||||
|
msg = (_('revert_to_snapshot, '
|
||||||
|
'snapshot volume not found on ETERNUS. '
|
||||||
|
'snapshot: %(snapshot)s. ')
|
||||||
|
% {'snapshot': snapshot})
|
||||||
|
LOG.error(msg)
|
||||||
|
raise exception.VolumeBackendAPIException(data=msg)
|
||||||
|
|
||||||
|
LOG.debug('revert_to_snapshot, Exit method. ')
|
||||||
|
|
||||||
def _get_copy_sessions_list(self, **param):
|
def _get_copy_sessions_list(self, **param):
|
||||||
"""Get copy sessions list."""
|
"""Get copy sessions list."""
|
||||||
LOG.debug('_get_copy_sessions_list, Enter method.')
|
LOG.debug('_get_copy_sessions_list, Enter method.')
|
||||||
|
@ -195,6 +195,10 @@ class FJDXFCDriver(driver.FibreChannelDriver):
|
|||||||
|
|
||||||
return model_update
|
return model_update
|
||||||
|
|
||||||
|
def revert_to_snapshot(self, context, volume, snapshot):
|
||||||
|
"""Revert volume to snapshot."""
|
||||||
|
return self.common.revert_to_snapshot(volume, snapshot)
|
||||||
|
|
||||||
def _get_metadata(self, volume):
|
def _get_metadata(self, volume):
|
||||||
v_metadata = volume.get('volume_metadata')
|
v_metadata = volume.get('volume_metadata')
|
||||||
if v_metadata:
|
if v_metadata:
|
||||||
|
@ -181,3 +181,7 @@ class FJDXISCSIDriver(driver.ISCSIDriver):
|
|||||||
'target volume meta: %s, Exit method.', model_update)
|
'target volume meta: %s, Exit method.', model_update)
|
||||||
|
|
||||||
return model_update
|
return model_update
|
||||||
|
|
||||||
|
def revert_to_snapshot(self, context, volume, snapshot):
|
||||||
|
"""Revert volume to snapshot."""
|
||||||
|
return self.common.revert_to_snapshot(volume, snapshot)
|
||||||
|
@ -47,6 +47,7 @@ Supported operations
|
|||||||
* Extend a volume.
|
* Extend a volume.
|
||||||
* Get volume statistics.
|
* Get volume statistics.
|
||||||
* Migrate Volume.
|
* Migrate Volume.
|
||||||
|
* Revert a volume to snapshot.
|
||||||
|
|
||||||
Preparation
|
Preparation
|
||||||
~~~~~~~~~~~
|
~~~~~~~~~~~
|
||||||
|
@ -905,7 +905,7 @@ driver.dell_emc_vmax_3=complete
|
|||||||
driver.dell_emc_vnx=complete
|
driver.dell_emc_vnx=complete
|
||||||
driver.dell_emc_powerflex=complete
|
driver.dell_emc_powerflex=complete
|
||||||
driver.dell_emc_xtremio=missing
|
driver.dell_emc_xtremio=missing
|
||||||
driver.fujitsu_eternus=missing
|
driver.fujitsu_eternus=complete
|
||||||
driver.fungible=missing
|
driver.fungible=missing
|
||||||
driver.hitachi_vsp=complete
|
driver.hitachi_vsp=complete
|
||||||
driver.hpe_3par=complete
|
driver.hpe_3par=complete
|
||||||
|
@ -0,0 +1,10 @@
|
|||||||
|
---
|
||||||
|
features:
|
||||||
|
- |
|
||||||
|
Fujitsu Eternus DX driver: Added support for revert to snapshot operation.
|
||||||
|
|
||||||
|
Added support of revert to snapshot functionality.
|
||||||
|
|
||||||
|
If a volume with snapshots has been extended, causing a mismatch in size
|
||||||
|
between the origin volume and the snapshot, reverting will be guarded by
|
||||||
|
cinder-api.
|
Loading…
x
Reference in New Issue
Block a user