diff options
author | wiktor w brodlo <wiktor@brodlo.net> | 2011-06-15 16:59:54 +0000 |
---|---|---|
committer | wiktor w brodlo <wiktor@brodlo.net> | 2011-06-15 16:59:54 +0000 |
commit | 2590d96369d0217e31dc2812690dde61dac417b5 (patch) | |
tree | 82276f787b08a28548e342c7921486f1acefab9f /text.py | |
parent | first commit (diff) | |
download | anaconda-2590d96369d0217e31dc2812690dde61dac417b5.tar.gz anaconda-2590d96369d0217e31dc2812690dde61dac417b5.tar.bz2 anaconda-2590d96369d0217e31dc2812690dde61dac417b5.zip |
Initial import from Sabayon (ver 0.9.9.56)
Diffstat (limited to 'text.py')
-rw-r--r-- | text.py | 693 |
1 files changed, 693 insertions, 0 deletions
@@ -0,0 +1,693 @@ +# +# text.py - text mode frontend to anaconda +# +# Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006 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 +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty 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, see <http://www.gnu.org/licenses/>. +# +# Author(s): Erik Troan <ewt@redhat.com> +# Matt Wilson <msw@redhat.com> +# + +from snack import * +import sys +import os +import isys +import iutil +import time +import signal +import parted +import product +import string +from language import expandLangs +from flags import flags +from constants_text import * +from constants import * +from network import hasActiveNetDev +from installinterfacebase import InstallInterfaceBase +import imputil + +import gettext +_ = lambda x: gettext.ldgettext("anaconda", x) +P_ = lambda x, y, z: gettext.ldngettext("anaconda", x, y, z) + +import logging +log = logging.getLogger("anaconda") + +stepToClasses = { + "language" : ("language_text", "LanguageWindow"), + "keyboard" : ("keyboard_text", "KeyboardWindow"), + "welcome" : ("welcome_text", "WelcomeWindow"), + "parttype" : ("partition_text", "PartitionTypeWindow"), + "addswap" : ("upgrade_text", "UpgradeSwapWindow"), + "upgrademigratefs" : ("upgrade_text", "UpgradeMigrateFSWindow"), + "zfcpconfig": ("zfcp_text", ("ZFCPWindow")), + "findinstall" : ("upgrade_text", ("UpgradeExamineWindow")), + "upgbootloader": ("upgrade_bootloader_text", "UpgradeBootloaderWindow"), + "network" : ("network_text", ("HostnameWindow")), + "timezone" : ("timezone_text", "TimezoneWindow"), + "accounts" : ("userauth_text", "RootPasswordWindow"), + "useraccounts" : ("userauth_text", "UserPasswordWindow"), + "tasksel": ("task_text", "TaskWindow"), + "install" : ("progress_text", "setupForInstall"), + "complete" : ("complete_text", "FinishedWindow"), +} + +if iutil.isS390(): + stepToClasses["bootloader"] = ("zipl_text", ( "ZiplWindow")) + +class InstallWindow: + def __call__ (self, screen): + raise RuntimeError, "Unimplemented screen" + +class WaitWindow: + def pop(self): + self.screen.popWindow() + self.screen.refresh() + + def refresh(self): + pass + + def __init__(self, screen, title, text): + self.screen = screen + width = 40 + if (len(text) < width): width = len(text) + + t = TextboxReflowed(width, text) + + g = GridForm(self.screen, title, 1, 1) + g.add(t, 0, 0) + g.draw() + self.screen.refresh() + +class OkCancelWindow: + def getrc(self): + return self.rc + + def __init__(self, screen, title, text): + rc = ButtonChoiceWindow(screen, title, text, + buttons=[TEXT_OK_BUTTON, _("Cancel")]) + if rc == string.lower(_("Cancel")): + self.rc = 1 + else: + self.rc = 0 + +class ProgressWindow: + def pop(self): + self.screen.popWindow() + self.screen.refresh() + del self.scale + self.scale = None + + def pulse(self): + pass + + def set(self, amount): + self.scale.set(int(float(amount) * self.multiplier)) + self.screen.refresh() + + def refresh(self): + pass + + def __init__(self, screen, title, text, total, updpct = 0.05, pulse = False): + self.multiplier = 1 + if total == 1.0: + self.multiplier = 100 + self.screen = screen + width = 55 + if (len(text) > width): width = len(text) + + t = TextboxReflowed(width, text) + + g = GridForm(self.screen, title, 1, 2) + g.add(t, 0, 0, (0, 0, 0, 1), anchorLeft=1) + + self.scale = Scale(int(width), int(float(total) * self.multiplier)) + if not pulse: + g.add(self.scale, 0, 1) + + g.draw() + self.screen.refresh() + +class LuksPassphraseWindow: + def __init__(self, screen, passphrase = "", preexist = False): + self.screen = screen + self.passphrase = passphrase + self.minLength = 8 + self.preexist = preexist + self.txt = _("Choose a passphrase for the encrypted devices. You " + "will be prompted for this passphrase during system boot.") + self.rc = None + + def run(self): + toplevel = GridForm(self.screen, _("Passphrase for encrypted device"), + 1, 5) + + txt = TextboxReflowed(65, self.txt) + toplevel.add(txt, 0, 0) + + passphraseentry = Entry(60, password = 1) + toplevel.add(passphraseentry, 0, 1, (0,0,0,1)) + + confirmentry = Entry(60, password = 1) + toplevel.add(confirmentry, 0, 2, (0,0,0,1)) + + if self.preexist: + globalcheckbox = Checkbox(_("Also add this passphrase to all existing encrypted devices"), isOn = True) + toplevel.add(globalcheckbox, 0, 3) + + buttons = ButtonBar(self.screen, [TEXT_OK_BUTTON, TEXT_CANCEL_BUTTON]) + toplevel.add(buttons, 0, 4, growx=1) + + passphraseentry.set(self.passphrase) + confirmentry.set(self.passphrase) + + while True: + rc = toplevel.run() + res = buttons.buttonPressed(rc) + + passphrase = None + if res == TEXT_OK_CHECK or rc == "F12": + passphrase = passphraseentry.value() + confirm = confirmentry.value() + + if passphrase != confirm: + ButtonChoiceWindow(self.screen, + _("Error with passphrase"), + _("The passphrases you entered were " + "different. Please try again."), + buttons=[TEXT_OK_BUTTON]) + passphraseentry.set("") + confirmentry.set("") + continue + + if len(passphrase) < self.minLength: + ButtonChoiceWindow(self.screen, + _("Error with passphrase"), + P_("The passphrase must be at least " + "%d character long.", + "The passphrase must be at least " + "%d characters long.", + self.minLength) + % (self.minLength,), + buttons=[TEXT_OK_BUTTON]) + passphraseentry.set("") + confirmentry.set("") + continue + else: + passphrase = self.passphrase + passphraseentry.set(self.passphrase) + confirmentry.set(self.passphrase) + + retrofit = False + if self.preexist: + retrofit = globalcheckbox.selected() + self.rc = passphrase + return (self.rc, retrofit) + + def pop(self): + self.screen.popWindow() + +class PassphraseEntryWindow: + def __init__(self, screen, device): + self.screen = screen + self.txt = _("Device %s is encrypted. In order to " + "access the device's contents during " + "installation you must enter the device's " + "passphrase below.") % (device,) + self.rc = None + + def run(self): + toplevel = GridForm(self.screen, _("Passphrase"), 1, 4) + + txt = TextboxReflowed(65, self.txt) + toplevel.add(txt, 0, 0) + + passphraseentry = Entry(60, password = 1) + toplevel.add(passphraseentry, 0, 1, (0,0,0,1)) + + globalcheckbox = Checkbox(_("This is a global passphrase")) + toplevel.add(globalcheckbox, 0, 2) + + buttons = ButtonBar(self.screen, [TEXT_OK_BUTTON, TEXT_CANCEL_BUTTON]) + toplevel.add(buttons, 0, 3, growx=1) + + rc = toplevel.run() + res = buttons.buttonPressed(rc) + + passphrase = None + isglobal = False + if res == TEXT_OK_CHECK: + passphrase = passphraseentry.value().strip() + isglobal = globalcheckbox.selected() + + self.rc = (passphrase, isglobal) + return self.rc + + def pop(self): + self.screen.popWindow() + +class InstallInterface(InstallInterfaceBase): + def progressWindow(self, title, text, total, updpct = 0.05, pulse = False): + return ProgressWindow(self.screen, title, text, total, updpct, pulse) + + def setInstallProgressClass(self, c): + self.instProgress = c + + def exitWindow(self, title, text): + return self.messageWindow(title, text, type="custom", + custom_buttons=[_("Exit installer")]) + + def messageWindow(self, title, text, type="ok", default = None, + custom_icon=None, custom_buttons=[]): + if type == "ok": + ButtonChoiceWindow(self.screen, title, text, + buttons=[TEXT_OK_BUTTON]) + elif type == "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 type == "custom": + tmpbut = [] + for but in custom_buttons: + tmpbut.append(string.replace(but,"_","")) + + rc = ButtonChoiceWindow(self.screen, title, text, width=60, + buttons=tmpbut) + + idx = 0 + for b in tmpbut: + if string.lower(b) == rc: + return idx + idx = idx + 1 + return 0 + else: + return OkCancelWindow(self.screen, title, text) + + def detailedMessageWindow(self, title, text, longText=None, type="ok", + default=None, custom_icon=None, + custom_buttons=[]): + t = TextboxReflowed(60, text, maxHeight=8) + lt = Textbox(60, 6, longText, scroll=1, wrap=1) + g = GridFormHelp(self.screen, title, help, 1, 3) + g.add(t, 0, 0) + g.add(lt, 0, 1, padding = (0, 1, 0, 1)) + + if type == "ok": + bb = ButtonBar(self.screen, [TEXT_OK_BUTTON]) + g.add(bb, 0, 2, growx = 1) + return bb.buttonPressed(g.runOnce(None, None)) + elif type == "yesno": + if default and default == "no": + buttons = [TEXT_NO_BUTTON, TEXT_YES_BUTTON] + else: + buttons = [TEXT_YES_BUTTON, TEXT_NO_BUTTON] + + bb = ButtonBar(self.screen, buttons) + g.add(bb, 0, 2, growx = 1) + rc = bb.buttonPressed(g.runOnce(None, None)) + + if rc == "yes": + return 1 + else: + return 0 + elif type == "custom": + buttons = [] + idx = 0 + + for button in custom_buttons: + buttons.append(string.replace(button, "_", "")) + + bb = ButtonBar(self.screen, buttons) + g.add(bb, 0, 2, growx = 1) + rc = bb.buttonPressed(g.runOnce(None, None)) + + for b in buttons: + if string.lower(b) == rc: + return idx + idx += 1 + + return 0 + else: + return self.messageWindow(title, text, type, default, custom_icon, + custom_buttons) + + def createRepoWindow(self): + self.messageWindow(_("Error"), + _("Repository editing is not available in text mode.")) + + def editRepoWindow(self, repoObj): + self.messageWindow(_("Error"), + _("Repository editing is not available in text mode.")) + + def entryWindow(self, title, text, prompt, entrylength = None): + (res, value) = EntryWindow(self.screen, title, text, [prompt]) + if res == "cancel": + return None + r = value[0] + r.strip() + return r + + def getLuksPassphrase(self, passphrase = "", preexist = False): + w = LuksPassphraseWindow(self.screen, passphrase = passphrase, + preexist = preexist) + rc = w.run() + w.pop() + return rc + + def passphraseEntryWindow(self, device): + w = PassphraseEntryWindow(self.screen, device) + (passphrase, isglobal) = w.run() + w.pop() + return (passphrase, isglobal) + + def enableNetwork(self): + if len(self.anaconda.network.netdevices) == 0: + return False + from netconfig_text import NetworkConfiguratorText + w = NetworkConfiguratorText(self.screen, self.anaconda) + ret = w.run() + return ret != INSTALL_BACK + + def kickstartErrorWindow(self, text): + s = _("The following error was found while parsing the " + "kickstart configuration file:\n\n%s") %(text,) + self.messageWindow(_("Error Parsing Kickstart Config"), + s, + type = "custom", + custom_buttons = [("_Reboot")], + custom_icon="error") + + def mainExceptionWindow(self, shortText, longTextFile): + from meh.ui.text import MainExceptionWindow + log.critical(shortText) + exnWin = MainExceptionWindow(shortText, longTextFile, screen=self.screen) + return exnWin + + def saveExceptionWindow(self, accountManager, signature, *args, **kwargs): + from meh.ui.text import SaveExceptionWindow + win = SaveExceptionWindow (accountManager, signature, screen=self.screen, + *args, **kwargs) + win.run() + + def waitWindow(self, title, text): + return WaitWindow(self.screen, title, text) + + def beep(self): + # no-op. could call newtBell() if it was bound + pass + + def drawFrame(self): + self.screen.drawRootText (0, 0, self.screen.width * " ") + if productArch: + self.screen.drawRootText (0, 0, _("Welcome to %(productName)s for %(productArch)s") % {'productName': productName, 'productArch': productArch}) + else: + self.screen.drawRootText (0, 0, _("Welcome to %s") % productName) + + self.screen.pushHelpLine(_(" <Tab>/<Alt-Tab> between elements | <Space> selects | <F12> next screen")) + + def setScreen(self, screen): + self.screen = screen + + def shutdown(self): + self.screen.finish() + self.screen = None + + def suspend(self): + self.screen.suspend() + + def resume(self): + self.screen.resume() + + def __init__(self): + InstallInterfaceBase.__init__(self) + signal.signal(signal.SIGINT, signal.SIG_IGN) + signal.signal(signal.SIGTSTP, signal.SIG_IGN) + self.screen = SnackScreen() + self.instProgress = None + self._initLabelAnswers = {} + self._inconsistentLVMAnswers = {} + + def __del__(self): + if self.screen: + self.screen.finish() + + def isRealConsole(self): + """Returns True if this is a _real_ console that can do things, False + for non-real consoles such as serial, i/p virtual consoles or xen.""" + if flags.serial or flags.virtpconsole: + return False + if isys.isPseudoTTY(0): + return False + if isys.isVioConsole(): + return False + return True + + def resetInitializeDiskQuestion(self): + self._initLabelAnswers = {} + + def questionInitializeDisk(self, path, description, size, details=""): + + retVal = False # The less destructive default + + if not path: + return retVal + + # we are caching answers so that we don't + # ask in each storage.reset() again + if path in self._initLabelAnswers: + log.info("UI not asking about disk initialization, " + "using cached answer: %s" % self._initLabelAnswers[path]) + return self._initLabelAnswers[path] + elif "all" in self._initLabelAnswers: + log.info("UI not asking about disk initialization, " + "using cached answer: %s" % self._initLabelAnswers["all"]) + return self._initLabelAnswers["all"] + + rc = self.messageWindow(_("Warning"), + _("Error processing drive:\n\n" + "%(path)s\n%(size)-0.fMB\n%(description)s\n\n" + "This device may need to be reinitialized.\n\n" + "REINITIALIZING WILL CAUSE ALL DATA TO BE LOST!\n\n" + "This action may also be applied to all other disks " + "needing reinitialization.%(details)s") + % {'path': path, 'size': size, + 'description': description, 'details': details}, + type="custom", + custom_buttons = [ _("_Ignore"), + _("Ignore _all"), + _("_Re-initialize"), + _("Re-ini_tialize all") ], + custom_icon="question") + if rc == 0: + retVal = False + elif rc == 1: + path = "all" + retVal = False + elif rc == 2: + retVal = True + elif rc == 3: + path = "all" + retVal = True + + self._initLabelAnswers[path] = retVal + return retVal + + def resetReinitInconsistentLVMQuestion(self): + self._inconsistentLVMAnswers = {} + + def questionReinitInconsistentLVM(self, pv_names=None, lv_name=None, vg_name=None): + + retVal = False # The less destructive default + allSet = frozenset(["all"]) + + if not pv_names or (lv_name is None and vg_name is None): + return retVal + + # We are caching answers so that we don't ask for ignoring + # in each storage.reset() again (note that reinitialization is + # done right after confirmation in dialog, not as a planned + # action). + key = frozenset(pv_names) + if key in self._inconsistentLVMAnswers: + log.info("UI not asking about disk initialization, " + "using cached answer: %s" % self._inconsistentLVMAnswers[key]) + return self._inconsistentLVMAnswers[key] + elif allSet in self._inconsistentLVMAnswers: + log.info("UI not asking about disk initialization, " + "using cached answer: %s" % self._inconsistentLVMAnswers[allSet]) + return self._inconsistentLVMAnswers[allSet] + + if vg_name is not None: + message = "Volume Group %s" % vg_name + elif lv_name is not None: + message = "Logical Volume %s" % lv_name + + na = {'msg': message, 'pvs': ", ".join(pv_names)} + rc = self.messageWindow(_("Warning"), + _("Error processing LVM.\n" + "There is inconsistent LVM data on %(msg)s. You can " + "reinitialize all related PVs (%(pvs)s) which will erase " + "the LVM metadata, or ignore which will preserve the " + "contents. This action may also be applied to all other " + "PVs with inconsistent metadata.") % na, + type="custom", + custom_buttons = [ _("_Ignore"), + _("Ignore _all"), + _("_Re-initialize"), + _("Re-ini_tialize all") ], + custom_icon="question") + if rc == 0: + retVal = False + elif rc == 1: + key = allSet + retVal = False + elif rc == 2: + retVal = True + elif rc == 3: + key = allSet + retval = True + + self._inconsistentLVMAnswers[key] = retVal + return retVal + + def run(self, anaconda): + self.anaconda = anaconda + instLang = anaconda.instLanguage + + if instLang.getFontFile(instLang.instLang) == "none": + if not anaconda.ksdata: + ButtonChoiceWindow(self.screen, "Language Unavailable", + "%s display is unavailable in text mode. " + "The installation will continue in " + "English." % (instLang.instLang,), + buttons=[TEXT_OK_BUTTON]) + + if not self.isRealConsole(): + self.screen.suspendCallback(spawnShell, self.screen) + + # drop into the python debugger on ctrl-z if we're running in test mode + if flags.debug: + self.screen.suspendCallback(debugSelf, self.screen) + + # draw the frame after setting up the fallback + self.drawFrame() + + lastrc = INSTALL_OK + (step, instance) = anaconda.dispatch.currentStep() + while step: + (file, classNames) = stepToClasses[step] + + if type(classNames) != type(()): + classNames = (classNames,) + + if lastrc == INSTALL_OK: + step = 0 + else: + step = len(classNames) - 1 + + while step >= 0 and step < len(classNames): + # reget the args. they could change (especially direction) + (foo, args) = anaconda.dispatch.currentStep() + nextWindow = None + + while 1: + try: + found = imputil.imp.find_module(file) + loaded = imputil.imp.load_module(classNames[step], + found[0], found[1], + found[2]) + nextWindow = loaded.__dict__[classNames[step]] + break + except ImportError, e: + rc = ButtonChoiceWindow(self.screen, _("Error!"), + _("An error occurred when attempting " + "to load an installer interface " + "component.\n\nclassName = %s") + % (classNames[step],), + buttons=[_("Exit"), _("Retry")]) + + if rc == string.lower(_("Exit")): + sys.exit(0) + + win = nextWindow() + + #log.info("TUI running step %s (class %s, file %s)" % + #(step, file, classNames)) + + rc = win(self.screen, instance) + + if rc == INSTALL_NOOP: + rc = lastrc + + if rc == INSTALL_BACK: + step = step - 1 + anaconda.dispatch.dir = DISPATCH_BACK + elif rc == INSTALL_OK: + step = step + 1 + anaconda.dispatch.dir = DISPATCH_FORWARD + + lastrc = rc + + if step == -1: + if not anaconda.dispatch.canGoBack(): + ButtonChoiceWindow(self.screen, _("Cancelled"), + _("I can't go to the previous step " + "from here. You will have to try " + "again."), + buttons=[_("OK")]) + anaconda.dispatch.gotoPrev() + else: + anaconda.dispatch.gotoNext() + + (step, args) = anaconda.dispatch.currentStep() + + self.screen.finish() + + def setSteps(self, anaconda): + anaconda.dispatch.skipStep("filtertype", permanent=1) + anaconda.dispatch.skipStep("filter", permanent=1) + anaconda.dispatch.skipStep("cleardiskssel", permanent=1) + anaconda.dispatch.skipStep("group-selection", permanent=1) + +def killSelf(screen): + screen.finish() + os._exit(0) + +def debugSelf(screen): + screen.suspend() + import pdb + try: + pdb.set_trace() + except: + sys.exit(-1) + screen.resume() + +def spawnShell(screen): + screen.suspend() + print("\n\nType <exit> to return to the install program.\n") + if os.path.exists("/bin/sh"): + iutil.execConsole() + else: + print("Unable to find /bin/sh to execute! Not starting shell") + time.sleep(5) + screen.resume() |