This adds $SUBJECT in such a way as to make it easy to also add other checks that must pass in order for installation to proceed from the summary hub. Nothing is done in threads since this check is a quick one. The check is only performed once all the spokes are completed, and the hub's info text and continue button both reflect the outcome of the check.
I did some pretty basic testing: start with a previous autopart install w/ plain partitions, mark old /boot for reformat and set it as /, save, observe warning text and continue button state, reenter custom, switch / to /boot, mark old / for reformat and set mountpoint to /, return to hub, observe window info cleared and continue button sensitive.
The numbers in patch 1 are empirical, based on recent smoke composes on x86_64.
--- pyanaconda/packaging/yumpayload.py | 5 +++-- 1 files changed, 3 insertions(+), 2 deletions(-)
diff --git a/pyanaconda/packaging/yumpayload.py b/pyanaconda/packaging/yumpayload.py index f1de826..404d68e 100644 --- a/pyanaconda/packaging/yumpayload.py +++ b/pyanaconda/packaging/yumpayload.py @@ -156,7 +156,7 @@ class YumPayload(PackagePayload): # This value comes from a default install of the x86_64 Fedora 18. It # is meant as a best first guess only. Once package metadata is # available we can use that as a better value. - self._space_required = Size(spec="2800 MB") + self._space_required = Size(spec="3000 MB")
self._groups = None self._packages = [] @@ -1123,7 +1123,8 @@ reposdir=%s for txmbr in self._yum.tsInfo.getMembers(): total += getattr(txmbr.po, "installedsize", 0)
- total += total * 0.10 # add 10% to account for metadata, &c + total += total * 0.35 # add 35% to account for the fact that the above + # method is laughably inaccurate self._space_required = Size(bytes=total)
return self._space_required
--- 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@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
@@ -30,6 +31,32 @@ class SummaryHub(Hub):
# FIXME: I really hate this.
This comment applied to the continueButton and rebootButton properties. You can just get rid of it though. I guess I don't hate it enough to do anything about it.
These patches look fine.
- Chris
anaconda-patches@lists.fedorahosted.org