Fixed nova VM live migration issue with 3PAR
Nova bypasses the cinder checks for a volume being available, when it tries to attach a volume to a new host during live migration. The assumption in cinder to this point has been that volumes can only be attached to one host. The 3PAR driver worked under that assumption. This assumption fell apart during detach time as the driver was only looking for a VLUN on the entire 3PAR, since it assumed it could only exist on one host. This patch ensures that the driver looks for the VLUN on the hostname it expects. Change-Id: Ie894ad386990794d270ca1cb72f40095bd40c2e6 Closes-Bug: 1288927
This commit is contained in:
parent
015555acb7
commit
2c1701e2f5
@ -701,7 +701,10 @@ class HP3PARBaseDriver(object):
|
||||
# setup_mock_client drive with default configuration
|
||||
# and return the mock HTTP 3PAR client
|
||||
mock_client = self.setup_driver()
|
||||
mock_client.getVLUN.return_value = {'lun': None, 'type': 0}
|
||||
mock_client.getHostVLUNs.return_value = [
|
||||
{'active': True,
|
||||
'volumeName': self.VOLUME_3PAR_NAME,
|
||||
'lun': None, 'type': 0}]
|
||||
|
||||
self.driver.terminate_connection(
|
||||
self.volume,
|
||||
@ -710,7 +713,7 @@ class HP3PARBaseDriver(object):
|
||||
|
||||
expected = [
|
||||
mock.call.login(HP3PAR_USER_NAME, HP3PAR_USER_PASS),
|
||||
mock.call.getVLUN(self.VOLUME_3PAR_NAME),
|
||||
mock.call.getHostVLUNs(self.FAKE_HOST),
|
||||
mock.call.deleteVLUN(
|
||||
self.VOLUME_3PAR_NAME,
|
||||
None,
|
||||
@ -986,7 +989,10 @@ class TestHP3PARFCDriver(HP3PARBaseDriver, test.TestCase):
|
||||
'vendor': None,
|
||||
'wwn': self.wwn[1]}]}]
|
||||
mock_client.findHost.return_value = self.FAKE_HOST
|
||||
mock_client.getVLUN.return_value = {'lun': 90}
|
||||
mock_client.getHostVLUNs.return_value = [
|
||||
{'active': True,
|
||||
'volumeName': self.VOLUME_3PAR_NAME,
|
||||
'lun': 90, 'type': 0}]
|
||||
mock_client.getPorts.return_value = {
|
||||
'members': self.FAKE_FC_PORTS + [self.FAKE_ISCSI_PORT]}
|
||||
|
||||
@ -994,16 +1000,16 @@ class TestHP3PARFCDriver(HP3PARBaseDriver, test.TestCase):
|
||||
|
||||
expected = [
|
||||
mock.call.login(HP3PAR_USER_NAME, HP3PAR_USER_PASS),
|
||||
mock.call.getVolume('osv-0DM4qZEVSKON-DXN-NwVpw'),
|
||||
mock.call.getVolume(self.VOLUME_3PAR_NAME),
|
||||
mock.call.getCPG(HP3PAR_CPG),
|
||||
mock.call.getHost(self.FAKE_HOST),
|
||||
mock.ANY,
|
||||
mock.call.getHost(self.FAKE_HOST),
|
||||
mock.call.createVLUN(
|
||||
'osv-0DM4qZEVSKON-DXN-NwVpw',
|
||||
self.VOLUME_3PAR_NAME,
|
||||
auto=True,
|
||||
hostname=self.FAKE_HOST),
|
||||
mock.call.getVLUN('osv-0DM4qZEVSKON-DXN-NwVpw'),
|
||||
mock.call.getHostVLUNs(self.FAKE_HOST),
|
||||
mock.call.getPorts(),
|
||||
mock.call.logout()]
|
||||
|
||||
@ -1015,7 +1021,10 @@ class TestHP3PARFCDriver(HP3PARBaseDriver, test.TestCase):
|
||||
# setup_mock_client drive with default configuration
|
||||
# and return the mock HTTP 3PAR client
|
||||
mock_client = self.setup_driver()
|
||||
mock_client.getVLUN.return_value = {'lun': None, 'type': 0}
|
||||
mock_client.getHostVLUNs.return_value = [
|
||||
{'active': True,
|
||||
'volumeName': self.VOLUME_3PAR_NAME,
|
||||
'lun': None, 'type': 0}]
|
||||
mock_client.getPorts.return_value = {
|
||||
'members': self.FAKE_FC_PORTS + [self.FAKE_ISCSI_PORT]}
|
||||
|
||||
@ -1026,7 +1035,7 @@ class TestHP3PARFCDriver(HP3PARBaseDriver, test.TestCase):
|
||||
|
||||
expected = [
|
||||
mock.call.login(HP3PAR_USER_NAME, HP3PAR_USER_PASS),
|
||||
mock.call.getVLUN(self.VOLUME_3PAR_NAME),
|
||||
mock.call.getHostVLUNs(self.FAKE_HOST),
|
||||
mock.call.deleteVLUN(
|
||||
self.VOLUME_3PAR_NAME,
|
||||
None,
|
||||
@ -1304,23 +1313,26 @@ class TestHP3PARISCSIDriver(HP3PARBaseDriver, test.TestCase):
|
||||
hpexceptions.HTTPNotFound('fake'),
|
||||
{'name': self.FAKE_HOST}]
|
||||
mock_client.findHost.return_value = self.FAKE_HOST
|
||||
mock_client.getVLUN.return_value = {'lun': self.TARGET_LUN}
|
||||
mock_client.getHostVLUNs.return_value = [
|
||||
{'active': True,
|
||||
'volumeName': self.VOLUME_3PAR_NAME,
|
||||
'lun': self.TARGET_LUN, 'type': 0}]
|
||||
|
||||
result = self.driver.initialize_connection(self.volume, self.connector)
|
||||
|
||||
expected = [
|
||||
mock.call.login(HP3PAR_USER_NAME, HP3PAR_USER_PASS),
|
||||
mock.call.getVolume('osv-0DM4qZEVSKON-DXN-NwVpw'),
|
||||
mock.call.getVolume(self.VOLUME_3PAR_NAME),
|
||||
mock.call.getCPG(HP3PAR_CPG),
|
||||
mock.call.getHost(self.FAKE_HOST),
|
||||
mock.call.findHost(iqn='iqn.1993-08.org.debian:01:222'),
|
||||
mock.call.getHost(self.FAKE_HOST),
|
||||
mock.call.createVLUN(
|
||||
'osv-0DM4qZEVSKON-DXN-NwVpw',
|
||||
self.VOLUME_3PAR_NAME,
|
||||
auto=True,
|
||||
hostname='fakehost',
|
||||
portPos={'node': 8, 'slot': 1, 'cardPort': 1}),
|
||||
mock.call.getVLUN('osv-0DM4qZEVSKON-DXN-NwVpw'),
|
||||
mock.call.getHostVLUNs(self.FAKE_HOST),
|
||||
mock.call.logout()]
|
||||
|
||||
mock_client.assert_has_calls(expected)
|
||||
|
@ -121,10 +121,11 @@ class HP3PARCommon(object):
|
||||
2.0.5 - Fix extend volume units bug #1284368
|
||||
2.0.6 - use loopingcall.wait instead of time.sleep
|
||||
2.0.7 - Allow extend volume based on snapshot bug #1285906
|
||||
2.0.8 - Fix detach issue for multiple hosts Bug #1288927
|
||||
|
||||
"""
|
||||
|
||||
VERSION = "2.0.7"
|
||||
VERSION = "2.0.8"
|
||||
|
||||
stats = {}
|
||||
|
||||
@ -450,6 +451,21 @@ class HP3PARCommon(object):
|
||||
'hp3par_cpg')})
|
||||
self.stats = stats
|
||||
|
||||
def _get_vlun(self, volume_name, hostname):
|
||||
"""find a VLUN on a 3PAR host."""
|
||||
vluns = self.client.getHostVLUNs(hostname)
|
||||
found_vlun = None
|
||||
for vlun in vluns:
|
||||
if volume_name in vlun['volumeName']:
|
||||
found_vlun = vlun
|
||||
break
|
||||
|
||||
msg = (_("3PAR vlun %(name)s not found on host %(host)s") %
|
||||
{'name': volume_name, 'host': hostname})
|
||||
if found_vlun is None:
|
||||
LOG.warn(msg)
|
||||
return found_vlun
|
||||
|
||||
def create_vlun(self, volume, host, nsp=None):
|
||||
"""Create a VLUN.
|
||||
|
||||
@ -457,17 +473,19 @@ class HP3PARCommon(object):
|
||||
"""
|
||||
volume_name = self._get_3par_vol_name(volume['id'])
|
||||
self._create_3par_vlun(volume_name, host['name'], nsp)
|
||||
return self.client.getVLUN(volume_name)
|
||||
return self._get_vlun(volume_name, host['name'])
|
||||
|
||||
def delete_vlun(self, volume, hostname):
|
||||
volume_name = self._get_3par_vol_name(volume['id'])
|
||||
vlun = self.client.getVLUN(volume_name)
|
||||
# VLUN Type of MATCHED_SET 4 requires the port to be provided
|
||||
if self.VLUN_TYPE_MATCHED_SET == vlun['type']:
|
||||
self.client.deleteVLUN(volume_name, vlun['lun'], hostname,
|
||||
vlun['portPos'])
|
||||
else:
|
||||
self.client.deleteVLUN(volume_name, vlun['lun'], hostname)
|
||||
vlun = self._get_vlun(volume_name, hostname)
|
||||
|
||||
if vlun is not None:
|
||||
# VLUN Type of MATCHED_SET 4 requires the port to be provided
|
||||
if self.VLUN_TYPE_MATCHED_SET == vlun['type']:
|
||||
self.client.deleteVLUN(volume_name, vlun['lun'], hostname,
|
||||
vlun['portPos'])
|
||||
else:
|
||||
self.client.deleteVLUN(volume_name, vlun['lun'], hostname)
|
||||
|
||||
try:
|
||||
self._delete_3par_host(hostname)
|
||||
|
Loading…
Reference in New Issue
Block a user