Esempio n. 1
0
    def __init__(self, app, data, storage, payload, instclass):
        NormalTUISpoke.__init__(self, app, data, storage, payload, instclass)

        self._ready = False
        self.selected_disks = self.data.ignoredisk.onlyuse[:]
        self.selection = None

        self.autopart = None
        self.clearPartType = None

        # This list gets set up once in initialize and should not be modified
        # except perhaps to add advanced devices. It will remain the full list
        # of disks that can be included in the install.
        self.disks = []
        self.errors = []
        self.warnings = []

        if self.data.zerombr.zerombr and arch.isS390():
            # if zerombr is specified in a ks file and there are unformatted
            # dasds, automatically format them
            to_format = make_unformatted_dasd_list(self.selected_disks)
            if to_format:
                self.run_dasdfmt(to_format)

        if not flags.automatedInstall:
            # default to using autopart for interactive installs
            self.data.autopart.autopart = True
Esempio n. 2
0
    def input(self, args, key):
        """Grab the disk choice and update things"""

        if key == "c":
            if self.selected_disks:
                # check selected disks to see if we have any unformatted DASDs
                # if we're on s390x, since they need to be formatted before we
                # can use them.
                if arch.isS390():
                    to_format = make_unformatted_dasd_list(self.selected_disks)
                    if to_format:
                        self.run_dasdfmt(to_format)
                        return None

                newspoke = AutoPartSpoke(self.app, self.data, self.storage,
                                         self.payload, self.instclass)
                self.app.switch_screen_modal(newspoke)
                self.apply()
                self.execute()
                self.close()
            return None

        try:
            number = int(key)
            self._update_disk_list(self.disks[number - 1])
            return None

        except (ValueError, KeyError, IndexError):
            return key
Esempio n. 3
0
    def input(self, args, key):
        """Grab the disk choice and update things"""

        try:
            keyid = int(key) - 1
            self.selection = keyid
            if len(self.disks) > 1 and keyid == len(self.disks):
                self._select_all_disks()
            else:
                self._update_disk_list(self.disks[keyid])
            return INPUT_PROCESSED
        except (ValueError, IndexError):
            if key.lower() == "c":
                if self.selected_disks:
                    # check selected disks to see if we have any unformatted DASDs
                    # if we're on s390x, since they need to be formatted before we
                    # can use them.
                    if arch.isS390():
                        to_format = make_unformatted_dasd_list(self.selected_disks)
                        if to_format:
                            self.run_dasdfmt(to_format)
                            return None

                    newspoke = AutoPartSpoke(self.app, self.data, self.storage,
                                             self.payload, self.instclass)
                    self.app.switch_screen_modal(newspoke)
                    self.apply()
                    self.execute()
                    self.close()
                return INPUT_PROCESSED
            else:
                return key
Esempio n. 4
0
    def run_dasdfmt(self):
        """
        Though the same function exists in pyanaconda.ui.gui.spokes.lib.dasdfmt,
        this instance doesn't include any of the UI pieces and should only
        really be getting called on ks installations with "zerombr".
        """
        # wait for the initial storage thread to complete before taking any new
        # actions on storage devices
        threadMgr.wait(constants.THREAD_STORAGE)

        to_format = make_unformatted_dasd_list(self.selected_disks)
        if not to_format:
            # nothing to do here; bail
            return

        hubQ.send_message(self.__class__.__name__, _("Formatting DASDs"))
        for disk in to_format:
            try:
                format_dasd(disk)
            except DasdFormatError as err:
                # Log errors if formatting fails, but don't halt the installer
                log.error(str(err))
                continue

        # now re-initialize storage to pick up the newly formatted disks
        protectedNames = [d.name for d in self.storage.protectedDevices]
        storageInitialize(self.storage, self.data, protectedNames)

        # I really hate doing this, but the way is the way; probably the most
        # correct way to kajigger the storage spoke into becoming ready
        self.execute()
