[PATCH 3/3] Enable anaconda to use the new rescue mode. (#965985)

Samantha N. Bueno sbueno+anaconda at redhat.com
Mon Jul 20 15:13:23 UTC 2015


On Fri, Jun 19, 2015 at 01:24:50PM +0200, Vratislav Podzimek wrote:
> On Thu, 2015-06-18 at 13:44 -0400, Samantha N. Bueno wrote:
> > This enables the installer to use the new rescue mode, and adds the new
> > rescue mode file, which now uses the current TUI back-end.
> > 
> > There are a couple of small hacks in here, which were unfortunately
> > necessary; otherwise it would mean basically rewriting the text mode
> > code for input handling, which is not prudent at this moment.
> > 
> > Resolves: rhbz#965985
> > ---
> >  anaconda                       |  41 +--
> >  pyanaconda/exception.py        |   2 +-
> >  pyanaconda/rescue.py           | 669 ++++++++++++++++++++---------------------
> >  pyanaconda/ui/tui/tuiobject.py |  47 +++
> >  4 files changed, 388 insertions(+), 371 deletions(-)
> > 
> > diff --git a/anaconda b/anaconda
> > index 360d5ad..4b70591 100755
> > --- a/anaconda
> > +++ b/anaconda
> > @@ -1358,6 +1338,27 @@ if __name__ == "__main__":
> >  
> >      threadMgr.add(AnacondaThread(name=constants.THREAD_WAIT_FOR_CONNECTING_NM, target=wait_for_connecting_NM_thread, args=(ksdata,)))
> >  
> > +    if flags.rescue_mode:
> > +        from pyanaconda.ui.tui.simpleline import App
> > +        from pyanaconda.rescue import RescueMode
> > +        app = App("Rescue Mode")
> > +        spoke = RescueMode(app, anaconda.ksdata, anaconda.storage)
> > +        app.schedule_screen(spoke)
> > +        app.run()
> > +    else:
> > +        cleanPStore()
> > +
> > +    # only install interactive exception handler in interactive modes
> > +    if ksdata.displaymode.displayMode != DISPLAY_MODE_CMDLINE or flags.debug:
> > +        from pyanaconda import exception
> > +        anaconda.mehConfig = exception.initExceptionHandling(anaconda)
> Why is the exception handler initialization moved after the rescue mode
> session? Does our exception handling not work with the new rescue mode?
> If so, we should fix that and enable the exception handler for rescue
> too (in a follow-up patch in the future).

Mostly because that was just oversight on my part. However, moving the
exception handler initialization to before the rescue mode session seems
to produce an error that I just can't seem to debug. I'll take your
advice and fix it in some follow-up patch since I really need/want to
get this set committed.

Thanks for that fourth patch you added as well!

Samantha
 
> > +
> > +    # add our own additional signal handlers
> > +    signal.signal(signal.SIGUSR1, lambda signum, frame:
> > +                  exception.test_exception_handling())
> > +    signal.signal(signal.SIGUSR2, lambda signum, frame: anaconda.dumpState())
> > +    atexit.register(exitHandler, ksdata.reboot, anaconda.storage)
> > +
> >      # Fallback to default for interactive or for a kickstart with no installation method.
> >      fallback = not (flags.automatedInstall and ksdata.method.method)
> >      payloadMgr.restartThread(anaconda.storage, ksdata, anaconda.payload, anaconda.instClass,
> > diff --git a/pyanaconda/exception.py b/pyanaconda/exception.py
> > index b18c6cc..07ff9fc 100644
> > --- a/pyanaconda/exception.py
> > +++ b/pyanaconda/exception.py
> > @@ -252,7 +252,7 @@ def initExceptionHandling(anaconda):
> >                                  "_bootloader.password",
> >                                  "payload._groups",
> >                                  "payload._yum"],
> > -                  localSkipList=[ "passphrase", "password", "_oldweak", "_password" ],
> > +                  localSkipList=[ "passphrase", "password", "_oldweak", "_password" "try_passphrase" ],
> >                    fileList=fileList)
> >  
> >      conf.register_callback("lsblk_output", lsblk_callback, attchmnt_only=True)
> > diff --git a/pyanaconda/rescue.py b/pyanaconda/rescue.py
> > index 63bc0c1..96fa21d 100644
> > --- a/pyanaconda/rescue.py
> > +++ b/pyanaconda/rescue.py
> > @@ -1,7 +1,7 @@
> >  #
> >  # rescue.py - anaconda rescue mode setup
> >  #
> > -# Copyright (C) 2001, 2002, 2003, 2004  Red Hat, Inc.  All rights reserved.
> > +# Copyright (C) 2015 Red Hat, Inc.  All rights reserved.
> >  #
> >  # This program is free software; you can redistribute it and/or modify
> >  # it under the terms of the GNU General Public License as published by
> > @@ -16,113 +16,35 @@
> >  # You should have received a copy of the GNU General Public License
> >  # along with this program.  If not, see <http://www.gnu.org/licenses/>.
> >  #
> > -# Author(s): Mike Fulbright <msf at redhat.com>
> > -#            Jeremy Katz <katzj at redhat.com>
> > +# Author(s): Samantha N. Bueno <sbueno at redhat.com>
> >  #
> > -import sys
> > -import os
> > -from pyanaconda import iutil
> > -import shutil
> > -import time
> > -import re
> > -import subprocess
> > -
> > -from snack import ButtonChoiceWindow, ListboxChoiceWindow,SnackScreen
> > +from blivet import mountExistingSystem, findExistingInstallations
> > +from blivet.errors import StorageError, DirtyFSError
> > +from blivet.devices import LUKSDevice
> >  
> > +from pyanaconda import iutil
> >  from pyanaconda.constants import ANACONDA_CLEANUP
> > -from pyanaconda.constants_text import TEXT_OK_BUTTON, TEXT_NO_BUTTON, TEXT_YES_BUTTON
> > -from pyanaconda.text import WaitWindow, OkCancelWindow, ProgressWindow, PassphraseEntryWindow
> > +from pyanaconda.constants_text import INPUT_PROCESSED
> >  from pyanaconda.flags import flags
> > -from pyanaconda.installinterfacebase import InstallInterfaceBase
> > -from pyanaconda.i18n import _
> > +from pyanaconda.i18n import _, N_
> >  from pyanaconda.kickstart import runPostScripts
> > -
> > -from blivet import mountExistingSystem
> > -from blivet.errors import StorageError, DirtyFSError
> > -from blivet.devices import LUKSDevice
> > +from pyanaconda.ui.tui.simpleline import TextWidget, ColumnWidget, CheckboxWidget
> > +from pyanaconda.ui.tui.spokes import NormalTUISpoke
> > +from pyanaconda.ui.tui.tuiobject import YesNoDialog, PasswordDialog
> >  
> >  from pykickstart.constants import KS_REBOOT, KS_SHUTDOWN
> >  
> > -import meh.ui.text
> > +import os
> > +import shutil
> > +import time
> >  
> >  import logging
> >  log = logging.getLogger("anaconda")
> >  
> > -class RescueInterface(InstallInterfaceBase):
> > -    def waitWindow(self, title, text):
> > -        return WaitWindow(self.screen, title, text)
> > -
> > -    def progressWindow(self, title, text, total, updpct = 0.05, pulse = False):
> > -        return ProgressWindow(self.screen, title, text, total, updpct, pulse)
> > -
> > -    def detailedMessageWindow(self, title, text, longText=None, ty="ok",
> > -                              default=None, custom_icon=None,
> > -                              custom_buttons=None, expanded=False):
> > -        return self.messageWindow(title, text, ty, default, custom_icon,
> > -                                  custom_buttons)
> > -
> > -    def messageWindow(self, title, text, ty = "ok", default = None,
> > -                      custom_icon=None, custom_buttons=None):
> > -        if custom_buttons is None:
> > -            custom_buttons = []
> > -
> > -        if ty == "ok":
> > -            ButtonChoiceWindow(self.screen, title, text, buttons=[TEXT_OK_BUTTON])
> > -        elif ty == "yesno":
> > -            if default and default == "no":
> > -                btnlist = [TEXT_NO_BUTTON, TEXT_YES_BUTTON]
> > -            else:
> > -                btnlist = [TEXT_YES_BUTTON, TEXT_NO_BUTTON]
> > -            rc = ButtonChoiceWindow(self.screen, title, text, buttons=btnlist)
> > -            if rc == "yes":
> > -                return 1
> > -            else:
> > -                return 0
> > -        elif ty == "custom":
> > -            tmpbut = []
> > -            for but in custom_buttons:
> > -                tmpbut.append(but.replace("_",""))
> > -
> > -            rc = ButtonChoiceWindow(self.screen, title, text, width=60, buttons=tmpbut)
> > -
> > -            idx = 0
> > -            for b in tmpbut:
> > -                if b.lower() == rc:
> > -                    return idx
> > -                idx += 1
> > -            return 0
> > -        else:
> > -            return OkCancelWindow(self.screen, title, text)
> > -
> > -    def passphraseEntryWindow(self, device):
> > -        w = PassphraseEntryWindow(self.screen, device)
> > -        passphrase = w.run()
> > -        w.pop()
> > -        return passphrase
> > -
> > -    @property
> > -    def meh_interface(self):
> > -        return self._meh_interface
> > -
> > -    @property
> > -    def tty_num(self):
> > -        return 1
> > -
> > -    def shutdown (self):
> > -        self.screen.finish()
> > -
> > -    def suspend(self):
> > -        pass
> > +__all__ = ["RescueMode", "RootSpoke", "RescueMountSpoke"]
> >  
> > -    def resume(self):
> > -        pass
> > -
> > -    def __init__(self):
> > -        InstallInterfaceBase.__init__(self)
> > -        self.screen = SnackScreen()
> > -        self._meh_interface = meh.ui.text.TextIntf()
> > -
> > -def makeFStab(instPath = ""):
> > +def makeFStab(instPath=""):
> > +    """Make the fs tab."""
> >      if os.access("/proc/mounts", os.R_OK):
> >          f = open("/proc/mounts", "r")
> >          buf = f.read()
> > @@ -138,8 +60,30 @@ def makeFStab(instPath = ""):
> >      except IOError as e:
> >          log.info("failed to write /etc/fstab: %s", e)
> >  
> > -# make sure they have a resolv.conf in the chroot
> > +def run_shell():
> > +    """Launch a shell."""
> > +    if flags.imageInstall:
> > +        print(_("Run %s to unmount the system when you are finished.")
> > +                % ANACONDA_CLEANUP)
> > +    else:
> > +        print(_("When finished, please exit from the shell and your "
> > +                "system will reboot."))
> > +
> > +    proc = None
> > +    if proc is None or proc.returncode != 0:
> > +        if os.path.exists("/bin/bash"):
> > +            iutil.execConsole()
> > +        else:
> > +            print(_("Unable to find /bin/bash to execute!  Not starting shell."))
> > +            time.sleep(5)
> > +
> > +    if not flags.imageInstall:
> > +        iutil.execWithRedirect("systemctl", ["--no-wall", "reboot"])
> > +    else:
> > +        return None
> > +
> >  def makeResolvConf(instPath):
> > +    """Make the resolv.conf file in the chroot."""
> >      if flags.imageInstall:
> >          return
> >  
> > @@ -171,62 +115,125 @@ def makeResolvConf(instPath):
> >      f.write(buf)
> >      f.close()
> >  
> > -def runShell(screen = None, msg=""):
> > -    if screen:
> > -        screen.suspend()
> > +class RescueMode(NormalTUISpoke):
> > +    title = N_("Rescue")
> >  
> > -    print
> > -    if msg:
> > -        print(msg)
> > +    # If it acts like a spoke and looks like a spoke, is it a spoke? Not
> > +    # always. This is independent of any hub(s), so pass in some fake data
> > +    def __init__(self, app, data, storage=None, payload=None, instclass=None):
> > +        NormalTUISpoke.__init__(self, app, data, storage, payload, instclass)
> > +        if flags.automatedInstall:
> > +            self._ro = data.rescue.romount
> > +        else:
> > +            self._ro = False
> >  
> > -    if flags.imageInstall:
> > -        print(_("Run %s to unmount the system when you are finished.")
> > -              % ANACONDA_CLEANUP)
> > -    else:
> > -        print(_("When finished please exit from the shell and your "
> > -                "system will reboot."))
> > -    print
> > +        self._root = None
> > +        self._choices = (_("Continue"), _("Read-only mount"), _("Skip to shell"), ("Quit (Reboot)"))
> >  
> > -    proc = None
> > +    def initialize(self):
> > +        NormalTUISpoke.initialize(self)
> >  
> > -    if os.path.exists("/usr/bin/firstaidkit-qs"):
> > -        proc = subprocess.Popen(["/usr/bin/firstaidkit-qs"])
> > -        proc.wait()
> > +        for f in ["services", "protocols", "group", "man.config",
> > +                  "nsswitch.conf", "selinux", "mke2fs.conf"]:
> > +            try:
> > +                os.symlink('/mnt/runtime/etc/' + f, '/etc/' + f)
> > +            except OSError:
> > +                pass
> > +
> > +    def prompt(self, args=None):
> > +        """ Override the default TUI prompt."""
> > +        return _("Please make a selection from the above:  ")
> > +
> > +    def refresh(self, args=None):
> > +        NormalTUISpoke.refresh(self, args)
> > +
> > +        self._window += [TextWidget(_("The rescue environment will now attempt "
> > +                         "to find your Linux installation and mount it under "
> > +                         "the directory : %s.  You can then make any changes "
> > +                         "required to your system.  Choose '1' to proceed with "
> > +                         "this step.\nYou can choose to mount your file"
> > +                         "systems read-only instead of read-write by choosing "
> > +                         "'2'.\nIf for some reason this process does not work "
> > +                         "choose '3' to skip directly to a shell.\n\n") % (iutil.getSysroot())), ""]
> > +
> > +        for idx, choice in enumerate(self._choices):
> > +            number = TextWidget("%2d)" % (idx + 1))
> > +            c = ColumnWidget([(3, [number]), (None, [TextWidget(choice)])], 1)
> > +            self._window += [c, ""]
> > +
> > +        return True
> > +
> > +    def input(self, args, key):
> > +        """Override any input so we can launch rescue mode."""
> > +        try:
> > +            keyid = int(key) - 1
> > +        except ValueError:
> > +            pass
> >  
> > -    if proc is None or proc.returncode!=0:
> > -        if os.path.exists("/bin/bash"):
> > -            iutil.execConsole()
> > +        if keyid == 3:
> > +            # quit/reboot
> > +            d = YesNoDialog(self.app, _(self.app.quit_message))
> > +            self.app.switch_screen_modal(d)
> > +            if d.answer:
> > +                iutil.execWithRedirect("systemctl", ["--no-wall", "reboot"])
> Shouldn't this reboot be guarded with 'if flags.imageInstall:' too? 
> 
> -- 
> Vratislav Podzimek
> 
> Anaconda Rider | Red Hat, Inc. | Brno - Czech Republic
> 
> _______________________________________________
> anaconda-patches mailing list
> anaconda-patches at lists.fedorahosted.org
> https://lists.fedorahosted.org/mailman/listinfo/anaconda-patches


More information about the anaconda-patches mailing list