From 8be4cb689cac5755f09eaa27cdde86a30b2633f8 Mon Sep 17 00:00:00 2001
From: Julia Kreger <juliaashleykreger@gmail.com>
Date: Thu, 29 Aug 2019 15:02:42 -0400
Subject: [PATCH] Attempt tgtd session detach

With raid, we were able to observe a condition where
the file is still open for non-exlcusive access which
blocks the software raid from being shutdown... which
is realistically needed to force a rescan in that case.

Change-Id: I8dbfae091267f8af5340ff5ebeebcba375d05542
---
 ironic_python_agent/extensions/iscsi.py       | 15 ++++++++--
 .../tests/unit/extensions/test_iscsi.py       | 28 ++++++++++++++++---
 2 files changed, 37 insertions(+), 6 deletions(-)

diff --git a/ironic_python_agent/extensions/iscsi.py b/ironic_python_agent/extensions/iscsi.py
index bc7ef0d44..7fca41542 100644
--- a/ironic_python_agent/extensions/iscsi.py
+++ b/ironic_python_agent/extensions/iscsi.py
@@ -113,8 +113,19 @@ def clean_up(device):
     """Clean up iSCSI for a given device."""
     try:
         rts_root = rtslib_fb.RTSRoot()
-    except (EnvironmentError, rtslib_fb.RTSLibError) as exc:
-        LOG.info('Linux-IO is not available, not cleaning up. Error: %s.', exc)
+    except (OSError, EnvironmentError, rtslib_fb.RTSLibError) as exc:
+        LOG.info('Linux-IO is not available, attemting to stop tgtd mapping. '
+                 'Error: %s.', exc)
+        cmd = ['tgtadm', '--lld', 'iscsi', '--mode', 'target', '--op',
+               'unbind', '--tid', '1', '--initiator-address', 'ALL']
+        _execute(cmd, "Error when cleaning up iscsi binds.")
+
+        cmd = ['sync']
+        _execute(cmd, "Error flushing buffers to disk.")
+
+        cmd = ['tgtadm', '--lld', 'iscsi', '--mode', 'target', '--op',
+               'delete', '--tid', '1']
+        _execute(cmd, "Error deleting the iscsi target configuration.")
         return
 
     storage = None
diff --git a/ironic_python_agent/tests/unit/extensions/test_iscsi.py b/ironic_python_agent/tests/unit/extensions/test_iscsi.py
index 3dc509054..f275c0fc3 100644
--- a/ironic_python_agent/tests/unit/extensions/test_iscsi.py
+++ b/ironic_python_agent/tests/unit/extensions/test_iscsi.py
@@ -281,6 +281,30 @@ class TestISCSIExtensionLIO(base.IronicAgentTest):
         mock_destroy.assert_called_once_with('/dev/fake', 'my_node_uuid')
 
 
+@mock.patch.object(utils, 'execute', autospec=True)
+class TestISCSIExtensionCleanUpFallback(base.IronicAgentTest):
+
+    def setUp(self):
+        super(TestISCSIExtensionCleanUpFallback, self).setUp()
+        self.agent_extension = iscsi.ISCSIExtension()
+        self.fake_dev = '/dev/fake'
+        self.fake_iqn = 'iqn-fake'
+        self.rtsmock = mock.patch.object(
+            iscsi.rtslib_fb, 'RTSRoot',
+            side_effect=EnvironmentError(), autospec=True)
+
+    def test_lio_not_available(self, mock_execute):
+        mock_execute.return_value = ('', '')
+        expected = [mock.call('tgtadm', '--lld', 'iscsi', '--mode',
+                              'target', '--op', 'unbind', '--tid', '1',
+                              '--initiator-address', 'ALL'),
+                    mock.call('sync'),
+                    mock.call('tgtadm', '--lld', 'iscsi', '--mode', 'target',
+                              '--op', 'delete', '--tid', '1')]
+        iscsi.clean_up(self.fake_dev)
+        mock_execute.assert_has_calls(expected)
+
+
 @mock.patch.object(iscsi.rtslib_fb, 'RTSRoot', autospec=True)
 class TestISCSIExtensionCleanUp(base.IronicAgentTest):
 
@@ -290,10 +314,6 @@ class TestISCSIExtensionCleanUp(base.IronicAgentTest):
         self.fake_dev = '/dev/fake'
         self.fake_iqn = 'iqn-fake'
 
-    def test_lio_not_available(self, mock_rtslib):
-        mock_rtslib.side_effect = IOError()
-        iscsi.clean_up(self.fake_dev)
-
     def test_device_not_found(self, mock_rtslib):
         mock_rtslib.return_value.storage_objects = []
         iscsi.clean_up(self.fake_dev)