Esempio n. 5
0
    def run_dasdfmt(self):
        """
        Though the same function exists in pyanaconda.ui.gui.spokes.lib.dasdfmt,
        this instance doesn't include any of the UI pieces and should only
        really be getting called on ks installations with "zerombr".
        """
        # wait for the initial storage thread to complete before taking any new
        # actions on storage devices
        threadMgr.wait(constants.THREAD_STORAGE)

        to_format = make_unformatted_dasd_list(self.selected_disks)
        if not to_format:
            # nothing to do here; bail
            return

        hubQ.send_message(self.__class__.__name__, _("Formatting DASDs"))
        for disk in to_format:
            try:
                format_dasd(disk)
            except DasdFormatError as err:
                # Log errors if formatting fails, but don't halt the installer
                log.error(str(err))
                continue

        # now re-initialize storage to pick up the newly formatted disks
        protectedNames = [d.name for d in self.storage.protectedDevices]
        storageInitialize(self.storage, self.data, protectedNames)

        # I really hate doing this, but the way is the way; probably the most
        # correct way to kajigger the storage spoke into becoming ready
        self.execute()
Esempio n. 6
0
    def input(self, args, key):
        """Grab the disk choice and update things"""
        self.errors = []
        try:
            keyid = int(key) - 1
            self.selection = keyid
            if len(self.disks) > 1 and keyid == len(self.disks):
                self._select_all_disks()
            else:
                self._update_disk_list(self.disks[keyid])
            return INPUT_PROCESSED
        except (ValueError, IndexError):
            # TRANSLATORS: 'c' to continue
            if key.lower() == C_('TUI|Spoke Navigation', 'c'):
                if self.selected_disks:
                    # check selected disks to see if we have any unformatted or
                    # LDL DASDs if we're on s390x, since they need to be
                    # formatted before we can use them.
                    if arch.isS390():
                        unformatted = make_unformatted_dasd_list(
                            self.selected_disks)
                        ldl = [
                            d for d in self.selected_disks if is_ldl_dasd(d)
                        ]
                        # combine into one nice list
                        dasds = list(set(unformatted + ldl))
                        if dasds:
                            self.run_dasdfmt(dasds)
                            return None

                    # make sure no containers were split up by the user's disk
                    # selection
                    self.errors.extend(
                        checkDiskSelection(self.storage, self.selected_disks))
                    if self.errors:
                        # The disk selection has to make sense before we can
                        # proceed.
                        return None

                    newspoke = AutoPartSpoke(self.app, self.data, self.storage,
                                             self.payload, self.instclass)
                    self.app.switch_screen_modal(newspoke)
                    self.apply()
                    self.execute()
                    self.close()
                return INPUT_PROCESSED
            else:
                return key
Esempio n. 7
0
    def run_dasdfmt(self):
        """
        Though the same function exists in pyanaconda.ui.gui.spokes.lib.dasdfmt,
        this instance doesn't include any of the UI pieces and should only
        really be getting called on ks installations with "zerombr".
        """
        # wait for the initial storage thread to complete before taking any new
        # actions on storage devices
        threadMgr.wait(constants.THREAD_STORAGE)

        to_format = make_unformatted_dasd_list(d.name for d in getDisks(self.storage.devicetree))
        if not to_format:
            # nothing to do here; bail
            return

        hubQ.send_message(self.__class__.__name__, _("Formatting DASDs"))
        for disk in to_format:
            try:
                format_dasd(disk)
            except DasdFormatError as err:
                # Log errors if formatting fails, but don't halt the installer
                log.error(str(err))
                continue
Esempio n. 8
0
    def run_dasdfmt(self):
        """
        Though the same function exists in pyanaconda.ui.gui.spokes.lib.dasdfmt,
        this instance doesn't include any of the UI pieces and should only
        really be getting called on ks installations with "zerombr".
        """
        # wait for the initial storage thread to complete before taking any new
        # actions on storage devices
        threadMgr.wait(constants.THREAD_STORAGE)

        to_format = make_unformatted_dasd_list(
            d.name for d in getDisks(self.storage.devicetree))
        if not to_format:
            # nothing to do here; bail
            return

        hubQ.send_message(self.__class__.__name__, _("Formatting DASDs"))
        for disk in to_format:
            try:
                format_dasd(disk)
            except DasdFormatError as err:
                # Log errors if formatting fails, but don't halt the installer
                log.error(str(err))
                continue
