Merge "[VNX]Add `force detach` support" into stable/pike

This commit is contained in:
Zuul 2018-04-04 06:49:15 +00:00 committed by Gerrit Code Review
commit 181f90f4cf
7 changed files with 153 additions and 19 deletions

View File

@ -82,7 +82,11 @@ group: &group_base
status: 'creating'
replication_status: 'enabled'
connector: &connector_base
_properties:
host: host_1
initiator: ['iqn.2012-07.org.fake:01']
ip: 192.168.1.111
###########################################################
# TestCommonAdapter, TestISCSIAdapter, TestFCAdapter
@ -331,6 +335,13 @@ test_auto_register_initiator_no_white_list:
test_auto_register_initiator_no_port_to_reg:
volume: *volume_base
test_terminate_connection:
volume: *volume_base
connector: *connector_base
test_terminate_connection_force_detach:
volume: *volume_base
test_remove_host_access:
volume: *volume_base

View File

@ -1853,6 +1853,29 @@ test_build_provider_location:
_properties:
serial: 'vnx-serial'
test_terminate_connection:
sg: &sg_terminate_connection
_properties:
existed: True
vnx:
_methods:
get_sg: *sg_terminate_connection
test_terminate_connection_force_detach:
sg: &sg_terminate_connection_force_detach_1
_properties:
existed: True
sg: &sg_terminate_connection_force_detach_2
_properties:
existed: True
sgs: &sgs_terminate_connection_force_detach
_methods:
shadow_copy: [*sg_terminate_connection_force_detach_1,
*sg_terminate_connection_force_detach_2]
vnx:
_methods:
get_sg: *sgs_terminate_connection_force_detach
test_remove_host_access:
sg: &sg_remove_host_access
_properties:

View File

@ -1520,6 +1520,41 @@ class TestISCSIAdapter(test.TestCase):
self.assertRaises(exception.InvalidConfigurationValue,
vnx_iscsi._normalize_config)
@res_mock.mock_driver_input
@res_mock.patch_iscsi_adapter
def test_terminate_connection(self, adapter, mocked_res, mocked_input):
cinder_volume = mocked_input['volume']
connector = mocked_input['connector']
adapter.remove_host_access = mock.Mock()
adapter.update_storage_group_if_required = mock.Mock()
adapter.build_terminate_connection_return_data = mock.Mock()
adapter.terminate_connection_cleanup = mock.Mock()
adapter.terminate_connection(cinder_volume, connector)
adapter.remove_host_access.assert_called_once()
adapter.update_storage_group_if_required.assert_called_once()
adapter.build_terminate_connection_return_data \
.assert_called_once()
adapter.terminate_connection_cleanup.assert_called_once()
@res_mock.mock_driver_input
@res_mock.patch_iscsi_adapter
def test_terminate_connection_force_detach(self, adapter, mocked_res,
mocked_input):
cinder_volume = mocked_input['volume']
connector = None
adapter.remove_host_access = mock.Mock()
adapter.update_storage_group_if_required = mock.Mock()
adapter.build_terminate_connection_return_data = mock.Mock()
adapter.terminate_connection_cleanup = mock.Mock()
adapter.terminate_connection(cinder_volume, connector)
adapter.remove_host_access.assert_called()
adapter.update_storage_group_if_required.assert_called()
adapter.build_terminate_connection_return_data \
.assert_not_called()
adapter.terminate_connection_cleanup.assert_called()
class TestFCAdapter(test.TestCase):
STORAGE_PROTOCOL = common.PROTOCOL_FC
@ -1629,3 +1664,38 @@ class TestFCAdapter(test.TestCase):
self.assertEqual({'wwn1': ['5006016636E01CB2']}, tgt_map)
get_mapping.assert_called_once_with(
['wwn1', 'wwn2'], ['5006016636E01CB2'])
@res_mock.mock_driver_input
@res_mock.patch_iscsi_adapter
def test_terminate_connection(self, adapter, mocked_res, mocked_input):
cinder_volume = mocked_input['volume']
connector = mocked_input['connector']
adapter.remove_host_access = mock.Mock()
adapter.update_storage_group_if_required = mock.Mock()
adapter.build_terminate_connection_return_data = mock.Mock()
adapter.terminate_connection_cleanup = mock.Mock()
adapter.terminate_connection(cinder_volume, connector)
adapter.remove_host_access.assert_called_once()
adapter.update_storage_group_if_required.assert_called_once()
adapter.build_terminate_connection_return_data \
.assert_called_once()
adapter.terminate_connection_cleanup.assert_called_once()
@res_mock.mock_driver_input
@res_mock.patch_iscsi_adapter
def test_terminate_connection_force_detach(self, adapter, mocked_res,
mocked_input):
cinder_volume = mocked_input['volume']
connector = None
adapter.remove_host_access = mock.Mock()
adapter.update_storage_group_if_required = mock.Mock()
adapter.build_terminate_connection_return_data = mock.Mock()
adapter.terminate_connection_cleanup = mock.Mock()
adapter.terminate_connection(cinder_volume, connector)
adapter.remove_host_access.assert_called()
adapter.update_storage_group_if_required.assert_called()
adapter.build_terminate_connection_return_data \
.assert_not_called()
adapter.terminate_connection_cleanup.assert_called()

