----- Original Message -----
From: "Vratislav Podzimek" <vpodzime(a)redhat.com>
To: anaconda-patches(a)lists.fedorahosted.org
Sent: Monday, November 3, 2014 11:23:06 AM
Subject: [PATCHv2] Add timeout to callbacks waiting for enough entropy (#1073679)
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(a)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
I guess the pylint error about initializing attributes outside the constructor
must be suppressed in anaconda, but it still seems to me that it would be reasonable
to define self._num_loops here.
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
remaining is probably better like this:
remaining = (MAX_ENTROPY_WAIT - self._num_loops * (LOOP_TIMEOUT / 1000.0)) / 60.0
Note that you need to make 1000 non-integer to get the fractional quantity. Decimal
would do just as well, but the precision is unnecessary.
In your versionn:
1000 / LOOP_TIMEOUT is a correct value if 1000 is divisible by LOOP_TIMEOUT, as it
is now, but will not work correctly if the value of LOOP_TIMEOUT changes to any
non-divisor
of 1000. So, really, it is necessary to make 1000 a fractional quantity in that version as
well.
Alternatively you could get the difference in milliseconds, divide by 1000 to get whole
seconds, and then divide by 60.0 to get fractional minutes as:
(MAX_ENTROPY_WAIT * 1000 - self._num_loops * LOOP_TIMEOUT) / 1000 / 60.0
That seems the clearest approach to me.
+
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)
divide MAX_ENTROPY_WAIT here by 60 in some way in order to get minutes.
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
_______________________________________________
anaconda-patches mailing list
anaconda-patches(a)lists.fedorahosted.org
https://lists.fedorahosted.org/mailman/listinfo/anaconda-patches
A few inline comments.
- mulhern