Merge "3PAR: Fix live migration" into stable/ussuri

This commit is contained in:
Zuul 2020-06-24 15:23:47 +00:00 committed by Gerrit Code Review
commit 6f1b3b5d01
4 changed files with 482 additions and 282 deletions

View File

@ -116,6 +116,7 @@ class HPE3PARBaseDriver(test.TestCase):
SNAPSHOT_ID = '2f823bdc-e36e-4dc8-bd15-de1c7a28ff31' SNAPSHOT_ID = '2f823bdc-e36e-4dc8-bd15-de1c7a28ff31'
SNAPSHOT_NAME = 'snapshot-2f823bdc-e36e-4dc8-bd15-de1c7a28ff31' SNAPSHOT_NAME = 'snapshot-2f823bdc-e36e-4dc8-bd15-de1c7a28ff31'
VOLUME_3PAR_NAME = 'osv-0DM4qZEVSKON-DXN-NwVpw' VOLUME_3PAR_NAME = 'osv-0DM4qZEVSKON-DXN-NwVpw'
VOLUME_NAME_ID_3PAR_NAME = 'osv-L4I73ONuTci9Fd4ceij-MQ'
SNAPSHOT_3PAR_NAME = 'oss-L4I73ONuTci9Fd4ceij-MQ' SNAPSHOT_3PAR_NAME = 'oss-L4I73ONuTci9Fd4ceij-MQ'
RCG_3PAR_NAME = 'rcg-0DM4qZEVSKON-DXN-N' RCG_3PAR_NAME = 'rcg-0DM4qZEVSKON-DXN-N'
RCG_3PAR_GROUP_NAME = 'rcg-YET.38iJR1KQDyA50k' RCG_3PAR_GROUP_NAME = 'rcg-YET.38iJR1KQDyA50k'
@ -181,6 +182,15 @@ class HPE3PARBaseDriver(test.TestCase):
volume_type_id=None, volume_type_id=None,
multiattach=False) multiattach=False)
volume_name_id = fake_volume.fake_volume_obj(
context.get_admin_context(),
id=VOLUME_ID,
_name_id='2f823bdc-e36e-4dc8-bd15-de1c7a28ff31',
size=2,
host=FAKE_CINDER_HOST,
volume_type=None,
volume_type_id=None)
volume_src_cg = {'name': SRC_CG_VOLUME_NAME, volume_src_cg = {'name': SRC_CG_VOLUME_NAME,
'id': SRC_CG_VOLUME_ID, 'id': SRC_CG_VOLUME_ID,
'display_name': 'Foo Volume', 'display_name': 'Foo Volume',
@ -282,6 +292,14 @@ class HPE3PARBaseDriver(test.TestCase):
'volume_type': None, 'volume_type': None,
'volume_type_id': 'hos'} 'volume_type_id': 'hos'}
snapshot_volume = {'name': VOLUME_NAME,
'id': VOLUME_ID_SNAP,
'display_name': 'Foo Volume',
'size': 2,
'host': FAKE_CINDER_HOST,
'volume_type': None,
'volume_type_id': None}
snapshot = {'name': SNAPSHOT_NAME, snapshot = {'name': SNAPSHOT_NAME,
'id': SNAPSHOT_ID, 'id': SNAPSHOT_ID,
'user_id': USER_ID, 'user_id': USER_ID,
@ -293,7 +311,15 @@ class HPE3PARBaseDriver(test.TestCase):
'volume_size': 2, 'volume_size': 2,
'display_name': 'fakesnap', 'display_name': 'fakesnap',
'display_description': FAKE_DESC, 'display_description': FAKE_DESC,
'volume': volume} 'volume': snapshot_volume}
snapshot_name_id = {'id': SNAPSHOT_ID,
'volume_id': volume_name_id.id,
'volume_size': 2,
'volume': volume_name_id,
'display_name': 'display-name',
'display_description': 'description',
'volume_name': 'name'}
wwn = ["123456789012345", "123456789054321"] wwn = ["123456789012345", "123456789054321"]
@ -673,21 +699,19 @@ class HPE3PARBaseDriver(test.TestCase):
standard_logout = [ standard_logout = [
mock.call.logout()] mock.call.logout()]
class fake_volume_object(object): @staticmethod
def __init__(self, vol_id='d03338a9-9115-48a3-8dfc-35cdfcdc15a7'): def fake_volume_object(vol_id='d03338a9-9115-48a3-8dfc-35cdfcdc15a7',
self.id = vol_id **kwargs):
self.name = 'volume-d03338a9-9115-48a3-8dfc-35cdfcdc15a7' values = dict(id=vol_id,
self.display_name = 'Foo Volume' name='volume-%s' % vol_id,
self.size = 2 display_name='Foo Volume',
self.host = 'fakehost@foo#OpenStackCPG' size=2,
self.volume_type = None host='fakehost@foo#OpenStackCPG',
self.volume_type_id = None volume_type=None,
self.volume = {'volume_type_id': self.volume_type_id, volume_type_id=None)
'host': self.host, 'id': self.id, 'volume_type': values.update(kwargs)
self.volume_type} return fake_volume.fake_volume_obj(context.get_admin_context(),
**values)
def get(self, parm):
return self.volume[parm]
class fake_group_object(object): class fake_group_object(object):
def __init__(self, grp_id='6044fedf-c889-4752-900f-2039d247a5df'): def __init__(self, grp_id='6044fedf-c889-4752-900f-2039d247a5df'):
@ -2346,16 +2370,18 @@ class TestHPE3PARDriverBase(HPE3PARBaseDriver):
mock.call.getTask(1)] mock.call.getTask(1)]
mock_client.assert_has_calls(expected) mock_client.assert_has_calls(expected)
def test_delete_volume(self): @ddt.data('volume', 'volume_name_id')
def test_delete_volume(self, volume_attr):
# 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
mock_client = self.setup_driver() mock_client = self.setup_driver()
with mock.patch.object(hpecommon.HPE3PARCommon, with mock.patch.object(hpecommon.HPE3PARCommon,
'_create_client') as mock_create_client: '_create_client') as mock_create_client:
mock_create_client.return_value = mock_client mock_create_client.return_value = mock_client
self.driver.delete_volume(self.volume) self.driver.delete_volume(getattr(self, volume_attr))
expected = [mock.call.deleteVolume(self.VOLUME_3PAR_NAME)] name_3par = getattr(self, volume_attr.upper() + '_3PAR_NAME')
expected = [mock.call.deleteVolume(name_3par)]
mock_client.assert_has_calls( mock_client.assert_has_calls(
self.standard_login + self.standard_login +
@ -2539,7 +2565,10 @@ class TestHPE3PARDriverBase(HPE3PARBaseDriver):
self.standard_login + self.standard_login +
expected) expected)
def test_create_cloned_volume(self): @ddt.data('volume', 'volume_name_id')
def test_create_cloned_volume(self, volume_attr):
src_vref = getattr(self, volume_attr)
vol_name = getattr(self, volume_attr.upper() + '_3PAR_NAME')
# 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
mock_client = self.setup_driver() mock_client = self.setup_driver()
@ -2559,14 +2588,9 @@ class TestHPE3PARDriverBase(HPE3PARBaseDriver):
'size': 2, 'size': 2,
'host': volume_utils.append_host(self.FAKE_HOST, 'host': volume_utils.append_host(self.FAKE_HOST,
HPE3PAR_CPG2), HPE3PAR_CPG2),
'source_volid': HPE3PARBaseDriver.VOLUME_ID} 'source_volid': src_vref.id}
src_vref = {'id': HPE3PARBaseDriver.VOLUME_ID,
'name': HPE3PARBaseDriver.VOLUME_NAME,
'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)
common = hpecommon.HPE3PARCommon(None)
vol_name = common._get_3par_vol_name(src_vref['id'])
# snapshot name is random # snapshot name is random
snap_name = mock.ANY snap_name = mock.ANY
optional = mock.ANY optional = mock.ANY
@ -3176,61 +3200,109 @@ class TestHPE3PARDriverBase(HPE3PARBaseDriver):
temp_rename_side_effect, temp_rename_side_effect,
rename_side_effect): rename_side_effect):
mock_client = self.setup_driver() mock_client = self.setup_driver()
fake_old_volume = {'id': self.VOLUME_ID} mock_client.modifyVolume.side_effect = [temp_rename_side_effect,
rename_side_effect,
None]
fake_old_volume = self.fake_volume_object(self.VOLUME_ID)
provider_location = 'foo' provider_location = 'foo'
fake_new_volume = {'id': self.CLONE_ID, fake_new_volume = self.fake_volume_object(
'_name_id': self.CLONE_ID, self.CLONE_ID, _name_id=self.CLONE_ID,
'provider_location': provider_location} provider_location=provider_location)
original_volume_status = 'available' original_volume_status = 'available'
with mock.patch.object(hpecommon.HPE3PARCommon,
'_create_client') as mock_create_client:
mock_create_client.return_value = mock_client
mock_client.modifyVolume.side_effect = [
temp_rename_side_effect,
rename_side_effect,
None
]
actual_update = self.driver.update_migrated_volume(
context.get_admin_context(), fake_old_volume,
fake_new_volume, original_volume_status)
if rename_side_effect is None: _3common = hpecommon.HPE3PARCommon
expected_update = {'_name_id': None, self.mock_object(_3common, '_create_client', return_value=mock_client)
'provider_location': None} mock_get_comment = self.mock_object(_3common, '_get_updated_comment',
else: side_effect=[mock.sentinel.comm1,
expected_update = {'_name_id': fake_new_volume['_name_id'], mock.sentinel.comm2])
'provider_location': provider_location}
self.assertEqual(expected_update, actual_update)
# Initial temp rename always takes place actual_update = self.driver.update_migrated_volume(
expected = [ context.get_admin_context(), fake_old_volume,
fake_new_volume, original_volume_status)
if rename_side_effect is None:
expected_update = {'_name_id': None,
'provider_location': None}
else:
expected_update = {'_name_id': fake_new_volume['_name_id'],
'provider_location': provider_location}
self.assertEqual(expected_update, actual_update)
# Initial temp rename always takes place
expected = [
mock.call.modifyVolume(
'osv-0DM4qZEVSKON-DXN-NwVpw',
{'newName': u'tsv-0DM4qZEVSKON-DXN-NwVpw'})
]
comment_expected = []
# Primary rename will occur unless the temp rename fails
if temp_rename_side_effect != hpeexceptions.HTTPConflict:
expected += [
mock.call.modifyVolume( mock.call.modifyVolume(
'osv-0DM4qZEVSKON-DXN-NwVpw', 'osv-0DM4qZEVSKON-AAAAAAAAA',
{'newName': u'tsv-0DM4qZEVSKON-DXN-NwVpw'}), {'newName': u'osv-0DM4qZEVSKON-DXN-NwVpw',
'comment': mock.sentinel.comm1})
] ]
comment_expected.append(mock.call('osv-0DM4qZEVSKON-AAAAAAAAA',
volume_id=self.VOLUME_ID,
_name_id=None))
# Primary rename will occur unless the temp rename fails # Final temp rename will occur if both of the previous renames
if temp_rename_side_effect != hpeexceptions.HTTPConflict: # succeed.
expected += [ if (temp_rename_side_effect is None and
mock.call.modifyVolume( rename_side_effect is None):
'osv-0DM4qZEVSKON-AAAAAAAAA', expected += [
{'newName': u'osv-0DM4qZEVSKON-DXN-NwVpw'}), mock.call.modifyVolume(
] 'tsv-0DM4qZEVSKON-DXN-NwVpw',
{'newName': u'osv-0DM4qZEVSKON-AAAAAAAAA',
'comment': mock.sentinel.comm2})
]
comment_expected.append(mock.call('osv-0DM4qZEVSKON-DXN-NwVpw',
volume_id=self.CLONE_ID,
_name_id=None))
# Final temp rename will occur if both of the previous renames mock_client.assert_has_calls(
# succeed. self.standard_login +
if (temp_rename_side_effect is None and expected +
rename_side_effect is None): self.standard_logout)
expected += [
mock.call.modifyVolume(
'tsv-0DM4qZEVSKON-DXN-NwVpw',
{'newName': u'osv-0DM4qZEVSKON-AAAAAAAAA'})
]
mock_client.assert_has_calls( mock_get_comment.assert_has_calls(comment_expected)
self.standard_login +
expected + def test_update_migrated_volume_with_name_id(self):
self.standard_logout) """We don't use temp rename mechanism when source uses _name_id."""
mock_client = self.setup_driver()
fake_old_volume = self.fake_volume_object(
self.VOLUME_ID, _name_id=self.SRC_CG_VOLUME_ID)
fake_new_volume = self.fake_volume_object(self.CLONE_ID)
_3common = hpecommon.HPE3PARCommon
self.mock_object(_3common, '_create_client', return_value=mock_client)
mock_get_comment = self.mock_object(_3common, '_get_updated_comment',
side_effect=[mock.sentinel.comm])
actual_update = self.driver.update_migrated_volume(
context.get_admin_context(), fake_old_volume,
fake_new_volume, 'available')
expected_update = {'_name_id': None,
'provider_location': None}
self.assertEqual(expected_update, actual_update)
# # After successfully swapping names we have updated the comments
mock_get_comment.assert_called_once_with('osv-0DM4qZEVSKON-AAAAAAAAA',
volume_id=self.VOLUME_ID,
_name_id=None),
expected = [
mock.call.modifyVolume('osv-0DM4qZEVSKON-AAAAAAAAA',
{'newName': u'osv-0DM4qZEVSKON-DXN-NwVpw',
'comment': mock.sentinel.comm}),
]
mock_client.assert_has_calls(
self.standard_login +
expected +
self.standard_logout)
@ddt.data({'temp_rename_side_effect': hpeexceptions.HTTPConflict, @ddt.data({'temp_rename_side_effect': hpeexceptions.HTTPConflict,
'rename_side_effect': None}, 'rename_side_effect': None},
@ -3249,83 +3321,111 @@ class TestHPE3PARDriverBase(HPE3PARBaseDriver):
'_name_id': self.CLONE_ID, '_name_id': self.CLONE_ID,
'provider_location': provider_location} 'provider_location': provider_location}
original_volume_status = 'available' original_volume_status = 'available'
with mock.patch.object(hpecommon.HPE3PARCommon, _3common = hpecommon.HPE3PARCommon
'_create_client') as mock_create_client: self.mock_object(_3common, '_create_client', return_value=mock_client)
mock_create_client.return_value = mock_client mock_get_comment = self.mock_object(_3common, '_get_updated_comment',
mock_client.modifyVolume.side_effect = [ side_effect=[mock.sentinel.comm])
temp_rename_side_effect, mock_update_comment = self.mock_object(_3common, '_update_comment')
rename_side_effect, mock_client.modifyVolume.side_effect = [
None temp_rename_side_effect,
] rename_side_effect,
self.assertRaises(hpeexceptions.HTTPConflict, None
self.driver.update_migrated_volume, ]
context.get_admin_context(), actual_update = self.driver.update_migrated_volume(
fake_old_volume, context.get_admin_context(), fake_old_volume, fake_new_volume,
fake_new_volume, original_volume_status)
original_volume_status) expected_update = {'_name_id': self.CLONE_ID,
'provider_location': provider_location}
self.assertEqual(expected_update, actual_update)
# Initial temp rename always takes place # Initial temp rename always takes place
expected = [ expected = [
mock.call.modifyVolume(
'osv-0DM4qZEVSKON-DXN-NwVpw',
{'newName': u'tsv-0DM4qZEVSKON-DXN-NwVpw'}),
]
# Primary rename will occur unless the temp rename fails
if temp_rename_side_effect != hpeexceptions.HTTPConflict:
expected += [
mock.call.modifyVolume( mock.call.modifyVolume(
'osv-0DM4qZEVSKON-DXN-NwVpw', 'osv-0DM4qZEVSKON-AAAAAAAAA',
{'newName': u'tsv-0DM4qZEVSKON-DXN-NwVpw'}), {'newName': u'osv-0DM4qZEVSKON-DXN-NwVpw',
'comment': mock.sentinel.comm}),
] ]
mock_get_comment.assert_called_once_with(
'osv-0DM4qZEVSKON-AAAAAAAAA',
volume_id=self.VOLUME_ID,
_name_id=None)
else:
mock_get_comment.assert_not_called()
# Primary rename will occur unless the temp rename fails mock_update_comment.assert_called_once_with(
if temp_rename_side_effect != hpeexceptions.HTTPConflict: 'osv-0DM4qZEVSKON-AAAAAAAAA',
expected += [ volume_id=self.VOLUME_ID,
mock.call.modifyVolume( _name_id=self.CLONE_ID)
'osv-0DM4qZEVSKON-AAAAAAAAA',
{'newName': u'osv-0DM4qZEVSKON-DXN-NwVpw'}),
]
mock_client.assert_has_calls( mock_client.assert_has_calls(
self.standard_login + self.standard_login +
expected + expected +
self.standard_logout) self.standard_logout)
def test_update_migrated_volume_attached(self): def test_update_migrated_volume_attached(self):
mock_client = self.setup_driver() mock_client = self.setup_driver()
mock_client.getVolume.return_value = {
'comment': '{"volume_id": %s, "_name_id": ""}' % self.CLONE_ID}
# Simulate old volume had already been live migrated
fake_old_volume = {'id': self.VOLUME_ID} fake_old_volume = {'id': self.VOLUME_ID}
provider_location = 'foo' provider_location = 'foo'
fake_new_volume = {'id': self.CLONE_ID, fake_new_volume = {'id': self.CLONE_ID,
'_name_id': self.CLONE_ID, '_name_id': '',
'provider_location': provider_location} 'provider_location': provider_location}
original_volume_status = 'in-use' original_volume_status = 'in-use'
with mock.patch.object(hpecommon.HPE3PARCommon, _3common = hpecommon.HPE3PARCommon
'_create_client') as mock_create_client: self.mock_object(_3common, '_create_client', return_value=mock_client)
mock_create_client.return_value = mock_client mock_update = self.mock_object(_3common, '_update_comment')
actual_update = self.driver.update_migrated_volume( actual_update = self.driver.update_migrated_volume(
context.get_admin_context(), fake_old_volume, context.get_admin_context(), fake_old_volume,
fake_new_volume, original_volume_status) fake_new_volume, original_volume_status)
expected_update = {'_name_id': fake_new_volume['_name_id'], expected_update = {'_name_id': fake_new_volume['id'],
'provider_location': provider_location} 'provider_location': provider_location}
self.assertEqual(expected_update, actual_update) self.assertEqual(expected_update, actual_update)
def test_create_snapshot(self): vol_name = _3common._get_3par_vol_name(fake_new_volume)
mock_update.assert_called_once_with(vol_name,
volume_id=fake_old_volume['id'],
_name_id=fake_new_volume['id'])
@ddt.data(('snapshot', 'osv-dh-F5VGRTseuujPjbeRBVg'),
('snapshot_name_id', HPE3PARBaseDriver.VOLUME_NAME_ID_3PAR_NAME))
@ddt.unpack
def test_create_snapshot(self, snapshot_attr, vol_name):
# 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
mock_client = self.setup_driver() mock_client = self.setup_driver()
snapshot = getattr(self, snapshot_attr)
with mock.patch.object(hpecommon.HPE3PARCommon, with mock.patch.object(hpecommon.HPE3PARCommon,
'_create_client') as mock_create_client: '_create_client') as mock_create_client:
mock_create_client.return_value = mock_client mock_create_client.return_value = mock_client
self.driver.create_snapshot(self.snapshot) self.driver.create_snapshot(snapshot)
comment = Comment({ comment = {
"volume_id": "761fc5e5-5191-4ec7-aeba-33e36de44156", "volume_id": snapshot['volume_id'],
"display_name": "fakesnap", "display_name": snapshot['display_name'],
"description": "test description name", "description": snapshot['display_description'],
"volume_name": "volume_name": snapshot['volume_name'],
"volume-d03338a9-9115-48a3-8dfc-35cdfcdc15a7", }
}) if snapshot['volume'].get('_name_id'):
comment["_name_id"] = snapshot['volume']['_name_id']
expected = [ expected = [
mock.call.createSnapshot( mock.call.createSnapshot(
'oss-L4I73ONuTci9Fd4ceij-MQ', 'oss-L4I73ONuTci9Fd4ceij-MQ',
'osv-dh-F5VGRTseuujPjbeRBVg', vol_name,
{ {
'comment': comment, 'comment': Comment(comment),
'readOnly': True})] 'readOnly': True})]
mock_client.assert_has_calls( mock_client.assert_has_calls(
@ -3333,17 +3433,13 @@ class TestHPE3PARDriverBase(HPE3PARBaseDriver):
expected + expected +
self.standard_logout) self.standard_logout)
def test_revert_to_snapshot(self): @ddt.data(('snapshot', 'osv-dh-F5VGRTseuujPjbeRBVg'),
('snapshot_name_id', HPE3PARBaseDriver.VOLUME_NAME_ID_3PAR_NAME))
@ddt.unpack
def test_revert_to_snapshot(self, snapshot_attr, vol_name):
snapshot = getattr(self, snapshot_attr)
# 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
volume = {'name': self.VOLUME_NAME,
'id': self.VOLUME_ID_SNAP,
'display_name': 'Foo Volume',
'size': 2,
'host': self.FAKE_CINDER_HOST,
'volume_type': None,
'volume_type_id': None}
mock_client = self.setup_driver() mock_client = self.setup_driver()
mock_client.isOnlinePhysicalCopy.return_value = False mock_client.isOnlinePhysicalCopy.return_value = False
mock_client.promoteVirtualCopy.return_value = {'taskid': 1} mock_client.promoteVirtualCopy.return_value = {'taskid': 1}
@ -3352,10 +3448,11 @@ class TestHPE3PARDriverBase(HPE3PARBaseDriver):
with mock.patch.object(hpecommon.HPE3PARCommon, with mock.patch.object(hpecommon.HPE3PARCommon,
'_create_client') as mock_create_client: '_create_client') as mock_create_client:
mock_create_client.return_value = mock_client mock_create_client.return_value = mock_client
self.driver.revert_to_snapshot(self.ctxt, volume, self.snapshot) self.driver.revert_to_snapshot(self.ctxt, snapshot['volume'],
snapshot)
expected = [ expected = [
mock.call.isOnlinePhysicalCopy('osv-dh-F5VGRTseuujPjbeRBVg'), mock.call.isOnlinePhysicalCopy(vol_name),
mock.call.promoteVirtualCopy('oss-L4I73ONuTci9Fd4ceij-MQ', mock.call.promoteVirtualCopy('oss-L4I73ONuTci9Fd4ceij-MQ',
optional={}), optional={}),
mock.call.getTask(1) mock.call.getTask(1)
@ -6206,7 +6303,6 @@ class TestHPE3PARDriverBase(HPE3PARBaseDriver):
vol_ss_enable.return_value = True vol_ss_enable.return_value = True
mock_client = self.setup_driver() mock_client = self.setup_driver()
mock_client.getStorageSystemInfo.return_value = {'id': self.CLIENT_ID} mock_client.getStorageSystemInfo.return_value = {'id': self.CLIENT_ID}
volume = self.fake_volume_object()
type_info = {'cpg': 'OpenStackCPG', type_info = {'cpg': 'OpenStackCPG',
'tpvv': True, 'tpvv': True,
'tdvv': False, 'tdvv': False,
@ -6215,7 +6311,7 @@ class TestHPE3PARDriverBase(HPE3PARBaseDriver):
typ_info.return_value = type_info typ_info.return_value = type_info
source_volume = self.volume_src_cg source_volume = self.volume_src_cg
volume.volume['source_volid'] = source_volume['id'] volume = self.fake_volume_object(source_volid=source_volume['id'])
common = hpecommon.HPE3PARCommon(None) common = hpecommon.HPE3PARCommon(None)
vol_name = common._get_3par_vol_name(volume.id) vol_name = common._get_3par_vol_name(volume.id)
mock_client.getVolume.return_value = {'copyOf': vol_name} mock_client.getVolume.return_value = {'copyOf': vol_name}
@ -6312,7 +6408,6 @@ class TestHPE3PARDriverBase(HPE3PARBaseDriver):
task_id = 1 task_id = 1
mock_client.copyVolume.return_value = {'taskid': task_id} mock_client.copyVolume.return_value = {'taskid': task_id}
mock_client.getStorageSystemInfo.return_value = {'id': self.CLIENT_ID} mock_client.getStorageSystemInfo.return_value = {'id': self.CLIENT_ID}
volume = self.fake_volume_object()
type_info = {'cpg': 'OpenStackCPG', type_info = {'cpg': 'OpenStackCPG',
'tpvv': True, 'tpvv': True,
'tdvv': False, 'tdvv': False,
@ -6321,7 +6416,7 @@ class TestHPE3PARDriverBase(HPE3PARBaseDriver):
typ_info.return_value = type_info typ_info.return_value = type_info
source_volume = self.volume_src_cg source_volume = self.volume_src_cg
volume.volume['source_volid'] = source_volume['id'] volume = self.fake_volume_object(source_volid=source_volume['id'])
common = hpecommon.HPE3PARCommon(None) common = hpecommon.HPE3PARCommon(None)
vol_name = common._get_3par_vol_name(volume.id) vol_name = common._get_3par_vol_name(volume.id)
mock_client.getVolume.return_value = {'copyOf': vol_name} mock_client.getVolume.return_value = {'copyOf': vol_name}
@ -7032,6 +7127,7 @@ class TestHPE3PARDriverBase(HPE3PARBaseDriver):
self.assertIn(new_key, properties) self.assertIn(new_key, properties)
@ddt.ddt
class TestHPE3PARFCDriver(HPE3PARBaseDriver): class TestHPE3PARFCDriver(HPE3PARBaseDriver):
properties = { properties = {
@ -7071,7 +7167,10 @@ class TestHPE3PARFCDriver(HPE3PARBaseDriver):
mock_client.reset_mock() mock_client.reset_mock()
return mock_client return mock_client
def test_initialize_connection(self): @ddt.data('volume', 'volume_name_id')
def test_initialize_connection(self, volume_attr):
volume = getattr(self, volume_attr)
vol_name = getattr(self, volume_attr.upper() + '_3PAR_NAME')
# 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
mock_client = self.setup_driver() mock_client = self.setup_driver()
@ -7105,16 +7204,16 @@ class TestHPE3PARFCDriver(HPE3PARBaseDriver):
mock_client.getHostVLUNs.side_effect = [ mock_client.getHostVLUNs.side_effect = [
hpeexceptions.HTTPNotFound('fake'), hpeexceptions.HTTPNotFound('fake'),
[{'active': True, [{'active': True,
'volumeName': self.VOLUME_3PAR_NAME, 'volumeName': vol_name,
'remoteName': self.wwn[1], 'remoteName': self.wwn[1],
'lun': 90, 'type': 0}], 'lun': 90, 'type': 0}],
[{'active': True, [{'active': True,
'volumeName': self.VOLUME_3PAR_NAME, 'volumeName': vol_name,
'remoteName': self.wwn[0], 'remoteName': self.wwn[0],
'lun': 90, 'type': 0}]] 'lun': 90, 'type': 0}]]
location = ("%(volume_name)s,%(lun_id)s,%(host)s,%(nsp)s" % location = ("%(volume_name)s,%(lun_id)s,%(host)s,%(nsp)s" %
{'volume_name': self.VOLUME_3PAR_NAME, {'volume_name': vol_name,
'lun_id': 90, 'lun_id': 90,
'host': self.FAKE_HOST, 'host': self.FAKE_HOST,
'nsp': 'something'}) 'nsp': 'something'})
@ -7134,11 +7233,11 @@ class TestHPE3PARFCDriver(HPE3PARBaseDriver):
'_create_client') as mock_create_client: '_create_client') as mock_create_client:
mock_create_client.return_value = mock_client mock_create_client.return_value = mock_client
result = self.driver.initialize_connection( result = self.driver.initialize_connection(
self.volume, volume,
self.connector_multipath_enabled) self.connector_multipath_enabled)
expected = [ expected = [
mock.call.getVolume(self.VOLUME_3PAR_NAME), mock.call.getVolume(vol_name),
mock.call.getCPG(HPE3PAR_CPG), mock.call.getCPG(HPE3PAR_CPG),
mock.call.getHost(self.FAKE_HOST), mock.call.getHost(self.FAKE_HOST),
mock.call.queryHost(wwns=['123456789012345', mock.call.queryHost(wwns=['123456789012345',
@ -7147,7 +7246,7 @@ class TestHPE3PARFCDriver(HPE3PARBaseDriver):
mock.call.getPorts(), mock.call.getPorts(),
mock.call.getHostVLUNs(self.FAKE_HOST), mock.call.getHostVLUNs(self.FAKE_HOST),
mock.call.createVLUN( mock.call.createVLUN(
self.VOLUME_3PAR_NAME, vol_name,
auto=True, auto=True,
hostname=self.FAKE_HOST, hostname=self.FAKE_HOST,
lun=None), lun=None),
@ -7627,13 +7726,16 @@ class TestHPE3PARFCDriver(HPE3PARBaseDriver):
self.standard_logout) self.standard_logout)
self.assertDictEqual(expected_properties, result) self.assertDictEqual(expected_properties, result)
def test_terminate_connection(self): @ddt.data('volume', 'volume_name_id')
def test_terminate_connection(self, volume_attr):
volume = getattr(self, volume_attr)
vol_name = getattr(self, volume_attr.upper() + '_3PAR_NAME')
# 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
mock_client = self.setup_driver() mock_client = self.setup_driver()
effects = [ effects = [
[{'active': False, 'volumeName': self.VOLUME_3PAR_NAME, [{'active': False, 'volumeName': vol_name,
'lun': None, 'type': 0}], 'lun': None, 'type': 0}],
hpeexceptions.HTTPNotFound, hpeexceptions.HTTPNotFound,
hpeexceptions.HTTPNotFound] hpeexceptions.HTTPNotFound]
@ -7650,7 +7752,7 @@ class TestHPE3PARFCDriver(HPE3PARBaseDriver):
mock.call.queryHost(wwns=['123456789012345', '123456789054321']), mock.call.queryHost(wwns=['123456789012345', '123456789054321']),
mock.call.getHostVLUNs(self.FAKE_HOST), mock.call.getHostVLUNs(self.FAKE_HOST),
mock.call.deleteVLUN( mock.call.deleteVLUN(
self.VOLUME_3PAR_NAME, vol_name,
None, None,
hostname=self.FAKE_HOST), hostname=self.FAKE_HOST),
mock.call.getHostVLUNs(self.FAKE_HOST), mock.call.getHostVLUNs(self.FAKE_HOST),
@ -7661,7 +7763,7 @@ class TestHPE3PARFCDriver(HPE3PARBaseDriver):
with mock.patch.object(hpecommon.HPE3PARCommon, with mock.patch.object(hpecommon.HPE3PARCommon,
'_create_client') as mock_create_client: '_create_client') as mock_create_client:
mock_create_client.return_value = mock_client mock_create_client.return_value = mock_client
conn_info = self.driver.terminate_connection(self.volume, conn_info = self.driver.terminate_connection(volume,
self.connector) self.connector)
mock_client.assert_has_calls( mock_client.assert_has_calls(
self.standard_login + self.standard_login +
@ -7681,7 +7783,7 @@ class TestHPE3PARFCDriver(HPE3PARBaseDriver):
mock_client.deleteHost = mock.Mock( mock_client.deleteHost = mock.Mock(
side_effect=[delete_with_vlun, delete_with_hostset]) side_effect=[delete_with_vlun, delete_with_hostset])
conn_info = self.driver.terminate_connection(self.volume, conn_info = self.driver.terminate_connection(volume,
self.connector) self.connector)
mock_client.assert_has_calls( mock_client.assert_has_calls(
self.standard_login + self.standard_login +
@ -7690,7 +7792,7 @@ class TestHPE3PARFCDriver(HPE3PARBaseDriver):
mock_client.reset_mock() mock_client.reset_mock()
mock_client.getHostVLUNs.side_effect = effects mock_client.getHostVLUNs.side_effect = effects
conn_info = self.driver.terminate_connection(self.volume, conn_info = self.driver.terminate_connection(volume,
self.connector) self.connector)
mock_client.assert_has_calls( mock_client.assert_has_calls(
self.standard_login + self.standard_login +
@ -8755,6 +8857,7 @@ class TestHPE3PARFCDriver(HPE3PARBaseDriver):
self.migrate_volume_attached() self.migrate_volume_attached()
@ddt.ddt
class TestHPE3PARISCSIDriver(HPE3PARBaseDriver): class TestHPE3PARISCSIDriver(HPE3PARBaseDriver):
TARGET_IQN = 'iqn.2000-05.com.3pardata:21810002ac00383d' TARGET_IQN = 'iqn.2000-05.com.3pardata:21810002ac00383d'
@ -8816,7 +8919,10 @@ class TestHPE3PARISCSIDriver(HPE3PARBaseDriver):
driver=hpedriver.HPE3PARISCSIDriver, driver=hpedriver.HPE3PARISCSIDriver,
is_primera=True) is_primera=True)
def test_initialize_connection(self): @ddt.data('volume', 'volume_name_id')
def test_initialize_connection(self, volume_attr):
volume = getattr(self, volume_attr)
vol_name = getattr(self, volume_attr.upper() + '_3PAR_NAME')
# 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
mock_client = self.setup_driver() mock_client = self.setup_driver()
@ -8833,15 +8939,15 @@ class TestHPE3PARISCSIDriver(HPE3PARBaseDriver):
mock_client.getHostVLUNs.side_effect = [ mock_client.getHostVLUNs.side_effect = [
[{'hostname': self.FAKE_HOST, [{'hostname': self.FAKE_HOST,
'volumeName': self.VOLUME_3PAR_NAME, 'volumeName': vol_name,
'lun': self.TARGET_LUN, 'lun': self.TARGET_LUN,
'portPos': {'node': 8, 'slot': 1, 'cardPort': 1}}], 'portPos': {'node': 8, 'slot': 1, 'cardPort': 1}}],
[{'active': True, [{'active': True,
'volumeName': self.VOLUME_3PAR_NAME, 'volumeName': vol_name,
'lun': self.TARGET_LUN, 'type': 0}]] 'lun': self.TARGET_LUN, 'type': 0}]]
location = ("%(volume_name)s,%(lun_id)s,%(host)s,%(nsp)s" % location = ("%(volume_name)s,%(lun_id)s,%(host)s,%(nsp)s" %
{'volume_name': self.VOLUME_3PAR_NAME, {'volume_name': vol_name,
'lun_id': self.TARGET_LUN, 'lun_id': self.TARGET_LUN,
'host': self.FAKE_HOST, 'host': self.FAKE_HOST,
'nsp': 'something'}) 'nsp': 'something'})
@ -8851,11 +8957,11 @@ class TestHPE3PARISCSIDriver(HPE3PARBaseDriver):
'_create_client') as mock_create_client: '_create_client') as mock_create_client:
mock_create_client.return_value = mock_client mock_create_client.return_value = mock_client
result = self.driver.initialize_connection( result = self.driver.initialize_connection(
self.volume, volume,
self.connector) self.connector)
expected = [ expected = [
mock.call.getVolume(self.VOLUME_3PAR_NAME), mock.call.getVolume(vol_name),
mock.call.getCPG(HPE3PAR_CPG), mock.call.getCPG(HPE3PAR_CPG),
mock.call.getHost(self.FAKE_HOST), mock.call.getHost(self.FAKE_HOST),
mock.call.queryHost(iqns=['iqn.1993-08.org.debian:01:222']), mock.call.queryHost(iqns=['iqn.1993-08.org.debian:01:222']),
@ -10849,13 +10955,16 @@ class TestHPE3PARISCSIDriver(HPE3PARBaseDriver):
self.assertEqual(0, mock_client.deleteVLUN.call_count) self.assertEqual(0, mock_client.deleteVLUN.call_count)
self.assertEqual(0, mock_client.deleteHost.call_count) self.assertEqual(0, mock_client.deleteHost.call_count)
def test_terminate_connection(self): @ddt.data('volume', 'volume_name_id')
def test_terminate_connection(self, volume_attr):
volume = getattr(self, volume_attr)
vol_name = getattr(self, volume_attr.upper() + '_3PAR_NAME')
# 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
mock_client = self.setup_driver() mock_client = self.setup_driver()
mock_client.getHostVLUNs.return_value = [ mock_client.getHostVLUNs.return_value = [
{'active': False, {'active': False,
'volumeName': self.VOLUME_3PAR_NAME, 'volumeName': vol_name,
'lun': None, 'type': 0}] 'lun': None, 'type': 0}]
mock_client.queryHost.return_value = { mock_client.queryHost.return_value = {
@ -10868,7 +10977,7 @@ class TestHPE3PARISCSIDriver(HPE3PARBaseDriver):
'_create_client') as mock_create_client: '_create_client') as mock_create_client:
mock_create_client.return_value = mock_client mock_create_client.return_value = mock_client
self.driver.terminate_connection( self.driver.terminate_connection(
self.volume, volume,
self.connector, self.connector,
force=True) force=True)
@ -10876,7 +10985,7 @@ class TestHPE3PARISCSIDriver(HPE3PARBaseDriver):
mock.call.queryHost(iqns=[self.connector['initiator']]), mock.call.queryHost(iqns=[self.connector['initiator']]),
mock.call.getHostVLUNs(self.FAKE_HOST), mock.call.getHostVLUNs(self.FAKE_HOST),
mock.call.deleteVLUN( mock.call.deleteVLUN(
self.VOLUME_3PAR_NAME, vol_name,
None, None,
hostname=self.FAKE_HOST), hostname=self.FAKE_HOST),
mock.call.getHostVLUNs(self.FAKE_HOST), mock.call.getHostVLUNs(self.FAKE_HOST),
@ -10884,10 +10993,8 @@ class TestHPE3PARISCSIDriver(HPE3PARBaseDriver):
'fakehost', 'fakehost',
{'pathOperation': 2, {'pathOperation': 2,
'iSCSINames': ['iqn.1993-08.org.debian:01:222']}), 'iSCSINames': ['iqn.1993-08.org.debian:01:222']}),
mock.call.removeVolumeMetaData( mock.call.removeVolumeMetaData(vol_name, CHAP_USER_KEY),
self.VOLUME_3PAR_NAME, CHAP_USER_KEY), mock.call.removeVolumeMetaData(vol_name, CHAP_PASS_KEY)]
mock.call.removeVolumeMetaData(
self.VOLUME_3PAR_NAME, CHAP_PASS_KEY)]
mock_client.assert_has_calls( mock_client.assert_has_calls(
self.standard_login + self.standard_login +

View File

@ -55,6 +55,7 @@ from cinder import context
from cinder import exception from cinder import exception
from cinder import flow_utils from cinder import flow_utils
from cinder.i18n import _ from cinder.i18n import _
from cinder import objects
from cinder.objects import fields from cinder.objects import fields
from cinder import utils from cinder import utils
from cinder.volume import configuration from cinder.volume import configuration
@ -574,7 +575,7 @@ class HPE3PARCommon(object):
return None return None
def extend_volume(self, volume, new_size): def extend_volume(self, volume, new_size):
volume_name = self._get_3par_vol_name(volume['id']) volume_name = self._get_3par_vol_name(volume)
old_size = volume['size'] old_size = volume['size']
growth_size = int(new_size) - old_size growth_size = int(new_size) - old_size
LOG.debug("Extending Volume %(vol)s from %(old)s to %(new)s, " LOG.debug("Extending Volume %(vol)s from %(old)s to %(new)s, "
@ -689,8 +690,11 @@ class HPE3PARCommon(object):
for snapshot in snapshots: for snapshot in snapshots:
# Getting vol_name from snapshot, in case of group created # Getting vol_name from snapshot, in case of group created
# from group snapshot. # from group snapshot.
vol_name = ( # Don't use the "volume_id" from the snapshot directly in
self._get_3par_vol_name(snapshot.get('volume_id'))) # case the volume has been migrated and uses a different ID
# in the backend. This may trigger OVO lazy loading. Use
# dict compatibility to avoid changing all the unit tests.
vol_name = self._get_3par_vol_name(snapshot['volume'])
if src_vol_name == vol_name: if src_vol_name == vol_name:
vol_name = ( vol_name = (
self._get_3par_vol_name(snapshot.get('id'))) self._get_3par_vol_name(snapshot.get('id')))
@ -709,7 +713,7 @@ class HPE3PARCommon(object):
vol_name = self._get_3par_vol_name(src_vol_name) vol_name = self._get_3par_vol_name(src_vol_name)
snap_name = snap_vol_dict.get(vol_name) snap_name = snap_vol_dict.get(vol_name)
volume_name = self._get_3par_vol_name(volume.get('id')) volume_name = self._get_3par_vol_name(volume)
type_info = self.get_volume_settings_from_type(volume) type_info = self.get_volume_settings_from_type(volume)
cpg = type_info['cpg'] cpg = type_info['cpg']
snapcpg = type_info['snap_cpg'] snapcpg = type_info['snap_cpg']
@ -828,7 +832,7 @@ class HPE3PARCommon(object):
# TODO(kushal) : we will use volume as object when we re-write # TODO(kushal) : we will use volume as object when we re-write
# the design for unit tests to use objects instead of dicts. # the design for unit tests to use objects instead of dicts.
for volume in add_volumes: for volume in add_volumes:
volume_name = self._get_3par_vol_name(volume.get('id')) volume_name = self._get_3par_vol_name(volume)
vol_snap_enable = self.is_volume_group_snap_type( vol_snap_enable = self.is_volume_group_snap_type(
volume.get('volume_type')) volume.get('volume_type'))
try: try:
@ -862,7 +866,7 @@ class HPE3PARCommon(object):
raise exception.InvalidInput(reason=msg) raise exception.InvalidInput(reason=msg)
for volume in remove_volumes: for volume in remove_volumes:
volume_name = self._get_3par_vol_name(volume.get('id')) volume_name = self._get_3par_vol_name(volume)
if group.is_replicated: if group.is_replicated:
# Remove a volume from remote copy group # Remove a volume from remote copy group
@ -997,12 +1001,15 @@ class HPE3PARCommon(object):
display_name = None display_name = None
# Generate the new volume information based on the new ID. # Generate the new volume information based on the new ID.
new_vol_name = self._get_3par_vol_name(volume['id']) new_vol_name = self._get_3par_vol_name(volume)
# No need to worry about "_name_id" because this is a newly created
# volume that cannot have been migrated.
name = 'volume-' + volume['id'] name = 'volume-' + volume['id']
new_comment['volume_id'] = volume['id'] new_comment['volume_id'] = volume['id']
new_comment['name'] = name new_comment['name'] = name
new_comment['type'] = 'OpenStack' new_comment['type'] = 'OpenStack'
self._add_name_id_to_comment(new_comment, volume)
volume_type = None volume_type = None
if volume['volume_type_id']: if volume['volume_type_id']:
@ -1093,7 +1100,7 @@ class HPE3PARCommon(object):
raise exception.InvalidInput(reason=err) raise exception.InvalidInput(reason=err)
# Make sure the snapshot is being associated with the correct volume. # Make sure the snapshot is being associated with the correct volume.
parent_vol_name = self._get_3par_vol_name(volume['id']) parent_vol_name = self._get_3par_vol_name(volume)
if parent_vol_name != snap['copyOf']: if parent_vol_name != snap['copyOf']:
err = (_("The provided snapshot '%s' is not a snapshot of " err = (_("The provided snapshot '%s' is not a snapshot of "
"the provided volume.") % target_snap_name) "the provided volume.") % target_snap_name)
@ -1119,6 +1126,7 @@ class HPE3PARCommon(object):
new_snap_name = self._get_3par_snap_name(snapshot['id']) new_snap_name = self._get_3par_snap_name(snapshot['id'])
new_comment['volume_id'] = volume['id'] new_comment['volume_id'] = volume['id']
new_comment['volume_name'] = 'volume-' + volume['id'] new_comment['volume_name'] = 'volume-' + volume['id']
self._add_name_id_to_comment(new_comment, volume)
if snapshot.get('display_description', None): if snapshot.get('display_description', None):
new_comment['description'] = snapshot['display_description'] new_comment['description'] = snapshot['display_description']
else: else:
@ -1198,7 +1206,10 @@ class HPE3PARCommon(object):
"""Removes the specified volume from Cinder management.""" """Removes the specified volume from Cinder management."""
# Rename the volume's name to unm-* format so that it can be # Rename the volume's name to unm-* format so that it can be
# easily found later. # easily found later.
vol_name = self._get_3par_vol_name(volume['id']) vol_name = self._get_3par_vol_name(volume)
# Rename using the user visible ID ignoring the internal "_name_id"
# that may have been generated during a retype. This makes it easier
# to locate volumes in the backend.
new_vol_name = self._get_3par_unm_name(volume['id']) new_vol_name = self._get_3par_unm_name(volume['id'])
self.client.modifyVolume(vol_name, {'newName': new_vol_name}) self.client.modifyVolume(vol_name, {'newName': new_vol_name})
@ -1260,7 +1271,7 @@ class HPE3PARCommon(object):
def _extend_volume(self, volume, volume_name, growth_size_mib, def _extend_volume(self, volume, volume_name, growth_size_mib,
_convert_to_base=False): _convert_to_base=False):
model_update = None model_update = None
rcg_name = self._get_3par_rcg_name(volume['id']) rcg_name = self._get_3par_rcg_name(volume)
is_volume_replicated = self._volume_of_replicated_type( is_volume_replicated = self._volume_of_replicated_type(
volume, hpe_tiramisu_check=True) volume, hpe_tiramisu_check=True)
volume_part_of_group = ( volume_part_of_group = (
@ -1308,7 +1319,8 @@ class HPE3PARCommon(object):
{'vol': volume_name, 'ex': ex}) {'vol': volume_name, 'ex': ex})
return model_update return model_update
def _get_3par_vol_name(self, volume_id, temp_vol=False): @classmethod
def _get_3par_vol_name(cls, volume_id, temp_vol=False):
"""Get converted 3PAR volume name. """Get converted 3PAR volume name.
Converts the openstack volume id from Converts the openstack volume id from
@ -1322,8 +1334,16 @@ class HPE3PARCommon(object):
We strip the padding '=' and replace + with . We strip the padding '=' and replace + with .
and / with - and / with -
volume_id is a polymorphic parameter and can be either a string or a
volume (OVO or dict representation).
""" """
volume_name = self._encode_name(volume_id) # Accept OVOs (what we should only receive), dict (so we don't have to
# change all our unit tests), and ORM (because we some methods still
# pass it, such as terminate_connection).
if isinstance(volume_id, (objects.Volume, objects.Volume.model, dict)):
volume_id = volume_id.get('_name_id') or volume_id['id']
volume_name = cls._encode_name(volume_id)
if temp_vol: if temp_vol:
# is this a temporary volume # is this a temporary volume
# this is done during migration # this is done during migration
@ -1355,16 +1375,17 @@ class HPE3PARCommon(object):
return "unm-%s" % unm_name return "unm-%s" % unm_name
# v2 replication conversion # v2 replication conversion
def _get_3par_rcg_name(self, volume_id): def _get_3par_rcg_name(self, volume):
rcg_name = self._encode_name(volume_id) rcg_name = self._encode_name(volume.get('_name_id') or volume['id'])
rcg = "rcg-%s" % rcg_name rcg = "rcg-%s" % rcg_name
return rcg[:22] return rcg[:22]
def _get_3par_remote_rcg_name(self, volume_id, provider_location): def _get_3par_remote_rcg_name(self, volume, provider_location):
return self._get_3par_rcg_name(volume_id) + ".r" + ( return self._get_3par_rcg_name(volume) + ".r" + (
six.text_type(provider_location)) six.text_type(provider_location))
def _encode_name(self, name): @staticmethod
def _encode_name(name):
uuid_str = name.replace("-", "") uuid_str = name.replace("-", "")
vol_uuid = uuid.UUID('urn:uuid:%s' % uuid_str) vol_uuid = uuid.UUID('urn:uuid:%s' % uuid_str)
vol_encoded = base64.encode_as_text(vol_uuid.bytes) vol_encoded = base64.encode_as_text(vol_uuid.bytes)
@ -1741,7 +1762,7 @@ class HPE3PARCommon(object):
In order to export a volume on a 3PAR box, we have to create a VLUN. In order to export a volume on a 3PAR box, we have to create a VLUN.
""" """
volume_name = self._get_3par_vol_name(volume['id']) volume_name = self._get_3par_vol_name(volume)
vlun_info = self._create_3par_vlun(volume_name, host['name'], nsp, vlun_info = self._create_3par_vlun(volume_name, host['name'], nsp,
lun_id=lun_id, lun_id=lun_id,
remote_client=remote_client) remote_client=remote_client)
@ -1752,7 +1773,7 @@ class HPE3PARCommon(object):
remote_client) remote_client)
def _delete_vlun(self, client_obj, volume, hostname, wwn=None, iqn=None): def _delete_vlun(self, client_obj, volume, hostname, wwn=None, iqn=None):
volume_name = self._get_3par_vol_name(volume['id']) volume_name = self._get_3par_vol_name(volume)
if hostname: if hostname:
vluns = client_obj.getHostVLUNs(hostname) vluns = client_obj.getHostVLUNs(hostname)
else: else:
@ -2048,7 +2069,7 @@ class HPE3PARCommon(object):
raise exception.CinderException(ex) raise exception.CinderException(ex)
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)
vol = self.client.getVolume(volume_name) vol = self.client.getVolume(volume_name)
# Search for 'userCPG' in the get volume REST API, # Search for 'userCPG' in the get volume REST API,
# if found return userCPG , else search for snapCPG attribute # if found return userCPG , else search for snapCPG attribute
@ -2245,12 +2266,14 @@ class HPE3PARCommon(object):
'%(host)s)', '%(host)s)',
{'disp_name': volume['display_name'], {'disp_name': volume['display_name'],
'vol_name': volume['name'], 'vol_name': volume['name'],
'id': self._get_3par_vol_name(volume['id']), 'id': self._get_3par_vol_name(volume),
'host': volume['host']}) 'host': volume['host']})
try: try:
comments = {'volume_id': volume['id'], comments = {'volume_id': volume['id'],
'name': volume['name'], 'name': volume['name'],
'type': 'OpenStack'} 'type': 'OpenStack'}
self._add_name_id_to_comment(comments, volume)
# This flag denotes group level replication on # This flag denotes group level replication on
# hpe 3par. # hpe 3par.
hpe_tiramisu = False hpe_tiramisu = False
@ -2300,7 +2323,7 @@ class HPE3PARCommon(object):
extras['tdvv'] = tdvv extras['tdvv'] = tdvv
capacity = self._capacity_from_size(volume['size']) capacity = self._capacity_from_size(volume['size'])
volume_name = self._get_3par_vol_name(volume['id']) volume_name = self._get_3par_vol_name(volume)
if compression is not None: if compression is not None:
extras['compression'] = compression extras['compression'] = compression
@ -2432,7 +2455,7 @@ class HPE3PARCommon(object):
This is used by cloning a volume so that we can then This is used by cloning a volume so that we can then
issue extend volume against the original volume. issue extend volume against the original volume.
""" """
vol_name = self._get_3par_vol_name(volume['id']) vol_name = self._get_3par_vol_name(volume)
# create a brand new uuid for the temp snap # create a brand new uuid for the temp snap
snap_uuid = uuid.uuid4().hex snap_uuid = uuid.uuid4().hex
@ -2441,6 +2464,7 @@ class HPE3PARCommon(object):
extra = {'volume_name': volume['name'], extra = {'volume_name': volume['name'],
'volume_id': volume['id']} 'volume_id': volume['id']}
self._add_name_id_to_comment(extra, volume)
optional = {'comment': json.dumps(extra)} optional = {'comment': json.dumps(extra)}
@ -2455,8 +2479,8 @@ class HPE3PARCommon(object):
def create_cloned_volume(self, volume, src_vref): def create_cloned_volume(self, volume, src_vref):
try: try:
vol_name = self._get_3par_vol_name(volume['id']) vol_name = self._get_3par_vol_name(volume)
src_vol_name = self._get_3par_vol_name(src_vref['id']) src_vol_name = self._get_3par_vol_name(src_vref)
back_up_process = False back_up_process = False
vol_chap_enabled = False vol_chap_enabled = False
hpe_tiramisu = False hpe_tiramisu = False
@ -2589,7 +2613,7 @@ class HPE3PARCommon(object):
return return
try: try:
volume_name = self._get_3par_vol_name(volume['id']) volume_name = self._get_3par_vol_name(volume)
# Try and delete the volume, it might fail here because # Try and delete the volume, it might fail here because
# the volume is part of a volume set which will have the # the volume is part of a volume set which will have the
# volume set name in the error. # volume set name in the error.
@ -2687,10 +2711,11 @@ class HPE3PARCommon(object):
try: try:
if not snap_name: if not snap_name:
snap_name = self._get_3par_snap_name(snapshot['id']) snap_name = self._get_3par_snap_name(snapshot['id'])
volume_name = self._get_3par_vol_name(volume['id']) volume_name = self._get_3par_vol_name(volume)
extra = {'volume_id': volume['id'], extra = {'volume_id': volume['id'],
'snapshot_id': snapshot['id']} 'snapshot_id': snapshot['id']}
self._add_name_id_to_comment(extra, volume)
type_id = volume.get('volume_type_id', None) type_id = volume.get('volume_type_id', None)
@ -2785,12 +2810,15 @@ class HPE3PARCommon(object):
try: try:
snap_name = self._get_3par_snap_name(snapshot['id']) snap_name = self._get_3par_snap_name(snapshot['id'])
vol_name = self._get_3par_vol_name(snapshot['volume_id']) # Don't use the "volume_id" from the snapshot directly in case the
# volume has been migrated and uses a different ID in the backend.
# This may trigger OVO lazy loading. Use dict compatibility to
# avoid changing all the unit tests.
vol_name = self._get_3par_vol_name(snapshot['volume'])
extra = {'volume_name': snapshot['volume_name']} extra = {'volume_name': snapshot['volume_name'],
vol_id = snapshot.get('volume_id', None) 'volume_id': snapshot.get('volume_id')}
if vol_id: self._add_name_id_to_comment(extra, snapshot['volume'])
extra['volume_id'] = vol_id
try: try:
extra['display_name'] = snapshot['display_name'] extra['display_name'] = snapshot['display_name']
@ -2857,6 +2885,93 @@ class HPE3PARCommon(object):
dbg_ret) dbg_ret)
return ret return ret
def _rename_migrated(self, volume, dest_volume):
"""Rename the destination volume after a migration.
Returns whether the destination volume has the name matching the source
volume or not.
That way we know whether we need to set the _name_id or not.
"""
def log_error(vol_type, error, src, dest, rename_name=None,
original_name=None):
LOG.error("Changing the %(vol_type)s volume name from %(src)s to "
"%(dest)s failed because %(reason)s",
{'vol_type': vol_type, 'src': src, 'dest': dest,
'reason': error})
if rename_name:
original_name = original_name or dest
# Don't fail the migration, but help the user fix the
# source volume stuck in error_deleting.
LOG.error("Migration will fail to delete the original volume. "
"It must be manually renamed from %(rename_name)s to"
" %(original_name)s in the backend, and then we "
"have to tell cinder to delete volume %(vol_id)s",
{'rename_name': rename_name,
'original_name': original_name,
'vol_id': dest_volume['id']})
original_volume_renamed = False
# We don't need to rename the source volume if it uses a _name_id,
# since the id we want to use to rename the new volume is available.
if volume['id'] == (volume.get('_name_id') or volume['id']):
original_name = self._get_3par_vol_name(volume)
temp_name = self._get_3par_vol_name(volume, temp_vol=True)
# In case the original volume is on the same backend, try
# renaming it to a temporary name.
try:
volumeTempMods = {'newName': temp_name}
self.client.modifyVolume(original_name, volumeTempMods)
original_volume_renamed = True
except hpeexceptions.HTTPNotFound:
pass
except Exception as e:
log_error('original', e, original_name, temp_name)
return False
# Change the destination volume name to the source's ID name
current_name = self._get_3par_vol_name(dest_volume)
volume_id_name = self._get_3par_vol_name(volume['id'])
try:
# After this call the volume manager will call
# finish_volume_migration and swap the fields, so we want to
# have the right info on the comments if we succeed in renaming
# the volumes in the backend.
new_comment = self._get_updated_comment(current_name,
volume_id=volume['id'],
_name_id=None)
volumeMods = {'newName': volume_id_name, 'comment': new_comment}
self.client.modifyVolume(current_name, volumeMods)
LOG.info("Current volume changed from %(cur)s to %(orig)s",
{'cur': current_name, 'orig': volume_id_name})
except Exception as e:
if original_volume_renamed:
_name = temp_name
else:
_name = original_name = None
log_error('migrating', e, current_name, volume_id_name, _name,
original_name)
return False
# If it was renamed, rename the original volume again to the
# migrated volume's name (effectively swapping the names). If
# this operation fails, the newly migrated volume is OK but the
# original volume (with the temp name) may need to be manually
# cleaned up on the backend.
if original_volume_renamed:
try:
old_comment = self._get_updated_comment(
original_name,
volume_id=dest_volume['id'],
_name_id=volume.get('_name_id'))
volumeCurrentMods = {'newName': current_name,
'comment': old_comment}
self.client.modifyVolume(temp_name, volumeCurrentMods)
except Exception as e:
log_error('original', e, temp_name, current_name, temp_name)
return True
def update_migrated_volume(self, context, volume, new_volume, def update_migrated_volume(self, context, volume, new_volume,
original_volume_status): original_volume_status):
"""Rename the new (temp) volume to it's original name. """Rename the new (temp) volume to it's original name.
@ -2867,77 +2982,48 @@ class HPE3PARCommon(object):
""" """
LOG.debug("Update volume name for %(id)s", {'id': new_volume['id']}) LOG.debug("Update volume name for %(id)s", {'id': new_volume['id']})
new_volume_renamed = False
# For available volumes we'll try renaming the destination volume to
# match the id of the source volume.
if original_volume_status == 'available': if original_volume_status == 'available':
# volume isn't attached and can be updated new_volume_renamed = self._rename_migrated(volume, new_volume)
original_name = self._get_3par_vol_name(volume['id']) else:
current_name = self._get_3par_vol_name(new_volume['id']) new_volume_renamed = False
temp_name = self._get_3par_vol_name(volume['id'], temp_vol=True)
# In case the original volume is on the same backend, try
# renaming it to a temporary name.
original_volume_renamed = False
try:
volumeTempMods = {'newName': temp_name}
self.client.modifyVolume(original_name, volumeTempMods)
original_volume_renamed = True
except hpeexceptions.HTTPNotFound:
pass
except Exception as e:
LOG.error("Changing the original volume name from %(orig)s to "
"%(temp)s failed because %(reason)s",
{'orig': original_name, 'temp': temp_name,
'reason': e})
raise
# change the current volume name to the original
try:
volumeMods = {'newName': original_name}
self.client.modifyVolume(current_name, volumeMods)
new_volume_renamed = True
LOG.info("Current volume changed from %(cur)s to %(orig)s",
{'cur': current_name, 'orig': original_name})
except Exception as e:
LOG.error("Changing the migrating volume name from %(cur)s to "
"%(orig)s failed because %(reason)s",
{'cur': current_name, 'orig': original_name,
'reason': e})
if original_volume_renamed:
LOG.error("To restore the original volume, it must be "
"manually renamed from %(temp)s to %(orig)s",
{'temp': temp_name, 'orig': original_name})
raise
# If it was renamed, rename the original volume again to the
# migrated volume's name (effectively swapping the names). If
# this operation fails, the newly migrated volume is OK but the
# original volume (with the temp name) may need to be manually
# cleaned up on the backend.
if original_volume_renamed:
try:
volumeCurrentMods = {'newName': current_name}
self.client.modifyVolume(temp_name, volumeCurrentMods)
except Exception as e:
LOG.error("Changing the original volume name from "
"%(tmp)s to %(cur)s failed because %(reason)s",
{'tmp': temp_name, 'cur': current_name,
'reason': e})
# Don't fail the migration, but help the user fix the
# source volume stuck in error_deleting.
LOG.error("To delete the original volume, it must be "
"manually renamed from %(temp)s to %(orig)s",
{'temp': temp_name, 'orig': current_name})
if new_volume_renamed: if new_volume_renamed:
name_id = None name_id = None
# NOTE: I think this will break with replicated volumes.
provider_location = None provider_location = None
else: else:
# the backend can't change the name. # the backend can't change the name.
name_id = new_volume['_name_id'] or new_volume['id'] name_id = new_volume['_name_id'] or new_volume['id']
provider_location = new_volume['provider_location'] provider_location = new_volume['provider_location']
# Update the comment in the backend to reflect the _name_id
current_name = self._get_3par_vol_name(new_volume)
self._update_comment(current_name, volume_id=volume['id'],
_name_id=name_id)
return {'_name_id': name_id, 'provider_location': provider_location} return {'_name_id': name_id, 'provider_location': provider_location}
@staticmethod
def _add_name_id_to_comment(comment, volume):
name_id = volume.get('_name_id')
if name_id:
comment['_name_id'] = name_id
def _get_updated_comment(self, vol_name, **values):
vol = self.client.getVolume(vol_name)
comment = json.loads(vol['comment']) if vol['comment'] else {}
comment.update(values)
def _update_comment(self, vol_name, **values):
"""Update key-value pairs on the comment of a volume in the backend."""
if not values:
return
comment = self._get_updated_comment(vol_name, **values)
self.client.modifyVolume(vol_name, {'comment': json.dumps(comment)})
def _wait_for_task_completion(self, task_id): def _wait_for_task_completion(self, task_id):
"""This waits for a 3PAR background task complete or fail. """This waits for a 3PAR background task complete or fail.
@ -2970,7 +3056,7 @@ class HPE3PARCommon(object):
# Change the name such that it is unique since 3PAR # Change the name such that it is unique since 3PAR
# names must be unique across all CPGs # names must be unique across all CPGs
volume_name = self._get_3par_vol_name(volume['id']) volume_name = self._get_3par_vol_name(volume)
temp_vol_name = volume_name.replace("osv-", "omv-") temp_vol_name = volume_name.replace("osv-", "omv-")
compression = self.get_compression_policy( compression = self.get_compression_policy(
@ -3088,6 +3174,8 @@ class HPE3PARCommon(object):
v2['id'] = self._get_3par_vol_comment_value( v2['id'] = self._get_3par_vol_comment_value(
v2['comment'], 'volume_id') v2['comment'], 'volume_id')
v2['_name_id'] = self._get_3par_vol_comment_value(
v2['comment'], '_name_id')
v2['host'] = '#' + v1['userCPG'] v2['host'] = '#' + v1['userCPG']
@ -3399,8 +3487,7 @@ class HPE3PARCommon(object):
dictionary of its reported capabilities. Host validation dictionary of its reported capabilities. Host validation
is just skipped if host is None. is just skipped if host is None.
""" """
volume_id = volume['id'] volume_name = self._get_3par_vol_name(volume)
volume_name = self._get_3par_vol_name(volume_id)
new_type_name = None new_type_name = None
new_type_id = None new_type_id = None
if new_type: if new_type:
@ -3501,7 +3588,7 @@ class HPE3PARCommon(object):
old_volume_settings, host) old_volume_settings, host)
def remove_temporary_snapshots(self, volume): def remove_temporary_snapshots(self, volume):
vol_name = self._get_3par_vol_name(volume['id']) vol_name = self._get_3par_vol_name(volume)
snapshots_list = self.client.getVolumeSnapshots(vol_name) snapshots_list = self.client.getVolumeSnapshots(vol_name)
tmp_snapshots_list = [snap tmp_snapshots_list = [snap
for snap in snapshots_list for snap in snapshots_list
@ -3527,9 +3614,9 @@ class HPE3PARCommon(object):
:param volume: A dictionary describing the volume to revert :param volume: A dictionary describing the volume to revert
:param snapshot: A dictionary describing the latest snapshot :param snapshot: A dictionary describing the latest snapshot
""" """
volume_name = self._get_3par_vol_name(volume['id']) volume_name = self._get_3par_vol_name(volume)
snapshot_name = self._get_3par_snap_name(snapshot['id']) snapshot_name = self._get_3par_snap_name(snapshot['id'])
rcg_name = self._get_3par_rcg_name(volume['id']) rcg_name = self._get_3par_rcg_name(volume)
volume_part_of_group = ( volume_part_of_group = (
self._volume_of_hpe_tiramisu_type_and_part_of_group(volume)) self._volume_of_hpe_tiramisu_type_and_part_of_group(volume))
if volume_part_of_group: if volume_part_of_group:
@ -3593,7 +3680,7 @@ class HPE3PARCommon(object):
""" """
existing_vlun = None existing_vlun = None
try: try:
vol_name = self._get_3par_vol_name(volume['id']) vol_name = self._get_3par_vol_name(volume)
if remote_client: if remote_client:
host_vluns = remote_client.getHostVLUNs(host['name']) host_vluns = remote_client.getHostVLUNs(host['name'])
else: else:
@ -3615,7 +3702,7 @@ class HPE3PARCommon(object):
def find_existing_vluns(self, volume, host, remote_client=None): def find_existing_vluns(self, volume, host, remote_client=None):
existing_vluns = [] existing_vluns = []
try: try:
vol_name = self._get_3par_vol_name(volume['id']) vol_name = self._get_3par_vol_name(volume)
if remote_client: if remote_client:
host_vluns = remote_client.getHostVLUNs(host['name']) host_vluns = remote_client.getHostVLUNs(host['name'])
else: else:
@ -3709,7 +3796,7 @@ class HPE3PARCommon(object):
# Try and stop remote-copy on main array. We eat the # Try and stop remote-copy on main array. We eat the
# exception here because when an array goes down, the # exception here because when an array goes down, the
# groups will stop automatically. # groups will stop automatically.
rcg_name = self._get_3par_rcg_name(volume['id']) rcg_name = self._get_3par_rcg_name(volume)
self.client.stopRemoteCopy(rcg_name) self.client.stopRemoteCopy(rcg_name)
except Exception: except Exception:
pass pass
@ -3717,7 +3804,7 @@ class HPE3PARCommon(object):
try: try:
# Failover to secondary array. # Failover to secondary array.
remote_rcg_name = self._get_3par_remote_rcg_name( remote_rcg_name = self._get_3par_remote_rcg_name(
volume['id'], volume['provider_location']) volume, volume['provider_location'])
cl = self._create_replication_client(failover_target) cl = self._create_replication_client(failover_target)
cl.recoverRemoteCopyGroupFromDisaster( cl.recoverRemoteCopyGroupFromDisaster(
remote_rcg_name, self.RC_ACTION_CHANGE_TO_PRIMARY) remote_rcg_name, self.RC_ACTION_CHANGE_TO_PRIMARY)
@ -3790,9 +3877,8 @@ class HPE3PARCommon(object):
if self._volume_of_replicated_type(volume, if self._volume_of_replicated_type(volume,
hpe_tiramisu_check=True): hpe_tiramisu_check=True):
location = volume.get('provider_location') location = volume.get('provider_location')
remote_rcg_name = self._get_3par_remote_rcg_name( remote_rcg_name = self._get_3par_remote_rcg_name(volume,
volume['id'], location)
location)
rcg = self.client.getRemoteCopyGroup(remote_rcg_name) rcg = self.client.getRemoteCopyGroup(remote_rcg_name)
if not self._are_targets_in_their_natural_direction(rcg): if not self._are_targets_in_their_natural_direction(rcg):
return False return False
@ -3980,7 +4066,7 @@ class HPE3PARCommon(object):
return replicated_type return replicated_type
def _is_volume_in_remote_copy_group(self, volume): def _is_volume_in_remote_copy_group(self, volume):
rcg_name = self._get_3par_rcg_name(volume['id']) rcg_name = self._get_3par_rcg_name(volume)
try: try:
self.client.getRemoteCopyGroup(rcg_name) self.client.getRemoteCopyGroup(rcg_name)
return True return True
@ -4101,7 +4187,7 @@ class HPE3PARCommon(object):
reverse order, including the original volume. reverse order, including the original volume.
""" """
rcg_name = self._get_3par_rcg_name(volume['id']) rcg_name = self._get_3par_rcg_name(volume)
# If the volume is already in a remote copy group, return True # If the volume is already in a remote copy group, return True
# after starting remote copy. If remote copy is already started, # after starting remote copy. If remote copy is already started,
# issuing this command again will be fine. # issuing this command again will be fine.
@ -4140,7 +4226,7 @@ class HPE3PARCommon(object):
vol_settings = self.get_volume_settings_from_type(volume) vol_settings = self.get_volume_settings_from_type(volume)
local_cpg = vol_settings['cpg'] local_cpg = vol_settings['cpg']
vol_name = self._get_3par_vol_name(volume['id']) vol_name = self._get_3par_vol_name(volume)
# Create remote copy group on main array. # Create remote copy group on main array.
rcg_targets = [] rcg_targets = []
@ -4262,8 +4348,8 @@ class HPE3PARCommon(object):
-Delete volume from main array -Delete volume from main array
""" """
if not rcg_name: if not rcg_name:
rcg_name = self._get_3par_rcg_name(volume['id']) rcg_name = self._get_3par_rcg_name(volume)
vol_name = self._get_3par_vol_name(volume['id']) vol_name = self._get_3par_vol_name(volume)
# Stop remote copy. # Stop remote copy.
try: try:
@ -4299,7 +4385,7 @@ class HPE3PARCommon(object):
def _delete_replicated_failed_over_volume(self, volume): def _delete_replicated_failed_over_volume(self, volume):
location = volume.get('provider_location') location = volume.get('provider_location')
rcg_name = self._get_3par_remote_rcg_name(volume['id'], location) rcg_name = self._get_3par_remote_rcg_name(volume, location)
targets = self.client.getRemoteCopyGroup(rcg_name)['targets'] targets = self.client.getRemoteCopyGroup(rcg_name)['targets']
# When failed over, we want to temporarily disable config mirroring # When failed over, we want to temporarily disable config mirroring
# in order to be allowed to delete the volume and remote copy group # in order to be allowed to delete the volume and remote copy group
@ -4326,7 +4412,7 @@ class HPE3PARCommon(object):
def _delete_vvset(self, volume): def _delete_vvset(self, volume):
# volume is part of a volume set. # volume is part of a volume set.
volume_name = self._get_3par_vol_name(volume['id']) volume_name = self._get_3par_vol_name(volume)
vvset_name = self.client.findVolumeSet(volume_name) vvset_name = self.client.findVolumeSet(volume_name)
LOG.debug("Returned vvset_name = %s", vvset_name) LOG.debug("Returned vvset_name = %s", vvset_name)
if vvset_name is not None: if vvset_name is not None:
@ -4480,7 +4566,7 @@ class HPE3PARCommon(object):
def _remove_vol_from_remote_copy_group(self, group, volume): def _remove_vol_from_remote_copy_group(self, group, volume):
rcg_name = self._get_3par_rcg_name_of_group(group.id) rcg_name = self._get_3par_rcg_name_of_group(group.id)
vol_name = self._get_3par_vol_name(volume.get('id')) vol_name = self._get_3par_vol_name(volume)
try: try:
# Delete volume from remote copy group on secondary array. # Delete volume from remote copy group on secondary array.
@ -4588,7 +4674,7 @@ class HPE3PARCommon(object):
def _add_vol_to_remote(self, volume, rcg_name): def _add_vol_to_remote(self, volume, rcg_name):
# Add a volume to remote copy group. # Add a volume to remote copy group.
rcg_targets = [] rcg_targets = []
vol_name = self._get_3par_vol_name(volume.get('id')) vol_name = self._get_3par_vol_name(volume)
replication_mode_num = self._get_replication_mode_from_volume(volume) replication_mode_num = self._get_replication_mode_from_volume(volume)
for target in self._replication_targets: for target in self._replication_targets:
if target['replication_mode'] == replication_mode_num: if target['replication_mode'] == replication_mode_num:
@ -4627,7 +4713,7 @@ class HPE3PARCommon(object):
pass pass
for volume in volumes: for volume in volumes:
vol_name = self._get_3par_vol_name(volume.get('id')) vol_name = self._get_3par_vol_name(volume)
# Delete volume from remote copy group on secondary array. # Delete volume from remote copy group on secondary array.
try: try:
self.client.removeVolumeFromRemoteCopyGroup( self.client.removeVolumeFromRemoteCopyGroup(

View File

@ -510,7 +510,7 @@ class HPE3PARISCSIDriver(hpebasedriver.HPE3PARDriverBase):
Ignore exceptions caused by the keys not being present on a volume. Ignore exceptions caused by the keys not being present on a volume.
""" """
vol_name = common._get_3par_vol_name(volume['id']) vol_name = common._get_3par_vol_name(volume)
try: try:
common.client.removeVolumeMetaData(vol_name, CHAP_USER_KEY) common.client.removeVolumeMetaData(vol_name, CHAP_USER_KEY)
@ -616,7 +616,7 @@ class HPE3PARISCSIDriver(hpebasedriver.HPE3PARDriverBase):
if not remote_target: if not remote_target:
# Get the CHAP secret if CHAP is enabled # Get the CHAP secret if CHAP is enabled
if common._client_conf['hpe3par_iscsi_chap_enabled']: if common._client_conf['hpe3par_iscsi_chap_enabled']:
vol_name = common._get_3par_vol_name(volume['id']) vol_name = common._get_3par_vol_name(volume)
username = common.client.getVolumeMetaData( username = common.client.getVolumeMetaData(
vol_name, CHAP_USER_KEY)['value'] vol_name, CHAP_USER_KEY)['value']
password = common.client.getVolumeMetaData( password = common.client.getVolumeMetaData(
@ -723,7 +723,7 @@ class HPE3PARISCSIDriver(hpebasedriver.HPE3PARDriverBase):
"Generating new CHAP key.") "Generating new CHAP key.")
# Add CHAP credentials to the volume metadata # Add CHAP credentials to the volume metadata
vol_name = common._get_3par_vol_name(volume['id']) vol_name = common._get_3par_vol_name(volume)
common.client.setVolumeMetaData( common.client.setVolumeMetaData(
vol_name, CHAP_USER_KEY, chap_username) vol_name, CHAP_USER_KEY, chap_username)
common.client.setVolumeMetaData( common.client.setVolumeMetaData(
@ -750,7 +750,7 @@ class HPE3PARISCSIDriver(hpebasedriver.HPE3PARDriverBase):
""" """
common = self._login() common = self._login()
try: try:
vol_name = common._get_3par_vol_name(volume['id']) vol_name = common._get_3par_vol_name(volume)
common.client.getVolume(vol_name) common.client.getVolume(vol_name)
except hpeexceptions.HTTPNotFound: except hpeexceptions.HTTPNotFound:
LOG.error("Volume %s doesn't exist on array.", vol_name) LOG.error("Volume %s doesn't exist on array.", vol_name)

View File

@ -0,0 +1,7 @@
---
fixes:
- |
Fix HPE 3PAR driver issue where volumes that were live migrated to it would
end up being inaccessible. We would no longer be able to use the volume
for any operation, such as attach, detach, delete, snapshot, etc.
(bug 1697422)