[PATCH 1/2] Make progress hub spokes possible and move the root password there

Martin Sivak msivak at redhat.com
Thu Sep 13 11:57:43 UTC 2012


- Splits doInstall to doInstall and doConfiguration
- Updates text and gui mode to use doConfiguration
---
 pyanaconda/install.py                         | 67 ++++++++++++---------
 pyanaconda/ui/gui/categories/customization.py | 32 ++++++++++
 pyanaconda/ui/gui/categories/user_settings.py | 32 ++++++++++
 pyanaconda/ui/gui/hubs/__init__.py            |  6 +-
 pyanaconda/ui/gui/hubs/progress.glade         | 58 +++++++++++++++++-
 pyanaconda/ui/gui/hubs/progress.py            | 87 +++++++++++++++++++++------
 pyanaconda/ui/gui/spokes/password.py          | 10 ++-
 pyanaconda/ui/tui/hubs/progress.py            | 24 +++++---
 8 files changed, 255 insertions(+), 61 deletions(-)
 create mode 100644 pyanaconda/ui/gui/categories/customization.py
 create mode 100644 pyanaconda/ui/gui/categories/user_settings.py

diff --git a/pyanaconda/install.py b/pyanaconda/install.py
index 3db5ee0..d401e2f 100644
--- a/pyanaconda/install.py
+++ b/pyanaconda/install.py
@@ -42,6 +42,44 @@ def _writeKS(ksdata):
     # Make it so only root can read - could have passwords
     os.chmod(path, 0600)
 
