Merge "backup of active 3par ISCSI bootable volume fails" into stable/newton
This commit is contained in:
commit
79ad4598aa
|
@ -1743,6 +1743,83 @@ class HPE3PARBaseDriver(object):
|
||||||
expected +
|
expected +
|
||||||
self.standard_logout)
|
self.standard_logout)
|
||||||
|
|
||||||
|
def test_get_cpg_with_volume_return_usercpg(self):
|
||||||
|
# setup_mock_client drive with default configuration
|
||||||
|
# and return the mock HTTP 3PAR client
|
||||||
|
mock_client = self.setup_driver()
|
||||||
|
mock_client.getVolume.return_value = {'name': mock.ANY,
|
||||||
|
'userCPG': HPE3PAR_CPG2}
|
||||||
|
with mock.patch.object(hpecommon.HPE3PARCommon,
|
||||||
|
'_create_client') as mock_create_client:
|
||||||
|
mock_create_client.return_value = mock_client
|
||||||
|
volume = {'id': HPE3PARBaseDriver.VOLUME_ID,
|
||||||
|
'name': HPE3PARBaseDriver.VOLUME_NAME,
|
||||||
|
'display_name': 'Foo Volume',
|
||||||
|
'size': 2,
|
||||||
|
'host': volume_utils.append_host(self.FAKE_HOST,
|
||||||
|
HPE3PAR_CPG2)}
|
||||||
|
common = self.driver._login()
|
||||||
|
user_cpg = common.get_cpg(volume)
|
||||||
|
common = hpecommon.HPE3PARCommon(None)
|
||||||
|
vol_name = common._get_3par_vol_name(volume['id'])
|
||||||
|
self.assertEqual(HPE3PAR_CPG2, user_cpg)
|
||||||
|
expected = [mock.call.getVolume(vol_name)]
|
||||||
|
|
||||||
|
mock_client.assert_has_calls(
|
||||||
|
self.standard_login +
|
||||||
|
expected)
|
||||||
|
|
||||||
|
def test_get_cpg_with_volume_return_snapcpg(self):
|
||||||
|
# setup_mock_client drive with default configuration
|
||||||
|
# and return the mock HTTP 3PAR client
|
||||||
|
mock_client = self.setup_driver()
|
||||||
|
mock_client.getVolume.return_value = {'name': mock.ANY,
|
||||||
|
'snapCPG': HPE3PAR_CPG2}
|
||||||
|
with mock.patch.object(hpecommon.HPE3PARCommon,
|
||||||
|
'_create_client') as mock_create_client:
|
||||||
|
mock_create_client.return_value = mock_client
|
||||||
|
volume = {'id': HPE3PARBaseDriver.VOLUME_ID,
|
||||||
|
'name': HPE3PARBaseDriver.VOLUME_NAME,
|
||||||
|
'display_name': 'Foo Volume',
|
||||||
|
'size': 2,
|
||||||
|
'host': volume_utils.append_host(self.FAKE_HOST,
|
||||||
|
HPE3PAR_CPG2)}
|
||||||
|
common = self.driver._login()
|
||||||
|
snap_cpg = common.get_cpg(volume, allowSnap=True)
|
||||||
|
common = hpecommon.HPE3PARCommon(None)
|
||||||
|
vol_name = common._get_3par_vol_name(volume['id'])
|
||||||
|
self.assertEqual(HPE3PAR_CPG2, snap_cpg)
|
||||||
|
expected = [mock.call.getVolume(vol_name)]
|
||||||
|
|
||||||
|
mock_client.assert_has_calls(
|
||||||
|
self.standard_login +
|
||||||
|
expected)
|
||||||
|
|
||||||
|
def test_get_cpg_with_volume_return_no_cpg(self):
|
||||||
|
# setup_mock_client drive with default configuration
|
||||||
|
# and return the mock HTTP 3PAR client
|
||||||
|
mock_client = self.setup_driver()
|
||||||
|
mock_client.getVolume.return_value = {'name': mock.ANY}
|
||||||
|
with mock.patch.object(hpecommon.HPE3PARCommon,
|
||||||
|
'_create_client') as mock_create_client:
|
||||||
|
mock_create_client.return_value = mock_client
|
||||||
|
volume = {'id': HPE3PARBaseDriver.VOLUME_ID,
|
||||||
|
'name': HPE3PARBaseDriver.VOLUME_NAME,
|
||||||
|
'display_name': 'Foo Volume',
|
||||||
|
'size': 2,
|
||||||
|
'host': volume_utils.append_host(self.FAKE_HOST,
|
||||||
|
HPE3PAR_CPG2)}
|
||||||
|
common = self.driver._login()
|
||||||
|
cpg_name = common.get_cpg(volume)
|
||||||
|
common = hpecommon.HPE3PARCommon(None)
|
||||||
|
vol_name = common._get_3par_vol_name(volume['id'])
|
||||||
|
self.assertEqual(HPE3PAR_CPG2, cpg_name)
|
||||||
|
expected = [mock.call.getVolume(vol_name)]
|
||||||
|
|
||||||
|
mock_client.assert_has_calls(
|
||||||
|
self.standard_login +
|
||||||
|
expected)
|
||||||
|
|
||||||
def test_create_cloned_volume(self):
|
def test_create_cloned_volume(self):
|
||||||
# setup_mock_client drive with default configuration
|
# setup_mock_client drive with default configuration
|
||||||
# and return the mock HTTP 3PAR client
|
# and return the mock HTTP 3PAR client
|
||||||
|
@ -1762,7 +1839,7 @@ class HPE3PARBaseDriver(object):
|
||||||
'source_volid': HPE3PARBaseDriver.VOLUME_ID}
|
'source_volid': HPE3PARBaseDriver.VOLUME_ID}
|
||||||
src_vref = {'id': HPE3PARBaseDriver.VOLUME_ID,
|
src_vref = {'id': HPE3PARBaseDriver.VOLUME_ID,
|
||||||
'name': HPE3PARBaseDriver.VOLUME_NAME,
|
'name': HPE3PARBaseDriver.VOLUME_NAME,
|
||||||
'size': 2}
|
'size': 2, 'status': 'available'}
|
||||||
model_update = self.driver.create_cloned_volume(volume, src_vref)
|
model_update = self.driver.create_cloned_volume(volume, src_vref)
|
||||||
self.assertIsNone(model_update)
|
self.assertIsNone(model_update)
|
||||||
|
|
||||||
|
@ -1787,6 +1864,153 @@ class HPE3PARBaseDriver(object):
|
||||||
expected +
|
expected +
|
||||||
self.standard_logout)
|
self.standard_logout)
|
||||||
|
|
||||||
|
def test_backup_iscsi_volume_with_chap_disabled(self):
|
||||||
|
# setup_mock_client drive with default configuration
|
||||||
|
# and return the mock HTTP 3PAR client
|
||||||
|
mock_client = self.setup_driver()
|
||||||
|
mock_client.getVolume.return_value = {'name': mock.ANY}
|
||||||
|
mock_client.copyVolume.return_value = {'taskid': 1}
|
||||||
|
mock_client.getVolumeMetaData.side_effect = hpeexceptions.HTTPNotFound
|
||||||
|
|
||||||
|
with mock.patch.object(hpecommon.HPE3PARCommon,
|
||||||
|
'_create_client') as mock_create_client:
|
||||||
|
mock_create_client.return_value = mock_client
|
||||||
|
|
||||||
|
volume = {'name': HPE3PARBaseDriver.VOLUME_NAME,
|
||||||
|
'id': HPE3PARBaseDriver.CLONE_ID,
|
||||||
|
'display_name': 'Foo Volume',
|
||||||
|
'size': 2,
|
||||||
|
'host': volume_utils.append_host(self.FAKE_HOST,
|
||||||
|
HPE3PAR_CPG2)}
|
||||||
|
src_vref = {'id': HPE3PARBaseDriver.VOLUME_ID,
|
||||||
|
'name': HPE3PARBaseDriver.VOLUME_NAME,
|
||||||
|
'size': 2, 'status': 'backing-up'}
|
||||||
|
model_update = self.driver.create_cloned_volume(volume, src_vref)
|
||||||
|
self.assertIsNone(model_update)
|
||||||
|
|
||||||
|
common = hpecommon.HPE3PARCommon(None)
|
||||||
|
vol_name = common._get_3par_vol_name(src_vref['id'])
|
||||||
|
# snapshot name is random
|
||||||
|
snap_name = mock.ANY
|
||||||
|
optional = mock.ANY
|
||||||
|
|
||||||
|
expected = [
|
||||||
|
mock.call.createSnapshot(snap_name, vol_name, optional),
|
||||||
|
mock.call.getVolume(snap_name),
|
||||||
|
mock.call.copyVolume(
|
||||||
|
snap_name,
|
||||||
|
'osv-0DM4qZEVSKON-AAAAAAAAA',
|
||||||
|
HPE3PAR_CPG2,
|
||||||
|
{'snapCPG': 'OpenStackCPGSnap', 'tpvv': True,
|
||||||
|
'tdvv': False, 'online': True})]
|
||||||
|
|
||||||
|
mock_client.assert_has_calls(
|
||||||
|
self.standard_login +
|
||||||
|
expected +
|
||||||
|
self.standard_logout)
|
||||||
|
|
||||||
|
def test_create_clone_iscsi_volume_with_chap_disabled(self):
|
||||||
|
# setup_mock_client drive with default configuration
|
||||||
|
# and return the mock HTTP 3PAR client
|
||||||
|
config = self.setup_configuration()
|
||||||
|
config.hpe3par_iscsi_chap_enabled = True
|
||||||
|
mock_client = self.setup_driver(config=config)
|
||||||
|
mock_client.getVolume.return_value = {'name': mock.ANY}
|
||||||
|
mock_client.copyVolume.return_value = {'taskid': 1}
|
||||||
|
mock_client.getVolumeMetaData.side_effect = hpeexceptions.HTTPNotFound
|
||||||
|
|
||||||
|
with mock.patch.object(hpecommon.HPE3PARCommon,
|
||||||
|
'_create_client') as mock_create_client:
|
||||||
|
mock_create_client.return_value = mock_client
|
||||||
|
|
||||||
|
volume = {'name': HPE3PARBaseDriver.VOLUME_NAME,
|
||||||
|
'id': HPE3PARBaseDriver.CLONE_ID,
|
||||||
|
'display_name': 'Foo Volume',
|
||||||
|
'size': 2,
|
||||||
|
'host': volume_utils.append_host(self.FAKE_HOST,
|
||||||
|
HPE3PAR_CPG2)}
|
||||||
|
src_vref = {'id': HPE3PARBaseDriver.VOLUME_ID,
|
||||||
|
'name': HPE3PARBaseDriver.VOLUME_NAME,
|
||||||
|
'size': 2, 'status': 'available'}
|
||||||
|
model_update = self.driver.create_cloned_volume(volume, src_vref)
|
||||||
|
self.assertIsNone(model_update)
|
||||||
|
|
||||||
|
common = hpecommon.HPE3PARCommon(None)
|
||||||
|
vol_name = common._get_3par_vol_name(src_vref['id'])
|
||||||
|
# snapshot name is random
|
||||||
|
snap_name = mock.ANY
|
||||||
|
optional = mock.ANY
|
||||||
|
|
||||||
|
expected = [
|
||||||
|
mock.call.getVolumeMetaData(vol_name,
|
||||||
|
'HPQ-cinder-CHAP-name'),
|
||||||
|
mock.call.createSnapshot(snap_name, vol_name, optional),
|
||||||
|
mock.call.getVolume(snap_name),
|
||||||
|
mock.call.copyVolume(
|
||||||
|
snap_name,
|
||||||
|
'osv-0DM4qZEVSKON-AAAAAAAAA',
|
||||||
|
HPE3PAR_CPG2,
|
||||||
|
{'snapCPG': 'OpenStackCPGSnap', 'tpvv': True,
|
||||||
|
'tdvv': False, 'online': True})]
|
||||||
|
|
||||||
|
mock_client.assert_has_calls(
|
||||||
|
self.standard_login +
|
||||||
|
expected +
|
||||||
|
self.standard_logout)
|
||||||
|
|
||||||
|
def test_backup_iscsi_volume_with_chap_enabled(self):
|
||||||
|
# setup_mock_client drive with default configuration
|
||||||
|
# and return the mock HTTP 3PAR client
|
||||||
|
config = self.setup_configuration()
|
||||||
|
config.hpe3par_iscsi_chap_enabled = True
|
||||||
|
mock_client = self.setup_driver(config=config)
|
||||||
|
mock_client.getVolume.return_value = {'name': mock.ANY}
|
||||||
|
task_id = 1
|
||||||
|
mock_client.copyVolume.return_value = {'taskid': task_id}
|
||||||
|
mock_client.getVolumeMetaData.return_value = {
|
||||||
|
'value': 'random-key'}
|
||||||
|
mock_client.getTask.return_value = {'status': 1}
|
||||||
|
with mock.patch.object(hpecommon.HPE3PARCommon,
|
||||||
|
'_create_client') as mock_create_client:
|
||||||
|
mock_create_client.return_value = mock_client
|
||||||
|
|
||||||
|
volume = {'name': HPE3PARBaseDriver.VOLUME_NAME,
|
||||||
|
'id': HPE3PARBaseDriver.CLONE_ID,
|
||||||
|
'display_name': 'Foo Volume',
|
||||||
|
'size': 5,
|
||||||
|
'host': volume_utils.append_host(self.FAKE_HOST,
|
||||||
|
HPE3PAR_CPG2),
|
||||||
|
'source_volid': HPE3PARBaseDriver.VOLUME_ID}
|
||||||
|
src_vref = {'id': HPE3PARBaseDriver.VOLUME_ID,
|
||||||
|
'name': HPE3PARBaseDriver.VOLUME_NAME,
|
||||||
|
'size': 5, 'status': 'backing-up'}
|
||||||
|
model_update = self.driver.create_cloned_volume(volume, src_vref)
|
||||||
|
self.assertIsNone(model_update)
|
||||||
|
|
||||||
|
common = hpecommon.HPE3PARCommon(None)
|
||||||
|
vol_name = common._get_3par_vol_name(volume['id'])
|
||||||
|
src_vol_name = common._get_3par_vol_name(src_vref['id'])
|
||||||
|
optional = {'priority': 1}
|
||||||
|
comment = mock.ANY
|
||||||
|
|
||||||
|
expected = [
|
||||||
|
mock.call.getVolumeMetaData(src_vol_name,
|
||||||
|
'HPQ-cinder-CHAP-name'),
|
||||||
|
mock.call.createVolume(vol_name, 'fakepool',
|
||||||
|
5120, comment),
|
||||||
|
mock.call.copyVolume(
|
||||||
|
src_vol_name,
|
||||||
|
vol_name,
|
||||||
|
None,
|
||||||
|
optional=optional),
|
||||||
|
mock.call.getTask(task_id),
|
||||||
|
]
|
||||||
|
|
||||||
|
mock_client.assert_has_calls(
|
||||||
|
self.standard_login +
|
||||||
|
expected +
|
||||||
|
self.standard_logout)
|
||||||
|
|
||||||
def test_create_cloned_volume_offline_copy(self):
|
def test_create_cloned_volume_offline_copy(self):
|
||||||
# setup_mock_client drive with default configuration
|
# setup_mock_client drive with default configuration
|
||||||
# and return the mock HTTP 3PAR client
|
# and return the mock HTTP 3PAR client
|
||||||
|
@ -1808,7 +2032,7 @@ class HPE3PARBaseDriver(object):
|
||||||
'source_volid': HPE3PARBaseDriver.VOLUME_ID}
|
'source_volid': HPE3PARBaseDriver.VOLUME_ID}
|
||||||
src_vref = {'id': HPE3PARBaseDriver.VOLUME_ID,
|
src_vref = {'id': HPE3PARBaseDriver.VOLUME_ID,
|
||||||
'name': HPE3PARBaseDriver.VOLUME_NAME,
|
'name': HPE3PARBaseDriver.VOLUME_NAME,
|
||||||
'size': 2}
|
'size': 2, 'status': 'available'}
|
||||||
model_update = self.driver.create_cloned_volume(volume, src_vref)
|
model_update = self.driver.create_cloned_volume(volume, src_vref)
|
||||||
self.assertIsNone(model_update)
|
self.assertIsNone(model_update)
|
||||||
|
|
||||||
|
@ -1846,7 +2070,7 @@ class HPE3PARBaseDriver(object):
|
||||||
|
|
||||||
src_vref = {'id': HPE3PARBaseDriver.CLONE_ID,
|
src_vref = {'id': HPE3PARBaseDriver.CLONE_ID,
|
||||||
'name': HPE3PARBaseDriver.VOLUME_NAME,
|
'name': HPE3PARBaseDriver.VOLUME_NAME,
|
||||||
'size': 2}
|
'size': 2, 'status': 'available'}
|
||||||
volume = self.volume_qos.copy()
|
volume = self.volume_qos.copy()
|
||||||
host = "TEST_HOST"
|
host = "TEST_HOST"
|
||||||
pool = "TEST_POOL"
|
pool = "TEST_POOL"
|
||||||
|
|
|
@ -244,10 +244,13 @@ class HPE3PARCommon(object):
|
||||||
3.0.24 - Fix terminate connection on failover
|
3.0.24 - Fix terminate connection on failover
|
||||||
3.0.25 - Fix delete volume when online clone is active. bug #1349639
|
3.0.25 - Fix delete volume when online clone is active. bug #1349639
|
||||||
3.0.26 - Fix concurrent snapshot delete conflict. bug #1600104
|
3.0.26 - Fix concurrent snapshot delete conflict. bug #1600104
|
||||||
|
3.0.27 - Fix snapCPG error during backup of attached volume.
|
||||||
|
Bug #1646396 and also ,Fix backup of attached ISCSI
|
||||||
|
and CHAP enabled volume.bug #1644238.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
VERSION = "3.0.26"
|
VERSION = "3.0.27"
|
||||||
|
|
||||||
stats = {}
|
stats = {}
|
||||||
|
|
||||||
|
@ -1626,11 +1629,18 @@ class HPE3PARCommon(object):
|
||||||
def get_cpg(self, volume, allowSnap=False):
|
def get_cpg(self, volume, allowSnap=False):
|
||||||
volume_name = self._get_3par_vol_name(volume['id'])
|
volume_name = self._get_3par_vol_name(volume['id'])
|
||||||
vol = self.client.getVolume(volume_name)
|
vol = self.client.getVolume(volume_name)
|
||||||
|
# Search for 'userCPG' in the get volume REST API,
|
||||||
|
# if found return userCPG , else search for snapCPG attribute
|
||||||
|
# when allowSnap=True. For the cases where 3PAR REST call for
|
||||||
|
# get volume doesn't have either userCPG or snapCPG ,
|
||||||
|
# take the default value of cpg from 'host' attribute from volume param
|
||||||
|
LOG.debug("get volume response is: %s", vol)
|
||||||
if 'userCPG' in vol:
|
if 'userCPG' in vol:
|
||||||
return vol['userCPG']
|
return vol['userCPG']
|
||||||
elif allowSnap:
|
elif allowSnap and 'snapCPG' in vol:
|
||||||
return vol['snapCPG']
|
return vol['snapCPG']
|
||||||
return None
|
else:
|
||||||
|
return volume_utils.extract_host(volume['host'], 'pool')
|
||||||
|
|
||||||
def _get_3par_vol_comment(self, volume_name):
|
def _get_3par_vol_comment(self, volume_name):
|
||||||
vol = self.client.getVolume(volume_name)
|
vol = self.client.getVolume(volume_name)
|
||||||
|
@ -1983,13 +1993,31 @@ class HPE3PARCommon(object):
|
||||||
try:
|
try:
|
||||||
vol_name = self._get_3par_vol_name(volume['id'])
|
vol_name = self._get_3par_vol_name(volume['id'])
|
||||||
src_vol_name = self._get_3par_vol_name(src_vref['id'])
|
src_vol_name = self._get_3par_vol_name(src_vref['id'])
|
||||||
|
back_up_process = False
|
||||||
|
vol_chap_enabled = False
|
||||||
|
|
||||||
# if the sizes of the 2 volumes are the same
|
# Check whether a volume is ISCSI and CHAP enabled on it.
|
||||||
|
if self._client_conf['hpe3par_iscsi_chap_enabled']:
|
||||||
|
try:
|
||||||
|
vol_chap_enabled = self.client.getVolumeMetaData(
|
||||||
|
src_vol_name, 'HPQ-cinder-CHAP-name')['value']
|
||||||
|
except hpeexceptions.HTTPNotFound:
|
||||||
|
LOG.debug("CHAP is not enabled on volume %(vol)s ",
|
||||||
|
{'vol': src_vref['id']})
|
||||||
|
vol_chap_enabled = False
|
||||||
|
|
||||||
|
# Check whether a process is a backup
|
||||||
|
if str(src_vref['status']) == 'backing-up':
|
||||||
|
back_up_process = True
|
||||||
|
|
||||||
|
# if the sizes of the 2 volumes are the same and except backup
|
||||||
|
# process for ISCSI volume with chap enabled on it.
|
||||||
# we can do an online copy, which is a background process
|
# we can do an online copy, which is a background process
|
||||||
# on the 3PAR that makes the volume instantly available.
|
# on the 3PAR that makes the volume instantly available.
|
||||||
# We can't resize a volume, while it's being copied.
|
# We can't resize a volume, while it's being copied.
|
||||||
if volume['size'] == src_vref['size']:
|
if volume['size'] == src_vref['size'] and not (
|
||||||
LOG.debug("Creating a clone of same size, using online copy.")
|
back_up_process and vol_chap_enabled):
|
||||||
|
LOG.debug("Creating a clone of volume, using online copy.")
|
||||||
# create a temporary snapshot
|
# create a temporary snapshot
|
||||||
snapshot = self._create_temp_snapshot(src_vref)
|
snapshot = self._create_temp_snapshot(src_vref)
|
||||||
|
|
||||||
|
@ -2016,8 +2044,7 @@ class HPE3PARCommon(object):
|
||||||
# The size of the new volume is different, so we have to
|
# The size of the new volume is different, so we have to
|
||||||
# copy the volume and wait. Do the resize after the copy
|
# copy the volume and wait. Do the resize after the copy
|
||||||
# is complete.
|
# is complete.
|
||||||
LOG.debug("Clone a volume with a different target size. "
|
LOG.debug("Creating a clone of volume, using non-online copy.")
|
||||||
"Using non-online copy.")
|
|
||||||
|
|
||||||
# we first have to create the destination volume
|
# we first have to create the destination volume
|
||||||
model_update = self.create_volume(volume)
|
model_update = self.create_volume(volume)
|
||||||
|
|
Loading…
Reference in New Issue