aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorwiktor w brodlo <wiktor@brodlo.net>2011-06-15 16:59:54 +0000
committerwiktor w brodlo <wiktor@brodlo.net>2011-06-15 16:59:54 +0000
commit2590d96369d0217e31dc2812690dde61dac417b5 (patch)
tree82276f787b08a28548e342c7921486f1acefab9f /text.py
parentfirst commit (diff)
downloadanaconda-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.py693
1 files changed, 693 insertions, 0 deletions
diff --git a/text.py b/text.py
new file mode 100644
index 0000000..bab6fad
--- /dev/null
+++ b/text.py
@@ -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()