Skip to content

Commit 540f5fb

Browse files
committed
Add ProgressSpoke
Add a standalone spoke that reports progress of configuration tasks. This is heavily based on ProgressSpoke from anaconda itself, with few minor differences: - logic for "continue"/"reboot" buttons removed - automatic close on finish (as initial-setup normally does) - add basic error reporting - anaconda has elaborate exception handling machinery but initial setup doesn't; add simple try/except in the key place, so user will see the error, not a silent failure
1 parent 6e0cddf commit 540f5fb

File tree

3 files changed

+356
-6
lines changed

3 files changed

+356
-6
lines changed

initial_setup/__init__.py

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -280,12 +280,14 @@ def _apply(self):
280280
# Do not execute sections that were part of the original
281281
# anaconda kickstart file (== have .seen flag set)
282282

283-
task = InitialSetupTask(
284-
groups_already_configured=self._groups_already_configured,
285-
users_already_configured=self._users_already_configured,
286-
root_password_already_configured=self._root_password_already_configured,
287-
)
288-
task.start()
283+
if not self.gui_mode:
284+
# GUI has it already executed, but in TUI do it here
285+
task = InitialSetupTask(
286+
groups_already_configured=self._groups_already_configured,
287+
users_already_configured=self._users_already_configured,
288+
root_password_already_configured=self._root_password_already_configured,
289+
)
290+
task.start()
289291

