[anaconda][master/rhel7-branch][3/3] Add GUI and TUI logic to handle unformatted DASDs. (#1064423)

Samantha N. Bueno sbueno+anaconda at redhat.com
Mon Mar 3 17:53:38 UTC 2014


In both graphical and text, if a user selects unformatted DASDs from the
local disk store, they are given an option to format them  when either
clicking 'Done' or hitting 'c' to continue.

Following dasdfmt, storage initialization is run again in order
to properly add the new DASDs to the devicetree and seen by the
installer.

Resolves:rhbz#1064423
---
 pyanaconda/constants.py             |  1 +
 pyanaconda/ui/gui/spokes/storage.py | 79 ++++++++++++++++++++++++++++++++++++-
 pyanaconda/ui/tui/spokes/storage.py | 68 +++++++++++++++++++++++++++++--
 3 files changed, 142 insertions(+), 6 deletions(-)

diff --git a/pyanaconda/constants.py b/pyanaconda/constants.py
index cf8a8ea..8de29a9 100644
--- a/pyanaconda/constants.py
+++ b/pyanaconda/constants.py
@@ -131,6 +131,7 @@ THREAD_ISCSI_LOGIN = "AnaIscsiLoginThread"
 THREAD_GEOLOCATION_REFRESH = "AnaGeolocationRefreshThread"
 THREAD_DATE_TIME = "AnaDateTimeThread"
 THREAD_TIME_INIT = "AnaTimeInitThread"
+THREAD_DASDFMT = "AnaDasdfmtThread"
 
 # Geolocation constants
 
diff --git a/pyanaconda/ui/gui/spokes/storage.py b/pyanaconda/ui/gui/spokes/storage.py
index 6f719db..4db4ad0 100644
--- a/pyanaconda/ui/gui/spokes/storage.py
+++ b/pyanaconda/ui/gui/spokes/storage.py
@@ -50,17 +50,20 @@ from pyanaconda.ui.gui.spokes.lib.cart import SelectedDisksDialog
 from pyanaconda.ui.gui.spokes.lib.passphrase import PassphraseDialog
 from pyanaconda.ui.gui.spokes.lib.detailederror import DetailedErrorDialog
 from pyanaconda.ui.gui.spokes.lib.resize import ResizeDialog
+from pyanaconda.ui.gui.spokes.lib.dasdfmt import DasdFormatDialog
 from pyanaconda.ui.gui.categories.system import SystemCategory
-from pyanaconda.ui.gui.utils import enlightbox, gtk_call_once, gtk_action_wait
+from pyanaconda.ui.gui.utils import enlightbox, gtk_call_once, gtk_action_wait, gtk_action_nowait, ignoreEscape
 
 from pyanaconda.kickstart import doKickstartStorage, getAvailableDiskSpace
+from blivet import storageInitialize, arch
 from blivet.size import Size
 from blivet.devices import MultipathDevice
-from blivet.errors import StorageError
+from blivet.errors import StorageError, DasdFormatError
 from blivet.errors import SanityError
 from blivet.errors import SanityWarning
 from blivet.platform import platform
 from blivet.devicelibs import swap as swap_lib
+from blivet.devicelibs.dasd import make_unformatted_dasd_list, format_dasd
 from pyanaconda.threads import threadMgr, AnacondaThread
 from pyanaconda.product import productName
 from pyanaconda.flags import flags
@@ -292,6 +295,11 @@ class StorageSpoke(NormalSpoke, StorageChecker):
         self.autoPartType = None
         self.clearPartType = CLEARPART_TYPE_NONE
 
+        if self.data.zerombr.zerombr and arch.isS390():
+            # run dasdfmt on any unformatted DASDs automatically
+            threadMgr.add(AnacondaThread(name=constants.THREAD_DASDFMT,
+                            target=self.run_dasdfmt))
+
         self._previous_autopart = False
 
         self._last_clicked_overview = None
@@ -338,6 +346,7 @@ class StorageSpoke(NormalSpoke, StorageChecker):
                 request.size = swap_lib.swapSuggestion(disk_space=disk_space)
                 break
 
+    @gtk_action_nowait
     def execute(self):
         # Spawn storage execution as a separate thread so there's no big delay
         # going back from this spoke to the hub while StorageChecker.run runs.
@@ -348,6 +357,10 @@ class StorageSpoke(NormalSpoke, StorageChecker):
     def _doExecute(self):
         self._ready = False
         hubQ.send_not_ready(self.__class__.__name__)
+        # ugly and stupid, but on the off-chance dasdfmt is running, we can't
+        # proceed further with this
+        while threadMgr.get(constants.THREAD_DASDFMT):
+            continue
         hubQ.send_message(self.__class__.__name__, _("Saving storage configuration..."))
         try:
             doKickstartStorage(self.storage, self.data, self.instclass)
@@ -400,6 +413,8 @@ class StorageSpoke(NormalSpoke, StorageChecker):
 
         if not self._confirmed:
             msg = _("Not configured")
+        elif threadMgr.get(constants.THREAD_DASDFMT):
+            msg = _("Formatting DASDs")
         elif flags.automatedInstall and not self.storage.rootDevice:
             return msg
         elif self.data.ignoredisk.onlyuse:
@@ -672,6 +687,38 @@ class StorageSpoke(NormalSpoke, StorageChecker):
             if not selected and name in self.selected_disks:
                 self.selected_disks.remove(name)
 
+    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()
+
     # signal handlers
     def on_summary_clicked(self, button):
         # show the selected disks dialog
@@ -760,6 +807,34 @@ class StorageSpoke(NormalSpoke, StorageChecker):
             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 and now needs to go back
+                    # to the main storage spoke.
+                    dialog.window.destroy()
+                    # 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.
diff --git a/pyanaconda/ui/tui/spokes/storage.py b/pyanaconda/ui/tui/spokes/storage.py
index 088684b..a8f8c2e 100644
--- a/pyanaconda/ui/tui/spokes/storage.py
+++ b/pyanaconda/ui/tui/spokes/storage.py
@@ -1,6 +1,6 @@
 # Text storage configuration spoke classes
 #
-# Copyright (C) 2012  Red Hat, Inc.
+# Copyright (C) 2014  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
@@ -25,17 +25,20 @@
 from pyanaconda.ui.lib.disks import getDisks, size_str, applyDiskSelection
 from pyanaconda.ui.tui.spokes import NormalTUISpoke
 from pyanaconda.ui.tui.simpleline import TextWidget, CheckboxWidget
+from pyanaconda.ui.tui.tuiobject import YesNoDialog
 
 from pykickstart.constants import AUTOPART_TYPE_LVM, AUTOPART_TYPE_BTRFS, AUTOPART_TYPE_PLAIN
+from blivet import storageInitialize, arch
 from blivet.size import Size
-from blivet.errors import StorageError
+from blivet.errors import StorageError, DasdFormatError
 from blivet.errors import SanityError
 from blivet.errors import SanityWarning
 from blivet.devices import DASDDevice, FcoeDiskDevice, iScsiDiskDevice, MultipathDevice, ZFCPDiskDevice
+from blivet.devicelibs.dasd import format_dasd, make_unformatted_dasd_list
 from pyanaconda.flags import flags
 from pyanaconda.kickstart import doKickstartStorage
 from pyanaconda.threads import threadMgr, AnacondaThread
-from pyanaconda.constants import THREAD_STORAGE, THREAD_STORAGE_WATCHER
+from pyanaconda.constants import THREAD_STORAGE, THREAD_STORAGE_WATCHER, THREAD_DASDFMT
 from pyanaconda.i18n import _, P_, N_
 from pyanaconda.bootloader import BootLoaderError
 
@@ -75,6 +78,13 @@ class StorageSpoke(NormalTUISpoke):
         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
@@ -89,7 +99,7 @@ class StorageSpoke(NormalTUISpoke):
     def ready(self):
         # By default, the storage spoke is not ready.  We have to wait until
         # storageInitialize is done.
-        return self._ready and not threadMgr.get(THREAD_STORAGE_WATCHER)
+        return self._ready and not (threadMgr.get(THREAD_STORAGE_WATCHER) or threadMgr.get(THREAD_DASDFMT))
 
     @property
     def mandatory(self):
@@ -231,6 +241,15 @@ class StorageSpoke(NormalTUISpoke):
 
         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)
@@ -247,6 +266,47 @@ class StorageSpoke(NormalTUISpoke):
         except (ValueError, KeyError, IndexError):
             return key
 
+    def run_dasdfmt(self, to_format):
+        """
+        This generates the list of DASDs requiring dasdfmt and runs dasdfmt
+        against them.
+        """
+        # 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)
+
+        # ask user to verify they want to format if zerombr not in ks file
+        if not self.data.zerombr.zerombr:
+            # prepare our msg strings; copied directly from dasdfmt.glade
+            summary = _("The following unformatted DASDs have been detected on your system. You can choose to format them now with dasdfmt or cancel to leave them unformatted. Unformatted DASDs can not 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 DASDs will remain so unless zerombr
+            # is 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(str(err))
+                continue
+
+        # when finished formatting we need to reinitialize storage
+        protectedNames = [d.name for d in self.storage.protectedDevices]
+        storageInitialize(self.storage, self.data, protectedNames)
+
     def apply(self):
         self.autopart = self.data.autopart.autopart
         self.data.ignoredisk.onlyuse = self.selected_disks[:]
-- 
1.8.3.1



More information about the anaconda-patches mailing list