Esempio n. 9
0
    def on_back_clicked(self, button):
        # We can't exit early if it looks like nothing has changed because the
        # user might want to change settings presented in the dialogs shown from
        # within this method.

        # Remove all non-existing devices if autopart was active when we last
        # refreshed.
        if self._previous_autopart:
            self._previous_autopart = False
            for partition in self.storage.partitions[:]:
                # check if it's been removed in a previous iteration
                if not partition.exists and \
                   partition in self.storage.partitions:
                    self.storage.recursiveRemove(partition)

        # hide/unhide disks as requested
        for disk in self.disks:
            if disk.name not in self.selected_disks and \
               disk in self.storage.devices:
                self.storage.devicetree.hide(disk)
            elif disk.name in self.selected_disks and \
                 disk not in self.storage.devices:
                self.storage.devicetree.unhide(disk)

        # show the installation options dialog
        disks = [d for d in self.disks if d.name in self.selected_disks]
        disks_size = sum((d.size for d in disks), Size(bytes=0))

        # No disks selected?  The user wants to back out of the storage spoke.
        if not disks:
            NormalSpoke.on_back_clicked(self, button)
            return

        if arch.isS390():
            # check for unformatted DASDs and launch dasdfmt if any discovered
            dasds = make_unformatted_dasd_list(self.selected_disks)
            if len(dasds) > 0:
                dialog = DasdFormatDialog(self.data, self.storage, dasds)
                ignoreEscape(dialog.window)
                rc = self.run_lightbox_dialog(dialog)
                if rc == 1:
                    # User hit OK on the dialog, indicating they stayed on the
                    # dialog until formatting completed; make sure we stay on
                    # the storage spoke and don't return to the summary hub
                    self.skipTo = "StorageSpoke"
                    # we have to manaually call refresh so changes are picked up
                    self.refresh()
                elif rc == 2:
                    # User clicked uri to return to hub.
                    NormalSpoke.on_back_clicked(self, button)
                    return
                elif rc != 2:
                    # User either hit cancel on the dialog or closed it via escape,
                    # there was no formatting done.
                    # NOTE: rc == 2 means the user clicked on the link that takes t
                    # back to the hub.
                    return

        # Figure out if the existing disk labels will work on this platform
        # you need to have at least one of the platform's labels in order for
        # any of the free space to be useful.
        disk_labels = set(disk.format.labelType for disk in disks
                              if hasattr(disk.format, "labelType"))
        platform_labels = set(platform.diskLabelTypes)
        if disk_labels and platform_labels.isdisjoint(disk_labels):
            disk_free = 0
            fs_free = 0
            log.debug("Need disklabel: %s have: %s", ", ".join(platform_labels),
                                                     ", ".join(disk_labels))
        else:
            free_space = self.storage.getFreeSpace(disks=disks,
                                                   clearPartType=CLEARPART_TYPE_NONE)
            disk_free = sum(f[0] for f in free_space.itervalues())
            fs_free = sum(f[1] for f in free_space.itervalues())

        required_space = self.payload.spaceRequired
        auto_swap = sum((r.size for r in self.storage.autoPartitionRequests
                                if r.fstype == "swap"), Size(bytes=0))

        log.debug("disk free: %s  fs free: %s  sw needs: %s  auto swap: %s",
                  disk_free, fs_free, required_space, auto_swap)

        if disk_free >= required_space + auto_swap:
            dialog = None
        elif disks_size >= required_space:
            if self._customPart.get_active() or self._reclaim.get_active():
                dialog = None
            else:
                dialog = NeedSpaceDialog(self.data, payload=self.payload)
                dialog.refresh(required_space, auto_swap, disk_free, fs_free)
                rc = self.run_lightbox_dialog(dialog)
        else:
            dialog = NoSpaceDialog(self.data, payload=self.payload)
            dialog.refresh(required_space, auto_swap, disk_free, fs_free)
            rc = self.run_lightbox_dialog(dialog)

        if not dialog:
            # Plenty of room - there's no need to pop up a dialog, so just send
            # the user to wherever they asked to go.  That's either the custom
            # spoke or the hub.
            #    - OR -
            # Not enough room, but the user checked the reclaim button.

            self.encrypted = self._encrypted.get_active()

            if self._customPart.get_active():
                self.autopart = False
                self.skipTo = "CustomPartitioningSpoke"
            else:
                self.autopart = True

                # We might first need to ask about an encryption passphrase.
                if not self._check_encrypted():
                    return

                # Oh and then we might also want to go to the reclaim dialog.
                if self._reclaim.get_active():
                    self.apply()
                    if not self._show_resize_dialog(disks):
                        # User pressed cancel on the reclaim dialog, so don't leave
                        # the storage spoke.
                        return
        elif rc == RESPONSE_CANCEL:
            # A cancel button was clicked on one of the dialogs.  Stay on this
            # spoke.  Generally, this is because the user wants to add more disks.
            return
        elif rc == RESPONSE_MODIFY_SW:
            # The "Fedora software selection" link was clicked on one of the
            # dialogs.  Send the user to the software spoke.
            self.skipTo = "SoftwareSelectionSpoke"
        elif rc == RESPONSE_RECLAIM:
            # Not enough space, but the user can make enough if they do some
            # work and free up space.
            self.encrypted = self._encrypted.get_active()

            if not self._check_encrypted():
                return

            self.apply()
            if not self._show_resize_dialog(disks):
                # User pressed cancel on the reclaim dialog, so don't leave
                # the storage spoke.
                return

            # And then go to the custom partitioning spoke if they chose to
            # do so.
            if self._customPart.get_active():
                self.autopart = False
                self.skipTo = "CustomPartitioningSpoke"
            else:
                self.autopart = True
        elif rc == RESPONSE_QUIT:
            # Not enough space, and the user can't do anything about it so
            # they chose to quit.
            raise SystemExit("user-selected exit")
        else:
            # I don't know how we'd get here, but might as well have a
            # catch-all.  Just stay on this spoke.
            return

        self.applyOnSkip = True
        NormalSpoke.on_back_clicked(self, button)
