HPE 3PAR - Fix detach of multiattach volumes

Currently, volume detach call for the first instance deletes the luns
and host entry from 3par and due to this the other instances still
attached to volume loose the connectivity. This causes the volume to
go in 'detaching' state.

Change-Id: I31658cf986602d056978c29e12c0fc3cdbac7150
Closes-bug: #1834660
This commit is contained in:
Sneha Rai 2019-07-03 04:03:53 +05:30
parent 9dd5232ea5
commit ee8a2d8e99
6 changed files with 93 additions and 27 deletions

View File

@ -19,6 +19,7 @@ import copy
import mock
from oslo_utils import units
from oslo_utils import uuidutils
from cinder import context
from cinder import exception
@ -155,13 +156,25 @@ class HPE3PARBaseDriver(test.TestCase):
'iSCSIName': ('iqn.2000-05.com.3pardata:'
'21810002ac00383d'),
'linkState': 4}
volume = {'name': VOLUME_NAME,
'id': VOLUME_ID,
'display_name': 'Foo Volume',
'size': 2,
'host': FAKE_CINDER_HOST,
'volume_type': None,
'volume_type_id': None}
volume_snapshot = {'name': VOLUME_NAME,
'id': VOLUME_ID,
'display_name': 'Foo Volume',
'size': 2,
'host': FAKE_CINDER_HOST,
'volume_type': None,
'volume_type_id': None}
volume = fake_volume.fake_volume_obj(
context.get_admin_context(),
name=VOLUME_NAME,
id=VOLUME_ID,
display_name='Foo Volume',
size=2,
host=FAKE_CINDER_HOST,
volume_type=None,
volume_type_id=None,
multiattach=False)
volume_src_cg = {'name': SRC_CG_VOLUME_NAME,
'id': SRC_CG_VOLUME_ID,
@ -205,14 +218,16 @@ class HPE3PARBaseDriver(test.TestCase):
list_rep_targets = [{'backend_id': 'target'}]
volume_encrypted = {'name': VOLUME_NAME,
'id': VOLUME_ID,
'display_name': 'Foo Volume',
'size': 2,
'host': FAKE_CINDER_HOST,
'volume_type': None,
'volume_type_id': None,
'encryption_key_id': 'fake_key'}
volume_encrypted = fake_volume.fake_volume_obj(
context.get_admin_context(),
name=VOLUME_NAME,
id=VOLUME_ID,
display_name='Foo Volume',
size=2,
host=FAKE_CINDER_HOST,
volume_type=None,
volume_type_id=None,
encryption_key_id=uuidutils.generate_uuid())
volume_dedup_compression = {'name': VOLUME_NAME,
'id': VOLUME_ID,
@ -2046,7 +2061,7 @@ class TestHPE3PARDriverBase(HPE3PARBaseDriver):
self.standard_logout +
self.standard_login +
expected +
self.standard_logout, any_order =True)
self.standard_logout, any_order=True)
@mock.patch.object(volume_types, 'get_volume_type')
def test_retype_rep_type_to_rep_type(self, _mock_volume_types):
@ -2162,7 +2177,7 @@ class TestHPE3PARDriverBase(HPE3PARBaseDriver):
self.standard_logout +
self.standard_login +
expected +
self.standard_logout, any_order =True)
self.standard_logout, any_order=True)
@mock.patch.object(volume_types, 'get_volume_type')
def test_retype_qos_spec(self, _mock_volume_types):
@ -2273,7 +2288,7 @@ class TestHPE3PARDriverBase(HPE3PARBaseDriver):
self.standard_logout)
def _FakeRetrying(wait_func=None,
original_retrying = hpecommon.utils.retrying.Retrying,
original_retrying=hpecommon.utils.retrying.Retrying,
*args, **kwargs):
return original_retrying(wait_func=lambda *a, **k: 0,
*args, **kwargs)
@ -3235,7 +3250,7 @@ class TestHPE3PARDriverBase(HPE3PARBaseDriver):
'_create_client') as mock_create_client:
mock_create_client.return_value = mock_client
self.driver._login()
volume = self.volume.copy()
volume = self.volume_snapshot.copy()
model_update = self.driver.create_volume_from_snapshot(
volume,
self.snapshot)
@ -3295,7 +3310,7 @@ class TestHPE3PARDriverBase(HPE3PARBaseDriver):
'_create_client') as mock_create_client:
mock_create_client.return_value = mock_client
self.driver._login()
volume = self.volume.copy()
volume = self.volume_snapshot.copy()
model_update = self.driver.create_volume_from_snapshot(
volume,
self.snapshot)
@ -3336,7 +3351,7 @@ class TestHPE3PARDriverBase(HPE3PARBaseDriver):
mock_create_client.return_value = mock_client
common = self.driver._login()
volume = self.volume.copy()
volume = self.volume_snapshot.copy()
volume['size'] = self.volume['size'] + 10
model_update = self.driver.create_volume_from_snapshot(
volume,
@ -3455,7 +3470,7 @@ class TestHPE3PARDriverBase(HPE3PARBaseDriver):
with mock.patch.object(hpecommon.HPE3PARCommon,
'_create_client') as mock_create_client:
mock_create_client.return_value = mock_client
volume = self.volume.copy()
volume = self.volume_snapshot.copy()
volume['size'] = self.volume['size'] + 10
self.assertRaises(exception.CinderException,
@ -10122,6 +10137,29 @@ class TestHPE3PARISCSIDriver(HPE3PARBaseDriver):
def test_migrate_volume_attached(self):
self.migrate_volume_attached()
def test_terminate_connection_multiattach(self):
ctx = context.get_admin_context()
mock_client = self.setup_driver()
att_1 = fake_volume.fake_volume_attachment_obj(
ctx, id=uuidutils.generate_uuid())
att_2 = fake_volume.fake_volume_attachment_obj(
ctx, id=uuidutils.generate_uuid())
volume = fake_volume.fake_volume_obj(
ctx, multiattach=True, host=self.FAKE_CINDER_HOST)
volume.volume_attachment.objects = [att_1, att_2]
with mock.patch.object(hpecommon.HPE3PARCommon, '_create_client'
) as mock_create_client:
mock_create_client.return_value = mock_client
self.driver.terminate_connection(volume, self.connector)
# When volume is having mulitple instances attached, there
# should be no call to delete the VLUN(s) or the host. We
# can assert these methods were not called to make sure the
# proper code execution is followed.
self.assertEqual(0, mock_client.deleteVLUN.call_count)
self.assertEqual(0, mock_client.deleteHost.call_count)
def test_terminate_connection(self):
# setup_mock_client drive with default configuration
# and return the mock HTTP 3PAR client

View File

@ -279,11 +279,13 @@ class HPE3PARCommon(object):
4.0.10 - Added retry in delete_volume. bug #1783934
4.0.11 - Added extra spec hpe3par:convert_to_base
4.0.12 - Added multiattach support
4.0.13 - Fixed detaching issue for volume with type multiattach
enabled. bug #1834660
"""
VERSION = "4.0.12"
VERSION = "4.0.13"
stats = {}
@ -3022,7 +3024,24 @@ class HPE3PARCommon(object):
return host['name']
def terminate_connection(self, volume, hostname, wwn=None, iqn=None):
"""Driver entry point to unattach a volume from an instance."""
"""Driver entry point to detach a volume from an instance."""
if volume.multiattach:
attachment_list = volume.volume_attachment
LOG.debug("Volume attachment list: %(atl)s",
{'atl': attachment_list})
try:
attachment_list = attachment_list.objects
except AttributeError:
pass
if attachment_list is not None and len(attachment_list) > 1:
LOG.info("Volume %(volume)s is attached to multiple "
"instances on host %(host_name)s, "
"skip terminate volume connection",
{'volume': volume.name,
'host_name': volume.host.split('@')[0]})
return
# does 3par know this host by a different name?
hosts = None
if wwn:

View File

@ -37,6 +37,7 @@ except ImportError:
from oslo_log import log as logging
from oslo_utils.excutils import save_and_reraise_exception
from cinder import coordination
from cinder import interface
from cinder import utils
from cinder.volume.drivers.hpe import hpe_3par_base as hpebasedriver
@ -125,6 +126,7 @@ class HPE3PARFCDriver(hpebasedriver.HPE3PARDriverBase):
self.protocol = 'FC'
@utils.trace
@coordination.synchronized('3par-{volume.id}')
def initialize_connection(self, volume, connector):
"""Assigns the volume to a server.
@ -208,8 +210,9 @@ class HPE3PARFCDriver(hpebasedriver.HPE3PARDriverBase):
self._logout(common)
@utils.trace
@coordination.synchronized('3par-{volume.id}')
def terminate_connection(self, volume, connector, **kwargs):
"""Driver entry point to unattach a volume from an instance."""
"""Driver entry point to detach a volume from an instance."""
array_id = self.get_volume_replication_driver_data(volume)
common = self._login(array_id=array_id)
try:

View File

@ -38,6 +38,7 @@ except ImportError:
from oslo_log import log as logging
from oslo_utils.excutils import save_and_reraise_exception
from cinder import coordination
from cinder import exception
from cinder.i18n import _
from cinder import interface
@ -206,6 +207,7 @@ class HPE3PARISCSIDriver(hpebasedriver.HPE3PARDriverBase):
self.iscsi_ips[common._client_conf['hpe3par_api_url']] = iscsi_ip_list
@utils.trace
@coordination.synchronized('3par-{volume.id}')
def initialize_connection(self, volume, connector):
"""Assigns the volume to a server.
@ -364,8 +366,9 @@ class HPE3PARISCSIDriver(hpebasedriver.HPE3PARDriverBase):
self._logout(common)
@utils.trace
@coordination.synchronized('3par-{volume.id}')
def terminate_connection(self, volume, connector, **kwargs):
"""Driver entry point to unattach a volume from an instance."""
"""Driver entry point to detach a volume from an instance."""
array_id = self.get_volume_replication_driver_data(volume)
common = self._login(array_id=array_id)
try:

View File

@ -0,0 +1,3 @@
---
features:
- Fixed detach issue for multiattach capability in hpe3par driver.

View File

@ -727,7 +727,7 @@ driver.dell_emc_vnx=missing
driver.dell_emc_vxflexos=complete
driver.dell_emc_xtremio=complete
driver.fujitsu_eternus=missing
driver.hpe_3par=missing
driver.hpe_3par=complete
driver.hpe_lefthand=complete
driver.hpe_mmsa=missing
driver.huawei_t_v1=missing