[PATCH 2/2] Make sure software selection is checked against filesystem space. (#853636)

David Lehman dlehman at redhat.com
Tue Dec 11 16:33:14 UTC 2012


---
 pyanaconda/storage/__init__.py      |   25 +++++++++++++++
 pyanaconda/ui/gui/hubs/__init__.py  |   14 ++++++--
 pyanaconda/ui/gui/hubs/summary.py   |   27 ++++++++++++++++
 pyanaconda/ui/gui/spokes/storage.py |    4 +-
 pyanaconda/ui/lib/space.py          |   57 +++++++++++++++++++++++++++++++++++
 5 files changed, 121 insertions(+), 6 deletions(-)
 create mode 100644 pyanaconda/ui/lib/__init__.py
 create mode 100644 pyanaconda/ui/lib/space.py

diff --git a/pyanaconda/storage/__init__.py b/pyanaconda/storage/__init__.py
index 4ac19e1..c914eef 100644
--- a/pyanaconda/storage/__init__.py
+++ b/pyanaconda/storage/__init__.py
@@ -1410,6 +1410,31 @@ class Storage(object):
         self.devicetree.setDiskImages(self.config.diskImages)
         self.devicetree.setupDiskImages()
 
+    @property
+    def fileSystemFreeSpace(self):
+        mountpoints = ["/", "/usr"]
+        free = 0
+        btrfs_volumes = []
+        for mountpoint in mountpoints:
+            device = self.mountpoints.get(mountpoint)
+            if not device:
+                continue
+
+            # don't count the size of btrfs volumes repeatedly when multiple
+            # subvolumes are present
+            if isinstance(device, BTRFSSubVolumeDevice):
+                if device.volume in btrfs_volumes:
+                    continue
+                else:
+                    btrfs_volumes.append(device.volume)
+
+            if device.format.exists:
+                free += device.format.free
+            else:
+                free += device.size
+
+        return free
+
     def sanityCheck(self):
         """ Run a series of tests to verify the storage configuration.
 
diff --git a/pyanaconda/ui/gui/hubs/__init__.py b/pyanaconda/ui/gui/hubs/__init__.py
index c0df863..25907e0 100644
--- a/pyanaconda/ui/gui/hubs/__init__.py
+++ b/pyanaconda/ui/gui/hubs/__init__.py
@@ -89,6 +89,8 @@ class Hub(GUIObject, common.Hub):
         self._notReadySpokes = []
         self._spokes = {}
 
+        self._checker = None
+
     def _runSpoke(self, action):
         from gi.repository import Gtk
 
@@ -214,10 +216,11 @@ class Hub(GUIObject, common.Hub):
             if spoke not in self._incompleteSpokes:
                 self._incompleteSpokes.append(spoke)
 
-        self._updateContinueButton()
-
+        self.clear_info()
         if len(self._incompleteSpokes) == 0:
-            self.clear_info()
+            if self._checker and not self._checker.check():
+                self.set_warning(self._checker.error_message)
+                self.window.show_all()
         else:
             if flags.automatedInstall:
                 msg = _("When all items marked with this icon are complete, installation will automatically continue.")
@@ -225,10 +228,13 @@ class Hub(GUIObject, common.Hub):
                 msg = _("Please complete items marked with this icon before continuing to the next step.")
 
             self.set_warning(msg)
+            self.window.show_all()
+
+        self._updateContinueButton()
 
     @property
     def continuePossible(self):
-        return len(self._incompleteSpokes) == 0 and len(self._notReadySpokes) == 0
+        return len(self._incompleteSpokes) == 0 and len(self._notReadySpokes) == 0 and getattr(self._checker, "success", True)
         
     def _updateContinueButton(self):
         self.continueButton.set_sensitive(self.continuePossible)
diff --git a/pyanaconda/ui/gui/hubs/summary.py b/pyanaconda/ui/gui/hubs/summary.py
index c867e49..cdae0ca 100644
--- a/pyanaconda/ui/gui/hubs/summary.py
+++ b/pyanaconda/ui/gui/hubs/summary.py
@@ -20,6 +20,7 @@
 #
 
 from pyanaconda.ui.gui.hubs import Hub
+from pyanaconda.ui.lib.space import FileSystemSpaceChecker
 
 __all__ = ["SummaryHub"]
 
@@ -30,6 +31,32 @@ class SummaryHub(Hub):
 
     # FIXME:  I really hate this.
 
+    def __init__(self, data, storage, payload, instclass):
+        """Create a new Hub instance.
+
+           The arguments this base class accepts defines the API that Hubs
+           have to work with.  A Hub does not get free reign over everything
+           in the anaconda class, as that would be a big mess.  Instead, a
+           Hub may count on the following:
+
+           ksdata       -- An instance of a pykickstart Handler object.  The
+                           Hub uses this to populate its UI with defaults
+                           and to pass results back after it has run.
+           storage      -- An instance of storage.Storage.  This is useful for
+                           determining what storage devices are present and how
+                           they are configured.
+           payload      -- An instance of a packaging.Payload subclass.  This
+                           is useful for displaying and selecting packages to
+                           install, and in carrying out the actual installation.
+           instclass    -- An instance of a BaseInstallClass subclass.  This
+                           is useful for determining distribution-specific
+                           installation information like default package
+                           selections and default partitioning.
+        """
+        super(SummaryHub, self).__init__(data, storage, payload, instclass)
+
+        self._checker = FileSystemSpaceChecker(storage, payload)
+
     @property
     def continueButton(self):
         return self.builder.get_object("continueButton")
diff --git a/pyanaconda/ui/gui/spokes/storage.py b/pyanaconda/ui/gui/spokes/storage.py
index 0d73867..6bd71ec 100644
--- a/pyanaconda/ui/gui/spokes/storage.py
+++ b/pyanaconda/ui/gui/spokes/storage.py
@@ -399,7 +399,7 @@ class StorageSpoke(NormalSpoke, StorageChecker):
             doKickstartStorage(self.storage, self.data, self.instclass)
         except StorageError as e:
             log.error("storage configuration failed: %s" % e)
-            self.errors = str(e).split("\n")
+            StorageChecker.errors = str(e).split("\n")
             communication.send_message(self.__class__.__name__,
                                    _("Failed to save storage configuration..."))
             self.data.ignoredisk.drives = []
@@ -412,7 +412,7 @@ class StorageSpoke(NormalSpoke, StorageChecker):
         else:
             if self.autopart:
                 # this was already run as part of doAutoPartition. dumb.
-                self.errors = []
+                StorageChecker.errors = []
                 self.run()
         finally:
             self._ready = True
diff --git a/pyanaconda/ui/lib/__init__.py b/pyanaconda/ui/lib/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/pyanaconda/ui/lib/space.py b/pyanaconda/ui/lib/space.py
new file mode 100644
index 0000000..c7601d3
--- /dev/null
+++ b/pyanaconda/ui/lib/space.py
@@ -0,0 +1,57 @@
+# User interface library functions for filesystem/disk space checking
+#
+# Copyright (C) 2012  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): David Lehman <dlehman at redhat.com>
+#
+
+from pyanaconda.storage.size import Size
+
+import gettext
+
+_ = lambda x: gettext.ldgettext("anaconda", x)
+N_ = lambda x: x
+
+import logging
+log = logging.getLogger("anaconda")
+
+class FileSystemSpaceChecker(object):
+    error_template = N_("Not enough space in filesystems for the current "
+                        "software selection. An additional %s is needed.")
+
+    def __init__(self, storage, payload):
+        self.payload = payload
+        self.storage = storage
+
+        self.reset()
+
+    def reset(self):
+        self.success = False
+        self.deficit = Size(bytes=0)
+        self.error_message = ""
+
+    def check(self):
+        self.reset()
+        free = Size(spec="%.2f MB" % self.storage.fileSystemFreeSpace)
+        needed = self.payload.spaceRequired
+        log.info("fs space: %s  needed: %s" % (free, needed))
+        self.success = (free >= needed)
+        if not self.success:
+            self.deficit = needed - free
+            self.error_message = _(self.error_template) % self.deficit
+
+        return self.success
-- 
1.7.7.6



More information about the anaconda-patches mailing list