Esempio n. 10
0
    def run_dasdfmt(self, to_format=None):
        """
        This generates the list of DASDs requiring dasdfmt and runs dasdfmt
        against them.

        to_format is an optional list of DASDs to format. This shouldn't be
        passed if run_dasdfmt is called during a ks installation, and if called
        during a manual installation, a list of DASDs needs to be passed.
        """
        if not to_format:
            # go ahead and initialize this
            to_format = []

        # if the storage thread is running, wait on it to complete before taking
        # any further actions on devices; most likely to occur if user has
        # zerombr in their ks file
        threadMgr.wait(THREAD_STORAGE)

        if flags.automatedInstall:
            # automated install case
            unformatted = []
            ldl = []

            if self.data.zerombr.zerombr:
                # unformatted DASDs
                unformatted += make_unformatted_dasd_list(
                    [d.name for d in getDisks(self.storage.devicetree)])
            if self.data.clearpart.cdl:
                # LDL DASDs
                ldl += [
                    d.name for d in self.storage.devicetree.dasd
                    if is_ldl_dasd(d.name)
                ]
            # combine into one nice list
            to_format = list(set(unformatted + ldl))
        else:
            # manual install; ask to verify they want to run dasdfmt
            # prepare our msg strings; copied directly from dasdfmt.glade
            summary = _(
                "The following unformatted or LDL DASDs have been "
                "detected on your system. You can choose to format them "
                "now with dasdfmt or cancel to leave them unformatted. "
                "Unformatted DASDs cannot be used during installation.\n\n")

            warntext = _(
                "Warning: All storage changes made using the installer will be lost when you choose to format.\n\nProceed to run dasdfmt?\n"
            )

            displaytext = summary + "\n".join(
                "/dev/" + d for d in to_format) + "\n" + warntext

            # now show actual prompt; note -- in cmdline mode, auto-answer for
            # this is 'no', so unformatted and ldl DASDs will remain so unless
            # zerombr or cdl are added to the ks file
            question_window = YesNoDialog(self._app, displaytext)
            self._app.switch_screen_modal(question_window)
            if not question_window.answer:
                # no? well fine then, back to the storage spoke with you;
                return None

        for disk in to_format:
            try:
                print(_("Formatting /dev/%s. This may take a moment.") % disk)
                format_dasd(disk)
            except DasdFormatError as err:
                # Log errors if formatting fails, but don't halt the installer
                log.error("dasdfmt /dev/%s failed: %s", disk, err)
                continue

        # need to make devicetree aware of disk changes
        self.storage.devicetree.populate()
        if not flags.automatedInstall:
            # reinit storage
            threadMgr.add(
                AnacondaThread(
                    name=THREAD_STORAGE,
                    target=storageInitialize,
                    args=(self.storage, self.data,
                          self.storage.devicetree.protectedDevNames)))
            # update the summary screen with the changes
            self._initialize()
