Merge "3PAR: Fix live migration" into stable/ussuri
This commit is contained in:
commit
6f1b3b5d01
|
@ -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 +
|
||||||
|
|
|
@ -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(
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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)
|
Loading…
Reference in New Issue