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:
Vivek Soni 2018-03-01 01:37:58 -08:00
parent b53574cfb7
commit abca1abc7b
5 changed files with 107 additions and 34 deletions

View File

@ -7015,6 +7015,45 @@ class TestHPE3PARFCDriver(HPE3PARBaseDriver):
expected +
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')
def test_terminate_connection_with_lookup(self, mock_lookup):
# setup_mock_client drive with default configuration

View File

@ -265,11 +265,12 @@ class HPE3PARCommon(object):
differs from volume present in the source group in terms of
extra-specs. bug #1744025
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 = {}
@ -1659,7 +1660,11 @@ class HPE3PARCommon(object):
def delete_vlun(self, volume, hostname, wwn=None, iqn=None):
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
# 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
for vlun in volume_vluns:
if hostname is None:
hostname = vlun.get('hostname')
if 'portPos' in vlun:
self.client.deleteVLUN(volume_name, vlun['lun'],
hostname=hostname,
@ -1721,6 +1728,8 @@ class HPE3PARCommon(object):
# host, so it is worth the unlikely risk.
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)
except Exception as ex:
# Any exception down here is only logged. The vlun is deleted.
@ -2928,8 +2937,9 @@ class HPE3PARCommon(object):
elif iqn:
hosts = self.client.queryHost(iqns=[iqn])
if hosts and hosts['members'] and 'name' in hosts['members'][0]:
hostname = hosts['members'][0]['name']
if hosts is not None:
if hosts and hosts['members'] and 'name' in hosts['members'][0]:
hostname = hosts['members'][0]['name']
try:
self.delete_vlun(volume, hostname, wwn=wwn, iqn=iqn)
@ -2950,12 +2960,19 @@ class HPE3PARCommon(object):
"secondary target.")
return
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.error("Exception: %s", e)
if hosts is None:
# In case of 'force detach', hosts is None
LOG.exception("Exception: %s", e)
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:
# not a 'host does not exist' HTTPNotFound exception, re-throw
LOG.error("Exception: %s", e)

View File

@ -108,10 +108,11 @@ class HPE3PARFCDriver(hpebasedriver.HPE3PARDriverBase):
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.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.
CI_WIKI_NAME = "HPE_Storage_CI"
@ -209,28 +210,35 @@ class HPE3PARFCDriver(hpebasedriver.HPE3PARDriverBase):
"""Driver entry point to unattach a volume from an instance."""
common = self._login()
try:
hostname = common._safe_hostname(connector['host'])
common.terminate_connection(volume, hostname,
wwn=connector['wwpns'])
is_force_detach = connector is None
if is_force_detach:
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',
'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:
LOG.info("Need to remove FC Zone, building initiator "
"target map")

View File

@ -122,10 +122,11 @@ class HPE3PARISCSIDriver(hpebasedriver.HPE3PARDriverBase):
4.0.0 - Adds base class.
4.0.1 - Update CHAP on host record when volume is migrated
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.
CI_WIKI_NAME = "HPE_Storage_CI"
@ -364,11 +365,15 @@ class HPE3PARISCSIDriver(hpebasedriver.HPE3PARDriverBase):
"""Driver entry point to unattach a volume from an instance."""
common = self._login()
try:
hostname = common._safe_hostname(connector['host'])
common.terminate_connection(
volume,
hostname,
iqn=connector['initiator'])
is_force_detach = connector is None
if is_force_detach:
common.terminate_connection(volume, None, None)
else:
hostname = common._safe_hostname(connector['host'])
common.terminate_connection(
volume,
hostname,
iqn=connector['initiator'])
self._clear_chap_3par(common, volume)
finally:
self._logout(common)

View File

@ -0,0 +1,4 @@
---
features:
- |
Add support to force detach a volume from all hosts on 3PAR.