Esempio n. 11
0
    def on_back_clicked(self, button):
        # We can't exit early if it looks like nothing has changed because the
        # user might want to change settings presented in the dialogs shown from
        # within this method.

        # Remove all non-existing devices if autopart was active when we last
        # refreshed.
        if self._previous_autopart:
            self._previous_autopart = False
            for partition in self.storage.partitions[:]:
                # check if it's been removed in a previous iteration
                if not partition.exists and \
                   partition in self.storage.partitions:
                    self.storage.recursiveRemove(partition)

        # hide/unhide disks as requested
        for disk in self.disks:
            if disk.name not in self.selected_disks and \
               disk in self.storage.devices:
                self.storage.devicetree.hide(disk)
            elif disk.name in self.selected_disks and \
                 disk not in self.storage.devices:
                self.storage.devicetree.unhide(disk)

        # show the installation options dialog
        disks = [d for d in self.disks if d.name in self.selected_disks]
        disks_size = sum(Size(spec="%f MB" % d.size) for d in disks)

        # No disks selected?  The user wants to back out of the storage spoke.
        if not disks:
            NormalSpoke.on_back_clicked(self, button)
            return

        if arch.isS390():
            # check for unformatted DASDs and launch dasdfmt if any discovered
            dasds = make_unformatted_dasd_list(self.selected_disks)
            if len(dasds) > 0:
                dialog = DasdFormatDialog(self.data, self.storage, dasds)
                ignoreEscape(dialog.window)
                rc = self.run_lightbox_dialog(dialog)
                if rc == 1:
                    # User hit OK on the dialog, indicating they stayed on the
                    # dialog until formatting completed; make sure we stay on
                    # the storage spoke and don't return to the summary hub
                    self.skipTo = "StorageSpoke"
                    # we have to manaually call refresh so changes are picked up
                    self.refresh()
                elif rc == 2:
                    # User clicked uri to return to hub.
                    NormalSpoke.on_back_clicked(self, button)
                    return
                elif rc != 2:
                    # User either hit cancel on the dialog or closed it via escape, so
                    # there was no formatting done.
                    # NOTE: rc == 2 means the user clicked on the link that takes them
                    # back to the hub.
                    return

        # Figure out if the existing disk labels will work on this platform
        # you need to have at least one of the platform's labels in order for
        # any of the free space to be useful.
        disk_labels = set(disk.format.labelType for disk in disks
                          if hasattr(disk.format, "labelType"))
        platform_labels = set(platform.diskLabelTypes)
        if disk_labels and platform_labels.isdisjoint(disk_labels):
            disk_free = 0
            fs_free = 0
            log.debug("Need disklabel: %s have: %s" %
                      (", ".join(platform_labels), ", ".join(disk_labels)))
        else:
            free_space = self.storage.getFreeSpace(
                disks=disks, clearPartType=CLEARPART_TYPE_NONE)
            disk_free = sum(f[0] for f in free_space.itervalues())
            fs_free = sum(f[1] for f in free_space.itervalues())

        required_space = self.payload.spaceRequired
        auto_swap = Size(bytes=0)
        for autoreq in self.storage.autoPartitionRequests:
            if autoreq.fstype == "swap":
                auto_swap += Size(spec="%d MB" % autoreq.size)

        log.debug("disk free: %s  fs free: %s  sw needs: %s  auto swap: %s" %
                  (disk_free, fs_free, required_space, auto_swap))
        if disk_free >= required_space + auto_swap:
            dialog = None
        elif disks_size >= required_space:
            if self._customPart.get_active() or self._reclaim.get_active():
                dialog = None
            else:
                dialog = InstallOptions1Dialog(self.data, payload=self.payload)
                dialog.refresh(required_space, auto_swap, disk_free, fs_free)
                rc = self.run_lightbox_dialog(dialog)
        else:
            dialog = InstallOptions2Dialog(self.data, payload=self.payload)
            dialog.refresh(required_space, auto_swap, disk_free, fs_free)
            rc = self.run_lightbox_dialog(dialog)

        if not dialog:
            # Plenty of room - there's no need to pop up a dialog, so just send
            # the user to wherever they asked to go.  That's either the custom
            # spoke or the hub.
            #    - OR -
            # Not enough room, but the user checked the reclaim button.

            self.encrypted = self._encrypted.get_active()

            if self._customPart.get_active():
                self.autopart = False
                self.skipTo = "CustomPartitioningSpoke"
            else:
                self.autopart = True

                # We might first need to ask about an encryption passphrase.
                if not self._check_encrypted():
                    return

                # Oh and then we might also want to go to the reclaim dialog.
                if self._reclaim.get_active():
                    self.apply()
                    if not self._show_resize_dialog(disks):
                        # User pressed cancel on the reclaim dialog, so don't leave
                        # the storage spoke.
                        return
        elif rc == RESPONSE_CANCEL:
            # A cancel button was clicked on one of the dialogs.  Stay on this
            # spoke.  Generally, this is because the user wants to add more disks.
            return
        elif rc == RESPONSE_MODIFY_SW:
            # The "Fedora software selection" link was clicked on one of the
            # dialogs.  Send the user to the software spoke.
            self.skipTo = "SoftwareSelectionSpoke"
        elif rc == RESPONSE_RECLAIM:
            # Not enough space, but the user can make enough if they do some
            # work and free up space.
            self.encrypted = self._encrypted.get_active()

            if not self._check_encrypted():
                return

            self.apply()
            if not self._show_resize_dialog(disks):
                # User pressed cancel on the reclaim dialog, so don't leave
                # the storage spoke.
                return

            # And then go to the custom partitioning spoke if they chose to
            # do so.
            if self._customPart.get_active():
                self.autopart = False
                self.skipTo = "CustomPartitioningSpoke"
            else:
                self.autopart = True
        elif rc == RESPONSE_QUIT:
            # Not enough space, and the user can't do anything about it so
            # they chose to quit.
            raise SystemExit("user-selected exit")
        else:
            # I don't know how we'd get here, but might as well have a
            # catch-all.  Just stay on this spoke.
            return

        self.applyOnSkip = True
        NormalSpoke.on_back_clicked(self, button)
