diff --git a/cinder/tests/test_hp3par.py b/cinder/tests/test_hp3par.py index 9990d4a5fe0..d8f38273a7b 100644 --- a/cinder/tests/test_hp3par.py +++ b/cinder/tests/test_hp3par.py @@ -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) diff --git a/cinder/volume/drivers/san/hp/hp_3par_common.py b/cinder/volume/drivers/san/hp/hp_3par_common.py index 41010b30d04..b392d9f2536 100644 --- a/cinder/volume/drivers/san/hp/hp_3par_common.py +++ b/cinder/volume/drivers/san/hp/hp_3par_common.py @@ -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)