diff --git a/cinder/tests/unit/volume/drivers/dell_emc/unity/fake_exception.py b/cinder/tests/unit/volume/drivers/dell_emc/unity/fake_exception.py index 3f7f3fccee4..cd415decde6 100644 --- a/cinder/tests/unit/volume/drivers/dell_emc/unity/fake_exception.py +++ b/cinder/tests/unit/volume/drivers/dell_emc/unity/fake_exception.py @@ -58,6 +58,14 @@ class DetachIsCalled(Exception): pass +class DetachAllIsCalled(Exception): + pass + + +class DetachFromIsCalled(Exception): + pass + + class LunDeleteIsCalled(Exception): pass diff --git a/cinder/tests/unit/volume/drivers/dell_emc/unity/test_adapter.py b/cinder/tests/unit/volume/drivers/dell_emc/unity/test_adapter.py index e871a353a50..fbf809a0278 100644 --- a/cinder/tests/unit/volume/drivers/dell_emc/unity/test_adapter.py +++ b/cinder/tests/unit/volume/drivers/dell_emc/unity/test_adapter.py @@ -140,6 +140,12 @@ class MockClient(object): if host.name == 'host1' and lun_or_snap.get_id() in error_ids: raise ex.DetachIsCalled() + @staticmethod + def detach_all(lun): + error_ids = ['lun_44'] + if lun.get_id() in error_ids: + raise ex.DetachAllIsCalled() + @staticmethod def get_iscsi_target_info(allowed_ports=None): return [{'portal': '1.2.3.4:1234', 'iqn': 'iqn.1-1.com.e:c.a.a0'}, @@ -443,6 +449,13 @@ class CommonAdapterTest(test.TestCase): self.assertRaises(ex.DetachIsCalled, f) + def test_terminate_connection_force_detach(self): + def f(): + volume = MockOSResource(provider_location='id^lun_44', id='id_44') + self.adapter.terminate_connection(volume, None) + + self.assertRaises(ex.DetachAllIsCalled, f) + def test_terminate_connection_snapshot(self): def f(): connector = {'host': 'host1'} diff --git a/cinder/tests/unit/volume/drivers/dell_emc/unity/test_client.py b/cinder/tests/unit/volume/drivers/dell_emc/unity/test_client.py index 5c623ab9857..f65b45cab22 100644 --- a/cinder/tests/unit/volume/drivers/dell_emc/unity/test_client.py +++ b/cinder/tests/unit/volume/drivers/dell_emc/unity/test_client.py @@ -100,6 +100,11 @@ class MockResource(object): if lun_or_snap.name == 'detach_failure': raise ex.DetachIsCalled() + @staticmethod + def detach_from(host): + if host is None: + raise ex.DetachFromIsCalled() + def get_hlu(self, lun): return self.alu_hlu_map.get(lun.get_id(), None) @@ -446,6 +451,13 @@ class ClientTest(unittest.TestCase): self.assertRaises(ex.DetachIsCalled, f) + def test_detach_all(self): + def f(): + lun = MockResource('lun_44') + self.client.detach_all(lun) + + self.assertRaises(ex.DetachFromIsCalled, f) + @mock.patch.object(coordination.Coordinator, 'get_lock') def test_create_host(self, fake): self.assertEqual('host2', self.client.create_host('host2').name) diff --git a/cinder/volume/drivers/dell_emc/unity/adapter.py b/cinder/volume/drivers/dell_emc/unity/adapter.py index 087fcfffa49..a1dd12023bc 100644 --- a/cinder/volume/drivers/dell_emc/unity/adapter.py +++ b/cinder/volume/drivers/dell_emc/unity/adapter.py @@ -312,8 +312,12 @@ class CommonAdapter(object): return self._initialize_connection(lun, connector, volume.id) def _terminate_connection(self, lun_or_snap, connector): - host = self.client.create_host(connector['host']) - self.client.detach(host, lun_or_snap) + is_force_detach = connector is None + if is_force_detach: + self.client.detach_all(lun_or_snap) + else: + host = self.client.create_host(connector['host']) + self.client.detach(host, lun_or_snap) @cinder_utils.trace def terminate_connection(self, volume, connector): diff --git a/cinder/volume/drivers/dell_emc/unity/client.py b/cinder/volume/drivers/dell_emc/unity/client.py index c7599bbaa2b..880d1259cb7 100644 --- a/cinder/volume/drivers/dell_emc/unity/client.py +++ b/cinder/volume/drivers/dell_emc/unity/client.py @@ -253,6 +253,15 @@ class UnityClient(object): lun_or_snap.update() host.detach(lun_or_snap) + @staticmethod + def detach_all(lun): + """Detaches a `UnityLun` from all hosts. + + :param lun: `UnityLun` object + """ + lun.update() + lun.detach_from(host=None) + def get_ethernet_ports(self): return self.system.get_ethernet_port() diff --git a/doc/source/configuration/block-storage/drivers/dell-emc-unity-driver.rst b/doc/source/configuration/block-storage/drivers/dell-emc-unity-driver.rst index 0e71d93446f..09008aabef0 100644 --- a/doc/source/configuration/block-storage/drivers/dell-emc-unity-driver.rst +++ b/doc/source/configuration/block-storage/drivers/dell-emc-unity-driver.rst @@ -301,6 +301,14 @@ For data path, please follow below steps: - If you create a volume using Cinder and attach it to a VM, the connection between this VM and volume will be IPv6-based iSCSI. +Force detach volume from all hosts +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +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 + Troubleshooting ~~~~~~~~~~~~~~~ diff --git a/releasenotes/notes/unity-force-detach-7c89e72105f9de61.yaml b/releasenotes/notes/unity-force-detach-7c89e72105f9de61.yaml new file mode 100644 index 00000000000..2a885fd7e5b --- /dev/null +++ b/releasenotes/notes/unity-force-detach-7c89e72105f9de61.yaml @@ -0,0 +1,4 @@ +--- +features: + - | + Add support to force detach a volume from all hosts on Unity.