Esempio n. 12
0
    def on_back_clicked(self, button):
        # We can't exit early if it looks like nothing has changed because the
        # user might want to change settings presented in the dialogs shown from
        # within this method.

        # Do not enter this method multiple times if user clicking multiple times
        # on back button
        if self._back_clicked:
            return
        else:
            self._back_clicked = True

        # Remove all non-existing devices if autopart was active when we last
        # refreshed.
        if self._previous_autopart:
            self._previous_autopart = False
            for partition in self.storage.partitions[:]:
                # check if it's been removed in a previous iteration
                if not partition.exists and \
                   partition in self.storage.partitions:
                    self.storage.recursiveRemove(partition)

        # make sure no containers were split up by the user's disk selection
        self.clear_info()
        self.errors = checkDiskSelection(self.storage, self.selected_disks)
        if self.errors:
            # The disk selection has to make sense before we can proceed.
            self.set_error(
                _("There was a problem with your disk selection. "
                  "Click here for details."))
            self._back_clicked = False
            return

        # hide/unhide disks as requested
        for disk in self.disks:
            if disk.name not in self.selected_disks and \
               disk in self.storage.devices:
                self.storage.devicetree.hide(disk)
            elif disk.name in self.selected_disks and \
                 disk not in self.storage.devices:
                self.storage.devicetree.unhide(disk)

        # show the installation options dialog
        disks = [d for d in self.disks if d.name in self.selected_disks]
        disks_size = sum((d.size for d in disks), Size(0))

        # No disks selected?  The user wants to back out of the storage spoke.
        if not disks:
            NormalSpoke.on_back_clicked(self, button)
            return

        if arch.isS390():
            # check for unformatted DASDs and launch dasdfmt if any discovered
            dasds = make_unformatted_dasd_list(self.selected_disks)
            if len(dasds) > 0:
                # We want to apply current selection before running dasdfmt to
                # prevent this information from being lost afterward
                applyDiskSelection(self.storage, self.data,
                                   self.selected_disks)
                dialog = DasdFormatDialog(self.data, self.storage, dasds)
                ignoreEscape(dialog.window)
                rc = self.run_lightbox_dialog(dialog)
                if rc == 1:
                    # User hit OK on the dialog
                    self.refresh()
                elif rc == 2:
                    # User clicked uri to return to hub.
                    NormalSpoke.on_back_clicked(self, button)
                    return
                elif rc != 2:
                    # User either hit cancel on the dialog or closed it via escape,
                    # there was no formatting done.
                    # NOTE: rc == 2 means the user clicked on the link that takes t
                    # back to the hub.
                    self._back_clicked = False
                    return

        # Figure out if the existing disk labels will work on this platform
        # you need to have at least one of the platform's labels in order for
        # any of the free space to be useful.
        disk_labels = set(disk.format.labelType for disk in disks
                          if hasattr(disk.format, "labelType"))
        platform_labels = set(platform.diskLabelTypes)
        if disk_labels and platform_labels.isdisjoint(disk_labels):
            disk_free = 0
            fs_free = 0
            log.debug("Need disklabel: %s have: %s",
                      ", ".join(platform_labels), ", ".join(disk_labels))
        else:
            free_space = self.storage.getFreeSpace(
                disks=disks, clearPartType=CLEARPART_TYPE_NONE)
            disk_free = sum(f[0] for f in free_space.values())
            fs_free = sum(f[1] for f in free_space.values())

        required_space = self.payload.spaceRequired
        auto_swap = sum((r.size for r in self.storage.autoPartitionRequests
                         if r.fstype == "swap"), Size(0))
        if self.autopart and auto_swap == Size(0):
            # autopartitioning requested, but not applied yet (=> no auto swap
            # requests), ask user for enough space to fit in the suggested swap
            auto_swap = autopart.swapSuggestion()

        log.debug("disk free: %s  fs free: %s  sw needs: %s  auto swap: %s",
                  disk_free, fs_free, required_space, auto_swap)

        if disk_free >= required_space + auto_swap:
            dialog = None
        elif disks_size >= required_space:
            if self._customPart.get_active() or self._reclaim.get_active():
                dialog = None
            else:
                dialog = NeedSpaceDialog(self.data, payload=self.payload)
                dialog.refresh(required_space, auto_swap, disk_free, fs_free)
                rc = self.run_lightbox_dialog(dialog)
        else:
            dialog = NoSpaceDialog(self.data, payload=self.payload)
            dialog.refresh(required_space, auto_swap, disk_free, fs_free)
            rc = self.run_lightbox_dialog(dialog)

        if not dialog:
            # Plenty of room - there's no need to pop up a dialog, so just send
            # the user to wherever they asked to go.  That's either the custom
            # spoke or the hub.
            #    - OR -
            # Not enough room, but the user checked the reclaim button.

            self.encrypted = self._encrypted.get_active()

            if self._customPart.get_active():
                self.autopart = False
                self.skipTo = "CustomPartitioningSpoke"
            else:
                self.autopart = True

                # We might first need to ask about an encryption passphrase.
                if not self._check_encrypted():
                    self._back_clicked = False
                    return

                # Oh and then we might also want to go to the reclaim dialog.
                if self._reclaim.get_active():
                    self.apply()
                    if not self._show_resize_dialog(disks):
                        # User pressed cancel on the reclaim dialog, so don't leave
                        # the storage spoke.
                        self._back_clicked = False
                        return
        elif rc == RESPONSE_CANCEL:
            # A cancel button was clicked on one of the dialogs.  Stay on this
            # spoke.  Generally, this is because the user wants to add more disks.
            self._back_clicked = False
            return
        elif rc == RESPONSE_MODIFY_SW:
            # The "Fedora software selection" link was clicked on one of the
            # dialogs.  Send the user to the software spoke.
            self.skipTo = "SoftwareSelectionSpoke"
        elif rc == RESPONSE_RECLAIM:
            # Not enough space, but the user can make enough if they do some
            # work and free up space.
            self.encrypted = self._encrypted.get_active()

            if not self._check_encrypted():
                return

            self.apply()
            if not self._show_resize_dialog(disks):
                # User pressed cancel on the reclaim dialog, so don't leave
                # the storage spoke.
                self._back_clicked = False
                return

            # And then go to the custom partitioning spoke if they chose to
            # do so.
            if self._customPart.get_active():
                self.autopart = False
                self.skipTo = "CustomPartitioningSpoke"
            else:
                self.autopart = True
        elif rc == RESPONSE_QUIT:
            # Not enough space, and the user can't do anything about it so
            # they chose to quit.
            raise SystemExit("user-selected exit")
        else:
            # I don't know how we'd get here, but might as well have a
            # catch-all.  Just stay on this spoke.
            self._back_clicked = False
            return

        if self.autopart:
            refreshAutoSwapSize(self.storage)
        self.applyOnSkip = True
        NormalSpoke.on_back_clicked(self, button)