View File

@ -1010,19 +1010,32 @@ class CommonAdapter(replication.ReplicationAdapter):
:param volume: `common.Volume` object with volume information.
:param connector: connector information from Nova.
"""
host = self.build_host(connector)
sg = self.client.get_storage_group(host.name)
self.remove_host_access(volume, host, sg)
# None `connector` means force detach the volume from all hosts.
is_force_detach = False
if connector is None:
LOG.info('Force detaching volume %s from all hosts.', volume.name)
is_force_detach = True
# build_terminate_connection return data should go before
# terminate_connection_cleanup. The storage group may be deleted in
# the terminate_connection_cleanup which is needed during getting
# return data
self.update_storage_group_if_required(sg)
re = self.build_terminate_connection_return_data(host, sg)
self.terminate_connection_cleanup(host, sg)
host = None if is_force_detach else self.build_host(connector)
sg_list = (self.client.filter_sg(volume.vnx_lun_id) if is_force_detach
else [self.client.get_storage_group(host.name)])
return re
return_data = None
for sg in sg_list:
self.remove_host_access(volume, host, sg)
# build_terminate_connection return data should go before
# terminate_connection_cleanup. The storage group may be deleted in
# the terminate_connection_cleanup which is needed during getting
# return data
self.update_storage_group_if_required(sg)
if not is_force_detach:
# force detach will return None
return_data = self.build_terminate_connection_return_data(
host, sg)
self.terminate_connection_cleanup(host, sg)
return return_data
def update_storage_group_if_required(self, sg):
if sg.existed and self.destroy_empty_sg:
@ -1036,17 +1049,19 @@ class CommonAdapter(replication.ReplicationAdapter):
:param sg: object of `storops` storage group.
"""
lun = self.client.get_lun(lun_id=volume.vnx_lun_id)
hostname = host.name
if not sg.existed:
LOG.warning("Storage Group %s is not found. "
"Nothing can be done in terminate_connection().",
hostname)
# `host` is None when force-detach
if host is not None:
# Only print this warning message when normal detach
LOG.warning("Storage Group %s is not found. "
"Nothing can be done in terminate_connection().",
host.name)
else:
try:
sg.detach_alu(lun)
except storops_ex.VNXDetachAluNotFoundError:
LOG.warning("Volume %(vol)s is not in Storage Group %(sg)s.",
{'vol': volume.name, 'sg': hostname})
{'vol': volume.name, 'sg': sg.name})
def build_terminate_connection_return_data(self, host, sg):
raise NotImplementedError()
@ -1064,7 +1079,8 @@ class CommonAdapter(replication.ReplicationAdapter):
LOG.info("Storage Group %s is empty.", sg.name)
sg.disconnect_host(sg.name)
sg.delete()
if self.itor_auto_dereg:
if host is not None and self.itor_auto_dereg:
# `host` is None when force-detach
self._deregister_initiator(host)
except storops_ex.StoropsException:
LOG.warning("Failed to destroy Storage Group %s.",

View File

@ -726,3 +726,6 @@ class Client(object):
def add_lun_to_ioclass(self, ioclass_name, lun_id):
ioclass = self.vnx.get_ioclass(name=ioclass_name)
ioclass.add_lun(lun_id)
def filter_sg(self, attached_lun_id):
return self.vnx.get_sg().shadow_copy(attached_lun=attached_lun_id)

View File

@ -16,7 +16,7 @@ System requirements
- VNX Operational Environment for Block version 5.32 or higher.
- VNX Snapshot and Thin Provisioning license should be activated for VNX.
- Python library ``storops`` to interact with VNX.
- Python library ``storops`` version 0.5.7 or higher to interact with VNX.
- Navisphere CLI v7.32 or higher is installed along with the driver.
Supported operations
@ -599,6 +599,13 @@ Obsolete extra specs
- ``storagetype:provisioning``
- ``storagetype:pool``
Force detach
------------
The user could use `os-force_detach` action to detach a volume from all its attached hosts.
For more detail, please refer to
https://developer.openstack.org/api-ref/block-storage/v2/?expanded=force-detach-volume-detail#force-detach-volume
Advanced features
~~~~~~~~~~~~~~~~~

View File

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