+def doConfiguration(storage, payload, ksdata, instClass):
+    from pyanaconda import progress
+    from pyanaconda.kickstart import runPostScripts
+
+    progress.send_init(4)
+    
+    # Now run the execute methods of ksdata that require an installed system
+    # to be present first.
+    with progress_report(_("Configuring installed system")):
+        ksdata.authconfig.execute(storage, ksdata, instClass)
+        ksdata.selinux.execute(storage, ksdata, instClass)
+        ksdata.firstboot.execute(storage, ksdata, instClass)
+        ksdata.services.execute(storage, ksdata, instClass)
+        ksdata.keyboard.execute(storage, ksdata, instClass)
+        ksdata.timezone.execute(storage, ksdata, instClass)
+        ksdata.lang.execute(storage, ksdata, instClass)
+        
+    with progress_report(_("Writing network configuration")):
+        writeNetworkConf(storage, ksdata, instClass)
+
+    # Creating users and groups requires some pre-configuration.
+    with progress_report(_("Creating users")):
+        u = Users()
+        createLuserConf(ROOT_PATH, algoname=getPassAlgo(ksdata.authconfig.authconfig))
+
+        ksdata.rootpw.execute(storage, ksdata, instClass, u)
+        ksdata.group.execute(storage, ksdata, instClass, u)
+        ksdata.user.execute(storage, ksdata, instClass, u)
+
+    with progress_report(_("Running post install scripts")):
+        runPostScripts(ksdata.scripts)
+
+    # Write the kickstart file to the installed system (or, copy the input
+    # kickstart file over if one exists).
+    _writeKS(ksdata)
+
+    progress.send_complete()
+    
 def doInstall(storage, payload, ksdata, instClass):
     """Perform an installation.  This method takes the ksdata as prepared by
        the UI (the first hub, in graphical mode) and applies it to the disk.
@@ -56,8 +94,7 @@ def doInstall(storage, payload, ksdata, instClass):
     steps = len(storage.devicetree.findActions(type="create", object="format")) + \
             len(storage.devicetree.findActions(type="resize", object="format")) + \
             len(storage.devicetree.findActions(type="migrate", object="format"))
-    steps += 5  # packages setup, packages, bootloader, post install,
-                # configuring
+    steps += 4  # packages setup, packages, bootloader, post install
     progress.send_init(steps)
 
     # Do partitioning.
@@ -85,30 +122,4 @@ def doInstall(storage, payload, ksdata, instClass):
     with progress_report(_("Installing bootloader")):
         writeBootLoader(storage, payload, instClass)
 
-    with progress_report(_("Configuring installed system")):
-        # Now run the execute methods of ksdata that require an installed system
-        # to be present first.
-        ksdata.authconfig.execute(storage, ksdata, instClass)
-        ksdata.selinux.execute(storage, ksdata, instClass)
-        ksdata.firstboot.execute(storage, ksdata, instClass)
-        ksdata.services.execute(storage, ksdata, instClass)
-        ksdata.keyboard.execute(storage, ksdata, instClass)
-        ksdata.timezone.execute(storage, ksdata, instClass)
-        ksdata.lang.execute(storage, ksdata, instClass)
-
-        writeNetworkConf(storage, ksdata, instClass)
-
-        # Creating users and groups requires some pre-configuration.
-        createLuserConf(ROOT_PATH, algoname=getPassAlgo(ksdata.authconfig.authconfig))
-        u = Users()
-        ksdata.rootpw.execute(storage, ksdata, instClass, u)
-        ksdata.group.execute(storage, ksdata, instClass, u)
-        ksdata.user.execute(storage, ksdata, instClass, u)
-
-    runPostScripts(ksdata.scripts)
-
-    # Write the kickstart file to the installed system (or, copy the input
-    # kickstart file over if one exists).
-    _writeKS(ksdata)
-
     progress.send_complete()
diff --git a/pyanaconda/ui/gui/categories/customization.py b/pyanaconda/ui/gui/categories/customization.py
new file mode 100644
index 0000000..68b422c
--- /dev/null
+++ b/pyanaconda/ui/gui/categories/customization.py
@@ -0,0 +1,32 @@
+# Localization category classes
+#
+# Copyright (C) 2011  Red Hat, Inc.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions of
+# the GNU General Public License v.2, or (at your option) any later version.
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY expressed or implied, including the implied warranties of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General
+# Public License for more details.  You should have received a copy of the
+# GNU General Public License along with this program; if not, write to the
+# Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301, USA.  Any Red Hat trademarks that are incorporated in the
+# source code or documentation are not subject to the GNU General Public
+# License and may only be used or replicated with the express permission of
+# Red Hat, Inc.
+#
+# Red Hat Author(s): Chris Lumens <clumens at redhat.com>
+#                    Martin Sivak <msivak at redhat.com>
+#
+
+N_ = lambda x: x
+
+from pyanaconda.ui.gui.categories import SpokeCategory
+from pyanaconda.ui.gui.hubs.progress import ProgressHub
+
+__all__ = ["CustomizationCategory"]
+
+class CustomizationCategory(SpokeCategory):
+    displayOnHub = ProgressHub
+    title = N_("CUSTOMIZATION")
diff --git a/pyanaconda/ui/gui/categories/user_settings.py b/pyanaconda/ui/gui/categories/user_settings.py
new file mode 100644
index 0000000..b9838b8
--- /dev/null
+++ b/pyanaconda/ui/gui/categories/user_settings.py
@@ -0,0 +1,32 @@
+# Localization category classes
+#
+# Copyright (C) 2011  Red Hat, Inc.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions of
+# the GNU General Public License v.2, or (at your option) any later version.
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY expressed or implied, including the implied warranties of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General
+# Public License for more details.  You should have received a copy of the
+# GNU General Public License along with this program; if not, write to the
+# Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301, USA.  Any Red Hat trademarks that are incorporated in the
+# source code or documentation are not subject to the GNU General Public
+# License and may only be used or replicated with the express permission of
+# Red Hat, Inc.
+#
+# Red Hat Author(s): Chris Lumens <clumens at redhat.com>
+#                    Martin Sivak <msivak at redhat.com>
+#
+
+N_ = lambda x: x
+
+from pyanaconda.ui.gui.categories import SpokeCategory
+from pyanaconda.ui.gui.hubs.progress import ProgressHub
+
+__all__ = ["UserSettingsCategory"]
+
+class UserSettingsCategory(SpokeCategory):
+    displayOnHub = ProgressHub
+    title = N_("USER SETTINGS")
diff --git a/pyanaconda/ui/gui/hubs/__init__.py b/pyanaconda/ui/gui/hubs/__init__.py
index 4d8c6e3..13f6192 100644
--- a/pyanaconda/ui/gui/hubs/__init__.py
+++ b/pyanaconda/ui/gui/hubs/__init__.py
@@ -224,8 +224,12 @@ class Hub(GUIObject, common.Hub):
 
             self.window.set_info(Gtk.MessageType.WARNING, msg)
 
+    @property
+    def continuePossible(self):
+        return len(self._incompleteSpokes) == 0 and len(self._notReadySpokes) == 0
+        
     def _updateContinueButton(self):
-        self.continueButton.set_sensitive(len(self._incompleteSpokes) == 0 and len(self._notReadySpokes) == 0)
+        self.continueButton.set_sensitive(self.continuePossible)
 
     def _update_spokes(self):
         from pyanaconda.ui.gui import communication
diff --git a/pyanaconda/ui/gui/hubs/progress.glade b/pyanaconda/ui/gui/hubs/progress.glade
index 036fcb4..39a1532 100644
--- a/pyanaconda/ui/gui/hubs/progress.glade
+++ b/pyanaconda/ui/gui/hubs/progress.glade
@@ -6,7 +6,7 @@
     <property name="startup_id">filler</property>
     <property name="can_focus">False</property>
     <property name="startup_id">filler</property>
-    <property name="window_name">PERSONALIZATION</property>
+    <property name="window_name">CONFIGURATION</property>
     <child internal-child="main_box">
       <object class="GtkBox" id="AnacondaHubWindow-main_box1">
         <property name="can_focus">False</property>
@@ -29,6 +29,7 @@
             <property name="can_focus">False</property>
             <property name="yalign">0</property>
             <property name="xscale">0.5</property>
+            <property name="yscale">0.5</property>
             <child internal-child="action_area">
               <object class="GtkBox" id="progressWindow-actionArea">
                 <property name="can_focus">False</property>
@@ -111,6 +112,59 @@
                         <property name="show_tabs">False</property>
                         <property name="show_border">False</property>
                         <child>
+                          <object class="GtkGrid" id="configurationGrid">
+                            <property name="visible">True</property>
+                            <property name="can_focus">False</property>
+                            <property name="hexpand">True</property>
+                            <property name="row_spacing">6</property>
+                            <child>
+                              <object class="GtkLabel" id="configurationLabel">
+                                <property name="visible">True</property>
+                                <property name="can_focus">False</property>
+                                <property name="halign">end</property>
+                                <property name="label" translatable="yes">%s is now successfully installed on your system, but some configuration still needs to be done. 
+Finish it and then click the Finish configuration button please.</property>
+                              </object>
+                              <packing>
+                                <property name="left_attach">1</property>
+                                <property name="top_attach">0</property>
+                                <property name="width">1</property>
+                                <property name="height">1</property>
+                              </packing>
+                            </child>
+                            <child>
+                              <object class="GtkButton" id="configureButton">
+                                <property name="label" translatable="yes">_Finish configuration</property>
+                                <property name="use_action_appearance">False</property>
+                                <property name="visible">True</property>
+                                <property name="can_focus">True</property>
+                                <property name="receives_default">True</property>
+                                <property name="halign">end</property>
+                                <property name="valign">end</property>
+                                <property name="hexpand">True</property>
+                                <property name="use_action_appearance">False</property>
+                                <property name="use_underline">True</property>
+                                <signal name="clicked" handler="_do_configuration" swapped="no"/>
+                              </object>
+                              <packing>
+                                <property name="left_attach">1</property>
+                                <property name="top_attach">1</property>
+                                <property name="width">1</property>
+                                <property name="height">1</property>
+                              </packing>
+                            </child>
+                            <child>
+                              <placeholder/>
+                            </child>
+                            <child>
+                              <placeholder/>
+                            </child>
+                          </object>
+                          <packing>
+                            <property name="position">3</property>
+                          </packing>
+                        </child>
+                        <child>
                           <object class="GtkGrid" id="rebootGrid">
                             <property name="visible">True</property>
                             <property name="can_focus">False</property>
@@ -159,7 +213,7 @@ for you to use!  Go ahead and reboot to start using it!</property>
                             </child>
                           </object>
                           <packing>
-                            <property name="position">1</property>
+                            <property name="position">3</property>
                           </packing>
                         </child>
                         <child type="tab">
diff --git a/pyanaconda/ui/gui/hubs/progress.py b/pyanaconda/ui/gui/hubs/progress.py
index 4033d7b..c1fd030 100644
--- a/pyanaconda/ui/gui/hubs/progress.py
+++ b/pyanaconda/ui/gui/hubs/progress.py
@@ -49,8 +49,33 @@ class ProgressHub(Hub):
 
         self._totalSteps = 0
         self._currentStep = 0
+        self._configurationDone = False
 
-    def _update_progress(self):
+    def _do_configuration(self, widget = None, reenable_ransom = True):
+        from pyanaconda.install import doConfiguration
+        from pyanaconda.threads import threadMgr, AnacondaThread
+
+        assert self._configurationDone == False
+        
+        self._configurationDone = True
+        
+        # Disable all personalization spokes
+        self.builder.get_object("progressWindow-scroll").set_sensitive(False)
+
+        if reenable_ransom:
+            self._start_ransom_notes()
+            
+        self._progress_id = GLib.timeout_add(250, self._update_progress, self._configuration_done)
+        threadMgr.add(AnacondaThread(name="AnaConfigurationThread", target=doConfiguration,
+                                     args=(self.storage, self.payload, self.data, self.instclass)))
+
+    def _start_ransom_notes(self):
+        # Adding this as a timeout below means it'll get called after 60
+        # seconds, so we need to do the first call manually.
+        self._cycle_rnotes()
+        self._rnotes_id = GLib.timeout_add_seconds(60, self._cycle_rnotes)
+        
+    def _update_progress(self, callback = None):
         from pyanaconda import progress
         import Queue
 
@@ -78,13 +103,8 @@ class ProgressHub(Hub):
                 self._progress_bar_complete()
                 q.task_done()
 
-                GLib.source_remove(self._rnotes_id)
-
-                self._progressNotebook.set_current_page(0)
-
-                # kickstart install, continue automatically if reboot or shutdown selected
-                if flags.automatedInstall and self.data.reboot.action in [KS_REBOOT, KS_SHUTDOWN]:
-                    self.continueButton.emit("clicked")
+                if callback:
+                    callback()
 
                 return False
 
@@ -92,6 +112,31 @@ class ProgressHub(Hub):
 
         return True
 
+    
+    def _configuration_done(self):
+        # Configuration done, remove ransom notes timer
+        # and switch to the Reboot page
+    
+        GLib.source_remove(self._rnotes_id)
+        self._progressNotebook.set_current_page(1)
+
+        # kickstart install, continue automatically if reboot or shutdown selected
+        if flags.automatedInstall and self.data.reboot.action in [KS_REBOOT, KS_SHUTDOWN]:
+            self.continueButton.emit("clicked")
+
+            
+    def _install_done(self):
+        # package installation done, check personalization spokes
+        # and start the configuration step if all is ready
+        if not self._inSpoke and self.continuePossible:
+            self._do_configuration(reenable_ransom = False)
+            
+        else:
+            # some mandatory spokes are not ready
+            # switch to configure and finish page
+            GLib.source_remove(self._rnotes_id)
+            self._progressNotebook.set_current_page(0)
+            
     def _get_rnotes(self):
         import glob
 
@@ -130,6 +175,9 @@ class ProgressHub(Hub):
         self._progressLabel = self.builder.get_object("progressLabel")
         self._progressNotebook = self.builder.get_object("progressNotebook")
 
+        lbl = self.builder.get_object("configurationLabel")
+        lbl.set_text(lbl.get_text() % productName)
+
         lbl = self.builder.get_object("rebootLabel")
         lbl.set_text(lbl.get_text() % productName)
 
@@ -142,14 +190,14 @@ class ProgressHub(Hub):
                 self._progressNotebook.append_page(img, None)
 
             # An infinite list of the page numbers containing ransom notes images.
-            self._rnotesPages = itertools.cycle(range(1, self._progressNotebook.get_n_pages()-1))
+            self._rnotesPages = itertools.cycle(range(2, self._progressNotebook.get_n_pages()-2))
         else:
             # Add a blank page to the notebook and we'll just cycle to that
             # over and over again.
             blank = Gtk.Box()
             blank.show()
             self._progressNotebook.append_page(blank, None)
-            self._rnotesPages = itertools.cycle([1])
+            self._rnotesPages = itertools.cycle([2])
 
     def refresh(self):
         from pyanaconda.install import doInstall
@@ -157,19 +205,21 @@ class ProgressHub(Hub):
 
         Hub.refresh(self)
 
-        # Adding this as a timeout below means it'll get called after 60
-        # seconds, so we need to do the first call manually.
-        self._cycle_rnotes()
-
-        self._progress_id = GLib.timeout_add(250, self._update_progress)
-        self._rnotes_id = GLib.timeout_add_seconds(60, self._cycle_rnotes)
+        self._start_ransom_notes()
+        self._progress_id = GLib.timeout_add(250, self._update_progress, self._install_done)
         threadMgr.add(AnacondaThread(name="AnaInstallThread", target=doInstall,
                                      args=(self.storage, self.payload, self.data, self.instclass)))
 
+    def _updateContinueButton(self):
+        if self._configurationDone:
+            self.continueButton.set_sensitive(self.continuePossible)
+        else:
+            self.builder.get_object("configureButton").set_sensitive(self.continuePossible)
+            
     @property
     def continueButton(self):
         return self.builder.get_object("rebootButton")
-
+        
     def _init_progress_bar(self, steps):
         self._totalSteps = steps
         self._currentStep = 0
@@ -177,6 +227,9 @@ class ProgressHub(Hub):
         with gdk_threaded():
             self._progressBar.set_fraction(0.0)
 
+            spinner = self.builder.get_object("progressSpinner")
+            spinner.start()
+            
     def _step_progress_bar(self):
         if not self._totalSteps:
             return
diff --git a/pyanaconda/ui/gui/spokes/password.py b/pyanaconda/ui/gui/spokes/password.py
index ac6e353..b3d33de 100644
--- a/pyanaconda/ui/gui/spokes/password.py
+++ b/pyanaconda/ui/gui/spokes/password.py
@@ -30,8 +30,7 @@ import pwquality
 import string
 
 from pyanaconda.ui.gui.spokes import NormalSpoke
-# Need a new category I guess
-from pyanaconda.ui.gui.categories.localization import LocalizationCategory
+from pyanaconda.ui.gui.categories.user_settings import UserSettingsCategory
 #from _isys import isCapsLockEnabled
 
 __all__ = ["PasswordSpoke"]
@@ -43,8 +42,7 @@ class PasswordSpoke(NormalSpoke):
     mainWidgetName = "passwordWindow"
     uiFile = "spokes/password.glade"
 
-    # update this category to something... new?
-    category = LocalizationCategory
+    category = UserSettingsCategory
 
     icon = "dialog-password-symbolic"
     title = N_("ROOT PASSWORD")
@@ -102,7 +100,7 @@ class PasswordSpoke(NormalSpoke):
         # We are by default complete, but locked.  If a user attempts to set
         # a password but fails, then we are no longer complete.
         return not self._error
-
+        
     def _validatePassword(self):
         # Do various steps to validate the password
         # sets self._error to an error string
@@ -164,4 +162,4 @@ class PasswordSpoke(NormalSpoke):
             self.window.clear_info()
             self.window.set_info(Gtk.MessageType.WARNING, self._error)
             self.pw.grab_focus()
-            self.window.show_all()
\ No newline at end of file
+            self.window.show_all()
diff --git a/pyanaconda/ui/tui/hubs/progress.py b/pyanaconda/ui/tui/hubs/progress.py
index cbf384d..e0a0176 100644
--- a/pyanaconda/ui/tui/hubs/progress.py
+++ b/pyanaconda/ui/tui/hubs/progress.py
@@ -80,11 +80,6 @@ class ProgressHub(TUIHub):
                 # There shouldn't be any more progress updates, so return
                 q.task_done()
 
-                # kickstart install, continue automatically if reboot or shutdown selected
-                if flags.automatedInstall and self.data.reboot.action in [KS_REBOOT, KS_SHUTDOWN]:
-                    # Just pretend like we got input, and our input doesn't care
-                    # what it gets, it just quits.
-                    self.input(None, None)
 
                 if self._stepped:
                     print('')
@@ -94,7 +89,7 @@ class ProgressHub(TUIHub):
         return True
 
     def refresh(self, args):
-        from pyanaconda.install import doInstall
+        from pyanaconda.install import doInstall, doConfiguration
         from pyanaconda.threads import threadMgr, AnacondaThread
 
         # We print this here because we don't really use the window object
@@ -105,7 +100,22 @@ class ProgressHub(TUIHub):
                                            self.instclass)))
 
         # This will run until we're all done with the install thread.
-        return self._update_progress()
+        self._update_progress()
+
+        threadMgr.add(AnacondaThread(name="AnaConfigurationThread", target=doConfiguration,
+                                     args=(self.storage, self.payload, self.data,
+                                           self.instclass)))
+
+        # This will run until we're all done with the configuration thread.
+        self._update_progress()
+
+        # kickstart install, continue automatically if reboot or shutdown selected
+        if flags.automatedInstall and self.data.reboot.action in [KS_REBOOT, KS_SHUTDOWN]:
+            # Just pretend like we got input, and our input doesn't care
+            # what it gets, it just quits.
+            self.input(None, None)
+        
+        return True
 
     def prompt(self, args = None):
         return(_("\tInstallation complete.  Press return to quit"))
-- 
1.7.11.4



More information about the anaconda-patches mailing list