[rhel7-branch 1/1] Fix "anaconda hangs while trying to discover iscsi..." (#1166652)

jkonecny12 installerbot-noreply at redhat.com
Tue Apr 21 14:18:35 UTC 2015


From: Jiri Konecny <jkonecny at redhat.com>

This was 2 errors in one.

First problem: anaconda freeze for some time.
Second problem: libiscsi was run on thread and libiscsi the library
is not thread safe.

Anaconda freeze is because of GIL is taken on library thread which is
trying to connect to server.
The thread safe problem is because libiscsi is using signals internally
which were delivered to another thread (this is hard to solve in python
threads...). Solution is to start discover method on new process.

So new process is now created to call libiscsi discover_sendtargets
method. This should fix both problems.
---
 blivet/iscsi.py | 81 +++++++++++++++++++++++++++++++++++++++++++++++++++++----
 1 file changed, 76 insertions(+), 5 deletions(-)

diff --git a/blivet/iscsi.py b/blivet/iscsi.py
index c8c4e5d..a5228d7 100644
--- a/blivet/iscsi.py
+++ b/blivet/iscsi.py
@@ -23,6 +23,7 @@
 from .flags import flags
 from .i18n import _
 from .storage_log import log_exception_info
+from multiprocessing import Process, Pipe
 import os
 import logging
 import shutil
@@ -57,6 +58,50 @@ def has_iscsi():
 
     return True
 
+
+def _call_discover_targets(conn_pipe, ipaddr, port, authinfo):
+    """ Function to separate iscsi discover_sendtargets call to it's own process.
+
+        The call must be started on special process because the libiscsi
+        library is not thread safe. When thread is used or it's called on
+        main thread the main thread will freeze.
+
+        Also the discover is about four times slower (only for timeout)
+        this is caused by signals which are delivered to bad (python)
+        thread instead of C library thread.
+
+        To transfer data to main process conn_pipe (write only) is used.
+        Pipe returns tuple (ok, data).
+            ok  : True if everything was ok
+            data: Dictionary with libiscsi.node parameters if ok was True
+                  Exception if ok was False
+    """
+    try:
+        found_nodes = libiscsi.discover_sendtargets(address=ipaddr,
+                                                    port=int(port),
+                                                    authinfo=authinfo)
+        if found_nodes is None:
+            found_nodes = []
+
+    except IOError as ex:
+        conn_pipe.send((False, ex))
+        conn_pipe.close()
+        return
+
+    nodes = []
+
+    # the node object is not pickable so it can't be send with pipe
+    # TODO: change libiscsi.node to pickable object
+    for node in found_nodes:
+        nodes.append({'name': node.name,
+                     'tpgt': node.tpgt,
+                     'address': node.address,
+                     'port': node.port,
+                     'iface': node.iface})
+
+    conn_pipe.send((True, nodes))
+
+
 class iscsi(object):
     """ iSCSI utility class.
 
@@ -277,11 +322,37 @@ def discover(self, ipaddr, port="3260", username=None, password=None,
                                                  reverse_password=r_password)
             self.startup()
 
-            # Note may raise an IOError
-            found_nodes = libiscsi.discover_sendtargets(address=ipaddr,
-                                                        port=int(port),
-                                                        authinfo=authinfo)
-            if found_nodes is None:
+            # start libiscsi discover_sendtargets in a new process
+            # threads can't be used here because the libiscsi library
+            # using signals internally which are send to bad thread
+            (con_recv, con_write) = Pipe(False)
+            p = Process(target=_call_discover_targets, args=(con_write,
+                                                             ipaddr,
+                                                             port,
+                                                             authinfo, ))
+            p.start()
+
+            found_nodes = []
+
+            try:
+                (ok, data) = con_recv.recv()
+                if not ok:
+                    found_nodes =  None
+                    log.debug("iSCSI: exception raised when "
+                              "discover_sendtargets process called: %s",
+                              str(data))
+            except EOFError:
+                found_nodes = None
+                log.error("iSCSI: can't receive response from "
+                          "_call_discover_targets")
+
+            p.join()
+
+            # convert dictionary back to iscsi nodes object
+            if found_nodes is not None:
+                for node in data:
+                    found_nodes.append(libiscsi.node(**node))
+            else:
                 return []
             self.discovered_targets[(ipaddr, port)] = []
             for node in found_nodes:


-- 
To view this commit on github, visit https://github.com/rhinstaller/blivet/commit/56a1ef6994758b4e4acae11ff6b8ff570b069c60


More information about the anaconda-patches mailing list