3PAR: Add force detach
support
Add support to force detach a volume from all hosts on 3PAR. Change-Id: I2ddd0be0d59018db43dca297585d5cb2ee459ede Closes-bug: #1686745
This commit is contained in:
parent
b53574cfb7
commit
abca1abc7b
@ -7015,6 +7015,45 @@ class TestHPE3PARFCDriver(HPE3PARBaseDriver):
|
|||||||
expected +
|
expected +
|
||||||
self.standard_logout)
|
self.standard_logout)
|
||||||
|
|
||||||
|
def test_force_detach_volume(self):
|
||||||
|
# setup_mock_client drive with default configuration
|
||||||
|
# and return the mock HTTP 3PAR client
|
||||||
|
mock_client = self.setup_driver()
|
||||||
|
|
||||||
|
mock_client.getVLUNs.return_value = {
|
||||||
|
'members': [{
|
||||||
|
'active': False,
|
||||||
|
'volumeName': self.VOLUME_3PAR_NAME,
|
||||||
|
'hostname': self.FAKE_HOST,
|
||||||
|
'lun': None, 'type': 0}]}
|
||||||
|
|
||||||
|
mock_client.queryHost.return_value = {
|
||||||
|
'members': [{
|
||||||
|
'name': self.FAKE_HOST
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
|
||||||
|
mock_client.getHostVLUNs.side_effect = hpeexceptions.HTTPNotFound
|
||||||
|
|
||||||
|
expected = [
|
||||||
|
mock.call.getVLUNs(),
|
||||||
|
mock.call.deleteVLUN(
|
||||||
|
self.VOLUME_3PAR_NAME,
|
||||||
|
None,
|
||||||
|
hostname=self.FAKE_HOST),
|
||||||
|
mock.call.getHostVLUNs(self.FAKE_HOST),
|
||||||
|
mock.call.deleteHost(self.FAKE_HOST)]
|
||||||
|
|
||||||
|
with mock.patch.object(hpecommon.HPE3PARCommon,
|
||||||
|
'_create_client') as mock_create_client:
|
||||||
|
mock_create_client.return_value = mock_client
|
||||||
|
self.driver.terminate_connection(self.volume, None)
|
||||||
|
|
||||||
|
mock_client.assert_has_calls(
|
||||||
|
self.standard_login +
|
||||||
|
expected +
|
||||||
|
self.standard_logout)
|
||||||
|
|
||||||
@mock.patch('cinder.zonemanager.utils.create_lookup_service')
|
@mock.patch('cinder.zonemanager.utils.create_lookup_service')
|
||||||
def test_terminate_connection_with_lookup(self, mock_lookup):
|
def test_terminate_connection_with_lookup(self, mock_lookup):
|
||||||
# setup_mock_client drive with default configuration
|
# setup_mock_client drive with default configuration
|
||||||
|
@ -265,11 +265,12 @@ class HPE3PARCommon(object):
|
|||||||
differs from volume present in the source group in terms of
|
differs from volume present in the source group in terms of
|
||||||
extra-specs. bug #1744025
|
extra-specs. bug #1744025
|
||||||
4.0.6 - Monitor task of promoting a virtual copy. bug #1749642
|
4.0.6 - Monitor task of promoting a virtual copy. bug #1749642
|
||||||
|
4.0.7 - Handle force detach case. bug #1686745
|
||||||
|
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
VERSION = "4.0.6"
|
VERSION = "4.0.7"
|
||||||
|
|
||||||
stats = {}
|
stats = {}
|
||||||
|
|
||||||
@ -1659,7 +1660,11 @@ class HPE3PARCommon(object):
|
|||||||
|
|
||||||
def delete_vlun(self, volume, hostname, wwn=None, iqn=None):
|
def delete_vlun(self, volume, hostname, wwn=None, iqn=None):
|
||||||
volume_name = self._get_3par_vol_name(volume['id'])
|
volume_name = self._get_3par_vol_name(volume['id'])
|
||||||
vluns = self.client.getHostVLUNs(hostname)
|
if hostname:
|
||||||
|
vluns = self.client.getHostVLUNs(hostname)
|
||||||
|
else:
|
||||||
|
# In case of 'force detach', hostname is None
|
||||||
|
vluns = self.client.getVLUNs()['members']
|
||||||
|
|
||||||
# When deleteing VLUNs, you simply need to remove the template VLUN
|
# When deleteing VLUNs, you simply need to remove the template VLUN
|
||||||
# and any active VLUNs will be automatically removed. The template
|
# and any active VLUNs will be automatically removed. The template
|
||||||
@ -1681,6 +1686,8 @@ class HPE3PARCommon(object):
|
|||||||
|
|
||||||
# VLUN Type of MATCHED_SET 4 requires the port to be provided
|
# VLUN Type of MATCHED_SET 4 requires the port to be provided
|
||||||
for vlun in volume_vluns:
|
for vlun in volume_vluns:
|
||||||
|
if hostname is None:
|
||||||
|
hostname = vlun.get('hostname')
|
||||||
if 'portPos' in vlun:
|
if 'portPos' in vlun:
|
||||||
self.client.deleteVLUN(volume_name, vlun['lun'],
|
self.client.deleteVLUN(volume_name, vlun['lun'],
|
||||||
hostname=hostname,
|
hostname=hostname,
|
||||||
@ -1721,6 +1728,8 @@ class HPE3PARCommon(object):
|
|||||||
# host, so it is worth the unlikely risk.
|
# host, so it is worth the unlikely risk.
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
# TODO(sonivi): since multiattach is not supported for now,
|
||||||
|
# delete only single host, if its not exported to volume.
|
||||||
self._delete_3par_host(hostname)
|
self._delete_3par_host(hostname)
|
||||||
except Exception as ex:
|
except Exception as ex:
|
||||||
# Any exception down here is only logged. The vlun is deleted.
|
# Any exception down here is only logged. The vlun is deleted.
|
||||||
@ -2928,8 +2937,9 @@ class HPE3PARCommon(object):
|
|||||||
elif iqn:
|
elif iqn:
|
||||||
hosts = self.client.queryHost(iqns=[iqn])
|
hosts = self.client.queryHost(iqns=[iqn])
|
||||||
|
|
||||||
if hosts and hosts['members'] and 'name' in hosts['members'][0]:
|
if hosts is not None:
|
||||||
hostname = hosts['members'][0]['name']
|
if hosts and hosts['members'] and 'name' in hosts['members'][0]:
|
||||||
|
hostname = hosts['members'][0]['name']
|
||||||
|
|
||||||
try:
|
try:
|
||||||
self.delete_vlun(volume, hostname, wwn=wwn, iqn=iqn)
|
self.delete_vlun(volume, hostname, wwn=wwn, iqn=iqn)
|
||||||
@ -2950,12 +2960,19 @@ class HPE3PARCommon(object):
|
|||||||
"secondary target.")
|
"secondary target.")
|
||||||
return
|
return
|
||||||
else:
|
else:
|
||||||
# use the wwn to see if we can find the hostname
|
if hosts is None:
|
||||||
hostname = self._get_3par_hostname_from_wwn_iqn(wwn, iqn)
|
# In case of 'force detach', hosts is None
|
||||||
# no 3par host, re-throw
|
LOG.exception("Exception: %s", e)
|
||||||
if hostname is None:
|
|
||||||
LOG.error("Exception: %s", e)
|
|
||||||
raise
|
raise
|
||||||
|
else:
|
||||||
|
# use the wwn to see if we can find the hostname
|
||||||
|
hostname = self._get_3par_hostname_from_wwn_iqn(
|
||||||
|
wwn,
|
||||||
|
iqn)
|
||||||
|
# no 3par host, re-throw
|
||||||
|
if hostname is None:
|
||||||
|
LOG.exception("Exception: %s", e)
|
||||||
|
raise
|
||||||
else:
|
else:
|
||||||
# not a 'host does not exist' HTTPNotFound exception, re-throw
|
# not a 'host does not exist' HTTPNotFound exception, re-throw
|
||||||
LOG.error("Exception: %s", e)
|
LOG.error("Exception: %s", e)
|
||||||
|
@ -108,10 +108,11 @@ class HPE3PARFCDriver(hpebasedriver.HPE3PARDriverBase):
|
|||||||
4.0.1 - Added check to remove FC zones. bug #1730720
|
4.0.1 - Added check to remove FC zones. bug #1730720
|
||||||
4.0.2 - Create one vlun in single path configuration. bug #1727176
|
4.0.2 - Create one vlun in single path configuration. bug #1727176
|
||||||
4.0.3 - Create FC vlun as host sees. bug #1734505
|
4.0.3 - Create FC vlun as host sees. bug #1734505
|
||||||
|
4.0.4 - Handle force detach case. bug #1686745
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
VERSION = "4.0.3"
|
VERSION = "4.0.4"
|
||||||
|
|
||||||
# The name of the CI wiki page.
|
# The name of the CI wiki page.
|
||||||
CI_WIKI_NAME = "HPE_Storage_CI"
|
CI_WIKI_NAME = "HPE_Storage_CI"
|
||||||
@ -209,28 +210,35 @@ class HPE3PARFCDriver(hpebasedriver.HPE3PARDriverBase):
|
|||||||
"""Driver entry point to unattach a volume from an instance."""
|
"""Driver entry point to unattach a volume from an instance."""
|
||||||
common = self._login()
|
common = self._login()
|
||||||
try:
|
try:
|
||||||
hostname = common._safe_hostname(connector['host'])
|
is_force_detach = connector is None
|
||||||
common.terminate_connection(volume, hostname,
|
if is_force_detach:
|
||||||
wwn=connector['wwpns'])
|
common.terminate_connection(volume, None, None)
|
||||||
|
# TODO(sonivi): remove zones, if not required
|
||||||
|
# for now, do not remove zones
|
||||||
|
zone_remove = False
|
||||||
|
else:
|
||||||
|
hostname = common._safe_hostname(connector['host'])
|
||||||
|
common.terminate_connection(volume, hostname,
|
||||||
|
wwn=connector['wwpns'])
|
||||||
|
|
||||||
|
zone_remove = True
|
||||||
|
try:
|
||||||
|
vluns = common.client.getHostVLUNs(hostname)
|
||||||
|
except hpeexceptions.HTTPNotFound:
|
||||||
|
# No more exports for this host.
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
# Vlun exists, so check for wwpn entry.
|
||||||
|
for wwpn in connector.get('wwpns'):
|
||||||
|
for vlun in vluns:
|
||||||
|
if (vlun.get('active') and
|
||||||
|
vlun.get('remoteName') == wwpn.upper()):
|
||||||
|
zone_remove = False
|
||||||
|
break
|
||||||
|
|
||||||
info = {'driver_volume_type': 'fibre_channel',
|
info = {'driver_volume_type': 'fibre_channel',
|
||||||
'data': {}}
|
'data': {}}
|
||||||
|
|
||||||
zone_remove = True
|
|
||||||
try:
|
|
||||||
vluns = common.client.getHostVLUNs(hostname)
|
|
||||||
except hpeexceptions.HTTPNotFound:
|
|
||||||
# No more exports for this host.
|
|
||||||
pass
|
|
||||||
else:
|
|
||||||
# Vlun exists, so check for wwpn entry.
|
|
||||||
for wwpn in connector.get('wwpns'):
|
|
||||||
for vlun in vluns:
|
|
||||||
if vlun.get('active') and \
|
|
||||||
vlun.get('remoteName') == wwpn.upper():
|
|
||||||
zone_remove = False
|
|
||||||
break
|
|
||||||
|
|
||||||
if zone_remove:
|
if zone_remove:
|
||||||
LOG.info("Need to remove FC Zone, building initiator "
|
LOG.info("Need to remove FC Zone, building initiator "
|
||||||
"target map")
|
"target map")
|
||||||
|
@ -122,10 +122,11 @@ class HPE3PARISCSIDriver(hpebasedriver.HPE3PARDriverBase):
|
|||||||
4.0.0 - Adds base class.
|
4.0.0 - Adds base class.
|
||||||
4.0.1 - Update CHAP on host record when volume is migrated
|
4.0.1 - Update CHAP on host record when volume is migrated
|
||||||
to new compute host. bug # 1737181
|
to new compute host. bug # 1737181
|
||||||
|
4.0.2 - Handle force detach case. bug #1686745
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
VERSION = "4.0.1"
|
VERSION = "4.0.2"
|
||||||
|
|
||||||
# The name of the CI wiki page.
|
# The name of the CI wiki page.
|
||||||
CI_WIKI_NAME = "HPE_Storage_CI"
|
CI_WIKI_NAME = "HPE_Storage_CI"
|
||||||
@ -364,11 +365,15 @@ class HPE3PARISCSIDriver(hpebasedriver.HPE3PARDriverBase):
|
|||||||
"""Driver entry point to unattach a volume from an instance."""
|
"""Driver entry point to unattach a volume from an instance."""
|
||||||
common = self._login()
|
common = self._login()
|
||||||
try:
|
try:
|
||||||
hostname = common._safe_hostname(connector['host'])
|
is_force_detach = connector is None
|
||||||
common.terminate_connection(
|
if is_force_detach:
|
||||||
volume,
|
common.terminate_connection(volume, None, None)
|
||||||
hostname,
|
else:
|
||||||
iqn=connector['initiator'])
|
hostname = common._safe_hostname(connector['host'])
|
||||||
|
common.terminate_connection(
|
||||||
|
volume,
|
||||||
|
hostname,
|
||||||
|
iqn=connector['initiator'])
|
||||||
self._clear_chap_3par(common, volume)
|
self._clear_chap_3par(common, volume)
|
||||||
finally:
|
finally:
|
||||||
self._logout(common)
|
self._logout(common)
|
||||||
|
4
releasenotes/notes/bug-1686745-e8f1569455f998ba.yaml
Normal file
4
releasenotes/notes/bug-1686745-e8f1569455f998ba.yaml
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
---
|
||||||
|
features:
|
||||||
|
- |
|
||||||
|
Add support to force detach a volume from all hosts on 3PAR.
|
Loading…
x
Reference in New Issue
Block a user