290292
# Write the kickstart data to file
291293
log.info("writing the Initial Setup kickstart file %s", OUTPUT_KICKSTART_PATH)
Lines changed: 168 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,168 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<!-- Generated with glade 3.22.1 -->
3+
<interface>
4+
<requires lib="gtk+" version="3.0"/>
5+
<requires lib="AnacondaWidgets" version="3.0"/>
6+
<object class="AnacondaStandaloneWindow" id="progressWindow">
7+
<property name="can_focus">False</property>
8+
<property name="window_name" translatable="yes">INSTALLATION PROGRESS</property>
9+
<child internal-child="main_box">
10+
<object class="GtkBox" id="AnacondaStandaloneWindow-main_box1">
11+
<property name="can_focus">False</property>
12+
<property name="orientation">vertical</property>
13+
<child internal-child="nav_box">
14+
<object class="GtkEventBox" id="AnacondaStandaloneWindow-nav_box1">
15+
<property name="can_focus">False</property>
16+
<child internal-child="nav_area">
17+
<object class="GtkGrid" id="AnacondaStandaloneWindow-nav_area1">
18+
<property name="can_focus">False</property>
19+
<child>
20+
<placeholder/>
21+
</child>
22+
<child>
23+
<placeholder/>
24+
</child>
25+
</object>
26+
</child>
27+
</object>
28+
<packing>
29+
<property name="expand">False</property>
30+
<property name="fill">False</property>
31+
<property name="position">0</property>
32+
</packing>
33+
</child>
34+
<child internal-child="alignment">
35+
<object class="GtkAlignment" id="AnacondaStandaloneWindow-alignment1">
36+
<property name="can_focus">False</property>
37+
<property name="yalign">0</property>
38+
<property name="left_padding">12</property>
39+
<property name="right_padding">6</property>
40+
<child internal-child="action_area">
41+
<object class="GtkBox" id="progressWindow-actionArea">
42+
<property name="can_focus">False</property>
43+
<property name="orientation">vertical</property>
44+
<property name="spacing">6</property>
45+
<child>
46+
<object class="GtkBox" id="progressBox">
47+
<property name="visible">True</property>
48+
<property name="can_focus">False</property>
49+
<property name="valign">center</property>
50+
<property name="orientation">vertical</property>
51+
<property name="spacing">6</property>
52+
<child>
53+
<object class="GtkBox" id="box1">
54+
<property name="visible">True</property>
55+
<property name="can_focus">False</property>
56+
<property name="valign">center</property>
57+
<property name="spacing">6</property>
58+
<child>
59+
<object class="GtkSpinner" id="progressSpinner">
60+
<property name="visible">True</property>
61+
<property name="can_focus">False</property>
62+
<property name="active">True</property>
63+
</object>
64+
<packing>
65+
<property name="expand">False</property>
66+
<property name="fill">True</property>
67+
<property name="position">0</property>
68+
</packing>
69+
</child>
70+
<child>
71+
<object class="GtkLabel" id="progressLabel">
72+
<property name="visible">True</property>
73+
<property name="can_focus">False</property>
74+
<property name="halign">start</property>
75+
<property name="label" translatable="yes">Preparing to install</property>
76+
</object>
77+
<packing>
78+
<property name="expand">True</property>
79+
<property name="fill">True</property>
80+
<property name="position">1</property>
81+
</packing>
82+
</child>
83+
</object>
84+
<packing>
85+
<property name="expand">False</property>
86+
<property name="fill">True</property>
87+
<property name="position">0</property>
88+
</packing>
89+
</child>
90+
<child>
91+
<object class="GtkProgressBar" id="progressBar">
92+
<property name="visible">True</property>
93+
<property name="can_focus">False</property>
94+
<property name="valign">center</property>
95+
</object>
96+
<packing>
97+
<property name="expand">False</property>
98+
<property name="fill">True</property>
99+
<property name="position">1</property>
100+
</packing>
101+
</child>
102+
</object>
103+
<packing>
104+
<property name="expand">True</property>
105+
<property name="fill">True</property>
106+
<property name="position">0</property>
107+
</packing>
108+
</child>
109+
<child>
110+
<object class="GtkNotebook" id="progressNotebook">
111+
<property name="visible">True</property>
112+
<property name="can_focus">False</property>
113+
<property name="valign">end</property>
114+
<property name="margin_bottom">10</property>
115+
<property name="show_tabs">False</property>
116+
<property name="show_border">False</property>
117+
<child>
118+
<object class="GtkBox" id="emptyBox">
119+
<property name="visible">True</property>
120+
<property name="can_focus">False</property>
121+
<child>
122+
<placeholder/>
123+
</child>
124+
</object>
125+
</child>
126+
<child>
127+
<object class="GtkLabel" id="rebootLabel">
128+
<property name="visible">True</property>
129+
<property name="can_focus">False</property>
130+
<property name="halign">end</property>
131+
<property name="valign">end</property>
132+
<property name="label">%s is now successfully installed and ready for you to use!
133+
Go ahead and reboot to start using it!</property>
134+
<property name="justify">right</property>
135+
<property name="wrap">True</property>
136+
</object>
137+
<packing>
138+
<property name="position">1</property>
139+
</packing>
140+
</child>
141+
<child type="tab">
142+
<placeholder/>
143+
</child>
144+
</object>
145+
<packing>
146+
<property name="expand">False</property>
147+
<property name="fill">True</property>
148+
<property name="position">1</property>
149+
</packing>
150+
</child>
151+
</object>
152+
</child>
153+
</object>
154+
<packing>
155+
<property name="expand">True</property>
156+
<property name="fill">True</property>
157+
<property name="position">1</property>
158+
</packing>
159+
</child>
160+
</object>
161+
</child>
162+
<child internal-child="accessible">
163+
<object class="AtkObject" id="progressWindow-atkobject">
164+
<property name="AtkObject::accessible-name" translatable="yes">INSTALLATION PROGRESS</property>
165+
</object>
166+
</child>
167+
</object>
168+
</interface>
Lines changed: 180 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,180 @@
1+
#
2+
# Copyright (C) 2011-2013 Red Hat, Inc.
3+
#
4+
# This copyrighted material is made available to anyone wishing to use,
5+
# modify, copy, or redistribute it subject to the terms and conditions of
6+
# the GNU General Public License v.2, or (at your option) any later version.
7+
# This program is distributed in the hope that it will be useful, but WITHOUT
8+
# ANY WARRANTY expressed or implied, including the implied warranties of
9+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
10+
# Public License for more details. You should have received a copy of the
11+
# GNU General Public License along with this program; if not, write to the
12+
# Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
13+
# 02110-1301, USA. Any Red Hat trademarks that are incorporated in the
14+
# source code or documentation are not subject to the GNU General Public
15+
# License and may only be used or replicated with the express permission of
16+
# Red Hat, Inc.
17+
import gi
18+
gi.require_version('Gtk', '3.0')
19+
from gi.repository import Gtk
20+
21+
from initial_setup import InitialSetupTask
22+
from initial_setup.gui.hubs import InitialSetupMainHub
23+
from pyanaconda.anaconda_loggers import get_module_logger
24+
from pyanaconda.core.i18n import _, C_
25+
from pyanaconda.modules.common.constants.services import USERS
26+
from pyanaconda.product import productName
27+
from pyanaconda.flags import flags
28+
from pyanaconda.core import util
29+
from pyanaconda.core.configuration.anaconda import conf
30+
from pyanaconda.core.constants import IPMI_FINISHED
31+
from pyanaconda.ui.common import FirstbootOnlySpokeMixIn
32+
from pykickstart.constants import KS_SHUTDOWN, KS_REBOOT
33+
from pyanaconda.ui.gui.spokes import StandaloneSpoke
34+
from pyanaconda.ui.gui.utils import gtk_call_once
35+
36+
log = get_module_logger(__name__)
37+
38+
__all__ = ["ProgressSpoke"]
39+
40+
41+
class ProgressSpoke(FirstbootOnlySpokeMixIn, StandaloneSpoke):
42+
"""
43+
.. inheritance-diagram:: ProgressSpoke
44+
:parts: 3
45+
"""
46+
47+
builderObjects = ["progressWindow"]
48+
mainWidgetName = "progressWindow"
49+
uiFile = "setup_progress.glade"
50+
postForHub = InitialSetupMainHub
51+
52+
@staticmethod
53+
def get_screen_id():
54+
"""Return a unique id of this UI screen."""
55+
return "installation-progress"
56+
57+
def __init__(self, data, storage, payload):
58+
super().__init__(data, storage, payload)
59+
self._progressBar = self.builder.get_object("progressBar")
60+
self._progressLabel = self.builder.get_object("progressLabel")
61+
self._progressNotebook = self.builder.get_object("progressNotebook")
62+
self._spinner = self.builder.get_object("progressSpinner")
63+
self._task = None
64+
65+
# Record if groups, users or root password has been set before Initial Setup
66+
# has been started, so that we don't trample over existing configuration.
67+
log.info("collecting initial state")
68+
users_proxy = USERS.get_proxy()
69+
self._groups_already_configured = bool(users_proxy.Groups)
70+
self._users_already_configured = bool(users_proxy.Users)
71+
self._root_password_already_configured = users_proxy.IsRootPasswordSet
72+
73+
@property
74+
def completed(self):
75+
"""This spoke is never completed, initially."""
76+
return False
77+
78+
def apply(self):
79+
"""There is nothing to apply."""
80+
pass
81+
82+
def _on_installation_done(self):
83+
log.debug("The initial setup has finished.")
84+
85+
# Stop the spinner.
86+
gtk_call_once(self._spinner.stop)
87+
gtk_call_once(self._spinner.hide)
88+
89+
# Finish the installation task. Re-raise tracebacks if any.
90+
try:
91+
self._task.finish()
92+
except Exception as e:
93+
log.exception("Initial setup failed")
94+
self.showErrorMessageHelper(str(e))
95+
96+
util.ipmi_report(IPMI_FINISHED)
97+
98+
if conf.license.eula:
99+
self.set_warning(_("Use of this product is subject to the license agreement "
100+
"found at %s") % conf.license.eula)
101+
self.window.show_all()
102+
103+
# Show the reboot message.
104+
self._progressNotebook.set_current_page(1)
105+
106+
# Enable the continue button.
107+
self.window.set_may_continue(True)
108+
109+
# Hide the quit button.
110+
quit_button = self.window.get_quit_button()
111+
quit_button.hide()
112+
113+
# automatically close; if there was an error, showErrorMessageHelper
114+
# already waited for the user to click ok
115+
self.window.emit("continue-clicked")
116+
117+
def initialize(self):
118+
super().initialize()
119+
# Disable the continue button.
120+
self.window.set_may_continue(False)
121+
122+
# Set the label of the continue button.
123+
continue_label = C_("GUI|Progress", "_Finish Installation")
124+
125+
continue_button = self.window.get_continue_button()
126+
continue_button.set_label(continue_label)
127+
128+
# Set the reboot label.
129+
continue_text = _(
130+
"%s is now successfully installed and ready for you to use!\n"
131+
"Go ahead and quit the application to start using it!"
132+
) % productName
133+
134+
label = self.builder.get_object("rebootLabel")
135+
label.set_text(continue_text)
136+
137+
# Don't show the reboot message.
138+
self._progressNotebook.set_current_page(0)
139+
140+
def refresh(self):
141+
from pyanaconda.installation import RunInstallationTask
142+
super().refresh()
143+
144+
# Initialize the progress bar.
145+
gtk_call_once(self._progressBar.set_fraction, 0.0)
146+
147+
# Start the installation task.
148+
self._task = InitialSetupTask(
149+
groups_already_configured=self._groups_already_configured,
150+
users_already_configured=self._users_already_configured,
151+
root_password_already_configured=self._root_password_already_configured,
152+
)
153+
self._task.progress_changed_signal.connect(
154+
self._on_progress_changed
155+
)
156+
self._task.stopped_signal.connect(
157+
self._on_installation_done
158+
)
159+
self._task.start()
160+
161+
# Start the spinner.
162+
gtk_call_once(self._spinner.start)
163+
164+
log.debug("The installation has started.")
165+
166+
def showErrorMessageHelper(self, text):
167+
dlg = Gtk.MessageDialog(title="Error", message_type=Gtk.MessageType.ERROR, buttons=Gtk.ButtonsType.OK, text=text)
168+
dlg.set_position(Gtk.WindowPosition.CENTER)
169+
dlg.set_modal(True)
170+
dlg.set_transient_for(self.main_window)
171+
dlg.run()
172+
dlg.destroy()
173+
174+
def _on_progress_changed(self, step, message):
175+
"""Handle a new progress report."""
176+
if message:
177+
gtk_call_once(self._progressLabel.set_text, message)
178+
179+
if self._task.steps > 0:
180+
gtk_call_once(self._progressBar.set_fraction, step/self._task.steps)

0 commit comments

Comments
 (0)