[PATCHv2] Add timeout to callbacks waiting for enough entropy (#1073679)

Vratislav Podzimek vpodzime at redhat.com
Mon Nov 3 16:23:06 UTC 2014


On some systems there might never be enough entropy so instead of making the
installation stuck forever we should just continue after some time.

Signed-off-by: Vratislav Podzimek <vpodzime at redhat.com>
---
 pyanaconda/constants.py                           |  4 +++
 pyanaconda/ui/gui/spokes/lib/entropy_dialog.glade | 25 ++++++++++++++--
 pyanaconda/ui/gui/spokes/lib/entropy_dialog.py    | 31 +++++++++++++++++---
 pyanaconda/ui/lib/entropy.py                      | 35 +++++++++++++++++------
 4 files changed, 80 insertions(+), 15 deletions(-)

diff --git a/pyanaconda/constants.py b/pyanaconda/constants.py
index 45b2ca8..d94cc18 100644
--- a/pyanaconda/constants.py
+++ b/pyanaconda/constants.py
@@ -154,3 +154,7 @@ TAR_SUFFIX = (".tar", ".tbz", ".tgz", ".txz", ".tar.bz2", "tar.gz", "tar.xz")
 # screenshots
 SCREENSHOTS_DIRECTORY = "/tmp/anaconda-screenshots"
 SCREENSHOTS_TARGET_DIRECTORY = "/root/anaconda-screenshots"
+
+# for how long (in seconds) we try to wait for enough entropy for LUKS
+# keep this a multiple of 60 (minutes)
+MAX_ENTROPY_WAIT = 10 * 60
diff --git a/pyanaconda/ui/gui/spokes/lib/entropy_dialog.glade b/pyanaconda/ui/gui/spokes/lib/entropy_dialog.glade
index d0f7e41..3cb530e 100644
--- a/pyanaconda/ui/gui/spokes/lib/entropy_dialog.glade
+++ b/pyanaconda/ui/gui/spokes/lib/entropy_dialog.glade
@@ -24,7 +24,7 @@
             <property name="margin_top">6</property>
             <property name="margin_bottom">6</property>
             <property name="orientation">vertical</property>
-            <property name="spacing">6</property>
+            <property name="spacing">3</property>
             <child>
               <object class="GtkLabel" id="messageLabel">
                 <property name="visible">True</property>
@@ -32,7 +32,7 @@
                 <property name="valign">center</property>
                 <property name="vexpand">True</property>
                 <property name="xalign">0</property>
-                <property name="label" translatable="yes">The system needs better quality of random data, you can improve it by typing randomly on keyboard and moving your mouse</property>
+                <property name="label" translatable="yes">The system needs better quality of random data, you can improve it by typing randomly on keyboard and moving your mouse. The installation will continue automatically regardless of random data quality when time runs out.</property>
                 <property name="wrap">True</property>
                 <property name="width_chars">60</property>
                 <property name="max_width_chars">60</property>
@@ -44,6 +44,25 @@
               </packing>
             </child>
             <child>
+              <object class="GtkLabel" id="headerLabel">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="valign">center</property>
+                <property name="vexpand">True</property>
+                <property name="xalign">0</property>
+                <property name="label" translatable="yes">Random data quality:</property>
+                <property name="wrap">True</property>
+                <property name="width_chars">60</property>
+                <property name="max_width_chars">60</property>
+                <property name="margin_top">3</property>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">True</property>
+                <property name="position">1</property>
+              </packing>
+            </child>
+            <child>
               <object class="GtkProgressBar" id="progressBar">
                 <property name="visible">True</property>
                 <property name="can_focus">False</property>
@@ -52,7 +71,7 @@
               <packing>
                 <property name="expand">False</property>
                 <property name="fill">True</property>
-                <property name="position">1</property>
+                <property name="position">2</property>
               </packing>
             </child>
           </object>
diff --git a/pyanaconda/ui/gui/spokes/lib/entropy_dialog.py b/pyanaconda/ui/gui/spokes/lib/entropy_dialog.py
index d04b08d..3ad1c74 100644
--- a/pyanaconda/ui/gui/spokes/lib/entropy_dialog.py
+++ b/pyanaconda/ui/gui/spokes/lib/entropy_dialog.py
@@ -20,15 +20,21 @@
 #
 
 import time
+import math
 
 from gi.repository import Gtk, GLib
 
+from pyanaconda.i18n import P_
+from pyanaconda.constants import MAX_ENTROPY_WAIT
 from pyanaconda.ui.gui import GUIObject
 from pyanaconda.ui.gui.utils import gtk_action_wait
 from blivet.util import get_current_entropy
 
 __all__ = ["run_entropy_dialog"]
 
+# in milliseconds
+LOOP_TIMEOUT = 250
+
 @gtk_action_wait
 def run_entropy_dialog(ksdata, desired_entropy):
     """Show dialog with waiting for entropy"""
@@ -36,6 +42,8 @@ def run_entropy_dialog(ksdata, desired_entropy):
     dialog = EntropyDialog(ksdata, desired_entropy)
     dialog.run()
 
+    return dialog.force_cont
+
 class EntropyDialog(GUIObject):
     builderObjects = ["entropyDialog"]
     mainWidgetName = "entropyDialog"
@@ -46,10 +54,17 @@ class EntropyDialog(GUIObject):
         self._desired_entropy = desired_entropy
         self._progress_bar = self.builder.get_object("progressBar")
         self._terminate = False
+        self._started = 0
+        self.force_cont = False
 
     def run(self):
         self.window.show_all()
-        GLib.timeout_add(250, self._update_progress)
+
+        # XXX: Is it better to rely on Gtk running the self._update_progress
+        # method every ~250msec or rely on NTP not changing system time right
+        # now and use time.time()?
+        self._num_loops = 0
+        GLib.timeout_add(LOOP_TIMEOUT, self._update_progress)
         Gtk.main()
         self.window.destroy()
 
@@ -61,13 +76,21 @@ class EntropyDialog(GUIObject):
             # remove the method from idle queue
             return False
         else:
+            self._num_loops += 1
             current_entropy = get_current_entropy()
             current_fraction = min(float(current_entropy) / self._desired_entropy, 1.0)
+            remaining = (MAX_ENTROPY_WAIT - self._num_loops / (1000 / LOOP_TIMEOUT)) / 60.0
+
             self._progress_bar.set_fraction(current_fraction)
+            self._progress_bar.set_text("%(pct)d %% (%(rem)d %(min)s remaining)" % {"pct": (int(current_fraction * 100)),
+                                                                                    "rem": math.ceil(remaining),
+                                                                                    "min": P_("minute", "minutes", int(remaining))})
+
+            # if we have enough our time ran out, terminate the dialog, but let
+            # the progress_bar refresh in the main loop
+            self._terminate = (current_entropy >= self._desired_entropy) or (remaining <= 0)
 
-            # if we have enough, terminate the dialog, but let the progress_bar
-            # refresh in the main loop
-            self._terminate = current_entropy >= self._desired_entropy
+            self.force_cont = (remaining <= 0)
 
             # keep updating
             return True
diff --git a/pyanaconda/ui/lib/entropy.py b/pyanaconda/ui/lib/entropy.py
index 1fe5634..451f093 100644
--- a/pyanaconda/ui/lib/entropy.py
+++ b/pyanaconda/ui/lib/entropy.py
@@ -28,12 +28,14 @@ import time
 import select
 import sys
 import termios
+import math
 
 from pyanaconda.progress import progress_message
+from pyanaconda.constants import MAX_ENTROPY_WAIT
 from pykickstart.constants import DISPLAY_MODE_GRAPHICAL
 from blivet.util import get_current_entropy
 
-from pyanaconda.i18n import _
+from pyanaconda.i18n import _, P_
 
 def wait_for_entropy(msg, desired_entropy, ksdata):
     """
@@ -43,6 +45,8 @@ def wait_for_entropy(msg, desired_entropy, ksdata):
     :type ksdata: pykickstart.base.BaseHandler
     :param desired_entropy: entropy level to wait for
     :type desired_entropy: int
+    :returns: whether to force continuing regardless of the available entropy level
+    :rtype: bool
 
     """
 
@@ -51,15 +55,17 @@ def wait_for_entropy(msg, desired_entropy, ksdata):
         # in some cases
         from pyanaconda.ui.gui.spokes.lib.entropy_dialog import run_entropy_dialog
         progress_message(_("The system needs more random data entropy"))
-        run_entropy_dialog(ksdata, desired_entropy)
+        return run_entropy_dialog(ksdata, desired_entropy)
     else:
-        _tui_wait(msg, desired_entropy)
+        return _tui_wait(msg, desired_entropy)
 
 def _tui_wait(msg, desired_entropy):
     """Tell user we are waiting for entropy"""
 
     print(msg)
     print(_("Entropy can be increased by typing randomly on keyboard"))
+    print(_("After %d minutes, the installation will continue regardless of the "
+            "amount of available entropy") % MAX_ENTROPY_WAIT)
     fd = sys.stdin.fileno()
     old = termios.tcgetattr(fd)
     new = termios.tcgetattr(fd)
@@ -67,20 +73,31 @@ def _tui_wait(msg, desired_entropy):
     new[6][termios.VMIN] = 1
     termios.tcsetattr(fd, termios.TCSANOW, new)
 
-    # wait for the entropy to become high enough
+    # wait for the entropy to become high enough or time has run out
     cur_entr = get_current_entropy()
-    while cur_entr < desired_entropy:
-        print(_("Available entropy: %(av_entr)s, Required entropy: %(req_entr)s [%(pct)d %%]")
+    secs = 0
+    while cur_entr < desired_entropy and secs <= MAX_ENTROPY_WAIT:
+        remaining = (MAX_ENTROPY_WAIT - secs) / 60.0
+        print(_("Available entropy: %(av_entr)s, Required entropy: %(req_entr)s [%(pct)d %%] (%(rem)d %(min)s remaining)")
                 % {"av_entr": cur_entr, "req_entr": desired_entropy,
-                   "pct": int((float(cur_entr) / desired_entropy) * 100)})
+                   "pct": int((float(cur_entr) / desired_entropy) * 100),
+                   "min": P_("minute", "minutes", remaining),
+                   "rem": math.ceil(remaining)})
         time.sleep(1)
         cur_entr = get_current_entropy()
+        secs += 1
 
     # print the final state as well
     print(_("Available entropy: %(av_entr)s, Required entropy: %(req_entr)s [%(pct)d %%]")
             % {"av_entr": cur_entr, "req_entr": desired_entropy,
                "pct": int((float(cur_entr) / desired_entropy) * 100)})
-    print(_("Enough entropy gathered, please stop typing."))
+
+    if secs <= MAX_ENTROPY_WAIT:
+        print(_("Enough entropy gathered, please stop typing."))
+        force_cont = False
+    else:
+        print(_("Giving up, time (%d minutes) ran out.") % (MAX_ENTROPY_WAIT / 60))
+        force_cont = True
 
     # we are done
     # first let the user notice we are done and stop typing
@@ -91,3 +108,5 @@ def _tui_wait(msg, desired_entropy):
     while sys.stdin in select.select([sys.stdin], [], [], 0)[0]:
         _in_data = sys.stdin.read(1)
     termios.tcsetattr(fd, termios.TCSAFLUSH, old)
+
+    return force_cont
-- 
1.9.3



More information about the anaconda-patches mailing list